14 KiB
Hermes Self-Improvement Review (Background Memory/Skill Review)
Source: codebase analysis of hermes-agent/agent/background_review.py, run_agent.py, agent/conversation_loop.py, agent/agent_init.py.
Что это
После каждого юзерского сообщения (run_conversation) AIAgent может запустить фоновый daemon-thread, который форкает ещё один AIAgent, даёт ему снимок всего разговора и просит оценить — нужно ли сохранить что-то в память (Memory) или обновить/создать скилл (Skill).
Основной поток продолжается немедленно. Ревью не блокирует пользователя.
Where it lives
| File | Lines | Role |
|---|---|---|
agent/background_review.py |
1–597 | Центральный модуль — промпты, _run_review_in_thread, spawn_background_review_thread, summarize_background_review_actions |
run_agent.py |
1360–1400 | Форвардеры: _spawn_background_review, _summarize_background_review_actions, импорт констант-промптов |
agent/conversation_loop.py |
4779–4805 | Место вызова — после завершения ответа, перед возвратом result |
agent/codex_runtime.py |
150–162 | Аналогичный вызов для Codex Response API режима |
agent/agent_init.py |
1067–1075, 1187–1190 | Инициализация счётчиков и интервалов |
agent/tool_executor.py |
207–209, 776–778 | Сброс счётчиков при вызове memory или skill_manage |
tui_gateway/server.py |
2452–2464 | Подключение background_review_callback для TUI (Ink) |
gateway/run.py |
17849–17893 | Подключение background_review_callback для gateway-сообщений |
Как триггерится
Два независимых счётчика:
Memory review (turn-based)
- Счётчик:
_turns_since_memory - Интервал:
_memory_nudge_interval(default = 10) - Задаётся из
config.yaml: memory.nudge_interval - Инкрементится в начале каждого
run_conversation(строка 559) - Проверка:
if _turns_since_memory >= _memory_nudge_interval→_should_review_memory = True - Сброс: при вызове
memorytool (строка 207 в tool_executor.py) - Условия:
"memory" in valid_tool_namesи_memory_store != None
Если memory_enabled: false → ревью не триггерится.
Skills review (iteration-based)
- Счётчик:
_iters_since_skill - Интервал:
_skill_nudge_interval(default = 10) - Инкрементится внутри tool-call цикла после каждого батча (строка 860)
- Проверка:
if _iters_since_skill >= _skill_nudge_interval→_should_review_skills = True - Сброс: при вызове
skill_managetool (строки 209, 778 в tool_executor.py) - Условия:
"skill_manage" in valid_tool_names
Финальная проверка
if final_response and not interrupted and (_should_review_memory or _should_review_skills):
agent._spawn_background_review(
messages_snapshot=list(messages),
review_memory=_should_review_memory,
review_skills=_should_review_skills,
)
Что происходит внутри
_run_review_in_thread (background_review.py:327–559)
-
Создаёт форк AIAgent, наследуя:
model,provider,base_url,api_key,credential_poolот родителя (чтобы попасть в тот же prefix cache у Anthropic/OpenRouter)_cached_system_prompt— byte-идентичный, чтобы не разогревать кеш зановоsession_id,session_start— те же_memory_store,_memory_enabled,_user_profile_enabled— те же- Но
skip_memory=True(чтобы форк не создавал свой MemoryManager) _memory_nudge_interval = 0,_skill_nudge_interval = 0(чтобы не рекурсил)
-
Устанавливает tool whitelist (строки 459–472):
review_whitelist = { t["function"]["name"] for t in get_tool_definitions( enabled_toolsets=["memory", "skills"], quiet_mode=True, ) }Вне whitelist — всё блокируется с сообщением
"Background review denied non-whitelisted tool: ..." -
Запускает
review_agent.run_conversation(...)с:- Выбранным промптом (см. ниже) + суффикс
"You can only call memory and skill management tools. Other tools will be denied at runtime — do not attempt them." conversation_history=messages_snapshot— весь разговор до этого моментаmax_iterations=16(жёстко, не из конфига)
- Выбранным промптом (см. ниже) + суффикс
-
После завершения — собирает успешные tool-результаты через
summarize_background_review_actions(), фильтруя те, что уже были вmessages_snapshot(чтобы не показывать старые как новые). -
Выводит пользователю:
f" 💾 Self-improvement review: {summary}" # Например: "💾 Self-improvement review: Memory updated · Skill 'xxx' patched"Через
agent._safe_print()и черезagent.background_review_callback()(для TUI/gateway). -
На ошибках — пишет в лог
"Background memory/skill review failed: ..."и шлёт_emit_auxiliary_failure. Не ломает основной разговор.
3 варианта промпта
Выбираются в spawn_background_review_thread() по комбинации флагов:
review_memory |
review_skills |
Промпт | Суть |
|---|---|---|---|
| ✅ | ❌ | _MEMORY_REVIEW_PROMPT (~10 строк) |
Сохранить факты о пользователе (persona, preferences, style). Если нечего — 'Nothing to save.' |
| ❌ | ✅ | _SKILL_REVIEW_PROMPT (~120 строк) |
Самая большая инструкция. Искать сигналы: коррекции, frustration, новые техники, устаревшие скиллы. Приоритет: loaded skill → existing umbrella → support file → new umbrella. Запрещено: env-зависимости, негативные фиксации, session-specific. |
| ✅ | ✅ | _COMBINED_REVIEW_PROMPT (~80 строк) |
Смесь обоих — сначала memory, потом skills с той же логикой. |
Потенциальные проблемы
- Нет конфигурируемости для skills interval —
_skill_nudge_intervalхардкожен в 10, не читается из config.yaml (в отличие от memory). - Tool whitelist был фиксирован — теперь настраивается через
review.allowed_toolsetsв config.yaml (см. ниже). - Форк не имеет MCP-серверов — MCP tools (obsidian-mcp, etc.) не регенерируются в форке.
- max_iterations=16 — жёстко, не из конфига. Может не хватить на комплексное ревью многоскилловых сессий.
Реализованные расширения (2026-06-24)
Сделаны два изменения в исходном коде Hermes Agent для того, чтобы промпты и whitelist ревью были настраиваемы из config.yaml:
1. agent_init.py — чтение секции review: из config.yaml
Место: agent/agent_init.py (после блока skills config).
Читает секцию:
review:
# Пути к .md файлам с кастомными промптами.
# Если файл не найден/не читается — тихо падает на встроенный хардкод.
skill_prompt_path: ~/.hermes/review/skill_prompt.md
memory_prompt_path: ~/.hermes/review/memory_prompt.md
combined_prompt_path: ~/.hermes/review/combined_prompt.md
# Какие toolsets доступны форку ревью.
# По дефолту: ["memory", "skills"].
# Чтобы форк мог читать/писать .md файлы Obsidian — добавить "file".
allowed_toolsets:
- memory
- skills
# - file
Что делает код:
- Выставляет
agent._review_allowed_toolsets(по дефолту["memory", "skills"]) - Для каждого из трёх путей (
skill_prompt_path,memory_prompt_path,combined_prompt_path) — пытается прочитать файл и записать содержимое вagent._SKILL_REVIEW_PROMPT/agent._MEMORY_REVIEW_PROMPT/agent._COMBINED_REVIEW_PROMPTсоответственно. - Если путь не указан, файл не найден или не читается — тихо игнорируется, остаётся встроенный хардкод.
2. background_review.py — использование _review_allowed_toolsets с инстанса
Место: agent/background_review.py, функция _run_review_in_thread, whitelist (строка ~459).
Было:
review_whitelist = {
t["function"]["name"]
for t in get_tool_definitions(
enabled_toolsets=["memory", "skills"],
quiet_mode=True,
)
}
Стало:
_allowed_sets = getattr(agent, "_review_allowed_toolsets", ["memory", "skills"])
review_whitelist = {
t["function"]["name"]
for t in get_tool_definitions(
enabled_toolsets=_allowed_sets,
quiet_mode=True,
)
}
А также сообщение для форка динамически подставляет имена toolsets вместо хардкода "memory and skill".
3. Примеры промптов
Созданы в ~/.hermes/review/:
skill_prompt.md— копия встроенного_SKILL_REVIEW_PROMPT+ секция--- Obsidian documentation update ---, которая инструктирует форк после каждого обновления скилла читать и патчить соответствующий.mdв/Users/admin/obsidian/.memory_prompt.md— копия_MEMORY_REVIEW_PROMPT+ инструкция обновлятьpersonal/profiles/Alex.mdпри сохранении user preference.
Как включить Obsidian-обновление
- Раскомментировать в
~/.hermes/config.yaml:review: skill_prompt_path: ~/.hermes/review/skill_prompt.md memory_prompt_path: ~/.hermes/review/memory_prompt.md allowed_toolsets: - memory - skills - file - Перезапустить Hermes (новый сессионный конфиг прочитается при старте
AIAgent.__init__). - После каждого десятого tool-батча (или любого вызова
skill_manageраньше) форк будет: создать/обновить скилл и прочитать/пропатчить соответствующий Obsidian-документ черезread_file/patch.
Замечание по архитектуре
File tools работают напрямую с файловой системой, в обход obsidian-mcp. Это значит:
- Линки
[[wiki]]в Obsidian не резолвятся автоматически — форк видит сырой.md. - Frontmatter (YAML заголовки) — видит как часть текста, может патчить.
- Vault search недоступен — только прямой путь к файлу.
Для большинства задач (пропатчить существующий doc, создать новый .md с frontmatter) file tools достаточно. Если нужен полный Obsidian API — надо дорабатывать подключение MCP в форке (Вариант B из первоначального исследования, пока не реализован).
Related files
~/.hermes/hermes-whale/review/skill_prompt.md— кастомный skills промпт с Obsidian-инструкцией (для всех артефактов: планы, конфиги, этапы проектов)~/.hermes/hermes-whale/review/memory_prompt.md— кастомный memory промпт с Obsidian-инструкциейagent/agent_init.py— чтение секцииreview:из config.yamlagent/background_review.py— использование_review_allowed_toolsetsи кастомных промптов- Config:
config.yaml → review.*(см. секцию выше)
Хронология
- 2026-06-24: Исследование и документирование механизма self-improvement review (Whale/Alex).
- 2026-06-24: Реализация настройки через config.yaml: промпты (через пути к .md) + allowed_toolsets. Созданы примеры промптов с Obsidian-инструкцией.
- 2026-06-24: Реализация thread-scoped memory + custom memory instruction (см.
personal/plans/thread-scoped-memory.md). - 2026-06-24: Тесты thread-scoped memory: 9 новых тестов (76/76 passed), закоммичено в
4ebad4f69.