hermes-hub/agents/inbox/2026-08-30-A35-hub-must-actually-route-hermes.md
Hermes Team 2f4866fffa 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>
2026-08-30 21:51:23 +07:00

11 KiB
Raw Permalink Blame History

Задание 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:

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:

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.