Сжатие контекста и кэширование

Агент Hermes использует двойное сжатие системы и кэширование подсказок Anthropic для эффективного управления использованием контекста окна в длительных диалогах.

Исходные файлы: agent/context_engine.py (ABC), agent/context_compressor.py (стандартный движок), agent/prompt_caching.py, gateway/run.py (гигиена сессии), run_agent.py (поиск _compress_context)

Подключаемый контекст движка

Управление контекстом построено на абстрактном базовом классе ContextEngine (agent/context_engine.py). Встроенный ContextCompressor реализован по умолчанию, но плагины могут заменить его альтернативными движками (например, Управление контекстом без потерь).

context:
  engine: "compressor"    # по умолчанию — встроенное сжатие с потерями
  engine: "lcm"           # пример — плагин, обеспечивающий контекст без потерь

Мовок отвечает за: - Определение момента, когда должно произойти уплотнение (should_compress()) - Выполнение уплотнения (compress()) - Опциональное предоставление инструментов, которые могут работать с агентом (например, lcm_grep) - Отслеживание использования токенов из API ответов.

Выбор осуществляется через конфигурацию с помощью context.engine в config.yaml. Порядок разрешения: 1. Проверка каталога plugins/context_engine/<имя>/ 2. Проверка всей системы плагинов (register_context_engine()) 3. Возврат встроенного ContextCompressor

Движки плагинов никогда не активируются автоматически — пользователь должен явно установить context.engine по имени плагина. По умолчанию "компрессор" всегда используется встроенный.

Настройте через hermesplugins → Provider Plugins → Context Engine, или напрямую отредактируйте config.yaml.

Для создания плагина движка контекста см. Плагины движка контекста.

Двойная система сжатия

Hermes имеет два нижних уровня сжатия, производится независимо:

                     ┌──────────────────────────┐
  Входящее сообщение │   Гигиена сессии шлюза   │  Срабатывает при 85% контекста
  ─────────────────► │   (до агента, грубая оценка)│  Предохранитель для больших сессий
                     └─────────────┬────────────┘
                                   │
                                   ▼
                     ┌──────────────────────────┐
                     │   Agent ContextCompressor │  Срабатывает при 50% контекста (по умолч.)
                     │   (в цикле, реальные токены)│  Обычное управление контекстом
                     └──────────────────────────┘

1. Гигиена шлюза сессии (порог 85%)

Находится в gateway/run.py (поиск Гигиена сеанса: автосжатие). Это предохранитель, который запускается до того, как агент обработает сообщение. Вы допускаете ошибку API, когда сеансы становятся слишком высокими между большими оборотами (например, ночное накопление в Telegram/Discord).

Порог гигиены шлюза намеренно выше, чем у агента компрессора. Установка его на 50% (как у агента) привела к преждевременному сжатию на каждом обороте в ходе сеансов шлюза.

2. Agent ContextCompressor (порог 50%, настраиваемый)

Находится в agent/context_compressor.py. Это основная система сжатия, которая работает внутри циклических инструментов агента с доступом к лучшим подсчетам токенов, сообщаемым API.

Конфигурация

Все настройки сжатия читаются из config.yaml в разделе compression:

compression:
  enabled: true              # Включить/отключить сжатие (по умолчанию: true)
  threshold: 0.50            # Доля окна контекста (по умолчанию: 0.50 = 50%)
  target_ratio: 0.20         # Сколько от порога сохранять как хвост (по умолчанию: 0.20)
  protect_last_n: 20         # Минимальное количество защищенных хвостовых сообщений (по умолчанию: 20)

# Модель/провайдер суммаризации настраивается в auxiliary:
auxiliary:
  compression:
    model: null              # Переопределить модель для суммаризации (по умолчанию: автоопределение)
    provider: auto           # Провайдер: "auto", "openrouter", "nous", "main" и т.д.
    base_url: null           # Пользовательская конечная точка, совместимая с OpenAI

Детальные параметры

Параметр По умолчанию Диапазон Описание
порог 0,50 0,0-1,0 Сжатие реализации, когда токены подсказки ≥ threshold × context_length
target_ratio 0,20 0,10-0,80 Управляет бюджетом токенов хвоста: threshold_tokens × target_ratio
protect_last_n 20 ≥1 Минимальное количество последних сообщений, всегда сохраняемое
protect_first_n 3 (жестко запрограммировано) Системная подсказка + первый обмен всегда выбирают

Вычисленные значения (для моделей с контекстом 200К при задании по умолчанию)

context_length       = 200,000
threshold_tokens     = 200,000 × 0.50 = 100,000
tail_token_budget    = 100,000 × 0.20 = 20,000
max_summary_tokens   = min(200,000 × 0.05, 12,000) = 10,000

Алгоритм сжатия

Метод ContextCompressor.compress() следует 4-фазному алгоритму:

Фаза 1: Обрезка старых результатов инструментами (дешево, без вызова LLM)

Старые результаты инструментов (>200 символов) для обеспечения защищенного хвоста для изменения:

[Old tool output cleared to save context space]

Это эффективный предварительный проход, который экономит количество токенов от многих инструментов вывода (содержание файлов, вывод терминала, поиск результатов).

Фаза 2: Определение границ

┌─────────────────────────────────────────────────────────────┐
│  Список сообщений                                           │
│                                                             │
│  [0..2]  ← protect_first_n (система + первый обмен)         │
│  [3..N]  ← средние обороты → СУММАРИЗИРОВАНЫ                │
│  [N..end] ← хвост (по бюджету токенов ИЛИ protect_last_n)   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Защита хвоста опирается на бюджет токенов: идет назад от конца, накапливая токены, пока бюджет не исчерпан. Возвращается к фиксированному результату protect_last_n, если бюджет защитил меньше сообщений.

Границы спортивны, чтобы избежать разделения на группыtool_call/tool_result. Метод _align_boundary_backward() проходит через следующие инструменты результатов, чтобы найти родительское сообщение ассистента, сохраняющего группу нетронутыми.

Фаза 3: Генерация структурированной сводки:::предупреждение Длина контекста модели масштабирования

Модель более крупного размера должна иметь контекст окна не меньше, чем у основной модели агента. Весь средний раздел предполагает реализацию модели в одном вызове call_llm(task="compression"). Если контекстная модель уменьшена меньше, API возвращает ошибку длины контекста —_generate_summary()перехватывает ее, записывает предупреждение и возвращаетNone`. Затем компрессор выбрасывает средние обороты без сводки, молча теряя контекст разговора. Это наиболее распространенная причина ухудшения качества уплотнения.

Средние обороты постепенно преобразуются с использованием вспомогательной LLM по структурированному шаблону:

## Цель
[Чего пользователь пытается достичь]

## Ограничения и предпочтения
[Предпочтения пользователя, стиль кодирования, ограничения, важные решения]

## Прогресс
### Сделано
[Завершенная работа — конкретные пути файлов, выполненные команды, результаты]
### В процессе
[Работа, выполняемая в данный момент]
### Заблокировано
[Любые блокировки или возникшие проблемы]

## Ключевые решения
[Важные технические решения и почему]

## Соответствующие файлы
[Файлы, прочитанные, измененные или созданные — с кратким примечанием о каждом]

## Следующие шаги
[Что должно произойти дальше]

## Критический контекст
[Конкретные значения, сообщения об ошибках, детали конфигурации]

Бюджет сводки масштабируется в зависимости от объема сжимаемого показателя: - Формула: content_tokens × 0,20 (константа _SUMMARY_RATIO) - Минимум: 2 000 токенов - Максимум: min(context_length × 0,05, 12 000) токенов

Фаза 4: Сборка сжатых текстов

Список сжатых сообщений: 1. Головные сообщения (с примечанием, добавленным к системной подсказке при первом сжатии) 2. Сводки сообщений (роль вы меняете так, чтобы избежать возникновения последовательности одинаковых ролей) 3. Хвостовые сообщения (без изменений)

Осиротевшие парыtool_call/tool_result очищаются с помощью _sanitize_tool_pairs(): - Инструменты результатов, ссылающиеся на удаленные вызовы → удаляются - Вызовы инструментов, результаты которых были удалены → включается заглушка результата

Итеративное повторное растяжение

При уменьшении сжатия предыдущая сводка модели LLM с инструкцией обновить ее, а не постепенно оптимизировать с нуля. Это сохраняет информацию при нескольких уплотнениях — элементы перемещаются из «В процессе» в «Сделано», добавляется новый прогресс, а закрытая информация удаляется.

Поле _previous_summary в экземпляре компрессора сохраняет последний текст сводки для этой цели.

Пример до/после

До сжатия (45 сообщений, ~95К токенов)

[0] system:    "You are a helpful assistant..." (system prompt)
[1] user:      "Help me set up a FastAPI project"
[2] assistant: <tool_call> terminal: mkdir project </tool_call>
[3] tool:      "directory created"
[4] assistant: <tool_call> write_file: main.py </tool_call>
[5] tool:      "file written (2.3KB)"... 30 more turns of file editing, testing, debugging...
[38] assistant: <tool_call> terminal: pytest </tool_call>
[39] tool:      "8 passed, 2 failed\n..."  (5KB output)
[40] user:      "Fix the failing tests"
[41] assistant: <tool_call> read_file: tests/test_api.py </tool_call>
[42] tool:      "import pytest\n..."  (3KB)
[43] assistant: "I see the issue with the test fixtures..."
[44] user:      "Great, also add error handling"

После сжатия (25 сообщений, ~45К токенов)

[0] system:    "You are a helpful assistant...
               [Note: Some earlier conversation turns have been compacted...]"
[1] user:      "Help me set up a FastAPI project"
[2] assistant: "[CONTEXT COMPACTION] Earlier turns were compacted...

               ## Goal
               Set up a FastAPI project with tests and error handling

               ## Progress
               ### Done
               - Created project structure: main.py, tests/, requirements.txt
               - Implemented 5 API endpoints in main.py
               - Wrote 10 test cases in tests/test_api.py
               - 8/10 tests passing

               ### In Progress
               - Fixing 2 failing tests (test_create_user, test_delete_user)

               ## Relevant Files
               - main.py  FastAPI app with 5 endpoints
               - tests/test_api.py  10 test cases
               - requirements.txt  fastapi, pytest, httpx

               ## Next Steps
               - Fix failing test fixtures
               - Add error handling"
[3] user:      "Fix the failing tests"
[4] assistant: <tool_call> read_file: tests/test_api.py </tool_call>
[5] tool:      "import pytest\n..."
[6] assistant: "I see the issue with the test fixtures..."
[7] user:      "Great, also add error handling"

Кэширование подсказок (Anthropic)

Источник: agent/prompt_caching.py

Снижает затраты на входные токены примерно на 75% в многократных диалогах за счет кэширования префикса разговора. Использует точки остановки «cache_control» от Anthropic.

Стратегия: system_and_3

Anthropic допускает не более 4 точек остановки cache_control при запросе. Гермес использует блокировку "system_and_3":

Точка останова 1: Системная подсказка           (стабильна на всех оборотах)
Точка останова 2: 3-е с конца несистемное сообщение  ─┐
Точка останова 3: 2-е с конца несистемное сообщение   ├─ Скользящее окно
Точка останова 4: Последнее несистемное сообщение      ─┘

Как это работает

apply_anthropic_cache_control() глубоко копирует сообщения и вставляет маркеры cache_control:

# Формат маркера кэша
marker = {"type": "ephemeral"}
# Или для TTL в 1 час:
marker = {"type": "ephemeral", "ttl": "1h"}

Маркер применяется по-разному, в зависимости от типа макета:

Тип оценки Куда внести маркер
Строковое требование Преобразуется в [{"type": "text", "text":..., "cache_control":...}]
Содержимое списка Добавлено в словарь последний элемент
Нет/пусто Добавляется как msg["cache_control"]
Сообщения инструменты Добавляется как msg["cache_control"] (только родной Anthropic)

Шаблоны проектирования с учетом кэша

  1. Стабильная системная подсказка: Системная подсказка является точкой остановки 1 и кэшируется на всех оборотах. Избегайте ее изменений в середине разговора (сжатие вносится только при первом уплотнении).
  2. Порядок сообщений имеет значение: Попадания в кэш требуют совпадения префикса. Добавление или удаление сообщений в середине делает кэш недействительным для всего последующего.
  3. Взаимодействие сжатия и кэша: После сжатия кэш становится недействительным для сжатой области, но системная подсказка кэша сохраняется. Скользящее окно из 3 сообщений регулирует кэширование в течение 1-2 оборотов.
  4. Выбор TTL: По умолчанию 5m (5 минут). Используйте 1h для длительных сеансов, когда пользователь делает перерывы между оборотами.

Включение кэширования подсказок

Кэширование подсказок автоматически включается, когда: - Модель является моделью Anthropic Claude (определяется по имени модели) - Провайдер поддержки cache_control (собственный API Anthropic или OpenRouter)

# config.yaml — TTL настраивается (должно быть "5m" или "1h")
prompt_caching:
  cache_ttl: "5m"

CLI показывает статус кэширования при запуске:

💾 Prompt caching: ENABLED (Claude via OpenRouter, 5m TTL)

Предупреждения о ламповом контексте

Промежуточные отражения о свете были контекста (см. блок iteration-budget в run_agent.py, где отмечено: «Нет промежуточных предупреждений об удаленном свете — они заставляли модели временно 'сдаваться' на сложных задачах»). Сжатие с внедрением, когда токены подсказки стандартного compression.threshold (по умолчанию 50%) без предварительного шага интерпретации; гигиеническая сессия шлюза работает как вторичный предохранитель при 85% модели контекста окна.