Один Telegram gateway для десятков Hermes
Один Telegram gateway для десятков Hermes
Навигация: Hermes как sales funnel bot · Nanobot / Hermes / Agent Garden · Agent Garden
Вопрос
Можно ли сделать так, чтобы был один Telegram bot / один Telegram gateway, а за ним сидели десятки отдельных Hermes-агентов: по клиенту, по чату, по пользователю или через ручной выбор агента ботом?
Цель: не создавать 10 Telegram-ботов для 10 клиентов, а иметь один публичный/операторский бот, который маршрутизирует запросы в нужный независимый Hermes.
Короткий вывод
Да, архитектурно можно и это правильнее, чем десяток Telegram-ботов. Но в текущем Hermes это не является готовой “галочкой” для многих backend-профилей за одним Telegram bot token.
Из коробки Hermes уже умеет:
- один Telegram bot обслуживает много чатов/групп/топиков;
- Telegram gateway строит разные session key по
platform/chat_type/chat_id/thread_id/user_id; - один профиль Hermes может иметь много параллельных Telegram-сессий;
- Telegram DM topics и group forum topics дают отдельные сессии внутри одного бота;
- profiles дают отдельные Hermes с отдельной памятью, конфигом, skills, cron, state;
- API Server даёт каждому профилю HTTP endpoint;
- gateway proxy mode умеет пересылать сообщения в Hermes API Server.
Но ограничение сейчас такое:
- один Hermes gateway profile рассчитан на один backend-agent контур;
GATEWAY_PROXY_URL/gateway.proxy_url— один URL для всего gateway, без встроенной per-chat/per-user маршрутизации;- один Telegram bot token нельзя одновременно использовать несколькими gateway-процессами: Hermes ставит lock по token, а Telegram long polling/webhook тоже предполагает одного активного consumer’а.
Значит, оптимальный путь: один центральный Telegram ingress/router владеет bot token, а дальше по таблице маршрутизации отправляет запрос в нужный Hermes profile API Server.
Что уже есть в Hermes
1. Telegram gateway поддерживает много чатов одним bot token
Документация Telegram integration описывает один BotFather token и allowlists:
TELEGRAM_BOT_TOKEN=...
TELEGRAM_ALLOWED_USERS=...
Один бот может работать в DMs, группах, супергруппах и forum topics.
Для групп есть отдельные gates:
TELEGRAM_ALLOWED_USERS/allow_from— глобальный доступ;TELEGRAM_GROUP_ALLOWED_USERS/group_allow_from— кто может вызывать бота в группах;TELEGRAM_GROUP_ALLOWED_CHATS/group_allowed_chats— какие группы разрешены целиком.
Это уже позволяет использовать одного Telegram бота для многих клиентских чатов, если все они должны попадать в один Hermes profile.
2. Session routing уже различает чат, пользователя и thread/topic
В gateway/session.py есть SessionSource:
platform
chat_id
chat_type
user_id
thread_id
И build_session_key() строит ключи вида:
agent:main:{platform}:dm:{chat_id}
agent:main:{platform}:dm:{chat_id}:{thread_id}
agent:main:{platform}:{chat_type}:{chat_id}:{thread_id}:{user_id?}
Для Telegram это означает:
- DM с разными пользователями — разные сессии;
- DM topic/thread — отдельная сессия;
- group/forum topic — отдельная сессия;
- обычная группа может быть shared или per-user в зависимости от правил.
3. Telegram topics уже решают “много сессий в одном боте”
Hermes docs описывают:
- Private Chat Topics — фиксированные топики в DM, заданные оператором через config;
- Multi-session DM mode
/topic— пользователь сам включает topics и создаёт параллельные Hermes-сессии через Telegram UI; - Group Forum Topic Skill Binding — topic в супергруппе может auto-load конкретный skill.
Это полезно, если нужно много рабочих пространств внутри одного Hermes profile.
Но это не равно “десятки независимых Hermes профилей/клиентов”, потому что память, конфиг, credentials, toolsets и state всё ещё принадлежат одному profile.
4. Profiles дают настоящую изоляцию агентов
Hermes profiles — это отдельные home directories:
config.yaml;.env;SOUL.md;- memory;
- sessions;
- skills;
- cron jobs;
- gateway state;
- logs.
Команды:
hermes profile create client-a
hermes profile create client-b
hermes -p client-a gateway
hermes -p client-b gateway
Для независимых клиентских агентов profiles — правильная единица изоляции.
5. API Server позволяет каждому profile быть backend-agent’ом
API Server включается env vars:
API_SERVER_ENABLED=true
API_SERVER_PORT=8643
API_SERVER_KEY=client-a-secret
hermes -p client-a gateway
Для многих профилей:
hermes profile create client-a
hermes profile create client-b
# client-a/.env
API_SERVER_ENABLED=true
API_SERVER_PORT=8643
API_SERVER_KEY=client-a-secret
API_SERVER_MODEL_NAME=client-a
# client-b/.env
API_SERVER_ENABLED=true
API_SERVER_PORT=8644
API_SERVER_KEY=client-b-secret
API_SERVER_MODEL_NAME=client-b
hermes -p client-a gateway &
hermes -p client-b gateway &
API Server поддерживает:
/v1/chat/completions;/v1/responses;/v1/runs;- SSE streaming;
X-Hermes-Session-Idдля session continuity;X-Hermes-Session-Keyдля долгоживущего per-channel memory scope.
6. Gateway proxy mode уже есть, но сейчас он один-к-одному
В gateway/run.py есть proxy mode:
GATEWAY_PROXY_URL
# или gateway.proxy_url
Когда он задан, gateway становится thin relay:
Telegram/Matrix/etc adapter
→ GatewayRunner
→ POST remote /v1/chat/completions
Но текущая реализация _get_proxy_url() возвращает один URL. Значит, это подходит для split deployment одного backend-agent’а, но не для routing map “чат A → profile A, чат B → profile B”.
Почему нельзя просто запустить 10 Hermes gateway с одним Telegram token
Есть два слоя ограничения:
- Telegram Bot API: long polling
getUpdatesконфликтует, если два процесса читают один bot token. Webhook у бота тоже один активный endpoint. - Hermes: adapters с уникальными credentials используют
acquire_scoped_lock(scope, identity)— вgateway/status.pyпрямо указано, что это предотвращает одновременное использование одного Telegram bot token разнымиHERMES_HOME/profiles.
Следовательно, должен быть один владелец Telegram token: либо Hermes gateway-router, либо внешний bot-router.
Рекомендуемая архитектура MVP
Telegram users/groups/topics
→ one Telegram bot token
→ central Telegram Router / Ingress
→ routing DB: chat_id/user_id/thread_id/customer → Hermes profile endpoint
→ Hermes profile API Server per client/agent
→ answer back to same Telegram chat/topic
Backend agents
Каждый клиент/агент — отдельный Hermes profile:
client-a: http://127.0.0.1:8643/v1 API_SERVER_KEY=a
client-b: http://127.0.0.1:8644/v1 API_SERVER_KEY=b
client-c: http://127.0.0.1:8645/v1 API_SERVER_KEY=c
Router DB
Минимальная таблица:
CREATE TABLE telegram_agent_routes (
id INTEGER PRIMARY KEY,
telegram_chat_id TEXT NOT NULL,
telegram_user_id TEXT,
telegram_thread_id TEXT,
route_scope TEXT NOT NULL, -- chat | user | thread | manual
agent_id TEXT NOT NULL,
api_base_url TEXT NOT NULL,
api_key_ref TEXT NOT NULL,
session_key TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'active',
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
UNIQUE(telegram_chat_id, telegram_user_id, telegram_thread_id)
);
Практический route priority:
- exact
chat_id + thread_id + user_id; chat_id + thread_id;chat_id + user_id;chat_id;user_id;- default / ask user.
Что отправлять в Hermes API Server
Для каждого сообщения router вызывает нужный backend:
POST http://127.0.0.1:8643/v1/chat/completions
Authorization: Bearer client-a-secret
X-Hermes-Session-Id: tg:{chat_id}:{thread_id_or_root}:{user_id}:transcript
X-Hermes-Session-Key: tg:{chat_id}:{thread_id_or_root}:{user_id}
Content-Type: application/json
Body:
{
"model": "hermes-agent",
"stream": true,
"messages": [
{"role": "user", "content": "..."}
]
}
Для обычного multi-turn можно использовать X-Hermes-Session-Id, чтобы backend сам грузил историю из своего state.db. X-Hermes-Session-Key полезен, чтобы long-term memory понимала, что это тот же канал даже после /new или смены transcript.
UX routing: как бот узнаёт агента
Вариант A — по чату
Лучший для клиентских групп:
- каждый клиент добавляет одного общего бота в свою группу;
- admin один раз делает
/bind client-a; - дальше весь chat_id маршрутизируется в
client-aprofile.
Плюсы:
- просто;
- меньше ошибок;
- клиент не выбирает агента каждый раз;
- подходит для B2B “один клиент = один чат”.
Минусы:
- если в одном чате нужно несколько агентов, нужны topics или команды переключения.
Вариант B — по Telegram topic/thread
Лучший для одного общего супергруппового “портала”:
- topic “Client A” → Hermes profile A;
- topic “Client B” → Hermes profile B;
- topic “Support” → support-agent.
Плюсы:
- один Telegram group/forum как консоль управления;
- thread_id является естественным route key.
Минусы:
- нужно аккуратно настроить Telegram topics и права;
- в topic shared context между участниками, если не включать per-user logic.
Вариант C — по пользователю
Подходит для личного DM:
- user_id
123всегда маршрутизируется в его личный Hermes; /switch client-bменяет default route пользователя.
Плюсы:
- хороший UX для “у каждого клиента/сотрудника свой агент”.
Минусы:
- если один пользователь управляет многими агентами, нужна команда выбора.
Вариант D — бот спрашивает
Если route не найден:
К какому агенту подключить этот чат?
[client-a] [client-b] [создать нового] [одноразовый запрос]
После выбора router сохраняет binding.
Плюсы:
- удобно при onboarding;
- меньше ручной настройки.
Минусы:
- надо реализовать pending state;
- нужна защита от того, что обычный пользователь привяжет чат к чужому агенту.
Можно ли сделать это внутри Hermes без внешнего router?
Да, но это уже изменение Hermes gateway.
Нужна доработка proxy mode:
gateway:
proxy_routes:
- match:
platform: telegram
chat_id: "-1001111111111"
proxy_url: "http://127.0.0.1:8643"
proxy_key_env: "CLIENT_A_API_KEY"
- match:
platform: telegram
user_id: "123456789"
proxy_url: "http://127.0.0.1:8644"
proxy_key_env: "CLIENT_B_API_KEY"
Кодово это означает:
- заменить
_get_proxy_url()на_resolve_proxy_route(source); - прокинуть в
_run_agent_via_proxy()sourceи выбранныйproxy_key; - добавить route table/config loader;
- добавить fallback behavior “ask/bind”;
- добавить tests для chat_id/user_id/thread_id priority;
- не ломать существующий
GATEWAY_PROXY_URLкак default route.
Плюс желательно добавить slash-команды:
/bind <agent-id>
/unbind
/routes
/switch <agent-id>
/whoami
Почему внешний router быстрее для MVP
Внешний router проще и безопаснее, потому что:
- не нужно патчить core Hermes;
- можно сделать на
python-telegram-botили Node.js; - router отвечает только за Telegram, ACL, binding и HTTP proxy;
- каждый Hermes profile остаётся vanilla;
- rollback простой: остановить router, не трогая backend profiles.
Минимальная реализация:
router.py
- receives Telegram update
- extracts chat_id/user_id/thread_id/text/media
- resolves agent route from SQLite/Postgres
- if missing route: ask authorized user to bind
- calls selected Hermes API Server
- streams/collects response
- sends response to same chat/thread
Безопасность / multi-tenant правила
Если это для десятков клиентов, нельзя делать только “модель сама поймёт клиента”. Нужны hard boundaries:
- Изоляция через profiles: отдельные state/memory/skills/env на клиента.
- Отдельные API keys: router хранит key refs, не отдаёт пользователям.
- ACL на bind/switch: только owner/admin может привязать chat_id к agent_id.
- Route visibility:
/whoamiпоказывает текущего agent_id и route scope. - Audit log: кто привязал чат, когда, к какому агенту.
- No cross-client tools: credentials клиента лежат только в его profile
.env. - Rate/usage limits: quota лучше считать в router до вызова Hermes.
- Media handling: если прокидывать файлы, router должен либо передавать URL/base64 image, либо скачивать файл в безопасное shared storage; API Server сейчас лучше поддерживает inline images, а не произвольные file uploads.
Decision matrix
Если нужна быстро работающая схема
Выбрать внешний Telegram router + Hermes API Server per profile.
Если хочется сделать upstream feature в Hermes
Делать gateway.proxy_routes / route resolver внутри Hermes gateway.
Если клиенты не требуют строгой изоляции
Можно начать с одного Hermes profile, одного Telegram bot и route-by-chat/session topics. Это самый быстрый путь, но слабее по безопасности и кастомизации.
Если один оператор управляет многими агентами в личке
Использовать Telegram DM topics или команды /switch, где каждый topic/user binding указывает на backend profile.
MVP-план на 1–2 дня
- Создать 2–3 Hermes profiles:
client-a,client-b,client-c. - В каждом включить API Server на своём port и secret.
- Написать маленький Telegram router:
- SQLite routes;
/bind agent_id;/whoami;- обычное сообщение → selected API Server;
- ответ обратно в тот же chat/thread.
- Проверить:
- один DM → client-a;
- одна группа → client-b;
- два topic в одной группе → разные clients;
- unauthorized user не может bind/switch.
- Если UX подтверждается — оформить как Hermes plugin/patch или оставить внешним сервисом.
Финальный ответ
Твоя идея правильная: не плодить Telegram bots. Один bot должен быть входной дверью. За ним — routing layer и десятки Hermes profiles/API Servers.
Самый практичный MVP: внешний Telegram router. Самый красивый upstream-вариант: расширить Hermes gateway.proxy_url до gateway.proxy_routes, чтобы Hermes gateway сам выбирал backend по chat_id, user_id или thread_id и мог спрашивать привязку при первом сообщении.
Источники и проверенные места
- Hermes Telegram docs:
website/docs/user-guide/messaging/telegram.md - Hermes Gateway Internals:
website/docs/developer-guide/gateway-internals.md - Hermes API Server docs:
website/docs/user-guide/features/api-server.md - Hermes profiles docs: https://hermes-agent.nousresearch.com/docs/user-guide/profiles
gateway/session.py:SessionSource,build_session_key()gateway/run.py:_get_proxy_url(),_run_agent_via_proxy()gateway/status.py:acquire_scoped_lock()prevents same Telegram token across profilesgateway/platforms/api_server.py:/v1/chat/completions,X-Hermes-Session-Id,X-Hermes-Session-Key