Агент цикла внутреннего устройства

Основной движок оркестрации — класс AIAgent из run_agent.py — большой файл (более 15 000 строк), который обрабатывает всё: от промпта сборки до инструментов диспетчеризации и переключения между провайдерами.

Основные обязанности

«AIAgent» отвечает за:

Две точки входа

# Простой интерфейс — возвращает итоговую строку ответа
response = agent.chat("Исправь баг в main.py")

# Полный интерфейс — возвращает словарь с сообщениями, метаданными, статистикой использования
result = agent.run_conversation(
    user_message="Исправь баг в main.py",
    system_message=None,           # создаётся автоматически, если не указан
    conversation_history=None,      # загружается из сессии, если не указана
    task_id="task_abc123"
)

chat() — это тонкая окёртка вокруг run_conversation(), которая извлекает поле final_response из результирующего словаря.

Режимы API

Hermes поддерживает три режима выполнения API, определяемые на основе выбора провайдера, явных аргументов и эвристического базового URL:

Режим API Используется для Тип клиента
чат_завершения Эндпоинты, совместимые с OpenAI (OpenRouter, кастомные, большинство провайдеров) openai.OpenAI
codex_responses Кодекс OpenAI/API ответов openai.OpenAI в формате Ответы
антропные_сообщения Нативный API антропных сообщений anthropic.Anthropic через адаптер

Режим определяет, как форматируются сообщения, как структурируются инструменты вызова, как разбираются ответы и как работает кэширование/стриминг. Все три режима сводятся к единому внешнему формату сообщений (словари в стиле OpenAI с полями role/content/tool_calls) до и после вызовов API.

Порядок определения режима: 1. Явный аргумент конструктора api_mode (наивысший приоритет) 2. Определение на основе провайдера (например, провайдер anthropicanthropic_messages) 3. Базовый URL Эвристики (например, api.anthropic.comanthropic_messages) 4. По умолчанию: chat_completions

Жизненный цикл шага

каждая итерация цикла агента дает результат по следующей последовательности:

run_conversation()
  1. Сгенерировать task_id, если не указан
  2. Добавить сообщение пользователя в историю диалога
  3. Собрать или использовать кэшированный системный промпт (prompt_builder.py)
  4. Проверить, нужно ли предварительное сжатие (>50% контекста)
  5. Собрать сообщения API из истории диалога
     - chat_completions: формат OpenAI как есть
     - codex_responses: преобразовать во входные элементы Responses API
     - anthropic_messages: преобразовать через anthropic_adapter.py
  6. Внедрить эфемерные слои промпта (предупреждения о бюджете, давление контекста)
  7. Применить маркеры кэширования промпта, если используется Anthropic
  8. Выполнить прерываемый вызов API (_interruptible_api_call)
  9. Разобрать ответ:
     - Если есть tool_calls: выполнить их, добавить результаты, вернуться к шагу 5
     - Если текстовый ответ: сохранить сессию, сбросить память при необходимости, вернуть результат

Формат сообщения

Все средства внутренне используются в форматах, совместимых с OpenAI:

{"role": "system", "content": "..."}
{"role": "user", "content": "..."}
{"role": "assistant", "content": "...", "tool_calls": [...]}
{"role": "tool", "tool_call_id": "...", "content": "..."}

Содержимое рассуждений (от моделей, поддерживающих расширенное мышление) хранится в assistant_msg["reasoning"] и опционально отображается через reasoning_callback.

Правила очередности сообщений

Цикл агента строго соблюдает чередование ролей сообщений:

Провайдеры проверяют эти последовательности и отклоняют некорректированную историю.

Прерываемые вызовы API

API-запросы обёрнуты в _interruptible_api_call(), который выполняет фактический HTTP-вызов в фоновом потоке, отслеживая события прерывания:

┌────────────────────────────────────────────────────┐
│  Основной поток              Поток API             │
│                                                    │
│   ожидание:                   HTTP POST            │
│    - готовность ответа  ───▶  провайдеру           │
│    - событие прерывания                            │
│    - таймаут                                       │
└────────────────────────────────────────────────────┘

При отключении (пользователь отправил новое сообщение, команду /stop или сигнал): - Поток API завершается (ответ отбрасывается) - Агент может обработать новое введение или корректно выполнить работу. - Частный ответ не добавляется в историю диалога

Инструменты для завершения

Последовательное и параллельное

Когда модель вызывает вызовы инструментов:

Поток выполнения

для каждого tool_call в response.tool_calls:
    1. Найти обработчик из tools/registry.py
    2. Вызвать хук плагина pre_tool_call
    3. Проверить, является ли команда опасной (tools/approval.py)
       - Если опасная: вызвать approval_callback, ждать пользователя
    4. Выполнить обработчик с аргументами + task_id
    5. Вызвать хук плагина post_tool_call
    6. Добавить {"role": "tool", "content": результат} в историю

Инструменты уровня агента

Некоторые инструменты перехватываются run_agent.py до того, как они попадут в handle_function_call():

Инструмент Почему перехватывается
todo Читает/записывает состояние задач агента
memory Записывает в постоянные файлы памяти с ограничением на количество символов
session_search Запрашивает историю сессии через БД сессий агента
delegate_task Создаёт подчинённого агента(ов) с изолированным контекстом

Эти инструменты напрямую изменяют состояние агента и возвращают синтетические результаты, минуя реестр.

Поверхности обратных вызовов

AIAgent поддерживает специфичные для платформы обратные вызовы, которые обеспечивают отображение прогресса в реальном времени в CLI, шлюзе и интеграциях ACP:

Обратный вызов Когда вызывается Используется
tool_progress_callback До/после каждого выполнения инструмента Спиннер CLI, сообщения о прогрессе шлюза
thinking_callback Когда модель начинает/заканчивает думать Индикатор "думает..." в CLI
reasoning_callback Когда модель возвращает содержимое рассуждений Отображение рассуждений в CLI, блоки рассуждений шлюза
clarify_callback Когда вызывается инструмент clarify Приглашение ввода в CLI, интерактивное сообщение шлюза
step_callback После каждого полного шага агента Отслеживание шагов шлюза, прогресс ACP
stream_delta_callback Каждый токен стриминга (если включено) Отображение стриминга в CLI
tool_gen_callback Когда вызов инструмента разобран из потока Предпросмотр инструмента в спиннере CLI
status_callback Изменения состояния (думает, выполняет и т.д.) Обновления статуса ACP

Бюджет и поведение при откате

Бюджет итераций

Агент отслеживает итерации через IterationBudget:

Запасная модель

Когда основная модель даёт сбой (429 ограничение скорости, 5xx ошибка сервера, 401/403 ошибка аутентификации):

  1. Проверить список fallback_providers в конфигурации
  2. Пробовать каждую запасную по порядку
  3. При успехе продолжить диалог с новым провайдером
  4. При 401/403 попытаться обновить учётные данные перед переключением

Система запасных вариантов также независимо охватывает вспомогательные задачи — зрение, сжатие, извлечение веб-страниц и поиск по сессиям — каждая имеет свою цепочку запасных вариантов, настраиваемую через раздел конфигурации auxiliary.*.

Сжатие и сохранение

Когда срабатывает сжатие

Что происходит во время сжатия

  1. Память сначала сбрасывается на диск (предотвращение потери данных)
  2. Средние шаги диалога суммируются в компактную сводку
  3. Последние N сообщений сохраняются нетронутыми (compression.protect_last_n, по умолчанию: 20)
  4. Пары сообщений вызов инструмента/результат сохраняются вместе (никогда не разделяются)
  5. Генерируется новый идентификатор линии сессии (сжатие создаёт "дочернюю" сессию)

Сохранение сессии

После каждого шага: - Сообщения сохраняются в хранилище сессий (SQLite через hermes_state.py) - Изменения памяти сбрасываются в MEMORY.md / USER.md - Сессию можно возобновить позже через /resume или hermes chat --resume

Ключевые исходные файлы

Файл Назначение
run_agent.py Класс AIAgent — полный цикл агента
agent/prompt_builder.py Сборка системного промпта из памяти, навыков, контекстных файлов, личности
agent/context_engine.py ABC ContextEngine — подключаемое управление контекстом
agent/context_compressor.py Движок по умолчанию — алгоритм сжатия с потерями
agent/prompt_caching.py Маркеры кэширования промпта Anthropic и метрики кэша
agent/auxiliary_client.py Вспомогательный LLM-клиент для побочных задач (зрение, суммаризация)
model_tools.py Сбор схем инструментов, диспетчеризация handle_function_call()

Связанные документы