Стабильность длинных сессий: Контекстное сжатие, кеширование подписей и сжатие результатов инструментов
Когда вы используете Claude Code / Cherry Studio и другие клиенты для запуска длинных сессий, самое раздражающее не в том, что модель недостаточно умна, а в том, что диалог внезапно начинает выдавать ошибки: Prompt is too long, ошибки подписи 400, прерывается цепочка вызовов инструментов, или инструментальный цикл становится всё медленнее.
В этом уроке будут подробно разъяснены три вещи, которые Antigravity Tools делает для решения этих проблем: контекстное сжатие (постепенное вмешательство на трёх уровнях), кеширование подписей (сохранение цепочки подписей Thinking) и сжатие результатов инструментов (чтобы результаты инструментов не переполнили контекст).
Что вы сможете сделать после изучения
- Понять, что делает трёхуровневое прогрессивное сжатие контекста и каковы издержки каждого уровня
- Знать, что хранится в кеше подписей (три уровня: Tool/Family/Session) и влияние TTL в 2 часа
- Понять правила сжатия результатов инструментов: когда будут отброшены base64-изображения, когда снимки браузера будут преобразованы в сводку head+tail
- При необходимости отрегулировать пороговое значение срабатывания сжатия через настройки порога
proxy.experimental
Ваша текущая проблема
- После длинного диалога внезапно начинают возникать 400: похоже на сбой подписи, но вы не знаете, откуда подпись, и где она теряется
- Вызовов инструментов становится всё больше, исторические tool_result накапливаются до прямого отказа вышестоящего провайдера (или становятся крайне медленными)
- Вы хотите использовать сжатие для спасения, но беспокоитесь, что это нарушит Prompt Cache, повлияет на согласованность или заставит модель потерять информацию
Когда использовать этот подход
- Вы выполняете задачи с длинными цепочками инструментов (поиск/чтение файлов/снимки браузера/циклы инструментов)
- Вы используете Thinking-модели для сложных рассуждений, и сессии часто превышают десятки раундов
- Вы расследуете проблемы стабильности, которые клиент может воспроизвести, но вы не можете ясно объяснить
Что такое сжатие контекста
Сжатие контекста — это автоматическое шумоподавление и уменьшение размера исторических сообщений, которое выполняет прокси при обнаружении чрезмерного давления контекста: сначала обрезаются старые раунды инструментов, затем старые тексты Thinking сжимаются в заполнители, но сохраняются подписи, и в крайних случаях генерируется сводка XML и создаётся разветвление новой сессии для продолжения диалога, тем самым снижая количество сбоев из-за слишком длинного промпта и разрыва цепочки подписей.
Как рассчитывается давление контекста?
Процессор Claude выполняет лёгкую оценку с помощью ContextManager::estimate_token_usage() и калибрует её с помощью estimation_calibrator, затем получает процент давления через usage_ratio = estimated_usage / context_limit (в журнале будут выводиться значения raw/calibrated).
🎒 Подготовка перед началом
- Вы уже запустили локальный прокси, и клиент действительно использует путь
/v1/messages(см. запуск локального обратного прокси и подключение первого клиента) - Вы можете просматривать журналы прокси (отладка разработчика или локальные файлы журналов). План тестирования в репозитории даёт пример пути к файлу журнала и способа grep (см.
docs/testing/context_compression_test_plan.md)
Совместное использование с Proxy Monitor для лучшего позиционирования
Если вы хотите сопоставить срабатывание сжатия с определённым типом запроса/учётной записью/раундом вызова инструмента, рекомендуется одновременно открывать Proxy Monitor.
Основная идея
Этот дизайн стабильности не заключается в простом удалении всей истории, а в пошаговом вмешательстве от низкой к высокой цене:
| Уровень | Точка срабатывания (настраивается) | Что делает | Цена/побочный эффект |
|---|---|---|---|
| Уровень 1 | proxy.experimental.context_compression_threshold_l1 (по умолчанию 0.4) | Идентифицирует раунды инструментов, сохраняет только последние N раундов (в коде это 5), удаляет более старые пары tool_use/tool_result | Не изменяет содержимое оставшихся сообщений, более дружественно для Prompt Cache |
| Уровень 2 | proxy.experimental.context_compression_threshold_l2 (по умолчанию 0.55) | Сжимает старые тексты Thinking в "...", но сохраняет signature, защищает последние 4 сообщения | Будет изменять историческое содержимое, в комментариях явно указано, что это нарушит cache, но может сохранить цепочку подписей |
| Уровень 3 | proxy.experimental.context_compression_threshold_l3 (по умолчанию 0.7) | Вызывает фоновую модель для генерации XML-сводки, затем разветвляет новую последовательность сообщений для продолжения диалога | Зависит от вызова фоновой модели; при сбое вернёт 400 (с дружелюбным напоминанием) |
Далее каждый уровень будет подробно описан, при этом кеширование подписей и сжатие результатов инструментов будут представлены вместе.
Уровень 1: Обрезка раундов инструментов (Trim Tool Messages)
Ключевой момент Уровня 1 заключается в удалении только целых раундов взаимодействия инструментов, чтобы избежать несогласованности контекста из-за частичного удаления.
- Правило идентификации раунда инструментов находится в
identify_tool_rounds(): когда вassistantпоявляетсяtool_use, начинается новый раунд, последующийtool_resultвuserвсё ещё считается этим же раундом, пока не встретится обычный текст user для окончания этого раунда. - Фактическое выполнение обрезки выполняется в
ContextManager::trim_tool_messages(&mut messages, 5): когда исторические раунды инструментов превышают 5 раундов, удаляются сообщения, связанные с более ранними раундами.
Уровень 2: Сжатие Thinking, но сохранение подписей
Многие проблемы 400 вызваны не слишком длинными Thinking, а тем, что цепочка подписей Thinking разорвана. Стратегия Уровня 2:
- Обрабатывает только
ContentBlock::Thinking { thinking, signature, .. }в сообщенияхassistant - Сжимает только когда
signature.is_some()иthinking.len() > 10, меняяthinkingнапрямую на"..." - Последние
protected_last_n = 4сообщения не сжимаются (примерно последние 2 раунда user/assistant)
Таким образом можно сэкономить большое количество токенов, но signature остаётся в истории, чтобы при восстановлении цепочки инструментов было откуда восстановить.
Уровень 3: Разветвление + XML-сводка (последний резерв)
Когда давление продолжает расти, процессор Claude попытается перезапустить сессию, не потеряв ключевую информацию:
- Извлекает последнюю действительную подпись Thinking из исходных сообщений (
ContextManager::extract_last_valid_signature()) - Собирает всю историю +
CONTEXT_SUMMARY_PROMPTв запрос на генерацию XML-сводки, модель фиксируется какBACKGROUND_MODEL_LITE(в текущем коде этоgemini-2.5-flash) - В сводке требуется включить
<latest_thinking_signature>, используемый для продолжения цепочки подписей - Разветвляет новую последовательность сообщений:
User: Context has been compressed... + XML summaryAssistant: I have reviewed...- Затем прикрепляет последнее сообщение user исходного запроса (если это не только что инструкция сводки)
Если разветвление + сводка не удастся, напрямую вернётся StatusCode::BAD_REQUEST, с предложением использовать /compact или /clear и другие способы ручной обработки (см. JSON ошибки, возвращаемый процессором).
Обходной путь 1: Трёхуровневое кеширование подписей (Tool / Family / Session)
Кеширование подписей — это предохранитель сжатия контекста, особенно когда клиент обрезает/отбрасывает поля подписей.
- TTL:
SIGNATURE_TTL = 2 * 60 * 60(2 часа) - Уровень 1:
tool_use_id -> signature(восстановление цепочки инструментов) - Уровень 2:
signature -> model family(проверка совместимости между моделями, чтобы избежать переноса подписей Claude на модели семейства Gemini) - Уровень 3:
session_id -> latest signature(изоляция на уровне сессии, чтобы избежать загрязнения разных диалогов)
Эти три уровня кеша будут записаны/прочитаны при SSE-поточном анализе и преобразовании запроса Claude:
- При потоковом анализе подпись
thinkingбудет записана в Session Cache (а также кешируется family) - При потоковом анализе подпись
tool_useбудет записана в Tool Cache + Session Cache - При преобразовании вызова инструмента Claude в
functionCallGemini приоритет отдаётся восстановлению подписи из Session Cache или Tool Cache
Обходной путь 2: Сжатие результатов инструментов (Tool Result Compressor)
Результаты инструментов легче переполняют контекст, чем текст чата, поэтому на этапе преобразования запросов выполняется предсказуемое уменьшение tool_result.
Основные правила (все в tool_result_compressor.rs):
- Верхний предел общего количества символов:
MAX_TOOL_RESULT_CHARS = 200_000 - Блоки base64-изображений напрямую удаляются (добавляется текст подсказки)
- Если обнаружено, что вывод сохранён в файл, извлекается ключевая информация и используется заполнитель
[tool_result omitted ...] - Если обнаружен снимок браузера (содержит
page snapshot/ref=и другие характеристики), изменяется на сводку head + tail и отмечено, сколько символов пропущено - Если ввод похож на HTML, сначала удаляются
<style>/<script>/фрагменты base64, затем выполняется усечение
Следуйте за мной
Шаг 1: Подтвердите пороги сжатия (и значения по умолчанию)
Почему Точки срабатывания сжатия не жёстко заданы, они берутся из proxy.experimental.*. Вам нужно сначала знать текущий порог, чтобы судить, почему он так рано/так поздно вмешивается.
Значения по умолчанию (с стороны Rust ExperimentalConfig::default()):
{
"proxy": {
"experimental": {
"enable_signature_cache": true,
"enable_tool_loop_recovery": true,
"enable_cross_model_checks": true,
"enable_usage_scaling": true,
"context_compression_threshold_l1": 0.4,
"context_compression_threshold_l2": 0.55,
"context_compression_threshold_l3": 0.7
}
}
}Вы должны увидеть: в вашей конфигурации существует proxy.experimental (имя поля совпадает с указанным выше), а пороги имеют значения типа 0.x (процентное значение).
Местоположение файла конфигурации в этом уроке не повторяется
Место сохранения файла конфигурации и то, нужно ли перезапускать после изменения, относятся к категории управления конфигурацией. Согласно этой системе руководств, в первую очередь ориентируйтесь на Полное описание конфигурации: AppConfig/ProxyConfig, место сохранения на диск и семантика горячего обновления.
Шаг 2: Подтвердите с помощью журналов, сработали ли Уровень 1/2/3
Почему Эти три уровня являются внутренними поведениями прокси, самый надёжный способ проверки — увидеть, появляются ли в журнале [Layer-1] / [Layer-2] / [Layer-3].
План тестирования в репозитории даёт пример команды (отрегулируйте по необходимости на фактический путь к журналу на вашем компьютере):
tail -f ~/Library/Application\ Support/com.antigravity.tools/logs/antigravity.log | grep -E "Layer-[123]"Вы должны увидеть: когда давление растёт, в журнале появляются записи, подобные Tool trimming triggered, Thinking compression triggered, Fork successful (конкретные поля берутся из оригинала журнала).
Шаг 3: Поймите разницу между очисткой и сжатием (не путайте ожидания)
Почему Некоторые проблемы (например, принудительное понижение до моделей, не поддерживающих Thinking) требуют очистки, а не сжатия. Очистка напрямую удалит блок Thinking; сжатие сохранит цепочку подписей.
В процессоре Claude, понижение фоновой задачи пойдёт по пути ContextManager::purify_history(..., PurificationStrategy::Aggressive), которое напрямую удалит исторические блоки Thinking.
Вы должны увидеть: вы можете различать два типа поведения:
- Очистка — это удаление блока Thinking
- Сжатие Уровня 2 — это замена старого текста Thinking на
"...", но подпись всё ещё существует
Шаг 4: Когда вы встречаете ошибку подписи 400, сначала посмотрите, попала ли Session Cache
Почему Корневая причина многих 400 не в отсутствии подписи, а в том, что подпись не идёт вместе с сообщением. При преобразовании запроса приоритет отдаётся восстановлению подписи из Session Cache.
Ключи (журналы на этапе преобразования запроса будут указывать на восстановление подписи из кеша SESSION/TOOL):
[Claude-Request] Recovered signature from SESSION cache ...[Claude-Request] Recovered signature from TOOL cache ...
Вы должны увидеть: когда клиент теряет подпись, но кеш прокси всё ещё существует, в журнале появляются записи о восстановлении подписи из ... cache.
Шаг 5: Поймите, что будет потеряно при сжатии результатов инструментов
Почему Если вы позволите инструменту возвращать большие куски HTML / снимки браузера / base64-изображения обратно в диалог, прокси активно сократит их. Вам нужно заранее знать, какое содержимое будет заменено заполнителем, чтобы не ошибочно думать, что модель не увидела.
Запомните три важных момента:
- base64-изображения будут удалены (заменены текстом подсказки)
- Снимки браузера станут сводкой head/tail (с указанием количества пропущенных символов)
- Свыше 200 000 символов будет усечено и добавлено подсказка
...[truncated ...]
Вы должны увидеть: в tool_result_compressor.rs эти правила имеют явные константы и ветвления, это не удаление на основе опыта.
Контрольная точка
- Вы можете ясно сказать, что точки срабатывания L1/L2/L3 берутся из
proxy.experimental.context_compression_threshold_*, по умолчанию это0.4/0.55/0.7 - Вы можете объяснить, почему Уровень 2 нарушит cache: потому что он изменяет содержимое исторического текста thinking
- Вы можете объяснить, почему Уровень 3 называется разветвлением: он превращает диалог в новую последовательность из сводки XML + подтверждение + последнее сообщение user
- Вы можете объяснить, что сжатие результатов инструментов удалит base64-изображения и превратит снимки браузера в сводку head/tail
Напоминания о возможных ошибках
| Явление | Возможная причина | Что вы можете сделать |
|---|---|---|
| После срабатывания Уровня 2 кажется, что контекст стал менее стабильным | Уровень 2 изменяет историческое содержимое, в комментариях явно указано, что это нарушит cache | Если вы полагаетесь на согласованность Prompt Cache, постарайтесь, чтобы Уровень 1 сначала решил проблему, или повышайте порог Уровня 2 |
| После срабатывания Уровня 3 сразу возвращается 400 | Разветвление + сводка вызвали фоновую модель неуспешно (сеть/учётная запись/ошибка вышестоящего провайдера и т. д.) | Сначала используйте /compact или /clear согласно рекомендации в JSON ошибки; одновременно проверьте цепочку вызова фоновой модели |
| Изображения/большое содержимое в выводе инструментов исчезли | tool_result удалит base64-изображения, усечёт сверхдлинный вывод | Важно поместить важное содержимое в локальный файл/ссылку, затем сослаться на него; не рассчитывайте на то, что можно напрямую вставить 100 000 строк текста обратно в диалог |
| Понятно используется модель Gemini, но с подписью Claude возникает ошибка | Подписи несовместимы между моделями (в коде есть проверка family) | Подтвердите источник подписи; при необходимости позвольте прокси удалять историческую подпись в сценариях retry (см. логику преобразования запроса) |
Краткое содержание урока
- Суть трёхуровневого сжатия — классификация по цене: сначала удаление старых раундов инструментов, затем сжатие старых Thinking, и только потом разветвление + XML-сводка
- Кеширование подписей — ключ к сохранению цепочки инструментов: три уровня Session/Tool/Family каждый управляет одним типом проблемы, TTL — 2 часа
- Сжатие результатов инструментов — жёсткий предел, позволяющий избежать переполнения контекста выводом инструментов: верхний предел 200 000 символов + специализация снимков/больших файлов
Предварительный просмотр следующего урока
Следующий урок мы поговорим о системных возможностях: мультиязычность/темы/обновления/автозапуск/HTTP API Server.
Приложение: Ссылка на исходный код
Нажмите, чтобы развернуть и просмотреть местоположение исходного кода
Дата обновления: 2026-01-23
| Функция | Путь к файлу | Номер строки |
|---|---|---|
| Экспериментальная конфигурация: пороги сжатия и переключатели по умолчанию | src-tauri/src/proxy/config.rs | 119-168 |
| Оценка контекста: оценка мультиязычных символов + запас 15% | src-tauri/src/proxy/mappers/context_manager.rs | 9-37 |
| Оценка потребления токенов: обход system/messages/tools/thinking | src-tauri/src/proxy/mappers/context_manager.rs | 103-198 |
| Уровень 1: идентификация раундов инструментов + обрезка старых раундов | src-tauri/src/proxy/mappers/context_manager.rs | 311-439 |
| Уровень 2: сжатие Thinking, но сохранение подписей (защита последних N) | src-tauri/src/proxy/mappers/context_manager.rs | 200-271 |
| Уровень 3 вспомогательный: извлечение последней действительной подписи | src-tauri/src/proxy/mappers/context_manager.rs | 73-109 |
| Понижение фоновой задачи: Aggressive очистка блоков Thinking | src-tauri/src/proxy/handlers/claude.rs | 540-583 |
| Основной процесс трёхуровневого сжатия: оценка, калибровка, срабатывание L1/L2/L3 по порогу | src-tauri/src/proxy/handlers/claude.rs | 379-731 |
| Уровень 3: реализация разветвления + сводка XML | src-tauri/src/proxy/handlers/claude.rs | 1560-1687 |
| Кеширование подписей: TTL/структура трёхуровневого кеша (Tool/Family/Session) | src-tauri/src/proxy/signature_cache.rs | 5-88 |
| Кеширование подписей: запись/чтение подписей сессии | src-tauri/src/proxy/signature_cache.rs | 141-223 |
| SSE потоковый анализ: кеширование подписей thinking/tool в Session/Tool cache | src-tauri/src/proxy/mappers/claude/streaming.rs | 766-776 |
| --- | --- | --- |
| Преобразование запроса: tool_use приоритетно восстанавливает подпись из Session/Tool cache | src-tauri/src/proxy/mappers/claude/request.rs | 1045-1142 |
| Преобразование запроса: tool_result вызывает сжатие результатов инструментов | src-tauri/src/proxy/mappers/claude/request.rs | 1159-1225 |
Сжатие результатов инструментов: вход compact_tool_result_text() | src-tauri/src/proxy/mappers/tool_result_compressor.rs | 28-69 |
| Сжатие результатов инструментов: сводка head/tail снимков браузера | src-tauri/src/proxy/mappers/tool_result_compressor.rs | 123-178 |
| --- | --- | --- |
| План тестирования: срабатывание трёхуровневого сжатия и проверка журналов | docs/testing/context_compression_test_plan.md | 1-116 |