docs(agents): задание A35 — настройки хаба не применяются в Hermes ни разу
Владелец заметил, что настройки Hermes не соответствуют настройкам хаба.
Проверка подтвердила худшее: хаб не участвует в вызовах Hermes вообще.
Доказано исполнением. В исходниках Hermes, agent/conversation_loop.py:3221,
middleware вызывается без параметра role — передаются model, provider,
base_url, session_id, task_id, platform, но не роль. А плагин при
неопределённой роли делает next_call(request), то есть пропускает вызов мимо
маршрутизатора. Подача того же набора аргументов на живой плагин:
как зовёт Hermes (без роли) -> МИМО хаба
если роль передана -> обработал хаб
Механизм исправен целиком, его просто никто не включает: аккаунты, цепочки,
квоты и переключение при исчерпании настраиваются и не применяются.
Пропуск появился не по небрежности, а как защита: раньше при неопределённой
роли всё уходило как orchestrator, цепочка исчерпывалась, и текст ошибки
роутера подставлялся вместо ответа модели. Поэтому задание требует третьего
пути — выводить роль из того, что Hermes уже передаёт, с настраиваемой ролью
по умолчанию, сохранив предохранитель на исчерпанную цепочку.
Hermes править запрещено: чужой продукт, правка затрётся при обновлении.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
1b03f03ea5
commit
2f4866fffa
1 changed files with 145 additions and 0 deletions
145
agents/inbox/2026-08-30-A35-hub-must-actually-route-hermes.md
Normal file
145
agents/inbox/2026-08-30-A35-hub-must-actually-route-hermes.md
Normal file
|
|
@ -0,0 +1,145 @@
|
||||||
|
# Задание A35: настройки хаба должны применяться в Hermes
|
||||||
|
|
||||||
|
## Дата поступления
|
||||||
|
2026-08-30
|
||||||
|
|
||||||
|
## База
|
||||||
|
|
||||||
|
Ветка ревьюера `review/a28-a31-fixes` (`ab2ee12`).
|
||||||
|
|
||||||
|
```
|
||||||
|
git fetch origin --prune
|
||||||
|
git checkout -b antigravity/a35-role-resolution origin/review/a28-a31-fixes
|
||||||
|
```
|
||||||
|
|
||||||
|
В `main` напрямую не пушить.
|
||||||
|
|
||||||
|
## Порядок исполнения
|
||||||
|
|
||||||
|
Два прохода: **Flash** реализует, **Pro** проводит аудит. Пункт **P0-5** написан для аудитора.
|
||||||
|
|
||||||
|
Идёт **параллельно A34** (его делает Codex). Пересечения по файлам почти нет: здесь `hermes_plugin.py` и `router_engine.py`, там веб-клиент и адаптеры. Границу соблюдать.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Задача
|
||||||
|
|
||||||
|
Владелец сформулировал так: «надо проверить, чтобы хаб реально работал с Hermes. Сейчас получается, что настройки в Hermes вообще не соответствуют настройкам в хабе. А мы делаем хаб, чтобы все настройки в нём работали и в Hermes».
|
||||||
|
|
||||||
|
Проверка подтвердила: **не работают.** Хаб сейчас — панель, которая ничем не управляет.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Что проверено исполнением — заново не выясняйте
|
||||||
|
|
||||||
|
**1. Hermes не передаёт роль.** В его исходниках, `agent/conversation_loop.py:3221`:
|
||||||
|
|
||||||
|
```python
|
||||||
|
run_llm_execution_middleware(
|
||||||
|
api_kwargs, _perform_api_call,
|
||||||
|
original_request=..., task_id=..., turn_id=..., api_request_id=...,
|
||||||
|
session_id=..., platform=..., model=..., provider=..., base_url=...,
|
||||||
|
api_mode=..., api_call_count=..., middleware_trace=...
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
Параметра `role` нет. Есть `model`, `provider`, `base_url`, `session_id`, `task_id`, `platform` — этого достаточно, см. P0-1.
|
||||||
|
|
||||||
|
**2. Без роли плагин пропускает вызов мимо хаба.** `hermes_plugin.py:42`:
|
||||||
|
|
||||||
|
```python
|
||||||
|
if not resolved_role:
|
||||||
|
if callable(next_call):
|
||||||
|
return next_call(request)
|
||||||
|
```
|
||||||
|
|
||||||
|
**3. Измерено на живом плагине**, подачей ровно того, что шлёт Hermes:
|
||||||
|
|
||||||
|
```
|
||||||
|
как зовёт Hermes (без роли) -> МИМО хаба, собственный вызов Hermes
|
||||||
|
если роль передана -> обработал хаб, маршрутизация сработала
|
||||||
|
```
|
||||||
|
|
||||||
|
Механизм исправен целиком. Его просто никто не включает: аккаунты, цепочки, квоты и переключение при исчерпании настраиваются и **не применяются ни разу**.
|
||||||
|
|
||||||
|
**4. Почему так сделано — это защита, а не небрежность.** `resolve_role` намеренно не угадывает роль по тексту: «no guessing from prompts». Раньше при неопределённой роли всё шло как `orchestrator`, цепочка исчерпывалась, и **текст ошибки роутера подставлялся вместо ответа модели** — владелец получал сообщение хаба там, где ждал ответ. Пропуск появился как безопасный откат после этой аварии.
|
||||||
|
|
||||||
|
Сейчас выбор стоит так: хаб либо молчит, либо врёт. Задание — сделать третье.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## P0-1. Определение роли по тому, что Hermes всё-таки передаёт
|
||||||
|
|
||||||
|
Порядок разрешения, сверху вниз:
|
||||||
|
|
||||||
|
1. **Явная роль** — если когда-нибудь появится в `kwargs`, `request`, `metadata`. Работает уже сейчас, не ломать.
|
||||||
|
2. **По модели и провайдеру.** Hermes передаёт `model` и `provider`. Если запрошенная модель или провайдер — основные у какой-то роли, берём её. Соответствие строится **из конфигурации**, а не из литералов в коде.
|
||||||
|
3. **По устойчивости сессии.** Передаётся `session_id`. Если для этой сессии роль уже определялась, брать её же: механизм `session_affinity` есть и работает.
|
||||||
|
4. **Роль по умолчанию — настройка.** Не подошло ничего — берём настраиваемую роль, а не молчим. Значение по умолчанию выбрать и обосновать в отчёте; **в код не зашивать**.
|
||||||
|
|
||||||
|
Пропуск мимо хаба остаётся только на случай, когда маршрутизатор выключен целиком.
|
||||||
|
|
||||||
|
## P0-2. Предохранитель снимать нельзя
|
||||||
|
|
||||||
|
Исчерпанная цепочка **обязана** уходить в `next_call`, а не подставлять текст ошибки вместо ответа модели. Это уже стоило владельцу рабочего дня.
|
||||||
|
|
||||||
|
Требуется тест, который падает, если ответ роутера с `router_error` окажется в ответе Hermes.
|
||||||
|
|
||||||
|
Отдельно: включение маршрутизации не должно ломать Hermes при пустой конфигурации. Нет ни одного подключённого аккаунта — вызов уходит вниз, а не превращается в ошибку.
|
||||||
|
|
||||||
|
## P0-3. Видно, что происходит
|
||||||
|
|
||||||
|
Владелец должен понимать, что хаб теперь участвует в вызовах.
|
||||||
|
|
||||||
|
1. **В журнале событий** — какая роль выбрана, по какому признаку (явная, по модели, по сессии, по умолчанию) и какой профиль отработал.
|
||||||
|
2. **В аналитике** вызовы Hermes должны появиться. Сейчас там пусто именно потому, что до хаба ничего не доходит.
|
||||||
|
3. Признак выбора роли — не выдумка, а факт: если взята роль по умолчанию, так и написать.
|
||||||
|
|
||||||
|
## P0-4. Проверка на живом Hermes
|
||||||
|
|
||||||
|
Отчёт без этого не принимается.
|
||||||
|
|
||||||
|
1. Запустить Hermes, дать ему задачу, убедиться по журналу, что **вызов прошёл через хаб** и через ожидаемый аккаунт.
|
||||||
|
2. Проверить, что смена цепочки в интерфейсе меняет то, чем Hermes реально отвечает.
|
||||||
|
3. Проверить исчерпание: отключить первый аккаунт в цепочке и убедиться, что переключение произошло, а Hermes продолжил работать.
|
||||||
|
4. Проверить пустую конфигурацию: Hermes работает как раньше.
|
||||||
|
|
||||||
|
Пункт 2 — суть задания. Пока смена настройки в хабе не меняет поведение Hermes, задание не выполнено.
|
||||||
|
|
||||||
|
## P0-5. Аудит вторым проходом
|
||||||
|
|
||||||
|
1. **Угадывание по тексту запроса.** Его не должно появиться: правило «no guessing from prompts» введено осознанно. Признаки — только явные поля.
|
||||||
|
2. **Литеральные соответствия модель→роль** в коде. Их быть не должно, всё из конфигурации.
|
||||||
|
3. **Предохранитель на исчерпанную цепочку** — проверить отдельно, тестом и руками.
|
||||||
|
4. **Запустить с живым Hermes**, а не только тестами.
|
||||||
|
5. **Побочные изменения** объяснить.
|
||||||
|
6. **Пропущенный пункт назвать пропущенным.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Ограничения
|
||||||
|
|
||||||
|
- **Hermes не править.** Это чужой продукт; правка `conversation_loop.py` будет затираться при каждом его обновлении. Работать только с тем, что он уже передаёт.
|
||||||
|
- Зона: `hermes_plugin.py`, `router_engine.py`, `router_config.py`, `settings_service.py`, соответствующие тесты. Веб-клиент и адаптеры — зона A34, туда не заходить.
|
||||||
|
- Правило честности без исключений.
|
||||||
|
- Тег `v0.1.1` не создавать.
|
||||||
|
|
||||||
|
## Критерии приёмки
|
||||||
|
|
||||||
|
1. Ветка в `origin` от `review/a28-a31-fixes`, `git status` чист.
|
||||||
|
2. Вызов Hermes без роли **доходит до маршрутизатора**; проверено подачей того же набора аргументов, что в `conversation_loop.py:3221`.
|
||||||
|
3. Признак выбора роли записывается в журнал и различим: явная, по модели, по сессии, по умолчанию.
|
||||||
|
4. Роль по умолчанию настраивается, значение не зашито.
|
||||||
|
5. Изменение цепочки в интерфейсе меняет поведение живого Hermes; **приложить вывод**.
|
||||||
|
6. Исчерпанная цепочка уходит в `next_call`, текст ошибки роутера в ответ Hermes не попадает; есть тест.
|
||||||
|
7. Пустая конфигурация не ломает Hermes.
|
||||||
|
8. Вызовы Hermes видны в аналитике.
|
||||||
|
9. `ruff check .` чисто; релизный гейт не ухудшен.
|
||||||
|
10. Отчёт: `START_HEAD`, `FINAL_HEAD`, `origin/main`, `git status`, `X passed / Y skipped / Z failed`. На ветке ревьюера сейчас **475 passed, 2 skipped**.
|
||||||
|
|
||||||
|
## Главное
|
||||||
|
|
||||||
|
Хаб делается ради того, чтобы аккаунтами и лимитами управлять из одного места, и чтобы это управление действовало в Hermes. Сейчас оно не действует ни в одной точке: каждый вызов проходит мимо. Это самая важная задача в очереди — без неё всё остальное остаётся витриной.
|
||||||
|
|
||||||
|
## Порядок сдачи
|
||||||
|
Передать точный `FINAL_COMMIT_SHA`.
|
||||||
Loading…
Reference in a new issue