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

14 KiB
Raw Permalink 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 1597 Центральный модуль — промпты, _run_review_in_thread, spawn_background_review_thread, summarize_background_review_actions
run_agent.py 13601400 Форвардеры: _spawn_background_review, _summarize_background_review_actions, импорт констант-промптов
agent/conversation_loop.py 47794805 Место вызова — после завершения ответа, перед возвратом result
agent/codex_runtime.py 150162 Аналогичный вызов для Codex Response API режима
agent/agent_init.py 10671075, 11871190 Инициализация счётчиков и интервалов
agent/tool_executor.py 207209, 776778 Сброс счётчиков при вызове memory или skill_manage
tui_gateway/server.py 24522464 Подключение background_review_callback для TUI (Ink)
gateway/run.py 1784917893 Подключение 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:327559)

  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 (строки 459472):

    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_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

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

  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.