Реализация контекстного окна и управления историей диалога AI в мобильном приложении
Контекст — это то, что превращает набор разрозненных вопросов в связный диалог. Отправить всю историю переписки в каждый запрос — самое наивное решение. Работает до первого переполнения окна или жалобы на счёт за API. Управление историей — отдельная инженерная задача, которую нужно проектировать заранее.
Как растёт контекст и почему это проблема
Каждый обмен добавляет токены: запрос пользователя + ответ модели. При среднем сообщении 50–100 токенов и 20 парах — уже 2000–4000 токенов только на историю, плюс системный промпт. При gpt-4o с ценой $5 за 1M input токенов — мелочь. При 1000 активных пользователей с 50 сообщениями в день — $250/день только на историю, которая могла бы быть компактнее.
Вторая проблема: у разных моделей разные лимиты. GPT-4o — 128K, Claude — 200K, YandexGPT — 8K. Приложение, которое нормально работало с GPT-4o, ломается при переключении на другую модель.
Три стратегии управления историей
1. Скользящее окно (Sliding Window)
Простейший подход: держим последние N сообщений, отбрасываем более ранние. Быстро, предсказуемо. Минус: модель «забывает» начало разговора — имя пользователя, договорённости из первых сообщений.
func buildMessages(history: [Message], systemPrompt: String, maxTokens: Int = 3000) -> [Message] {
var result: [Message] = []
var tokenCount = countTokens(systemPrompt)
// Идём с конца истории
for message in history.reversed() {
let msgTokens = countTokens(message.content)
if tokenCount + msgTokens > maxTokens { break }
result.insert(message, at: 0)
tokenCount += msgTokens
}
return result
}
2. Суммаризация (Summarization)
Когда история превышает порог — отправляем накопленные сообщения на суммаризацию через более дешёвую модель (gpt-4o-mini, claude-haiku, mistral-small). Получаем summary, сохраняем как system-сообщение или специальный assistant-блок, удаляем суммированные сообщения из активной истории.
Проблема суммаризации: теряются конкретные факты («пользователь сказал, что аллергичен на пенициллин»). Для медицинских, юридических и финансовых ассистентов суммаризация без явного сохранения ключевых фактов — риск.
3. Гибридный подход с памятью
Самый надёжный вариант для долгосрочных ассистентов:
- Кратковременная память — последние 10–15 сообщений, всегда в контексте
- Долгосрочная память — структурированные факты о пользователе и разговоре, хранятся отдельно
- Семантический поиск — при каждом запросе достаём из долгосрочной памяти релевантные факты через эмбеддинги
Долгосрочная память обновляется через дополнительный вызов: после каждого ответа модели просим модель извлечь факты для запоминания ("Какие новые факты о пользователе можно извлечь из этого диалога?").
Хранение истории на мобиле
SQLite — стандарт. Структура:
CREATE TABLE conversations (
id TEXT PRIMARY KEY,
created_at INTEGER,
title TEXT,
model TEXT,
summary TEXT -- суммаризация старых сообщений
);
CREATE TABLE messages (
id TEXT PRIMARY KEY,
conversation_id TEXT REFERENCES conversations(id),
role TEXT CHECK(role IN ('user', 'assistant', 'system')),
content TEXT,
token_count INTEGER,
created_at INTEGER
);
CREATE INDEX idx_messages_conversation ON messages(conversation_id, created_at);
token_count считается при сохранении — не при каждой загрузке. Это важно для производительности при длинных историях.
Подсчёт токенов на мобиле
Точный подсчёт требует tokenizerа для конкретной модели. На сервере — tiktoken для OpenAI, tokenizers от HuggingFace для остальных. На мобиле чаще используем эвристику:
- Английский текст: ~4 символа ≈ 1 токен
- Русский текст: ~2–2.5 символа ≈ 1 токен (кириллица кодируется большим числом токенов)
- Код: ~3 символа ≈ 1 токен
Для ответственного подсчёта (тарификация, лимиты) — серверная валидация.
UI: отображение истории
Список сообщений — UITableView с обратным порядком (новые снизу) или LazyColumn в Compose с reverseLayout = true. При стриминге последнее сообщение обновляется на месте без перескакивания скролла.
Индикация контекстного окна: показывать пользователю, сколько «памяти» занято — визуальная полоска или счётчик токенов. Не обязательная фича, но те приложения, которые её добавляют, получают меньше жалоб на «забывчивость» ассистента.
Ориентиры по срокам
Скользящее окно с SQLite хранением — 3–4 дня. Гибридная система с суммаризацией и долгосрочной памятью — 1,5–2,5 недели.







