Постоянные цели (Goals)
Постоянные цели (Persistent Goals) — механизм Hermes Agent для автономной работы над задачей. /goal задаёт стоячую цель, которая сохраняется между ходами агента. После каждого хода лёгкая судейская модель (judge) проверяет, достигнута ли цель. Если нет — Hermes автоматически подаёт себе продолжение и делает следующий шаг, пока цель не будет достигнута, приостановлена или не исчерпается бюджет ходов.
Это реализация паттерна Ralph loop, вдохновлённая Codex CLI 0.128.0 (Eric Traut, OpenAI). Ключевая идея — держать цель активной между ходами и не останавливаться, пока она не выполнена — принадлежит Codex. Реализация в Hermes самостоятельна и адаптирована к собственной архитектуре.
Когда использовать
- «Исправь все ошибки линтера в
src/и проверь, чтоruff checkпроходит» - «Портируй функцию X из репозитория Y с тестами и добейся зелёного CI»
- «Расследуй, почему session ID иногда дрейфуют при сжатии контекста, и напиши отчёт»
- «Построй CLI для переименования фото по EXIF-датам и протестируй на папке photos/»
Задачи, где агент делает один ход и останавливается, не нуждаются в /goal. Задачи, где вам пришлось бы сказать «продолжай» три раза — идеальный сценарий.
Быстрый старт
/goal Исправь все падающие тесты в tests/hermes_cli/ и убедись, что scripts/run_tests.sh проходит для этой директории
Что вы увидите:
- Цель принята —
⊙ Goal set (20-turn budget): <ваша цель> - Ход 1 — Hermes начинает работать, как если бы вы отправили цель как обычное сообщение.
- Судья оценивает — после хода судейская модель решает:
doneилиcontinue. - Цикл продолжается — если
continue:↻ Continuing toward goal (1/20): <причина судьи> - Завершение —
✓ Goal achieved: <причина>или⏸ Goal paused — N/20 turns used
Команды
| Команда | Описание |
|---|---|
/goal <текст> |
Установить (или заменить) цель. Сразу запускает первый ход. |
/goal draft <текст> |
Сгенерировать контракт выполнения из цели на естественном языке и установить его (см. «Контракты выполнения»). |
/goal show |
Показать активный контракт выполнения. |
/goal или /goal status |
Текущая цель, статус и использованные ходы. |
/goal pause |
Приостановить цикл без сброса цели. |
/goal resume |
Возобновить цикл (счётчик ходов сбрасывается). |
/goal clear |
Удалить цель полностью. |
/goal wait <pid> [причина] |
Припарковать цикл до завершения фонового процесса (см. «Парковка на фоновые процессы»). |
/goal unwait |
Снять парковку и возобновить цикл немедленно. |
Команды работают идентично в CLI и на всех платформах шлюза (Telegram, Discord, Slack, Matrix, Signal, WhatsApp, SMS, iMessage, Webhook, API-сервер и веб-панель).
Контракты выполнения (Completion contracts)
Обычный /goal <текст> работает, но размытая цель ведёт к размытой оценке — судья может проверить только то, что вы указали. Контракт добавляет структурированные критерии.
| Поле | Значение |
|---|---|
outcome |
Конечное состояние, которое должно быть истинным. |
verification |
Конкретный тест / команда / артефакт, доказывающий результат. |
constraints |
Что нельзя менять или ломать. |
boundaries |
Какие файлы, директории, инструменты входят в область. |
stop_when |
Условие, при котором Hermes должен остановиться и спросить пользователя. |
Два способа создать контракт
1. Автоматически через /goal draft (рекомендуется):
/goal draft Миграция auth-сервиса с session cookies на JWT
Hermes расширяет однострочную цель в полноценный контракт через вспомогательную модель goal_judge, устанавливает его и показывает результат. Если aux-модель недоступна, используется обычный формат — создание цели никогда не блокируется.
2. Inline-синтаксис с полями:
/goal Миграция auth на JWT
verify: pytest tests/auth проходит
constraints: сохранить форму ответа /login
boundaries: трогать только services/auth и тесты
stop when: потребовалась миграция схемы БД
Первые строки без двоеточий — заголовок цели; распознанные префиксы полей (verify:, constraints:, preserve:, boundaries:, scope:, stop when:, blocked: и т.д.) заполняют контракт. Обычная цель с двоеточием в тексте (Fix bug: the parser drops commas) не искажается.
Просмотреть контракт: /goal show. Контракты сохраняются в SessionDB.state_meta и переживают /resume.
Дополнительные критерии: /subgoal
Во время активной цели можно добавить дополнительные критерии без сброса цикла:
| Команда | Описание |
|---|---|
/subgoal <текст> |
Добавить критерий к активной цели. |
/subgoal |
Показать пронумерованный список критериев. |
/subgoal remove <N> |
Удалить N-й критерий (с 1). |
/subgoal clear |
Удалить все критерии, сохранив основную цель. |
Каждый вызов добавляет нумерованный пункт; промпт продолжения и промпт судьи перестраиваются с учётом всех subgoal — цель не будет отмечена как выполненная, пока не удовлетворены все критерии. Сохраняются в SessionDB.state_meta вместе с целью.
Парковка на фоновые процессы
Некоторые цели зависят от длительных фоновых задач (CI, сборка, тесты). Без автоматической парковки цикл будет каждый ход спрашивать «готово?» — напрасная трата бюджета.
Парковка автоматическая. Каждый ход судья видит фоновые процессы агента (реестр terminal(background=true) — pid, uptime, вывод, watch_patterns / notify_on_complete). Когда прогресс реально зависит от процесса, судья возвращает wait вместо continue — цикл паркуется до завершения ожидания. Судья может также парковаться на время (wait_for_seconds) для backoff.
wait_on_session <id>— освобождается при срабатывании триггера процесса (выход илиwatch_patterns).wait_on_pid <pid>— освобождается при завершении процесса.wait_for_seconds <n>— освобождается через N секунд.
/goal status показывает ⏳ Goal (parked …) во время парковки. Команды /goal wait и /goal unwait — ручная альтернатива. Парковка сохраняется в SessionDB.state_meta и переживает /resume. Если PID уже завершился или время истекло — барьер снимается автоматически.
Поведение
Судья
После каждого хода Hermes вызывает вспомогательную модель с:
- Текстом стоячей цели
- Последним ответом агента (~4 КБ текста)
- Системным промптом с инструкцией ответить строгим JSON:
{"done": bool, "reason": "..."}
Судья намеренно консервативен: цель считается выполненной только при явном подтверждении, когда конечный результат чётко произведён, или когда цель недостижима / заблокирована (обрабатывается как DONE с указанием причины, чтобы не тратить бюджет).
Отказоустойчивость (fail-open)
Если судья ошибается (сетевой сбой, некорректный ответ, недоступность aux-клиента), Hermes считает вердикт continue — сломанный судья никогда не блокирует прогресс. Бюджет ходов — основной ограничитель.
Бюджет ходов
По умолчанию — 20 ходов продолжения (goals.max_turns в config.yaml). При исчерпании:
⏸ Goal paused — 20/20 turns used. Use /goal resume to keep going, or /goal clear to stop.
/goal resume сбрасывает счётчик на ноль — можно работать управляемыми порциями.
Приоритет пользовательских сообщений
Любое реальное сообщение, отправленное во время активной цели, обрабатывается раньше автоматического продолжения. В CLI сообщение попадает в _pending_input перед очередью; в шлюзе — через FIFO адаптера. Судья проверяет снова после вашего хода — если он завершил цель, цикл остановится.
Безопасность в шлюзе (mid-run)
Пока агент работает, команды /goal status, /goal pause, /goal clear, /goal wait, /goal unwait безопасны — они затрагивают только контрольную плоскость. Установка новой цели (/goal <новый текст>) отклоняется с просьбой сначала выполнить /stop.
Кэш промптов
Промпт продолжения — обычное user-role сообщение, добавленное в историю. Он не меняет системный промпт, не переключает инструменты и не инвалидирует кэш промптов. 20-ходовая цель стоит столько же по кэшу, сколько 20 обычных ходов.
Конфигурация
В ~/.hermes/config.yaml:
goals:
# Максимальное количество ходов до автопаузы. По умолчанию 20.
max_turns: 20
Для переадресации судьи на быструю дешёвую модель:
auxiliary:
goal_judge:
provider: openrouter
model: google/gemini-3-flash-preview
По умолчанию судья использует основную модель (см. Auxiliary Models). Вызов судьи мал (~200 токенов за ход), поэтому дешёвая модель — оптимальный выбор.
Состояние цели хранится в SessionDB.state_meta по ключу goal:<session_id>. После перезапуска можно возобновить /resume — цель сохраняется в точности (активная, приостановленная или завершённая).