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

223 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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](https://github.com/NousResearch/hermes-agent)) 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` | 10671075, 11871190 | Инициализация счётчиков и интервалов |
| `agent/tool_executor.py` | 207209, 776778 | Сброс счётчиков при вызове `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`
### Финальная проверка
```python
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):
```python
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. **Выводит** пользователю:
```python
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).
Читает секцию:
```yaml
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).
**Было:**
```python
review_whitelist = {
t["function"]["name"]
for t in get_tool_definitions(
enabled_toolsets=["memory", "skills"],
quiet_mode=True,
)
}
```
**Стало:**
```python
_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`:
```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 из первоначального исследования, пока не реализован).
## 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.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`.