mistral-chat-app/AGENTS.md

497 lines
No EOL
26 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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*