43 дня назад я написал, как поднял Hermes Agent на сервере, подключил Telegram и дал агенту инструменты для реальной работы. Тогда стек выглядел довольно линейно: сообщение приходит в gateway, модель решает, что делать, затем вызывает terminal, web или файловые инструменты.
Этого хватило, чтобы перейти от разговоров к действиям. Агент мог читать репозиторий, менять код, смотреть состояние сервиса и приносить результат обратно в Telegram. В следующей статье я уже разбирал Hermes как инженерного исполнителя: с границами доступа, проверкой результата и ответственностью за изменение.
За полтора месяца система заметно разрослась. Потом я начал удалять лишнее.
Сейчас мой Hermes-стек — это шесть профилей, трёхслойная память, GBrain, Hindsight, локальные и общие навыки (skills), MCP-интеграции, дочерние агенты, cron-задачи и отдельные правила для каждого репозитория. Но главное изменение не в количестве компонентов. Я перестал пытаться запихнуть всю жизнь агента в один системный контекст.
Если коротко, эволюция выглядела так:
- один универсальный профиль → шесть изолированных ролей;
- вся память в двух файлах → постоянно загружаемая память, Hindsight и GBrain;
- все инструменты в каждом запросе → загрузка под конкретную задачу;
- один агент читает всё подряд → родитель делегирует узкие независимые куски;
- работа только по сообщению → три постоянные cron-задачи;
- ответ модели считается результатом → результат подтверждают Git, сборка или живая система.
Один большой агент быстро превращается в свалку#
Первая версия была универсальной. Один профиль знал мой стиль, инфраструктуру, проекты, редакционные правила и способы деплоя. К нему же подключались файловые инструменты, terminal, браузер, поиск, GBrain и несколько MCP-серверов.
На коротких задачах всё работало. В длинном Telegram-треде начинались проблемы:
- старые решения приезжали в новую задачу без причины;
- схемы десятков инструментов занимали контекст ещё до первого полезного ответа;
- проектные правила смешивались с личными предпочтениями;
- GBrain запрашивался даже там, где нужно было поправить одну строку;
- дочерние агенты получали слишком широкий запрос и заново исследовали то, что родитель уже выяснил;
- длинный контекст стоил дороже результата, который модель в итоге возвращала.
Проблема была не в «глупой модели». Я сам собрал ей плохое рабочее место: огромный стол, на котором одновременно лежат финансы, VPN, статья, продакшен-логи и двадцать отвёрток.
Логичным казалось добавить ещё памяти и инструментов. Рабочим решением оказалось разделить ответственность.
Как стек устроен сейчас#
Эта схема не означает, что каждый запрос проходит через все блоки. Просьбе «переформулируй абзац» не нужны три агента, GBrain и браузер. Сложная миграция, наоборот, без источников, проверки живой системы и повторного чтения результата быстро превращается в уверенную импровизацию.
Стек выбирает минимальный контур под задачу. Это важнее списка установленных компонентов.
Форк появился из конкретных проблем#
Первое существенное изменение понадобилось не ради самого Hermes. Мне был нужен OpenAI-совместимый API поверх уже оплаченной подписки Codex, чтобы другие приложения могли пользоваться той же авторизацией. За основу я взял PR #54877 с OpenAICodexAdapter, перенёс код в форк и подогнал его под текущую версию.
Текстовые запросы заработали быстро. Вызовы инструментов — нет. Исходный вариант терял tools и tool_choice, не возвращал function_call в стандартном tool_calls, а последовательность «вызов функции → результат → следующий ответ» разваливалась. Для простого чата это можно было не заметить. Для финансового приложения — уже нельзя.
В адаптере пришлось отдельно исправить:
- перевод параметров Chat Completions в формат Responses;
- возврат вызовов функций с
finish_reason: tool_calls; - приём результата инструмента в следующем запросе без зависимости от
previous_response_id; - потоковую выдачу текста и вызовов инструментов через SSE;
- передачу upstream-ошибок вместо пустого ответа с HTTP 200;
store: false, если клиент сам не попросил хранить ответ.
Codex не строит embeddings, поэтому для векторов появился отдельный маршрут через другой провайдер. Внешний API остался единым, но чат и embeddings не обязаны идти в одну модель.
Этот эпизод хорошо показывает, зачем нужен форк. Я не собирал коллекцию патчей ради красивого списка. Каждый перенос начинался с конкретной поломки рабочего контура; если исправление не окупает дальнейшее сопровождение при обновлении upstream, его проще удалить.
Профиль теперь означает отдельную роль#
Hermes поддерживает изолированные профили. У каждого свои настройки, сессии, skills, память и учётные данные. На одной машине могут жить несколько агентов, которые не тащат контекст друг друга.
У меня сейчас шесть профилей:
default— ежедневная работа через Telegram;copywriter— статьи, факт-чекинг и редактура;finance— семейные, рабочие и строительные финансы;security— отдельный контур для задач безопасности;xray— Xray и Remnawave с профильными источниками и правилами;dashboard— отдельная поверхность для веб-интерфейса.
Постоянно запущен только основной gateway. Специализированные профили я запускаю под задачу. Например, финансовому агенту не нужен доступ ко всей моей инфраструктуре, а Xray-профилю незачем знать редакционный стиль блога.
Upstream умеет хранить изолированные профили, но в установленной версии нельзя было взять текущий Telegram-тред и отправить его в copywriter или finance. Приходилось менять конфиг, перезапускать gateway либо заводить отдельного бота.
В форке появились команды:
/profile
/profile set copywriter
/profile list
/profile clear
Закреплённый профиль хранится отдельно для чата или темы и переживает перезапуск gateway. Новая сессия открывается уже в домашнем каталоге выбранного профиля; память и секреты из старого профиля туда не переезжают.
С кнопками пришлось сделать отдельную проверку: пользователь, сообщение, чат, тема, nonce и срок действия. Иначе один участник группы мог открыть меню, а другой — нажать кнопку и переключить профиль. После первого запуска нашёлся ещё один баг: в Telegram-теме picker работал, а в обычной группе превращался в текст из-за отсутствующего thread metadata. Исправление заодно затронуло меню /reasoning и /fast.
Изоляция здесь важнее удобства. Профиль — граница памяти и полномочий. Если агент ведёт финансы, он получает только нужную интеграцию и узкий набор skills. У него нет причины случайно уехать в деплой сайта или вспомнить детали VPN-проекта.
Модель при этом стала самым заменяемым слоем. Сейчас основной профиль работает через OpenAI Codex, но смена провайдера не должна менять архитектуру памяти, правила проектов и способы проверки. Я больше не строю систему вокруг конкретного названия модели.
У памяти появилось три разных владельца#
Самое полезное изменение — я перестал называть памятью всё подряд.
Встроенная память Hermes: знать всегда#
USER.md и MEMORY.md попадают в системный контекст каждого нового диалога. Там остаются стабильные вещи:
- как со мной разговаривать;
- как обращаться с секретами;
- какие действия требуют явного разрешения;
- какие рабочие привычки не меняются от проекта к проекту.
Эта память должна быть маленькой. Если туда положить историю всех миграций, список серверов и решения по каждому репозиторию, агент платит за этот груз в каждом сообщении.
Hindsight: вспомнить по ситуации#
Сейчас Hindsight подключён как активный внешний провайдер памяти Hermes. Он хранит рабочий опыт и возвращает релевантные фрагменты перед задачей. Его основные операции — retain, recall и reflect.
В Hindsight можно отправить большой корпус: прошлые сессии, историю редактора, технические разборы, промежуточные решения. Это оперативная память, а не канон. Автоматически извлечённый факт может оказаться шумным, устаревшим или слишком общим.
GBrain: хранить как проверенную правду#
GBrain у меня отвечает за канонические знания. Там лежат страницы проектов, текущие решения, связи, timeline и границы ответственности. Эти записи можно открыть, проверить и исправить руками.
GBrain не подмешивается целиком в каждый запрос. Агент идёт туда, когда задача зависит от прошлых решений или текущей архитектуры проекта. Для простой правки текста такой поход не нужен.
Получилась простая граница:
USER.md / MEMORY.md → что агент обязан знать всегда
Hindsight → что полезно вспомнить по текущей ситуации
GBrain → что считается проверенным и долговечным знанием
Есть ещё session_search. Это поиск по реальным прошлым разговорам, а не попытка пересказать их в память. Если нужно понять, где мы остановились в конкретной сессии, лучше найти сам разговор, чем доверять краткому воспоминанию о нём.
Skills — это процедуры, а не энциклопедия#
Поначалу я ставил skills почти как расширения в браузер: вдруг пригодится. Каждый навык выглядел дешёвым, но вместе они создавали шум. В одной из чисток я физически удалил 43 неиспользуемых навыка. Просто выключить их было недостаточно: мёртвые копии всё равно путали библиотеку и провоцировали дубли.
Теперь skill отвечает на вопрос «как выполнять повторяющуюся работу». Например:
- как безопасно менять Docker Compose;
- как проверять публичную техническую статью;
- как работать с Xray и Remnawave;
- как завершать задачу без оставленных контейнеров, временных файлов и процессов;
- как обращаться с секретами и учётными данными.
Факты проекта в skill не живут. Они быстро устаревают. Текущая архитектура проекта лежит в GBrain и репозитории, состояние живой системы читается напрямую, а skill хранит повторяемый порядок действий.
Для правил конкретного репозитория появились AGENTS.md и CLAUDE.md. Они говорят агенту, чем собирать проект, какие границы нельзя пересекать и что считается готовым результатом. Это дешевле и понятнее, чем создавать отдельный глобальный skill на каждый репозиторий.
Инструменты загружаются под задачу#
В первой версии я хотел дать агенту всё сразу. Это выглядело логично: раз инструмент доступен, почему бы не показать его модели?
Потому что схема инструмента тоже занимает контекст. Десятки MCP-операций превращаются в постоянный налог, даже если текущая задача требует только read_file и git diff.
Теперь основной набор остаётся скучным: файлы, terminal, web, браузер, Git и управление процессами. Интеграции вроде GBrain, Sentry, финансового MCP или чтение Telegram-сообщений подключаются по смыслу задачи и профилю.
Для меня MCP — транспорт к конкретной системе, а не знак качества архитектуры. Если задача решается чтением файла, отдельный MCP не нужен. Если нужен API живой системы с чёткими операциями и ограниченными правами, MCP подходит лучше shell-скрипта, который знает слишком много.
Дочерние агенты получают не весь чат, а короткое задание#
Hermes умеет запускать изолированных дочерних агентов. У каждого чистый контекст, свой terminal и ограниченный набор инструментов. Родитель получает только итоговый отчёт.
Сначала я использовал их слишком широко. Формулировка «разберись в проекте и предложи решение» отправляла каждого дочернего агента заново читать один и тот же репозиторий. Параллельность росла, полезная работа — не особо.
Сейчас родитель сначала собирает короткий Goal Brief: задача, уже известные факты, ограничения и ожидаемый результат. Потом отдаёт детям независимые куски:
- один читает официальную документацию;
- второй разбирает существующую реализацию;
- третий ищет блокирующие риски.
Детский отчёт не считается доказательством. Родитель читает изменённый файл, сверяет ссылку, запускает нужную проверку и только потом сообщает результат.
Позже я перенёс PR #66046, который добавил родителю управление уже запущенными дочерними агентами:
delegate_task(action="status")
delegate_task(action="steer", subagent_id="...")
delegate_task(action="interrupt", subagent_id="...")
Теперь можно посмотреть состояние своего дерева делегаций, уточнить задачу или остановить устаревший review. После рестарта процесса управление старым дочерним агентом не восстанавливается — это управление живой сессией, а не вечная очередь заданий.
Для тяжёлого планирования у меня остался Hermes Consensus Planner. Он нужен там, где важен спор нескольких участников и открытые возражения. Обычную задачу я не гоняю через консилиум: это дороже и чаще всего ничего не добавляет.
Cron отделяет расписание от диалога#
Некоторые задачи не должны жить в чате и ждать моего сообщения. Для них есть встроенный планировщик Hermes.
Сейчас у меня три постоянных задания:
- ежедневная сводка по GBrain, Git и рабочим сессиям;
- мониторинг новых объявлений по конкретному типу оборудования;
- безопасное обновление Docker-сервисов в лабораторном контуре.
Cron запускает новую изолированную сессию с собственным заданием, skills и рабочей директорией. Результат приходит в нужный Telegram-тред. Если рассуждение не требуется, работает обычный скрипт без LLM: пустой stdout означает, что сообщать нечего.
Из менее заметных изменений: cron-сводки могут принудительно использовать богатую разметку Telegram, не включая её для обычного чата. Длинные MarkdownV2-сообщения больше не срываются в plain text из-за суффиксов вроде (1/2) после разбиения на части.
Это хороший пример полезной лени. Проверке обновлений не нужен агент, если детерминированный скрипт уже знает условия. Модель включается только там, где надо собрать источники, дедуплицировать события или написать человеческую сводку.
Как одна задача проходит через стек#
Возьмём обычную задачу: добавить проект в раздел pet-projects сайта.
- Сообщение приходит в основной Telegram gateway.
- Hermes поднимает правила профиля и короткую встроенную память.
- Hindsight может вернуть связанный прошлый контекст, но ссылка пользователя всё равно остаётся первичным источником.
- Агент читает
AGENTS.mdрепозитория и понимает локальный контракт: не запускать тесты, проверять толькоpnpm lintиpnpm build. - Skill для портфолио требует сначала открыть живой проект, а затем найти существующую структуру карточек в коде.
- Агент обнаруживает, что карточка уже есть. Нужна одна ссылка, а не новая модель данных и не ещё один компонент.
- После первой правки выясняется, что кнопка жёстко подписана
GitHub, хотя ведёт на живой сервис. Общий компонент получает необязательную подпись ссылки, остальные карточки сохраняют прежнее поведение. - Проходят
pnpm lintи production-сборка. Временные зависимости и сборочный каталог удаляются. - Git фиксирует изменение, SHA в удалённом репозитории читается обратно.
- Итог дня попадает в timeline GBrain без секретов, внутренних путей и адресов инфраструктуры.
Раньше я бы описал это как «агент добавил ссылку». Сейчас вижу цепочку владельцев: живой сайт подтвердил назначение проекта, репозиторий задал структуру, правила проекта ограничили проверки, skill задал порядок работы, Git доказал доставку, GBrain сохранил итог.
Модель связала эти части, но не стала источником правды ни для одной из них.
Что пришлось выкинуть#
Эволюция стека шла не только через добавление сервисов.
Я убрал или ограничил несколько вещей:
- универсальный профиль для всех задач;
- огромную постоянно загружаемую память с проектными деталями;
- GBrain и тяжёлые MCP-схемы в каждом простом запросе;
- десятки skills «на всякий случай»;
- широкие задания дочерним агентам;
- промежуточные сообщения и лишние служебные отчёты в Telegram;
- привычку считать финальный ответ агента доказательством выполненной работы.
Остался скучный принцип: источник сначала, минимальное изменение потом, проверка в конце.
Где стек всё ещё ломается#
Система не стала автономным техническим директором.
Hindsight иногда возвращает не тот фрагмент или не сразу видит свежую запись. GBrain требует редакторской дисциплины: если складывать туда сырые логи и каждое промежуточное решение, канон снова превратится в свалку. Профили тоже надо поддерживать — обновлять skills, следить за границами учётных данных и не копировать настройки без причины.
Дочерние агенты могут уверенно ошибиться в отчёте. Cron может исправно присылать бесполезную сводку каждый день. Живой сервис может отличаться от репозитория, а старая память — от обоих.
Поэтому у источников есть приоритет:
Чем ближе задача к деньгам, безопасности или продакшену, тем меньше места остаётся для догадок.
Что изменилось для меня#
Раньше я формулировал подробное задание и надеялся, что модель удержит всё в голове. Теперь больше времени уходит на границы системы, зато сами запросы стали короче.
Я могу написать в Telegram: «проверь релиз», «добавь проект», «разбери падение CI-конвейера». Агент сам находит проектные правила, вспоминает релевантный контекст, выбирает процедуру и инструменты. Если данных не хватает, он идёт к первичному источнику, а не достраивает историю по памяти.
Это и есть главное изменение за 43 дня. Hermes перестал быть одним длинным разговором с моделью. Он стал рабочей системой, где у каждого знания и действия есть владелец.
Вместо вывода#
Если собрать мой текущий стек в одну строку, получится так:
Telegram → Hermes gateway → профиль → память и правила проекта
→ skills и tools → при необходимости дочерние агенты
→ проверка → Git / состояние живой системы → GBrain
Снаружи всё ещё выглядит как чат с ботом. Внутри это диспетчер, несколько изолированных специалистов, оперативная память, каноническая база знаний, планировщик и набор проверяемых рабочих процедур.
Самое полезное обновление оказалось архитектурно скучным: я перестал хранить всё везде. Меньше глобального контекста, меньше универсальных агентов, меньше инструментов «на всякий случай». Больше явных границ и повторного чтения результата после действий.
Если вы тоже строите личный AI-стек, подключаете Hermes к рабочим системам или пытаетесь разгрести память агента, напишите мне в Telegram или на почту. Покажу, какие слои у меня прижились, а какие пришлось удалить после первых недель эксплуатации.



