Files
knowledge-base/decisions/2026-06-20-german-hermes-out-of-usage.md
2026-06-23 13:58:02 +03:00

27 KiB
Raw Blame History

date, type, tags
date type tags
2026-06-20 decision
dttb
german
hermes
omniroute
cliproxy
max
out-of-usage
troubleshooting

German (Hermes LXC 141) — 400 out of extra usage на cc/* через OmniRoute

Handoff-документ. Проблема НЕ закрыта полностью на 2026-06-20. Сделаны два смягчающих фикса (привязка аккаунта + retry-патч), но остаточные пики out of usage остаются. Окончательное решение — за Олегом (Extra usage / разгрузка нагрузки). Ниже — всё, чтобы продолжить без повторного прохода по тупикам.

Симптом

German-бот (@german_dttb_bot, Hermes Agent v0.16.0, LXC 141) периодически отвечает ошибкой вместо ответа:

400 - You're out of extra usage. Add more at claude.ai/settings/usage and keep going.

Интермиттентно: ~7-12% запросов cc/* падают, остальные 200. Бот «то работает, то нет».

Что НЕ является причиной (проверено — НЕ тратить время заново)

  1. НЕ баг версии OmniRoute. Обновил 3.8.29 → 3.8.30 (npm install omniroute@latest в /root/.npm/_npx/cb5891f90ae65d14, ExecStart уже dist/server.js). Ошибка осталась. (В 2026-06-09 похожая ошибка реально была багом 3.8.7 — но не в этот раз.)
  2. НЕ реальный лимит подписки Олега. Скриншоты Claude Code Plan usage: 5h-limit 43→53%, Weekly 5-6%, Sonnet 0% — свободно. Олег прав: «лимитов нет».
  3. НЕ размер запроса. Тест: контекст 357K симв (~90K токенов) → 200 на opus-4-7/opus-4-8/sonnet. Размер ни при чём.

Архитектура (как cc/* доходит до Anthropic)

German (LXC 141) → OmniRoute (LXC 132, 10.0.0.179:20128) → cliproxy (/root/.cli-proxy-api:8319) → Anthropic Max OAuth
  • В OmniRoute provider_connections (БД /root/.omniroute/storage.sqlite) — два Claude-аккаунта:
    • e8a70f39-564d-4529-a911-4b5d0a47e512 — priority 1, перегружен: SwarmClaw (8 агентов) + code-server жгут через него ~13К opus-4-8/час → сыпет 400/429/502/529.
    • 883152e1-7160-4d5c-90eb-88822d50db31 = batlaew@gmail.com — рабочий аккаунт Олега (тот, что в скриншоте 53%), свободнее, почти всё 200.
  • Ключи German/openclaw в OmniRoute api_keys: claw и test-key (оба sk-225e902dc95ff… — это и есть OPENAI_API_KEY в /root/.hermes/.env German). SwarmClaw-нагрузка (13К) идёт БЕЗ api_key_name (мастер-путь), отдельно от этих ключей.

Главная диагностическая команда (с неё начинать в будущем)

# на LXC 132 (через pct exec 132 с Proxmox 10.0.0.250 root/1qaz!QAZ):
sqlite3 -column /root/.omniroute/storage.sqlite \
 "SELECT model, account, status, COUNT(*) n FROM call_logs WHERE model LIKE '%opus%' GROUP BY model,account,status ORDER BY n DESC LIMIT 20"

Поля account + status сразу показывают, какой аккаунт сыпет 400 и какой 200. Не гадать про версию/лимит — смотреть сюда.

Что СДЕЛАНО (два смягчающих фикса)

1. Привязка ключей German/openclaw к рабочему аккаунту

Ключи claw/test-key имели пустой allowed_connections → ротация кидала German на перегруженный e8a70f39. Привязал к batlaew:

# на LXC 132. Формат allowed_connections = JSON-массив id (JSON.parse, length>0 ограничивает)
sqlite3 /root/.omniroute/storage.sqlite \
 "UPDATE api_keys SET allowed_connections='[\"883152e1-7160-4d5c-90eb-88822d50db31\"]' WHERE name IN ('claw','test-key')"
systemctl restart omniroute   # сбросить кэш ключей
  • Бэкап БД: /root/.omniroute/storage.sqlite.bak-keybind.
  • Проверено: 10/10 запросов German-ключом → account=batlaew. SwarmClaw не затронут (другой путь).
  • Откат: UPDATE api_keys SET allowed_connections=NULL WHERE name IN ('claw','test-key').

2. Retry-патч Hermes (out-of-usage → retryable)

До патча Hermes считал 400 out of extra usage фатальной (Aborting). Сделал retryable:

  • Файл: /usr/local/lib/hermes-agent/agent/error_classifier.py — добавлен паттерн status_code==400 and "out of extra usage" in error_msg → FailoverReason.rate_limit, retryable=True (перед «── 2. HTTP status code classification»).
  • config.yaml: agent.api_max_retries 3 → 5.
  • Проверено вызовом classify_api_error: на этот текст RETRYABLE=True, обычные 400 остаются non-retryable.
  • Бэкап: error_classifier.py.bak-outofusage. Переналожатель: /root/hermes-patch-outofusage.py — запустить ПОСЛЕ обновления Hermes (патч в коде слетает).

Текущий статус (на 2026-06-20 ~16:00)

  • German отвечает, когда нет пика (проверено в логах: 14:16 inbound ? → ответ; и прямыми тестами все модели 200).
  • В длинные пики (>~50с, ретраи 5×40с не хватает) German всё ещё возвращает ошибку «model provider failed after retries».
  • Пульсация out of usage остаётся на ОБОИХ аккаунтах (e8a70f39 и batlaew) периодически — ДАЖЕ при свободном 5h (53%). Это похоже на burst/моментную механику Anthropic Max, из логов OmniRoute до конца НЕ объяснено.

Что осталось — варианты окончательного решения (ВЫБОР ОЛЕГА)

  1. Extra usage onclaude.ai/settings/usage, секция Extra usage (не Plan usage). Поднять spend limit → overflow в пик оплачивается, out of usage исчезает для ВСЕХ ботов. Самое прямое, платно, в биллинге Олега.
  2. Разгрузить главного пожирателя — SwarmClaw (8 агентов) + code-server с cc/claude-opus-4-8 (~13К/час) на Sonnet/меньше агентов → давление на Max-пул/burst падает.
  3. Восстановить не-Max тракт для German (вывести из Max-пула). На 2026-06-20 ВСЕ мертвы: Kiro (402/нет кредов), Codex (not supported/таймаут), gemini-cli (403 нет лицензии), GLM (429 баланс 0).
  4. Поднять retry-окно German ещё (api_max_retries + backoff) — пережидать и длинные пики, ценой задержки ответа на 1-2 мин (плохой UX).

Связанные факты

Продолжение 2026-06-20 (вечер) — e8a70f39 МЁРТВ, изоляция невозможна

Олег выбрал «реанимировать e8a70f39» → не сработало по жёсткой причине:

  • Флипнул is_active=1 (priority 1). При первом master-вызове OmniRoute попытался обновить протухший токен (expired 06-16) и получил Refresh token consumed (unrecoverable_refresh)авто-выключил аккаунт обратно (is_active=0), вызов свалился на batlaew (200). Это и есть причина, почему e8a70f39 был выключен 06-16: его OAuth refresh-токен сожжён безвозвратно. Поднять без свежего OAuth-логина нельзя. БД вернулась в исходное сама (бэкап storage.sqlite.bak-reactivate-e8a70f39).
  • Вывод: рабочий Claude-аккаунт в OmniRoute РОВНО ОДИН — batlaew. Двух-пуловая изоляция (фикс #1) больше неактуальна — изолировать не на что. Привязка ключей к batlaew стала бессмысленной (он и так единственный), но не вредит.

Профиль «почему то работает, то нет» (опровергает «весь день без ошибок»)

German-ключи (claw/test-key) сегодня по часам: 09 18×200/3×400/14×429, 13 2×200/**18×400**, 14 8×200/6×400, 15 24×200 (чисто), 16 1×200/6×400. То есть весь день интермиттирующие burst-провалы, худший в 13:00; в 15:00 — идеально. Олег тестировал в 16:xx → попал в burst. Спайки 1-в-1 со спайками out of extra usage у batlaew (09:5, 12:9, 13:21, 14:7, 16:7). «Лимитов нет» объясняется так: дашборд Plan usage (5h 53%) — сглаженное среднее и burst не показывает. Блок out of extra usage — это потолок Extra usage (pay-as-you-go overflow), который стоит на $0. В момент пика суммарный спрос на batlaew (German + SwarmClaw + code-server, все сошлись на нём после смерти e8a70f39 06-16) превышает включённый в план объём, а раз overflow $0 — Anthropic жёстко режет вместо очереди.

Сделано в этом проходе

  • German default-модель cc/claude-opus-4-7cc/claude-opus-4-8 (запрос Олега). Бэкап config.yaml.bak-opus48. Проверено: German-ключ → opus-4-8 → 200. Внимание: opus-4-8 НЕ снижает out-of-usage — тот же аккаунт/пул.

Реальные варианты (e8a70f39 вычеркнут) — ВЫБОР ОЛЕГА

  1. Extra usage ON на batlaew (claude.ai/settings/usage → Extra usage, не Plan) — единственное, что убирает out-of-usage насовсем при одном аккаунте. Платно, биллинг Олега. Рекомендация #1.
  2. Разгрузить burst-пожирателей — SwarmClaw (8 агентов) + code-server с cc/opus-4-8 на Sonnet/меньше агентов → суммарный пик влезает в план batlaew.
  3. Свежий OAuth второго Max-аккаунта в OmniRoute (заново залогинить — хоть тот же, что был e8a70f39, хоть новый) → восстановить двух-пуловую изоляцию. Требует интерактивного OAuth (Олег).
  4. Реальный backoff ретраев German (сейчас 5 ретраев летят за <1с — бесполезно против burst в секунды-минуту). Пережидать пик ценой задержки ответа.

Продолжение 2026-06-20 (вечер-2) — СМЕНА СТРАТЕГИИ: почему Антошка работает, а German нет

Олег ткнул верно: openclaw (Антошка) на том же OmniRoute/Opus 4.8 работает стабильно → теория «account-level cap» неполна. Сравнил два бота эмпирически (call_logs) — 3 реальные разницы:

  1. Объём. Антошка (ключ claw) сегодня = 2 вызова; German (test-key) = 114 (+ master/SwarmClaw+codeserver 130). German — половина нагрузки batlaew и worst fail-rate (39/114=34%). Антошка «работает» во многом потому что лёгкий → редко попадает в burst.
  2. Фоллбэк-цепочка. У German была cc/claude-haiku → claude/claude-haiku (ОБА Max → бьются в тот же out of extra usage, что и opus — проверено: sonnet-4-6 тоже ловит этот 400). У Антошки последний фоллбэк = kr/claude-sonnet-4.5 (Kiro, FREE, не-Max) → когда Max в пике, Антошка уезжает на не-Max и продолжает отвечать.
  3. Пин ключа (КОРЕНЬ). Фикс #1 (allowed_connections=['883152e1'/batlaew]) делался против перегруженного e8a70f39 — но тот мёртв. Пин же запер German на единственном перегруженном batlaew: эскейп-маршруты kr//cx/ ключом German отдавали 400 (connection-not-allowed). Пин из «защиты» превратился в «ловушку». ../../.claude/projects/-Users-ai-knowledge-base/memory/feedback_root_cause_recurring: лечил симптом, корень — в своём же конфиге.

Сделано (привёл German к схеме Антошки, всё в рамках моих прав, без биллинга)

  1. Снят пин с test-key и claw: UPDATE api_keys SET allowed_connections=NULL WHERE name IN ('test-key','claw'). Теперь opus-4-8 всё равно → batlaew (других Max-аккаунтов нет), а kr//cx/ доходят до своих провайдеров. Проверено: до — kr/cx=400, после — opus-4-8=200, kr/cx доходят (402/timeout = флап free-пулов, но маршрут открыт).
  2. Фоллбэк-цепочка German переписана как у Антошки: cc/claude-sonnet-4-6 → kr/claude-sonnet-4.5 → cx/gpt-5.5 (выкинул мёртвый haiku→haiku). Бэкап config.yaml.bak-fallback-*.
  3. Primary = cc/claude-opus-4-8. German стабилен (NRestarts=0), opus-4-8 → 200.

Честный остаток (Олегу знать)

Это не делает German неуязвимым — free-эскейпы (Kiro/Codex/GLM) сейчас сами полудохлые (Kiro: «reached the limit» 402 / «fetch failed» 502 / 429; Codex throttled; GLM баланс 0). В ГЛУБОКИЙ burst, когда и batlaew capped, и free-пулы лежат — German всё ещё может блипнуть (как блипнул бы и Антошка под такой нагрузкой). German теперь архитектурно равен рабочему боту, а не сломан. Для полной неуязвимости при тяжёлой нагрузке всё равно нужно одно из: Extra usage ON на batlaew / разгрузка master-пути (SwarmClaw 8 агентов + code-server = вторая половина нагрузки batlaew) / свежий 2-й Max-аккаунт (OAuth). Возможный твик при рецидиве: снизить api_max_retries 5→3 (сейчас burst → шторм 5×4 тира вызовов, сам прогревает cap).

Продолжение 2026-06-20 (вечер-3) — ДОКАЗАНО: это всё-таки account-level cap, протокол ни при чём

Олег давил: «дело не в лимитах, почему Антошка работает». Проверил гипотезу «формат запроса»:

  • Эндпоинт-разница реальна: Антошка (claw) шлёт нативный Anthropic /v1/messages (source_format=claude), German (test-key) — OpenAI-формат /v1/chat/completions+/v1/responses. История: claw 448×200 / 0×400; /v1/messages суммарно 355×200 / 0 out-of-usage, а ВСЕ 396 out of extra usage — на /v1/chat/completions. Выглядело как корень.
  • Перевёл German primary на provider: anthropic + /v1/messages (+ ANTHROPIC_API_KEY в .env). Подтвердил: трафик пошёл path=/v1/messages source_format=claude. И всё равно поймал out of extra usage 400 (17:11/17:12 на opus-4-8 через /v1/messages; в 17:04 был 200 — т.е. интермиттентно).
  • РЕШАЮЩИЙ ТЕСТ: бил /v1/messages opus-4-8 обоими ключами (claw Антошки + test-key German) залпами. В пик — оба 400, вне пика — оба 200×5. Ключ Антошки ловит ту же ошибку. Значит «0×400 у claw» в истории = следствие МАЛОГО ОБЪЁМА (claw сегодня 2 вызова против 114 у German), а не иммунитета протокола.
  • Вывод: out of extra usageaccount-level cap на batlaew, интермиттентный (burst), бьёт по ЛЮБОМУ пути (/v1/messages и /chat/completions) и ЛЮБОЙ модели (opus/sonnet/haiku — всё проверено). Антошка «работает» только потому что лёгкий. Дашборд Олега = Plan usage (5h-среднее, 53%), а режет потолок Extra usage (overflow) = $0. Это и есть лимит, просто не тот, что на дашборде.
  • Откат: протокол-правку вернул к проверенному provider: custom+/v1 (anthropic-режим не помог и не проверен на tool-нагрузке German — спекулятивная правка). ANTHROPIC_API_KEY убран. Бэкап отката config.yaml.bak-anthropic-170948.

Итоговое состояние German (что осталось включённым)

  • primary cc/claude-opus-4-8 (custom/openai-compat, проверенный путь), фоллбэк cc/sonnet-4-6 → kr/sonnet-4.5 → cx/gpt-5.5, ключ распинён (NULL). active, opus-4-8→200.

Финал (без иллюзий): убрать out of extra usage можно только так

  1. Extra usage ON на batlaewclaude.ai/settings/usage → секция Extra usage (не Plan). Это буквально то, что просит текст ошибки. Снимает cap для всех.
  2. Срезать конкурентную burst-нагрузку на batlaew: SwarmClaw (8 агентов) + code-server (cc/opus-4-8) = вторая половина трафика, льют параллельно → создают пики. Throttle/Sonnet/меньше агентов.
  3. German усиливает пики своим retry-штормом (5 ретраев × 4 тира мгновенно). Снизить api_max_retries 5→3 — меньше шторм, меньше вклад в cap.
  4. Свежий 2-й Max-аккаунт (OAuth) — изоляция German на отдельный пул.

Продолжение 2026-06-20 (вечер-4) — SwarmClaw НЕ ест лимит + фикс «работает» через overloaded-backoff

Олег: «SwarmClaw 3 дня не юзаю, как он ест лимит?» — прав, проверил:

  • Master-путь на batlaew (opus) по дням: 06-17=268, 06-18=591, 06-19=82, 06-20=74. Тяжёлый поток был 3 дня назад, сошёл на нет. SwarmClaw сейчас лимит НЕ ест — прежняя атрибуция неверна для текущего момента.
  • Крупнейший потребитель СЕЙЧАС — сам German: opus-токены batlaew сегодня — test-key(German) 916K fresh in / 877K cache; (master) 394K; claw(Антошка) 83K. German грузит большой KB-контекст в каждый ход × tool-loop → ест в 2.3× больше master и 11× больше Антошки. Антошка лёгкий → не упирается.

Почему backoff раньше «не работал» (казалось мгновенным)

В логе 16:17 5 ретраев были на timestamp 16:17:10 — иллюзия от буферизации (_buffer_vprint флашится разом). Реально backoff ЕСТЬ: conversation_loop.py:3439 jittered_backoff(base_delay=2.0, max_delay=60.0) + respects Retry-After. НО для rate_limit есть eager-failover (2764): при наличии фоллбэк-цепочки Hermes сразу прыгает на следующую модель, минуя ожидание opus — и каскадит через дохлые free-пулы (kr 400/cx 429) → быстро сдаётся.

Фикс «чтобы работал» (выбор Олега делегирован мне)

  1. out-of-usage классифицирован как FailoverReason.overloaded (было rate_limit) в error_classifier.py:674. overloaded НЕ триггерит eager-failover (2764 ловит только rate_limit/billing) → German пережидает burst на самом opus-4-8 с backoff (2с→60с jittered), а не каскадит на мёртвые фоллбэки. Проверено classify_api_error: out-of-usage→overloaded/retryable=True; обычная 400→model_not_found/non-retryable (узкий паттерн). Бэкап error_classifier.py.bak-overloaded-*, переналожатель /root/hermes-patch-outofusage.py обновлён (rate_limit→overloaded).
  2. api_max_retries 5→6 — окно пережидания ~1-2 мин (jittered 2с..60с × 6).
  3. Сохранены: opus-4-8 primary, распин ключа, цепочка фоллбэков (теперь — последний резерв ПОСЛЕ ожидания opus).

Механика: batlaew кратко капается → German ждёт (2с,4с,8с…до 60с) и повторяет opus, ловя восстановление за ~1-2 мин, вместо мгновенной ошибки. Цена — в пик ответ на десятки секунд позже (но ОТВЕЧАЕТ). Это не победит длинный (>2 мин) аккаунт-аутаж, но такие редки; обычный burst — секунды. Полностью убирает out-of-usage всё равно только Extra usage ON / урезание контекста German (RAG вместо полного KB).

Продолжение 2026-06-20 (вечер-5) — НАСТОЯЩЕЕ различие German vs Антошка: агентный burst

Олег: «какие ещё идеи, почему German не работает, а Антошка да». Проверил оставшиеся гипотезы на уровне запроса:

  • Per-key лимиты (test-key vs claw): у обоих пусто — исключено.

  • Холодный кэш (идея: German простаивает → cache_ttl 5m протухает → дорогой re-create): ОПРОВЕРГНУТО. German кэшируется нормально (3 дня: cache_read 1.5M vs cache_creation 450K). Антошка кэш вообще не читает (read=0), но ему и не надо.

  • Тела запросов (max_tokens/thinking): артефакты OmniRoute хранятся усечённо (~572 симв) → ненадёжно.

  • ★ НАЙДЕНО — агентный burst вызовов:

    Антошка (claw) German (test-key)
    макс. opus-вызовов/мин 1 8 (стабильно 6-7)
    вызовов за 3 дня 13 158

    German — агентный (tool-loop): 1 сообщение Олега → каскад 6-8 Opus-вызовов/мин, каждый тащит ~45K-контекст → ~360K Opus-токенов залпом в минуту. Антошка — разговорный, 1 вызов на обращение. 5h-лимит Max взвешенный (Opus ~5× Sonnet), и минутный burst German пробивает мгновенную взвешенную планку Opus → out of extra usage. Дашборд (53%) = 5h-среднее, не минутный пик. Это и есть «почему German, а не Антошка» — частота Opus-вызовов на сообщение (агентность vs разговорность), не формат/ключ/кэш/KB.

Сделано

  • agent.max_turns 80 → 25 (goals.max_turns 20 не трогал) — ограничивает размер burst: одна сложная задача больше не выстрелит до 80 Opus-вызовов подряд. Бэкап config.yaml.bak-maxturns-*. German active, opus-4-8→200.

Полный набор активных мер для German (итог всей цепочки)

  1. primary cc/claude-opus-4-8; ключ распинён (NULL); фоллбэк cc/sonnet-4-6 → kr/sonnet-4.5 → cx/gpt-5.5 (резерв).
  2. out-of-usage → overloaded (backoff-пережидание burst на opus, не каскад на дохлые фоллбэки) + api_max_retries 6.
  3. max_turns 25 (меньше burst). Остаточный полный фикс (если рецидив): Sonnet 4.6 как primary (в ~5× легче по весу Max, «Sonnet 0%» — почти без лимита) ИЛИ Extra usage ON.

Урок (мне на будущее)

Я трижды выдал неверный диагноз (баг версии → реальный лимит → перегрузка пула), прежде чем дошёл до call_logs по account. При out of usage на cc/ — СНАЧАЛА call_logs GROUP BY account,status, потом гипотезы.* См. ../../.claude/projects/-Users-ai-knowledge-base/memory/feedback_root_cause_recurring.