После 6+ лет в инфраструктуре я решил нормально разобраться с автономными AI-агентами. Не с чат-ботами, которые красиво отвечают в окно, а с системами, которые умеют планировать работу, запускать команды, читать код и трогать инфраструктуру.

Для кого этот разбор#

Эта статья не про «поставил чат-бота и поигрался». Hermes Agent интересен тогда, когда агент получает реальные инструменты: терминал, файлы, GitHub, браузер, память, интеграции и право что-то менять.

Поэтому основной вопрос инфраструктурный: как дать агенту достаточно возможностей для работы и не превратить его в неконтролируемый root-скрипт с красивым интерфейсом.

Когда наткнулся на Hermes Agent от Nous Research, решил поднять его у себя на сервере и проверить без демо-режима: поможет ли он с текстами, рутиной, инфраструктурными задачами и долгими исследованиями.

Спойлер: получилось полезнее, чем ожидал. Базовая установка заняла около четырёх часов, а самые важные выводы появились не в промптах, а в инфраструктурных мелочах.

Что у меня было на старте#

Сервер на обычной виртуалке — 4 GB RAM, 60 GB SSD, Ubuntu 22.04. Ничего особенного. Есть домен для внешних сервисов через Selectel DNS. Docker, Caddy, базовые навыки администрирования — стандартный набор.

Опыт с AI? Работаю с Cursor для pet-проектов, экспериментировал с ML/AI инструментами в разработке. Но концепция автономных агентов, способных выполнять цепочки действий без участия разработчика, заинтересовала как новый подход к автоматизации.

Цель была простая: поднять агента, которым реально можно пользоваться — с Telegram, русским языком, отдельными профилями, контролем расходов и нормальной обвязкой вокруг сервера.

Подготовка сервера#

Первым делом настроил чистую виртуалку под задачу. Отдельный SSH-ключ для безопасности, смена стандартного порта 22 на нестандартный — базовые меры предосторожности.

Установка Hermes прошла через официальный скрипт:

curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash

Скрипт сам настроил Python-окружение, поставил зависимости и создал команду запуска. После установки запустил мастер настройки:

hermes setup

Мастер настройки оказался нормальным: по шагам провёл через выбор модели, провайдера (я выбрал OpenRouter за гибкость) и базовые инструменты. На этом этапе все основные компоненты уже работали через CLI.

Но мне нужен был доступ через Telegram и веб-интерфейс. Тут началось интересное.

Первые грабли: веб-панель и настройка доступа#

По умолчанию веб-панель слушает только localhost:9119. Для внешнего доступа нужно было прокинуть через reverse proxy. Я привык к Caddy, поэтому решил использовать его.

Первая попытка — обычная docker-сеть:

# docker-compose.yml
services:
  caddy:
    image: caddy:2
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
    networks:
      - hermes-net

И сразу же проблема: веб-панель Hermes выдаёт «Invalid Host header» при обращении через прокси. Оказалось, что в коде есть защита от DNS rebinding атак — проверяется заголовок Host.

Проблему решил примерно за 20 минут. Нужно было прокидывать правильный заголовок:

your-domain.com {
    reverse_proxy 127.0.0.1:9119 {
        header_up Host {upstream_hostport}
    }
}

Но это работает только если Caddy может обратиться к 127.0.0.1:9119 на хосте. С docker-сетями это не так просто. Проще всего оказалось использовать network_mode: host: Caddy работает прямо в сетевом пространстве хоста и без проблем достучится до веб-панели.

Да, изоляции меньше, но для собственного сервера это нормальный компромисс. Главное — правильно настроить firewall.

Безопасность: UFW + базовая аутентификация#

С самого начала хотел сделать всё безопасно. Веб-панель — опасная штука: через неё можно выполнять команды на сервере. Оставлять такое без защиты — самоубийство.

UFW настроил по минимуму:

  • SSH на нестандартном порту для безопасности
  • 80/tcp и 443/tcp для веба
  • 443/udp для HTTP/3
  • Всё остальное — запрещено по умолчанию

Веб-панель Hermes слушает 0.0.0.0:9119, но порт закрыт снаружи. Доступ только через Caddy с TLS.

Для аутентификации выбрал базовый пароль поверх HTTPS. Для первого запуска этого достаточно, OAuth можно прикрутить позже. В конфиге Hermes:

auth_required: true
auth_password: "сложный_пароль_который_нигде_не_светится"

Дополнительно в Caddyfile настроил IP whitelist — только с определённых адресов можно вообще достучаться до сервиса:

@allowed_ips remote_ip 1.2.3.4 5.6.7.8
abort @denied

Telegram Gateway и первые команды#

Настройка Telegram-бота — отдельная история. В документации всё выглядело просто, но на практике тоже были каверзные моменты.

Создал бота через @BotFather, получил токен. Дальше началось интересное: часть настройки шла уже через сам Hermes в Telegram-чате. Агент сам себя настраивал, понимая команды типа "настрой webhook" или "добавь мой токен".

Проблема оказалась в том, что webhook URL должен быть доступен извне по HTTPS. Мой Caddy уже был настроен с Let's Encrypt, но URL указывал на localhost. Поправил конфиг:

telegram:
  webhook_url: https://your-domain.com/webhook/telegram
  bot_token: "токен_от_BotFather"

После этого бот ожил и начал отвечать. После первой проверки стало понятно, что это не игрушка: агент отвечает на русском, держит контекст и может выполнять команды.

Но хотелось большего — чтобы он мог работать с веб-страницами, анализировать документы, создавать собственную базу знаний.

Firecrawl: локальный web scraper#

Hermes умеет работать с веб-страницами через различные сервисы. Firecrawl показался наиболее подходящим — open source, можно развернуть локально, не зависит от внешних API.

Клонировал репозиторий Firecrawl, настроил через docker-compose. В базовой конфигурации всё работает, но есть засада — потребление памяти.

По умолчанию Firecrawl запускает 8 worker'ов, каждый жрёт 200-300 MB RAM. На моём сервере с 4 GB это критично — остаётся слишком мало места для других сервисов.

Пришлось урезать лимиты:

NUM_WORKERS_PER_QUEUE=3
CRAWL_CONCURRENT_REQUESTS=4
MAX_CONCURRENT_JOBS=2
BROWSER_POOL_SIZE=2

После этого потребление упало до разумных 800 MB, и система перестала давить остальные сервисы.

В конфиге Hermes указал локальный адрес:

web:
  firecrawl_api_url: http://127.0.0.1:3002

После этого агент смог читать веб-страницы, вытаскивать из них структурированный текст и использовать это в исследованиях.

gbrain: централизованная память для агентов#

gbrain оказался самой интересной частью настройки. Это общая база знаний для агентов: можно сохранять факты, искать по ним и связывать страницы между собой.

От PGLite к Postgres и HTTP MCP#

Установка через Bun:

curl -fsSL https://bun.sh/install | bash
bun install -g github:garrytan/gbrain
gbrain init

Первый вариант был максимально простой: локальная PGLite-база и gbrain serve как stdio MCP-сервер. Для проверки гипотезы этого хватило, но быстро стало понятно, что файловая локальная база плохо подходит под несколько клиентов: Hermes на сервере, Cursor на ноутбуке, отдельные профили, будущие агенты.

Поэтому gbrain переехал на Postgres и стал отдельным HTTP MCP-сервисом:

gbrain serve --http --port 3131 --bind 127.0.0.1

Сейчас это не одноразовый процесс внутри сессии Hermes, а systemd-сервис gbrain-http.service с Postgres-бэкендом. Hermes подключается к нему по HTTP MCP на localhost:

mcp_servers:
  gbrain:
    url: http://127.0.0.1:3131/mcp
    headers:
      Authorization: Bearer ${MCP_GBRAIN_API_KEY}
    enabled: true

Важная деталь: это legacy bearer token, не OAuth client credentials. Токен для клиента создаётся командой:

gbrain auth create hermes-gateway

Команда показывает секрет один раз, дальше хранит только хэш. Список токенов можно проверить без раскрытия секрета:

gbrain auth list

Второй клиент к той же базе: Cursor на ноутбуке#

После перехода на HTTP MCP появилась нормальная схема для другого компьютера: наружу публикуется не Postgres, а адрес gbrain MCP через Caddy.

Для отдельного клиента создаётся свой токен:

gbrain auth create cursor-laptop

В Cursor (Settings → MCP → Add new MCP server) добавляется:

{
  "mcpServers": {
    "gbrain": {
      "url": "https://gbrain.your-domain.com/mcp",
      "headers": {
        "Authorization": "Bearer <токен_из_auth_create>"
      }
    }
  }
}

Это важнее, чем звучит. Раньше я думал в сторону внешнего Postgres: открыть порт, TLS, пароль, sslmode=verify-full. Но после появления MCP-адреса gbrain прямой доступ к базе стал не нужен. Postgres остался внутренней базой gbrain, а все клиенты ходят в один HTTP MCP-слой.

Итог: записи, которые Hermes сохранил с сервера, видны Cursor на ноутбуке. Контекст, который нашёл один агент, доступен другому. Не через копипасту и не через export/import, а через одну общую Postgres-базу за gbrain.

Копирайтер-агент на русском языке#

Для текстов я сделал отдельный профиль Hermes — copywriter. У него свой конфиг, свои навыки и своя модель.

Скопировал базовый профиль:

hermes profile create copywriter --from default

Создал отдельный SOUL.md — файл с ролью и правилами агента:

You are Krolbot's copywriter — a focused Russian-language writing agent.

Your job: write, edit, and review Russian-language marketing copy,
blog posts, and site content for zaitsv.dev / webzaytsev projects.

## Always load on start

- `ru-text` — baseline Russian typography, grammar, info-style
- `krolbot-copywriter-voice` — project-specific voice override

Установил два навыка:

  • ru-text — базовые правила русской типографики и стиля
  • krolbot-copywriter-voice — специфичный голос для моих проектов

Позже переключил копирайтера на x-ai/grok-4.20 через OpenRouter. Для русскоязычного текста он оказался полезен именно живым тоном: нормально держит разговорный русский, не пугается мата там, где он уместен, и не превращает инженерный текст в стерильный корпоративный пресс-релиз.

Теперь я могу запустить копирайтера командой:

copywriter chat -q "Напиши пост про новую фичу"

Он работает отдельно от основного агента, подхватывает правила русской типографики и пишет в нужном голосе.

Telegram: Rich Messages оставил, streaming выключил#

С Telegram была отдельная серия граблей. Потоковая генерация выглядела красиво: агент пишет длинный ответ постепенно, и ты видишь прогресс почти сразу.

На практике связка streaming.enabled=true и частых rich Markdown-правок в группе начала упираться в Telegram flood control. Симптом неприятный: одно и то же сообщение могло прилететь дважды — сначала форматированное, потом plain text fallback. В логах это выглядело примерно так:

MarkdownV2 edit failed, falling back to plain text: Flood control exceeded
Telegram flood control, waiting 32-39s
sendRichMessage transient failure

Первый фикс был мягкий: отключить промежуточные сообщения и оставить потоковую генерацию. Это снизило шум, но не убрало проблему полностью. Финальный стабильный вариант такой:

streaming:
  enabled: false

display:
  streaming: false
  interim_assistant_messages: false
  tool_progress: new

Rich Messages при этом выключать не пришлось. Таблицы, код-блоки, списки и нормальное Telegram-форматирование остались. Просто агент больше не пытается постоянно править одно и то же сообщение, пока генерирует ответ.

Отдельный вывод: Telegram — не терминал. То, что удобно в CLI, в групповом чате может только мешать. Для длинных ответов лучше меньше постоянных правок и больше аккуратных финальных сообщений.

Практические находки и подводные камни#

За месяц использования накопилось много полезных наблюдений:

Память и ресурсы#

  • 4 GB RAM хватает, но только если ограничить Firecrawl workers и не запускать всё подряд без лимитов
  • Docker с network_mode: host проще настроить, чем возиться с bridge-сетями
  • Следите за лимитами worker'ов — они могут съесть всю память

Безопасность#

  • Базовой аутентификации через HTTPS достаточно для собственного сервера
  • IP allowlist в reverse proxy — дополнительная защита
  • UFW с deny по умолчанию — обязательно
  • Нестандартный SSH порт — от скриптовых атак

Экономия#

  • отдельный профиль copywriter на Grok 4.20 разгружает основной агент и лучше подходит для живого русского текста
  • Локальный Firecrawl вместо cloud API — бесплатно
  • Отдельные профили для разных задач — не переплачиваете за лишние возможности

Проблемы PATH в systemd#

  • для stdio MCP нужен полный путь (~/.bun/bin/gbrain), потому что systemd не наследует PATH из пользовательской сессии
  • для gbrain удобнее уйти от stdio к отдельному HTTP MCP-сервису на localhost
  • bearer-токены лучше заводить отдельно на каждого клиента, а не шарить один общий секрет

Обновления#

  • Docker bind-mount одного файла не подхватывает изменения после редактирования
  • После правки Caddyfile иногда нужен docker compose down && up, не только reload
  • Hermes gateway нужно перезапускать после изменения MCP-конфигов или env
  • после подключения/изменения MCP в живом Telegram-чате помогает /reload_mcp, но это не заменяет рестарт gateway, если поменялись env/config

Что получилось в итоге#

Сейчас у меня работает нормальная AI-инфраструктура:

  • Веб-панель — интерфейс с базовой аутентификацией
  • Telegram-бот без потоковой генерации, но с нормальным rich-форматированием и прогрессом по инструментам
  • Firecrawl для анализа веб-страниц (локальный, без внешних зависимостей)
  • gbrain как HTTP MCP-сервис поверх Postgres: один brain для Hermes, Cursor и других клиентов
  • Копирайтер-агент на Grok 4.20 с русской типографикой и проектным стилем
  • Безопасная обвязка — firewall, TLS, локальные bind'ы и наружу только нужные MCP/HTTP-адреса

Всё это стабильно работает на обычной 4-гигабайтной виртуалке, но не само по себе: пришлось ограничить Firecrawl, вынести gbrain в отдельный HTTP-сервис, отключить потоковую генерацию в Telegram и перестать открывать наружу то, что должно жить за MCP-слоем.

Стоит ли овчинка выделки?#

Да, если у вас есть задачи, где агент действительно снимает рутину, а не ограничивается красивыми ответами.

За месяц агент помог мне:

  • Написать несколько технических статей
  • Проанализировать документацию сторонних сервисов
  • Автоматизировать рутинные DevOps задачи
  • Исследовать новые технологии и подготовить сводки

Базовая настройка заняла примерно четыре часа днём: установка, Telegram, веб-панель, reverse proxy, Firecrawl, gbrain и первые профили. Дальше агент начал окупаться уже в первый месяц использования — на статьях, исследовании документации и автоматизации рутины.

Главное — не пытаться собрать всё за один присест. Сначала базовая установка, потом Telegram, потом Firecrawl, потом память и отдельные профили. И обязательно документируйте свои настройки — пригодится при обновлениях или переносе на другой сервер.


Если вы хотите поставить Hermes Agent для себя или команды, лучше сразу думать не только об установке, но и о границах доступа, памяти, профилях, интеграциях и проверке результата. Напишите мне в Telegram или на почту — помогу спроектировать такой контур без лишней магии и опасных упрощений.