Files
obsidian-vault/personal/tech/hermes-self-improvement-review.md
T

21 KiB
Executable File
Raw Blame History

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
  • Сброс: при вызове memory tool (строка 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_manage tool (строки 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)

  1. Создаёт форк 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 (чтобы не рекурсил)
  2. Устанавливает 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: ..."

  3. Запускает 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 (жёстко, не из конфига)
  4. После завершения — собирает успешные tool-результаты через summarize_background_review_actions(), фильтруя те, что уже были в messages_snapshot (чтобы не показывать старые как новые).

  5. Выводит пользователю:

    f"  💾 Self-improvement review: {summary}"
    # Например: "💾 Self-improvement review: Memory updated · Skill 'xxx' patched"
    

    Через agent._safe_print() и через agent.background_review_callback() (для TUI/gateway).

  6. На ошибках — пишет в лог "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 — конфигурируемый (исправлено 2026-09-29; ранее в доке ошибочно значилось «хардкожен в 10»). Читается из config.yaml → skills.creation_nudge_interval, дефолт в коде 10. Проверено: agent/agent_init.py:1224 → int(skills_config.get("creation_nudge_interval", 10)). На этом профиле (hermes-whale/config.yaml:280) стоит 0 (отключено 2026-09-29, коммит d2e1586).
  • Tool whitelist был фиксирован — теперь настраивается через review.allowed_toolsets в config.yaml (см. ниже).
  • Форк не имеет MCP-серверов — MCP tools (obsidian-mcp, etc.) не регенерируются в форке.
  • max_iterations=16 — жёстко, не из конфига. Может не хватить на комплексное ревью многоскилловых сессий.

🔴 Как ВЫКЛЮЧИТЬ автогенерацию скиллов (2026-09-29)

Задача Alex: «отключи автогенерацию скиллов». Разобрано до уровня кода; ВНЕСЕНО 2026-09-29 (вариант A — см. §Развилка). Выбор Alex: «да. только hermes-whale. без бэкапов, у нас гит. потом коммит».

Два независимых пути, которыми агент сам правит библиотеку скиллов

# Механизм Что делает Ключ конфига Сейчас
1 Skill nudge каждые N tool-итераций спавнит фоновый форк, который создаёт/патчит скиллы skills.creation_nudge_interval 15 → ставить 0
2 Review fork после сессии тот же форк, но с allowed_toolsets review.allowed_toolsets [memory, skills, file]

Код (проверено построчно)

# agent/agent_init.py:1220-1226
agent._skill_nudge_interval = 10
skills_config = _agent_cfg.get("skills", {})
agent._skill_nudge_interval = int(skills_config.get("creation_nudge_interval", 10))
# agent/conversation_loop.py:858-860 — инкремент внутри tool-цикла
if (agent._skill_nudge_interval > 0
        and "skill_manage" in agent.valid_tool_names):
    agent._iters_since_skill += 1
# agent/conversation_loop.py:4779-4785 — финальная проверка в конце хода
_should_review_skills = False
if (agent._skill_nudge_interval > 0                              # ← ГЛАВНЫЙ ВЫКЛЮЧАТЕЛЬ
        and agent._iters_since_skill >= agent._skill_nudge_interval
        and "skill_manage" in agent.valid_tool_names):
    _should_review_skills = True
    agent._iters_since_skill = 0
# agent/conversation_loop.py:4797 — спавн только если что-то взведено
if final_response and not interrupted and (_should_review_memory or _should_review_skills):
    agent._spawn_background_review(...)

Вывод: creation_nudge_interval: 0 делает первое условие ложным всегда → _should_review_skills никогда не станет True → скиллы от nudge-механизма не создаются. Это чистый выключатель, other logic не затронута. Счётчик _iters_since_skill при 0 даже не инкрементится (строка 858).

Промпты — где живут

Промпт Что делает Когда выбирается
_MEMORY_REVIEW_PROMPT сохранить факты о юзере review_memory=True, review_skills=False
_SKILL_REVIEW_PROMPT правит библиотеку скиллов («Be ACTIVE — most sessions produce at least one skill update») review_skills=True
_COMBINED_REVIEW_PROMPT и память, и скиллы оба True

Выбор — spawn_background_review_thread() (agent/background_review.py:583-588): if review_memory and review_skills → COMBINED; elif review_memory → MEMORY; else → SKILL. То есть скиллы правятся в ветках COMBINED и «else». Убрать skills из allowed_toolsets — упадёт сам tool, но промпт останется.

✅ Внесено (2026-09-29) — вариант A

Ключ Было Стало Файл
skills.creation_nudge_interval 15 0 (строка 280) ~/.hermes/hermes-whale/config.yaml

Коммит d2e1586 (chore(whale): disable auto skill generation), md5 ac0c7af01d48fa24fb763efbe56668a2. review.allowed_toolsets не тронут ([memory, skills, file]) — память продолжает обновляться. ~/.hermes/config.yaml не тронут (Alex: «только hermes-whale»).

Требуется перезапуск gateway, чтобы правка вступила в силу: POST /api/services/hermes_whale/restart — конфиг читается в AIAgent.__init__.

🔴 Питфолл: hermes config set перезаписывает ВЕСЬ YAML

hermes config set <key> <val> не делает точечную правку — он сериализует конфиг заново. Последствия на этом профиле: 63 строки комментариев срезано, host: "0.0.0.0" → 0.0.0.0, отступы списков переформатированы ( - memory → - memory).

Значения при этом не пострадали — сверено построчно через git diff --cached -U0 | grep -E "^[+-]" | grep -v "^[+-][+-]" | grep -vE "^[+-]\s*#". Всегда проверять дифф на смысловые изменения, а не только на свою строку.

🔴 Питфолл: patch/write_file НЕ могут писать в config.yaml

Инструменты возвращают Refusing to write to Hermes config file — конфиг помечен security-sensitive. Править только через hermes config set, руками (hermes config edit) или с одобрения Alex. Это не баг, обходить не надо.

🔴 Питфолл: hermes не в PATH

hermes: command not found — бинарь лежит в venv: /Users/admin/.hermes/hermes-agent/venv/bin/hermes. hermes config show не печатает секцию skills — проверять значение грепом по файлу (grep -n creation_nudge_interval <config>), а не через CLI. config get не существует (есть только show|edit|set|path|env-path|check|migrate).

Развилка (решение принято: A)

Вариант Что меняется Последствие
A. Только creation_nudge_interval: 0 ✅ 1 ключ скиллы не создаются сами; память продолжает копиться
B. A + убрать skills из review.allowed_toolsets 2 ключа глухо оба; но фоновый review перестанет писать и в memory — авто-обновление USER.md/MEMORY.md умрёт. Не выбран.

Файлы (две копии, они разные — см. питфолл ниже): ~/.hermes/hermes-whale/config.yaml (основная) и ~/.hermes/config.yaml.

Реализованные расширения (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

Что делает код:

  1. Выставляет agent._review_allowed_toolsets (по дефолту ["memory", "skills"])
  2. Для каждого из трёх путей (skill_prompt_path, memory_prompt_path, combined_prompt_path) — пытается прочитать файл и записать содержимое в agent._SKILL_REVIEW_PROMPT / agent._MEMORY_REVIEW_PROMPT / agent._COMBINED_REVIEW_PROMPT соответственно.
  3. Если путь не указан, файл не найден или не читается — тихо игнорируется, остаётся встроенный хардкод.

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-обновление

  1. Раскомментировать в ~/.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
    
  2. Перезапустить Hermes (новый сессионный конфиг прочитается при старте AIAgent.__init__).
  3. После каждого десятого 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 из первоначального исследования, пока не реализован).

  • ~/.hermes/hermes-whale/review/skill_prompt.md — кастомный skills промпт с Obsidian-инструкцией (для всех артефактов: планы, конфиги, этапы проектов)
  • ~/.hermes/hermes-whale/review/memory_prompt.md — кастомный memory промпт с Obsidian-инструкцией
  • agent/agent_init.py — чтение секции review: из config.yaml
  • agent/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.
  • 2026-09-29: Разбор механизма — интервал скиллов оказался конфигурируемым (ранее в доке значилось «хардкожен в 10»), проверено по agent_init.py:1224.
  • 2026-09-29: Автогенерация скиллов отключена — skills.creation_nudge_interval: 15 → 0, коммит d2e1586. Память оставлена включённой. См. §«Как ВЫКЛЮЧИТЬ».