Как снизить стоимость многоходового диалога AI API: обрезка истории и передача сводки
Управление историей, бюджетом Token и сводкой контекста: практический способ снизить повторный ввод без потери важных фактов.
Как снизить стоимость многоходового диалога AI API: обрезка истории и передача сводки
При каждом вызове API модель обычно не помнит предыдущий запрос автоматически. Чтобы сохранить контекст, клиент снова отправляет system prompt, историю сообщений, прошлые ответы и новый вопрос. С ростом диалога увеличиваются входные Token, задержка, нагрузка на контекст и расходы.
Задача не в том, чтобы бездумно удалить старые сообщения. Нужно сохранить сведения, необходимые для текущей задачи. Удобно разделить контекст на правила, недавний текст, устойчивые факты и завершённые действия, а затем применять бюджет и лимит окна модели.
Что входит в стоимость каждого вызова
Во вход могут попасть system prompt, определения инструментов, история, новый вопрос, текст вложений и повторяемые инструкции. Выход — сгенерированный текст. Итоговая тарификация зависит от ставок входа и выхода, кэширования, коэффициента посредника и минимальной единицы расчёта.
примерная стоимость ≈ входные Token × ставка входа
+ выходные Token × ставка выхода
+ кэш и другие позиции
Это оценка, а не формула счёта. Token-разбиение отличается у моделей, а русский текст, китайский текст, код, таблицы и изображения нельзя точно переводить в Token только по числу символов. Сверяйте расчёт с usage и детализацией провайдера. Основы расчёта разобраны в руководстве по Token и биллингу AI API.
Не храните всю историю целиком
Стратегия «оставить последние N ходов» проста, но может удалить раннюю цель или ограничение. Лучше разделить информацию по ценности:
| Содержимое | Как обрабатывать | Почему |
|---|---|---|
| Правила системы, формат и безопасность | Хранить и версионировать | Пропуск меняет поведение |
| Недавние вопросы и ответы | Хранить исходный текст | Нужны ссылки, код и продолжение мысли |
| Подтверждённые предпочтения и ограничения | Сжать в список фактов | Нет смысла повторять диалог |
| Завершённые задачи | Сводка или удаление | Меньше входных Token |
| Большой вывод инструментов и логов | Сохранить выводы, исходник получать по требованию | Не отправлять его снова каждый раз |
Перед обрезкой спросите: ссылается ли текущий вопрос на этот фрагмент и может ли он изменить следующее решение? Если нет, его можно убрать из обычного контекста.
Передавайте сводку вместо простого обрезания
Когда история подходит к бюджетному порогу, попросите модель создать структурированную сводку и замените ею старые сообщения. Разделяйте подтверждённые факты, незавершённые пункты и неизвестные сведения.
Составь сводку передачи для следующего диалога.
Оставь только:
1. подтверждённую цель и ограничения;
2. выполненные действия и результаты;
3. нерешённые вопросы и следующий шаг;
4. точные ID, имена полей и фрагменты кода, которые нельзя менять.
Неизвестное пометь «нужно подтвердить» и не добавляй факты от себя.
Сама сводка тоже стоит денег, поэтому не создавайте её на каждом ходу. Запускайте сжатие, когда вход достигает примерно 60–80% доступного окна или завершается отдельный этап. Это начальный порог, его следует корректировать по длине ответов и данным счёта.
Минимальный пример обрезки в Node.js
Код ниже показывает локальную фильтрацию по бюджету и не считает реальные Token. В production используйте tokenizer конкретной модели или usage от провайдера. System-сообщения сохраняются, а недавние сообщения добавляются с конца, пока не исчерпан приблизительный бюджет.
function roughTokens(text) {
return Math.ceil(String(text ?? '').length / 2)
}
function messageCost(message) {
const content = typeof message.content === 'string'
? message.content
: JSON.stringify(message.content ?? '')
return roughTokens(content) + 4
}
function buildContext(messages, inputBudget) {
const system = messages.filter(m => m.role === 'system')
const rest = messages.filter(m => m.role !== 'system')
const selected = []
let used = system.reduce((sum, m) => sum + messageCost(m), 0)
for (let i = rest.length - 1; i >= 0; i -= 1) {
const cost = messageCost(rest[i])
if (used + cost > inputBudget) break
selected.unshift(rest[i])
used += cost
}
return { messages: [...system, ...selected], estimatedInputTokens: used }
}
Оценка по символам годится только для предварительного отбора. Для китайского, русского, кода, таблиц и мультимодальных данных погрешность может быть большой. Перед запуском калибруйте её по фактическому input_tokens и храните внутреннюю метрику разницы до и после обрезки.
Установите границы для сводки и истории
История может содержать ключи, персональные данные, код клиентов и внутренние ссылки. Перед отправкой в модель для сводки удаляйте или маскируйте ненужные секреты. Установите отдельные лимиты на вход, число сообщений и размер сводки. При ошибке сводки сохраните последние ходы и покажите понятное сообщение, не создавая бесконечный цикл повторов.
У версии сводки должны быть время и номер, а замена старой версии — выполняться с возможностью восстановления. При удалении пользователем диалога применяйте правило одновременно к исходной истории, сводке, кэшу и поисковому индексу. В логах оставляйте только ID диалога, статистику Token и причину обрезки.
Проверьте, действительно ли расходы снизились
Сравните средние входные и выходные Token, стоимость хода, задержку до первого байта, полное время, долю вызовов сводки и количество повторных вопросов пользователя. Если вход стал меньше, но повторных вопросов стало больше, история обрезается слишком сильно.
Для A/B-теста используйте фиксированный обезличенный набор: полная история, последние N ходов и структурированная сводка. Сравнивайте сохранение ключевых фактов, стоимость и задержку на нескольких повторах. Короткий ответ сам по себе не доказывает улучшение качества.
Проверочный список перед запуском
- Разделить тарификацию входа, выхода, кэша и сводки;
- Не удалять правила системы, текущую задачу и необходимые ограничения;
- Калибровать оценку Token по tokenizer или
usage; - Задать отдельные лимиты для истории, сводки и одного запроса;
- Маскировать данные перед сводкой и не сохранять секреты в логах;
- Сравнить стоимость, задержку и полноту ответа на фиксированном наборе;
- Удалять исходник, сводку, кэш и индекс по одному правилу.
Цель оптимизации — передавать модели достаточно контекста без лишних повторов. Правила Token, кэша и контекстного окна у API-посредников различаются, поэтому ориентируйтесь на их актуальные документы и детализацию запросов.
Дополнительно: ошибка превышения контекста · проверка экономии от кэша