Владелец заметил, что настройки 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>
11 KiB
Задание 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 всё-таки передаёт
Порядок разрешения, сверху вниз:
- Явная роль — если когда-нибудь появится в
kwargs,request,metadata. Работает уже сейчас, не ломать. - По модели и провайдеру. Hermes передаёт
modelиprovider. Если запрошенная модель или провайдер — основные у какой-то роли, берём её. Соответствие строится из конфигурации, а не из литералов в коде. - По устойчивости сессии. Передаётся
session_id. Если для этой сессии роль уже определялась, брать её же: механизмsession_affinityесть и работает. - Роль по умолчанию — настройка. Не подошло ничего — берём настраиваемую роль, а не молчим. Значение по умолчанию выбрать и обосновать в отчёте; в код не зашивать.
Пропуск мимо хаба остаётся только на случай, когда маршрутизатор выключен целиком.
P0-2. Предохранитель снимать нельзя
Исчерпанная цепочка обязана уходить в next_call, а не подставлять текст ошибки вместо ответа модели. Это уже стоило владельцу рабочего дня.
Требуется тест, который падает, если ответ роутера с router_error окажется в ответе Hermes.
Отдельно: включение маршрутизации не должно ломать Hermes при пустой конфигурации. Нет ни одного подключённого аккаунта — вызов уходит вниз, а не превращается в ошибку.
P0-3. Видно, что происходит
Владелец должен понимать, что хаб теперь участвует в вызовах.
- В журнале событий — какая роль выбрана, по какому признаку (явная, по модели, по сессии, по умолчанию) и какой профиль отработал.
- В аналитике вызовы Hermes должны появиться. Сейчас там пусто именно потому, что до хаба ничего не доходит.
- Признак выбора роли — не выдумка, а факт: если взята роль по умолчанию, так и написать.
P0-4. Проверка на живом Hermes
Отчёт без этого не принимается.
- Запустить Hermes, дать ему задачу, убедиться по журналу, что вызов прошёл через хаб и через ожидаемый аккаунт.
- Проверить, что смена цепочки в интерфейсе меняет то, чем Hermes реально отвечает.
- Проверить исчерпание: отключить первый аккаунт в цепочке и убедиться, что переключение произошло, а Hermes продолжил работать.
- Проверить пустую конфигурацию: Hermes работает как раньше.
Пункт 2 — суть задания. Пока смена настройки в хабе не меняет поведение Hermes, задание не выполнено.
P0-5. Аудит вторым проходом
- Угадывание по тексту запроса. Его не должно появиться: правило «no guessing from prompts» введено осознанно. Признаки — только явные поля.
- Литеральные соответствия модель→роль в коде. Их быть не должно, всё из конфигурации.
- Предохранитель на исчерпанную цепочку — проверить отдельно, тестом и руками.
- Запустить с живым Hermes, а не только тестами.
- Побочные изменения объяснить.
- Пропущенный пункт назвать пропущенным.
Ограничения
- Hermes не править. Это чужой продукт; правка
conversation_loop.pyбудет затираться при каждом его обновлении. Работать только с тем, что он уже передаёт. - Зона:
hermes_plugin.py,router_engine.py,router_config.py,settings_service.py, соответствующие тесты. Веб-клиент и адаптеры — зона A34, туда не заходить. - Правило честности без исключений.
- Тег
v0.1.1не создавать.
Критерии приёмки
- Ветка в
originотreview/a28-a31-fixes,git statusчист. - Вызов Hermes без роли доходит до маршрутизатора; проверено подачей того же набора аргументов, что в
conversation_loop.py:3221. - Признак выбора роли записывается в журнал и различим: явная, по модели, по сессии, по умолчанию.
- Роль по умолчанию настраивается, значение не зашито.
- Изменение цепочки в интерфейсе меняет поведение живого Hermes; приложить вывод.
- Исчерпанная цепочка уходит в
next_call, текст ошибки роутера в ответ Hermes не попадает; есть тест. - Пустая конфигурация не ломает Hermes.
- Вызовы Hermes видны в аналитике.
ruff check .чисто; релизный гейт не ухудшен.- Отчёт:
START_HEAD,FINAL_HEAD,origin/main,git status,X passed / Y skipped / Z failed. На ветке ревьюера сейчас 475 passed, 2 skipped.
Главное
Хаб делается ради того, чтобы аккаунтами и лимитами управлять из одного места, и чтобы это управление действовало в Hermes. Сейчас оно не действует ни в одной точке: каждый вызов проходит мимо. Это самая важная задача в очереди — без неё всё остальное остаётся витриной.
Порядок сдачи
Передать точный FINAL_COMMIT_SHA.