# Mistral Chat App - Development Context ## Project Overview Android-приложение для чата с Mistral AI. Перспективный проект с развитием в сторону AI-агента с памятью, tools и автономной работой. **Основные технологии:** - Kotlin + Android (minSdk 26, targetSdk 34) - Room + SQLCipher (encrypted database) - OkHttp для API - Material Design 3 - Russian language UI **Расположение проекта:** ``` /Users/alexabudaev/Documents/Zed/mistral-chat-app/ ``` **⚠️ ВАЖНО: Принципы разработки** - Приложение должно работать БЕЗ платных подписок и инвестиций - Всегда использовать собственные разработки или бесплатные решения - Не рассчитывать на имеющиеся платные API при планировании функций - Сначала находим бесплатное решение, потом реализуем - **Если что-то невозможно сделать без платных API или ты не можешь понять задачу - говори честно!** - При планировании любого следующего этапа следует тщательно анализировать возможность реализовать ту или иную функцию не только исходя из программной совместимости, но и с учётом наличия бесплатных версий необходимых сервисов, API и других продуктов (или возможности написать собственное решение) --- ## Completed Work ### ✅ Core Features - Чат с Mistral API (Chat Completion) - Управление профилями (до 10 профилей) - Управление сессиями (множественные чаты) - **Генерация названия сессии** - после 2-го сообщения AI генерирует краткое название (3-5 слов) - Шифрованное хранилище (SQLCipher + EncryptedSharedPreferences) - Валидация API ключа (32+ символов, A-Z, a-z, 0-9) - Левое drawer-меню с диалогами - Тёмная/светлая тема ### ✅ UI/UX - Material Design 3 - Русский язык интерфейса - Отступы в поле ввода (12dp) - Прокрутка к новым сообщениям - **Долгий тап на сообщение** - меню Копировать/Редактировать/Удалить ### ✅ Security - API ключ: EncryptedSharedPreferences (AES-256-GCM) - Ключ БД: EncryptedSharedPreferences (AES-256-SIV + AES-256-GCM) - Профили, сессии, сообщения: SQLCipher --- ## Current Issues & Architecture ### ⚠️ Важное: Назначение web_search web_search НЕ является интерфейсом поисковика или Wikipedia. Это инструмент для AI-агента: **Правильная логика работы (Kai-style):** ``` 1. AI получает вопрос пользователя 2. AI решает что нужен поиск → вызывает web_search 3. Выполняются ВСЕ tool_calls параллельно 4. Результаты НЕ показываются пользователю - только отправляются AI 5. AI интерпретирует результаты → выдаёт ОДИН финальный ответ ``` **Проблемы с текущей реализацией:** - ❌ Показываем промежуточные ответы пользователю (каждый tool result = сообщение) - ❌ AI получает результаты и отвечает после КАЖДОГО tool_calls - ❌ AI выводит куски данных вместо интерпретации **Требуется исправление:** - ✅ Выполнить ВСЕ tool_calls за один проход (уже делаем) - ✅ Результаты НЕ показывать пользователю (только AI видит) - ✅ AI интерпретирует и выдаёт ОДИН ответ ### 🔍 Web Search (Текущая реализация - БЕСПЛАТНОЕ решение) **Используется:** Russian Wikipedia API (бесплатно, без API ключа) - **API:** `https://ru.wikipedia.org/w/api.php` - **Метод:** `query/list/search` - поиск статей по заголовкам - **Ограничение результатов:** до 10 статей (параметр `num_results`) - **Ограничение символов:** 4000 символов на ответ - **ПРИМЕЧАНИЕ:** Это временное решение! Позже можно добавить платный API для полноценного поиска (новости, погода, актуальная информация) **Логика работы:** 1. AI вызывает `web_search` с текстовым запросом 2. Выполняется поиск по Wikipedia API 3. Результаты (заголовки + сниппеты) обрезаются до 4000 символов 4. Результаты отправляются AI для интерпретации 5. AI выдаёт ОДИН финальный ответ пользователю **Tool Loop (MainActivity):** - Максимум итераций: 15 - Timeout на итерацию: 60 секунд - Retry (до 2 попыток) при ошибке "stream was reset: CANCEL" - AI может сделать несколько последовательных поисков если нужно ### 🌤️ Weather Tool (БЕСПЛАТНОЕ решение) **Используется:** Open-Meteo API (полностью бесплатно, без API ключа) - **Geocoding API:** `https://geocoding-api.open-meteo.com/v1/search` - определение координат города - **Weather API:** `https://api.open-meteo.com/v1/forecast` - текущая погода + прогноз на 7 дней - **Параметры:** температура, ветер, погодные коды, осадки **Логика работы:** 1. AI вызывает `get_weather` с названием города 2. Определяются координаты через Geocoding API 3. Запрашивается погода по координатам 4. Возвращается текущая погода + прогноз на 7 дней ### 🔗 OpenUrlTool (Часть Phase 3) **Статус:** ✅ Реализовано | **Оценка:** 1 день **Назначение:** Позволяет AI парсить любую веб-страницу по URL. **Гибридная схема (AI сам решает откуда взять URL):** 1. **RSS-ленты новостей** (рекомендуется): - lenta.ru → https://lenta.ru/rss/ - kommersant.ru → https://www.kommersant.ru/rss/news.xml 2. **Из памяти** - AI помнит рабочие URL 3. **Через web_search** - находит URL в интернете 4. **От пользователя** - пользователь может передать URL **Логика работы с новостями:** 1. Получив запрос о новостях → сначала проверь память пользователя (предпочтения по темам) 2. Открой RSS-ленту через open_url (самый эффективный способ) 3. Составь сводку с учётом интересов пользователя 4. Если источники недоступны → используй web_search 5. Проверь память приложения 6. Выдай ответ на основе всех доступных источников **Реализация:** - HTTP GET запрос к любому URL - Возврат ТОЛЬКО текста (удаляются HTML теги) - Ограничение: 4000 символов - Таймаут: 10 секунд - Блокировка опасных URL (javascript:, file:, data:) --- ### 📚 Изучено из Kai (open-source AI assistant) Kai имеет отличную документацию по tools: https://kai9000.com/docs/features/tools/ **Ключевые решения из Kai:** 1. **Execution Flow:** - Все tool calls выполняются параллельно (coroutine async/await) - TOOL_EXECUTING показывается в UI как "пульсирующий индикатор" - Результаты НЕ показываются пользователю - только отправляются AI - AI может вызвать еще tool calls → цикл повторяется - Когда AI отвечает без tool_calls → финальный текст показан пользователю 2. **Safety Guards (важно!):** - Iteration limit: максимум 15 итераций - Repeated call detection: если одинаковый tool с одинаковыми аргументами вызывается 3 раза подряд → остановка - Timeout: 30 секунд по умолчанию - Result truncation: результаты > 8000 символов обрезаются - Context trimming: между итерациями обрезается история сообщений 3. **Web Search в Kai:** - Есть встроенный web_search tool - Работает (вероятно использует платный API или свой парсинг) --- ## Active Plan (Phases 1-3) ### Phase 1: Расширенные профили (Extended Profiles) **Статус:** ✅ Завершена | **Оценка:** 1-2 дня Добавлено поле `systemPrompt` в профиль для отправки как role: "system". | Задача | Статус | |--------|--------| | Profile entity | ✅ Добавлено поле systemPrompt | | Profile dialog UI | ✅ Добавлен EditText с maxLength=4000 | | ProfileDao | ✅ CRUD работает | | MainActivity | ✅ Инжектирует systemPrompt как role: "system" | | MistralClient | ✅ Использует msg.role | --- ### Phase 2: Система памяти (Memory System) **Статус:** ✅ Завершена | **Оценка:** 2-3 дня Система запоминания информации с категориями и hitCount. | Задача | Статус | |--------|--------| | Memory entity | ✅ key, value, category, hitCount, timestamps | | MemoryDao | ✅ CRUD + getByCategory, incrementHitCount, getPromotionCandidates | | ChatDatabase | ✅ Добавлен MemoryDao, version=2 | | MemoryRepository | ✅ buildMemoryContext() для инжекции в prompt | **Memory categories:** - GENERAL — общие факты - LEARNING — выводы и паттерны - ERROR — известные ошибки - PREFERENCE — предпочтения пользователя - REMINDER_CAL — напоминания календаря (local mode) - CALENDAR_ERROR — ошибки подключения CalDAV **Prompt injection:** ``` === Важная информация === [Факты] - ключ: значение [Выводы] - ключ: значение (N использований) [Предпочтения пользователя] - ключ: значение ``` --- ### Phase 3: Tools / Tool Execution **Статус:** ✅ Завершена (тестирование) | **Оценка:** 3-4 дня Инструменты для AI (function calling) для выполнения действий. | Задача | Статус | |--------|--------| | Tool abstract class | ✅ name, description, inputSchema, executor | | GetTimeTool | ✅ get_local_time с timezone | | GetDateTool | ✅ get_date с timezone | | WebSearchTool | ✅ Протестировано (только Wikipedia) | | GetWeatherTool | ✅ Протестировано (Open-Meteo API) | | NotificationTool | ✅ send_notification | | MemoryStoreTool | ✅ Протестировано | | MemoryLearnTool | ✅ Протестировано | | MemoryForgetTool | ✅ Протестировано | | MemoryReinforceTool | ✅ Протестировано | | MemoryPreferenceTool | ✅ Протестировано | | ToolExecutor | ✅ управление всеми tools, updateSettings() | | MistralClient | ✅ tools в chat completion, обработка tool_calls | | Safety | ✅ Max iterations (15), timeout (30s), result truncation (2000 chars) | | **OpenUrlTool (RSS)** | ✅ Автоматическое определение и парсинг RSS/Atom | **CalDAV Calendar + Local Reminders (Phase 3 extension):** | Задача | Статус | |--------|--------| | iCalDAV зависимость (Apache 2.0) | ⏳ | | CalDavRepository (CRUD) | ⏳ | | UI: drawer_menu → диалог настроек CalDAV | ⏳ | | Настройка синхронизации (15м-сутки) | ⏳ | | caldav_get_events, create, update, delete | ⏳ | | Memory category REMINDER_CAL | ⏳ | | calendar_add_reminder tool | ⏳ | | calendar_get_reminders tool | ⏳ | | Напоминания (любой период: 5мин, 13мин, 2ч, 24ч) | ⏳ | | Счётчик ошибок CalDAV → memory | ⏳ | | UnifiedPush (опционально, на потом) | ⏳ | **Memory REMINDER_CAL fields:** - key: название напоминания - value: описание - triggerTime: unix timestamp когда напомнить - status: pending / triggered / expired **Trigger logic:** AI проверяет pending напоминания при каждом запросе **RSS-ленты (протестировано):** - lenta.ru/rss/ ✅ - kommersant.ru/rss/news.xml ✅ **Тестирование Phase 3:** - ✅ web_search (Wikipedia) - работает - ✅ get_weather (Open-Meteo) - работает - ✅ Memory tools - работает, изолирована по профилям (протестировано) **Location Settings (в рамках Phase 3):** | Задача | Статус | |--------|--------| | Preferences keys | ✅ KEY_DEFAULT_TIMEZONE, KEY_DEFAULT_CITY | | dialog_location.xml | ✅ UI для ввода timezone/city | | showLocationDialog() | ✅ Реализована в MainActivity | | drawer_menu.xml | ✅ Добавлен item action_location | | ic_location.xml | ✅ Создан vector drawable | | ToolExecutor.updateSettings() | ✅ Принимает timezone/city при сохранении | **Defaults:** - Timezone: Asia/Irkutsk - City: Иркутск --- ## Active Plan (Phases 1-5) ### Phase 3 (Active): CalDAV Calendar + Local Reminders **Статус:** 🔄 В разработке | **Оценка:** 4-5 дней **Два режима:** 1. CalDAV — синхронизация с Baikal сервером 2. Local — автономная напоминалка в памяти AI (работает БЕЗ интернета) **Подробнее:** см. таблицу в разделе Phase 3 выше --- ### Phase 4: Heartbeat (Scheduled) **Оценка:** 2-3 дня Автономная периодическая самопроверка: - WorkManager задача (каждые 30 минут) - Active hours (8:00-22:00) - Обработка ответа (молча vs уведомление) ### Phase 5: Email (IMAP/SMTP) **Оценка:** 4-5 дней Интеграция с email без OAuth: - IMAP клиент (чтение писем) - SMTP клиент (отправка) - UI настройки ящика (сервер, порт, логин, пароль) - Email tools для AI --- ## Technical Context ### ⚠️ ВАЖНО: Сборка APK после каждого изменения **После каждого исправления или добавления функций НЕОБХОДИМО собирать APK!** Пользователь должен иметь возможность сразу протестировать изменения. ```bash # Сборка APK JAVA_HOME=/opt/homebrew/opt/openjdk@17 ./gradlew assembleDebug # Путь к APK app/build/outputs/apk/debug/app-debug.apk ``` ### Key Files - `app/src/main/java/com/mistral/chat/ui/MainActivity.kt` — главная активность - `app/src/main/java/com/mistral/chat/api/MistralClient.kt` — API клиент - `app/src/main/java/com/mistral/chat/api/ToolExecutor.kt` — менеджер tools - `app/src/main/java/com/mistral/chat/data/ChatDatabase.kt` — база данных - `app/src/main/java/com/mistral/chat/data/Profile.kt` — профиль - `app/src/main/java/com/mistral/chat/data/Memory.kt` — память - `app/src/main/res/layout/dialog_location.xml` — настройки местоположения ### Current Issues - Кнопка STOP не работает (требует streaming mode) ### Model Selection - **Default:** mistral-medium-latest (быстрее, меньше ошибок) - **Доступные модели:** Large, Medium, Codestral, Pixtral - **OkHttp timeouts:** connect 60s, read 120s, write 60s ### Error Handling (Исправлено) - Ошибки tool execution (таймауты, network errors) НЕ сохраняются в БД - Показываются пользователю через Toast - Предотвращает "отравление" контекста сообщениями об ошибках --- ## ⚠️ ВАЖНЫЕ ПРАВИЛА РАЗРАБОТКИ ### Запрет на удаление реализованных функций **НИКОГДА не удаляй уже реализованные функции!** Даже если они кажутся неидеальными: - Если нужно изменить поведение - исправь, а не удаляй - Если что-то сломалось - почини, а не упрощай удалением - При удалении функций (даже "неиспользуемых") всегда согласовывай с пользователем ### Запрет на хардкодинг переменных **НИКОГДА не хардкодь значения, которые должны быть динамическими!** - Даты, года, время,地名, названия - всё должно подставляться из системы/контекста - Если что-то не получается реализовать без хардкода - ОБСУДИ с пользователем перед реализацией - Пример правильного подхода: `{CURRENT_YEAR}` → подставляется через `SimpleDateFormat` ### Сборка APK после каждого изменения **После каждого исправления или добавления функций ОБЯЗАТЕЛЬНО собирай APK!** - Пользователь должен иметь возможность сразу протестировать изменения - Команда: `JAVA_HOME=/opt/homebrew/opt/openjdk@17 ./gradlew assembleDebug` - Расположение: `app/build/outputs/apk/debug/app-debug.apk` ### Дублирование сообщений при переключении сессий (BUG FIX) **Проблема:** При переходе из второй сессии в первую (или любую другую) сообщения дублировались. **Причина:** Асинхронная загрузка сообщений без проверки актуальности sessionId. **Решение в MainActivity.kt:** 1. Очищаем список СРАЗУ при переключении (до асинхронной загрузки) 2. Используем `loadMessagesJob` для отмены предыдущей загрузки сообщений 3. Проверяем sessionId внутри async загрузки (несколько раз) 4. Передаём `expectedSessionId` в `addMessage` для правильного сохранения в БД 5. Прокрутка к последнему сообщению после загрузки ### ⚠️ ВАЖНО: Логика прокрутки чата **Правильная реализация:** 1. **К концу сообщения пользователя** - прокрутка к концу (scrollToPosition) через 100мс после добавления 2. **К началу ответа ИИ** - прокрутка к НАЧАЛУ (scrollToPositionWithOffset) через 150мс после добавления сообщения ИИ **Техническая реализация:** - В `addMessage()`: для сообщений ИИ (`!message.isUser`) - прокрутка к началу через 150мс - Используй `layoutManager.scrollToPositionWithOffset(position, 0)` для прокрутки к началу элемента - Используй `scrollToPosition(position)` для прокрутки к концу элемента - Проверяй `!userScrolledAfterSend` перед прокруткой к ответу ИИ ### Удаление debug логирования После отладки и подтверждения что баг исправлен - удали все `android.util.Log.d("DEBUG", ...)` из кода. ### Порядок действий при работе с багом 1. Проанализируй код и найди причину 2. Исправь проблему, а не симптомы 3. Не удаляй существующий функционал 4. Проверь что исправление не ломает другие сценарии 5. Документируй исправление в agents.md --- ## Выводы и предмет для обсуждения ### WebSearchTool - **Wikipedia API** - работает, но содержит только энциклопедические статьи (нет погоды, новостей) - **DuckDuckGo Instant Answer API** - возвращает 0 результатов для большинства запросов (ограничение бесплатного API) - **Вывод:** Текущая реализация web_search не может полноценно заменить поисковик ### OpenUrlTool (предложено, отложено) - AI не знает все URL наизусть - нужен либо справочник в system prompt, либо web_search для нахождения URL - При гибридном подходе: web_search находит URL → open_url парсит страницу - Проблема: в system prompt не влезет список URL для всех типичных запросов (погода, новости, курсы валют и т.д.) - **Вывод:** Реализация отложена до починки web_search ### Tool Execution Loop - Предыдущая реализация: 1 итерация → результаты → финальный запрос без tools - **Проблема:** Не даёт AI сделать несколько последовательных поисков (web_search → получить URL → open_url) - Новая реализация: до 15 итераций, как в Kai - AI сам решает сколько поисков нужно - Лимит iteration: 15 - Timeout на итерацию: 30 сек - Если API Mistral не выдержит - снизим до 10 или 5 --- ## 📋 Контекст сессии и оптимизация (В ОБСУЖДЕНИИ) ### Текущая реализация (без оптимизации) При каждом запросе отправляется полный контекст: 1. System prompt (профиль) 2. Текущая дата и время 3. Часовой пояс + город 4. Контекст профиля (имя, о себе) 5. Контекст памяти (факты, выводы, предпочтения) 6. **ВСЕ сообщения сессии** 7. Результаты tool calls (полностью, до 2000 символов каждый) **Проблемы:** - При 2-3 tool calls (RSS + статья) добавляется 4000-6000 символов в контекст - При росте сессии (100+ сообщений) запрос станет слишком большим - 503 ошибки чаще происходят при больших запросах - Превышение лимита токенов контекста ### Варианты решения **1. Trimming (простое)** - Оставлять только последние N сообщений + память + system prompt - Просто реализовать, но теряется история **2. Свёртывание tool results** - Не добавлять полный результат open_url в историю - Добавлять краткую выжимку: "Найдено 5 новостей о [тема]" - Сложнее реализовать, сохраняет суть **3. Контекстное окно (гибкое)** - Оставлять последние N сообщений + summary предыдущих - ИИ сам решает что важно - Сложная реализация **Статус:** Не решено, требует обсуждения с пользователем --- ## Conversation Context (for AI Agent) **При начале новой сессии:** Прочитай файл AGENTS.md для понимания текущего контекста разработки. **При запросе "продолжаем":** Мы работаем над Phase 3 (Tools). Последняя завершённая задача — добавление настроек location (timezone/city) в drawer menu. **Важно:** - Пушить в GitHub только после тестирования и подтверждения пользователя - Не делать push автоматически после каждого изменения --- *Last updated: 2026-04-10* *Version: 1.10*