Files
knowledge-base/decisions/2026-06-18-german-hermes-agent-deploy.md

72 lines
13 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.
---
date: 2026-06-18
tags: [decision, ai, hermes, telegram, lxc, assistant]
---
# German — Hermes Agent как личный супер-ассистент Олега (LXC 141)
## Контекст
Олег попросил поставить **Hermes Agent** (NousResearch, open-source, MIT, Python, model-agnostic — из ресёрча [[../notes/claude/2026-06-08-145004-найди-аналог-openclaw-для-меня-нужен-аркестратор-и]]) на homelab. Сначала — «для тестирования самого Hermes через Telegram», затем расширил: **настроить как супер-помощника с полным доступом к базе знаний**. Имя — **Герман** (German).
## Что развёрнуто
- **LXC 141 `german`** (10.0.0.141, Debian 12, 2 vCPU / 3 GB / 12 GB на local-lvm, nesting, unprivileged, onboot=1). Создан из `debian-12-standard 12.12`.
- **Hermes Agent v0.16.0** — установлен официальным `install.sh --non-interactive --skip-setup`. Код `/usr/local/lib/hermes-agent`, данные `/root/.hermes` (uv + managed Node).
- **Telegram-бот** «Герман Непомнящий» **@german_dttb_bot** (id 8885932329). Gateway = systemd `hermes-german.service` (`hermes gateway run --replace`, Restart=always, drain 210s, crash-guard, NoNewPrivileges/PrivateTmp).
## Модель
- Провайдер **OmniRoute** (OpenAI-совместимый шлюз на LXC 132): `base_url http://10.0.0.179:20128/v1`.
- **Активная модель: `cc/claude-opus-4-8`** (Opus 4.8 via Max). **Fallback-цепочка (2026-06-21): `cx/gpt-5.5` → `cc/claude-sonnet-4-6`; `api_max_retries: 1`.** (Было `api_max_retries: 6` + fallback `cc/sonnet-4-6` первым → German отвечал по **533с/9мин**: 6 ретраев с бэкоффом на каждой капнутой Max-модели. Фикс: 1 попытка + независимый `cx/gpt-5.5` первым → failover за секунды. Ответ 13с после фикса.) Fallback в Hermes **срабатывает на 400 «out of extra usage»** (проверено по логам cc→fallback) — поэтому рабочий fallback критичен. ⚠️ Прежний fallback `kr/claude-sonnet-4.5` сдох: **OmniRoute потерял креды провайдера Kiro** («No credentials for provider: kiro», 2026-06-19) → German падал ПОЛНОСТЬЮ (и primary капнут, и fallback мёртв). `cx/gpt-5.5` выбран финальным fallback потому, что Codex — **отдельный провайдер, не Max** → переживает полный кап Max-квоты. Проверено: cx/gpt-5.5 работает через агентский цикл с тулами+KB.
- **КОРЕНЬ 400 «out of extra usage» (исправленное понимание):** это НЕ персистентное исчерпание квоты. Олег верно заметил: «если бы лимиты — ты (Claude на Opus 4.8) тоже бы не работал». Проверка по факту 2026-06-18 ~22:30: `curl cc/claude-opus-4-8` к OmniRoute с system-prompt 14B / 2КБ / 8КБ → **все 200**. То есть 400 в 19:11/19:18 был **транзиентным** — краткий кап 5-часового окна Max в момент пиковой нагрузки (Max делят this-session/German/openclaw/swarmclaw/code-server). Окно отпускает само. Если 400 участятся — включить overflow (pay-as-you-go) на claude.ai/settings/usage.
- ⚠️ **`cc/claude-opus-4-8` (Max) ФЛАПАЕТ** `400: You're out of extra usage`: прямой curl к OmniRoute то проходит (19:16), то нет — реальные запросы Олега падали (19:11 «Привет», 19:18 «Бенелюкс», разные request_id). Причина: включённая Max-квота Opus в текущем окне исчерпана (overflow/pay-as-you-go выключен), а окно делят **openclaw (cc/opus-4-7) + swarmclaw (cc/opus-4-8) + code-server** через тот же OmniRoute `cc/*`.
- **400 — non-retryable BadRequestError → fallback НЕ срабатывает** (Hermes уводит в fallback только на rate-limit/5xx/connection). Поэтому fallback на Sonnet от Opus-400 не спасает.
- **Решение: primary = `kr/claude-sonnet-4.5`** (free, Kiro/AWS, не флапает, не ест Max-квоту, не конкурирует с другими ботами Олега). Проверено: запрос «Бенелюкс» через тулы вернул корректную сводку по KB.
- Вернуть Opus, когда окно Max освободится / включён overflow: `sed -i 's|kr/claude-sonnet-4.5|cc/claude-opus-4-8|' /root/.hermes/config.yaml && systemctl restart hermes-german`.
- Доп. правки сессии: отключён `display.platforms.telegram.streaming` (было задвоение сообщений в Telegram); fallback-цепочка протестирована (формат `fallback_providers: [{provider,model,base_url}]`, ключ берётся из env — работает), но снята, т.к. от 400 не помогает.
- Ключ — `OPENAI_API_KEY` + `OPENAI_BASE_URL` в `/root/.hermes/.env` (chmod 600).
- `auxiliary` (vision/web_extract/compression/session_search) → `provider: main` — иначе лезли бы в OpenRouter (ключа нет) и падали.
- ⚠️ **Грабля для будущего:** оба воркфлоу-ревьюера по коду утверждали, что на приватный IP-эндпоинт `OPENAI_API_KEY` из env «гейтится по хосту» и нужен `model.api_key: ${OPENAI_API_KEY}` в config.yaml, иначе 401. **Эмпирически опровергнуто** — CLI- и gateway-вызовы к 10.0.0.179 проходят с env-ключом без `model.api_key`. Если когда-нибудь начнёт давать 401 на первом вызове модели — добавить `api_key: ${OPENAI_API_KEY}` в секцию `model:` и `systemctl restart hermes-german`.
## База знаний (KB)
- Зеркало vault клонировано в **`/root/german/knowledge-base`** (workspace = `/root/german`, systemd `WorkingDirectory`).
- Обновление: `/root/kb-pull.sh` (cron `*/15`) делает `git fetch + git reset --hard origin/main` — это **read-only зеркало** (правки агента там затираются; для своих заметок у него `/root/german/notes` + native memory).
- **Безопасность кред:** из `.git/config` workspace убран токен Gitea (был `https://oleg:TOKEN@...`); remote сделан tokenless, креды вынесены в `/root/.git-credentials` (chmod 600, вне workspace).
- Доступ Германа к KB: тулсеты **file** (read_file/grep) + **terminal**. Семантического индекса нет — навигация через `knowledge-base/CLAUDE.md` → grep. Это прописано в `SOUL.md`.
## Конфиг (ключевое, `/root/.hermes/config.yaml`, 600)
- `platform_toolsets.telegram`**явный список** `[web, terminal, file, memory, todo, skills, session_search, tts]` (+ kanban по дефолту).
- **Почему не пресет `hermes-telegram`:** security-ревью (читало `toolsets.py:440`) показало, что пресет тянет полный core-набор (**browser, execute_code, computer_use**), а `disabled_toolsets` их НЕ убирает (категории — web/browser/terminal/code_execution/image_gen). Для бота с доступом к секретам это лишние каналы исхода. Проверено `hermes tools --summary`: на Telegram **9/26 тулов, browser/code-exec/computer-use отсутствуют**.
- **web-поиск без ключей:** `web.backend: ddgs` (DuckDuckGo, бесплатно). Browser недоступен (требует BROWSERBASE_API_KEY) — веб через ddgs + `curl` в terminal.
- `SOUL.md` (личность «Герман», навигация по KB, правила безопасности) — 600.
- Прочее: `display.language: ru`, `session_reset: idle 2880m`, `memory_enabled`, `security.redact_secrets: true` + `tirith_enabled: true`, `stt` (faster-whisper base — голосовые транскрибируются).
## Безопасность — модель угроз
- **Доступ только у Олега**: `TELEGRAM_ALLOWED_USERS=1292155421`, `guest_mode: false`, `unauthorized_dm_behavior: ignore`. Проверено по коду (`telegram.py`/`authz_mixin.py`): пустой allowlist = deny-all, fail-closed. Чужие сообщения молча игнорируются.
- terminal=local + root = Герман может выполнять любой bash на 141 и читать секреты из KB. **Это намеренно** (DevOps-ассистент), защита держится на allowlist. Жёсткий systemd-сэндбокс (ProtectSystem/ProtectHome) не ставил — сломал бы функции. Будущее ужесточение: отдельный непривилегированный юзер.
- `redact_secrets: true` — секреты вычищаются из tool output/логов/ответов перед доставкой.
## Эксплуатация
- Статус/логи: `systemctl status hermes-german`; логи **в файлах** `/root/.hermes/logs/{gateway,agent}.log` (не в journal).
- Рестарт после правок config/SOUL: `systemctl restart hermes-german` (дренаж до ~180с).
- Бэкапы конфигов: `/root/.hermes/config.yaml.bak.*`, `.env.bak.orig`.
- Эндпоинт-санити: `curl -s http://10.0.0.179:20128/v1/models` (должен отдавать cc/claude-opus-4-8).
## Проверено
- ✅ Установка, конфиг грузится, `hermes status` → Model cc/claude-opus-4-8 / Custom endpoint.
- ✅ End-to-end CLI: вопрос по KB → grep → верный ответ (IP openclaw 10.0.0.239).
- ✅ Прямой вызов OmniRoute cc/claude-opus-4-8 (стрим).
- ✅ Gateway: `✓ telegram connected` (polling), getMe ok, allowlist на Олега.
- ✅ Тулсет Telegram безопасен (без browser/code-exec/computer-use).
-**Live gateway-раунд-трип**: сообщение Олега «Привет» прошло Telegram→allowlist→OmniRoute→Anthropic (ключ резолвится в gateway без `model.api_key` — опасения ревью не подтвердились). На cc/opus упёрлись в квоту Max (400); на `kr/claude-sonnet-4.5` — KB-вопрос через тулы вернул верный IP.
## NetBird (добавлено 2026-06-18)
Чтобы German дотягивался до клиентских площадок в mesh (как openclaw/swarmclaw).
- **IP `100.70.99.82`**, `german.netbird.cloud`, группа **Claude-Diag**. Setup-key в [[../projects/dttb/credentials.md]] (`64DF527E-…`, создан через PAT/API).
- **Установка только через apt-репозиторий** `pkgs.netbird.io` — официальный `curl install.sh | sh` редиректит на `github.com/netbirdio/.../releases/download/v0.73.0/install.sh`, а **GitHub releases таймаутят из RU-сети** (connection timed out). Шаги: добавить ключ `pkgs.netbird.io/debian/public.key` в keyring + `deb [...] https://pkgs.netbird.io/debian stable main``apt install netbird` (v0.73.0). Работает в unprivileged-LXC через `nesting=1` + kernel WireGuard (wt0, tun не нужен).
- **Enroll с `--disable-dns`**: `netbird up --setup-key <KEY> --disable-dns --hostname german`. Это сознательно — на LXC 132/137 NetBird-DNS (resolv.conf через wt0) уже ломал связность; German держит свой `1.1.1.1`/`8.8.8.8` и ходит по IP. Magic-DNS `*.netbird.cloud` в контейнере НЕ резолвится (`search dttb.ru` хайджачит) — **только по IP 100.70.x**.
- Проверено: openclaw `100.70.167.54:18789` → 200; github/telegram/OmniRoute/DNS целы; `netbird` autostart enabled; hermes-german не пострадал.
## TODO / на будущее
- Fallback-цепочка (cc/sonnet-4-6, kr/sonnet-4.5, cx/gpt-5.4) — формат `fallback_providers` с `api_key_env` (НЕ `${VAR}` — путь резолва фолбэков не делает env-подстановку). Пока не ставил (primary надёжен).
- Настоящий web-search через self-hosted SearXNG (`SEARXNG_URL`) или ключ Tavily/Firecrawl — если ddgs будет мало.
- Рассмотреть запуск под non-root юзером для уменьшения blast radius.