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

60 lines
8.6 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`.
- **Активная модель: `kr/claude-sonnet-4.5`** (free, Kiro/AWS — основная free-модель из CLAUDE.md).
- ⚠️ **Изначально ставил `cc/claude-opus-4-8` (Max), но на первом же реальном сообщении Олега Anthropic вернул `400: You're out of extra usage` — квота Max исчерпана.** Весь конвейер при этом отработал (Telegram→allowlist→OmniRoute→Anthropic), упёрлись только в биллинг. 400 — non-retryable, на fallback НЕ уходит → primary должен быть рабочей моделью. Переключил на бесплатную `kr/claude-sonnet-4.5`. Вернуть Opus: `sed -i 's|kr/claude-sonnet-4.5|cc/claude-opus-4-8|' config.yaml && systemctl restart hermes-german` (когда квота Max сбросится).
- Ключ — `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.
## 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.