sidebar_position: 7 title: "Gateway Internals" description: "How the messaging gateway boots, authorizes users, routes sessions, and delivers messages" lang: ru
Внутреннее устройство шлюза
Шлюз обмена сообщениями — это длительный процесс, который соединяет Hermes с более чем 14 внешними платформами обмена сообщениями через унифицированную архитектуру.
Ключевые файлы
| Файл | Цель |
|---|---|
gateway/run.py |
GatewayRunner — основной цикл, слэш-команды, отправка сообщений (~12 000 строк) |
gateway/session.py |
SessionStore — сохранение диалога и создание сеансового ключа |
gateway/delivery.py |
Доставка исходящих сообщений на целевые платформы/каналы |
gateway/pairing.py |
Процесс сопряжения DM для авторизации пользователя |
gateway/channel_directory.py |
Сопоставляет идентификаторы чатов с удобочитаемыми именами для доставки cron |
gateway/hooks.py |
Обнаружение, загрузка и отправка событий жизненного цикла хуков |
gateway/mirror.py |
Межсессионное зеркалирование сообщений для send_message |
gateway/status.py |
Управление блокировкой токенов для экземпляров шлюза на уровне профиля |
gateway/builtin_hooks/ |
Точка расширения для всегда зарегистрированных перехватчиков (не поставляется) |
gateway/platforms/ |
Адаптеры платформы (по одному на каждую платформу обмена сообщениями) |
Обзор архитектуры
┌─────────────────────────────────────────────────┐
│ GatewayRunner │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Telegram │ │ Discord │ │ Slack │ │
│ │ Adapter │ │ Adapter │ │ Adapter │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ │ │ │ │
│ └─────────────┼─────────────┘ │
│ ▼ │
│ _handle_message() │
│ │ │
│ ┌───────────┼───────────┐ │
│ ▼ ▼ ▼ │
│ Slash command AIAgent Queue/BG │
│ dispatch creation sessions │
│ │ │
│ ▼ │
│ SessionStore │
│ (SQLite persistence) │
└───────┴─────────────┴─────────────┴─────────────┘
Поток сообщений
Когда сообщение приходит с любой платформы:
- Адаптер платформы получает необработанное событие, нормализует его в
MessageEvent - Базовый адаптер проверяет активную защиту сеанса:
- Если агент работает для этого сеанса → сообщение в очереди, установите событие прерывания.
- Если
/approve,/deny,/stop→ обход защиты (отправляется в линию) - GatewayRunner._handle_message() получает событие:
- Разрешить сеансовый ключ через
_session_key_for_source()(формат:agent:main:{platform}:{chat_type}:{chat_id}) - Проверьте авторизацию (см. Авторизация ниже)
- Проверьте, является ли это косой чертой → отправить обработчику команды.
- Проверьте, запущен ли агент → перехватывайте такие команды, как
/stop,/status. - В противном случае → создайте экземпляр
AIAgentи запустите диалог. - Ответ отправляется обратно через адаптер платформы.
Формат сеансового ключа
Ключи сеанса кодируют полный контекст маршрутизации:
agent:main:{platform}:{chat_type}:{chat_id}
Например: agent:main:telegram:private:123456789
Платформы с поддержкой потоков (темы форума Telegram, темы Discord, темы Slack) могут включать идентификаторы потоков в частьchat_id. Никогда не создавайте сеансовые ключи вручную — всегда используйте build_session_key() из gateway/session.py.
Двухуровневая защита сообщений
Когда агент активно работает, входящие сообщения проходят через две последовательные защиты:
-
Уровень 1 — Базовый адаптер (
gateway/platforms/base.py): проверяет_active_sessions. Если сеанс активен, ставит сообщение в очередь_pending_messagesи устанавливает событие прерывания. Это перехватывает сообщения прежде, чем* они достигнут шлюза. -
Уровень 2 — Управляющий шлюзом (
gateway/run.py): проверяет_running_agents. Перехватывает определенные команды (/stop,/new,/queue,/status,/approve,/deny) и маршрутизирует их соответствующим образом. Все остальное вызываетrunning_agent.interrupt().
Команды, которые должны достичь бегуна, пока агент заблокирован (например, /approve), отправляются инлайн через await self._message_handler(event) — они обходят систему фоновых задач, чтобы избежать условий гонки.
Авторизация
Шлюз использует многоуровневую проверку авторизации, которая оценивается в следующем порядке:
- Флаг разрешения всех для каждой платформы (например,
TELEGRAM_ALLOW_ALL_USERS) — если установлен, все пользователи на этой платформе авторизованы. - Список разрешенных платформ (например,
TELEGRAM_ALLOWED_USERS) — идентификаторы пользователей, разделенные запятыми. - Сопряжение с DM — прошедшие проверку подлинности пользователи могут связывать новых пользователей с помощью кода сопряжения.
- Глобальное разрешение (
GATEWAY_ALLOW_ALL_USERS) — если установлено, все пользователи на всех платформах авторизованы. - По умолчанию: Deny — неавторизованные пользователи отклоняются.
Порядок подключения DM
Admin: /pair
Gateway: "Pairing code: ABC123. Share with the user."
New user: ABC123
Gateway: "Paired! You're now authorized."
Состояние сопряжения сохраняется в gateway/pairing.py и сохраняется при перезапуске.
Отправка команды Slash
Все слэш-команды в шлюзе проходят через один и тот же конвейер разрешения:
resolve_command()изhermes_cli/commands.pyсопоставляет ввод с каноническим именем (обрабатывает псевдонимы, сопоставление префиксов)- Каноническое имя сверяется с
GATEWAY_KNOWN_COMMANDS. - Обработчик в рассылках
_handle_message()на основе канонического имени. - Некоторые команды привязаны к конфигурации (
gateway_config_gateнаCommandDef)
Защита бегущего агента
Команды, которые НЕ должны выполняться во время обработки агента, отклоняются досрочно:
if _quick_key in self._running_agents:
if canonical == "model":
return "⏳ Agent is running — wait for it to finish or /stop first."
Команды обхода (/stop, /new, /approve, /deny, /queue, /status) имеют специальную обработку.
Источники конфигурации
Шлюз считывает конфигурацию из нескольких источников:
| Источник | Что это дает |
|---|---|
~/.hermes/.env |
Ключи API, токены ботов, учетные данные платформы |
~/.hermes/config.yaml |
Настройки модели, конфигурация инструмента, параметры отображения |
| Переменные среды | Отменить любое из вышеперечисленного |
В отличие от CLI (который использует load_cli_config() с жестко запрограммированными настройками по умолчанию), шлюз читает config.yaml напрямую через загрузчик YAML. Это означает, что ключи конфигурации, которые существуют в словаре настроек по умолчанию CLI, но не в файле конфигурации пользователя, могут вести себя по-разному между CLI и шлюзом.
Адаптеры платформы
Каждая платформа обмена сообщениями имеет адаптер gateway/platforms/:
gateway/platforms/
├── base.py # BaseAdapter — shared logic for all platforms
├── telegram.py # Telegram Bot API (long polling or webhook)
├── discord.py # Discord bot via discord.py
├── slack.py # Slack Socket Mode
├── whatsapp.py # WhatsApp Business Cloud API
├── signal.py # Signal via signal-cli REST API
├── matrix.py # Matrix via mautrix (optional E2EE)
├── mattermost.py # Mattermost WebSocket API
├── email.py # Email via IMAP/SMTP
├── sms.py # SMS via Twilio
├── dingtalk.py # DingTalk WebSocket
├── feishu.py # Feishu/Lark WebSocket or webhook
├── wecom.py # WeCom (WeChat Work) callback
├── weixin.py # Weixin (personal WeChat) via iLink Bot API
├── bluebubbles.py # Apple iMessage via BlueBubbles macOS server
├── qqbot.py # QQ Bot (Tencent QQ) via Official API v2
├── webhook.py # Inbound/outbound webhook adapter
├── api_server.py # REST API server adapter
└── homeassistant.py # Home Assistant conversation integration
Адаптеры реализуют общий интерфейс:
- connect() / disconnect() — управление жизненным циклом
- send_message() — доставка исходящего сообщения
- on_message() — нормализация входящих сообщений → MessageEvent
Блокировки токенов
Адаптеры, подключающиеся с использованием уникальных учетных данных, вызывают acquire_scoped_lock() в connect() и release_scoped_lock() в disconnect(). Это предотвращает одновременное использование одного и того же токена бота двумя профилями.
Путь доставки
Исходящие поставки (gateway/delivery.py) обрабатываются:
- Прямой ответ — отправить ответ обратно в исходный чат.
- Доставка по домашнему каналу — маршрутизация выходных данных заданий cron и фоновых результатов на настроенный домашний канал.
- Явная целевая доставка — инструмент
send_message, указывающийtelegram:-1001234567890 - Кроссплатформенная доставка – доставка на платформу, отличную от исходного сообщения.
Доставки заданий Cron НЕ отражаются в истории сеансов шлюза — они живут только в своем собственном сеансе cron. Это осознанный выбор конструкции, позволяющий избежать нарушений чередования сообщений.
Крючки
Перехватчики шлюза — это модули Python, которые реагируют на события жизненного цикла:
События перехвата шлюза
| Событие | При увольнении |
|---|---|
gateway:startup |
Запускается процесс шлюза |
session:start |
Начинается новый сеанс разговора |
session:end |
Сеанс завершен или время ожидания истекло |
session:reset |
Пользователь сбрасывает сеанс с /new |
agent:start |
Агент начинает обработку сообщения |
agent:step |
Агент завершает одну итерацию вызова инструмента |
agent:end |
Агент завершает работу и возвращает ответ |
command:* |
Любая команда слэша выполняется |
Перехватчики обнаруживаются из gateway/builtin_hooks/ (всегда активен) и ~/.hermes/hooks/ (устанавливается пользователем). Каждый хук представляет собой каталог с манифестом HOOK.yaml и handler.py.
Интеграция поставщика памяти
Когда плагин поставщика памяти (например, Honcho) включен:
- Шлюз создает
AIAgentдля каждого сообщения с идентификатором сеанса. MemoryManagerинициализирует провайдера контекстом сеанса.- Инструменты провайдера (например,
honcho_profile,viking_search) маршрутизируются через:
AIAgent._invoke_tool()
→ self._memory_manager.handle_tool_call(name, args)
→ provider.handle_tool_call(name, args)
- При завершении/сбросе сеанса
on_session_end()срабатывает для очистки и окончательного сброса данных.
Жизненный цикл очистки памяти
Когда сеанс сбрасывается, возобновляется или истекает:
1. Встроенная память сбрасывается на диск.
2. Срабатывает on_session_end() провайдера памяти.
3. Временный AIAgent запускает ход разговора только в памяти.
4. Контекст затем удаляется или архивируется.
Фоновое обслуживание
Шлюз выполняет периодическое обслуживание наряду с обработкой сообщений:
- Проверка Cron — проверяет расписания заданий и запускает срочные задания.
- Истечение срока действия — очищает прерванные сеансы по истечении времени ожидания.
- Очистка памяти – упреждающая очистка памяти перед истечением сеанса.
- Обновление кэша — обновляются списки моделей и статус поставщиков.
Управление процессами
Шлюз работает как долговременный процесс, управляемый через:
hermes gateway start/hermes gateway stop— ручное управлениеsystemctl(Linux) илиlaunchctl(macOS) — управление сервисами- PID-файл
~/.hermes/gateway.pid— отслеживание процессов на уровне профиля.
На уровне профиля или глобально: start_gateway() использует PID-файлы на уровне профиля. hermes gateway stop останавливает только шлюз текущего профиля. hermes gateway stop --all использует глобальное сканирование ps aux для уничтожения всех процессов шлюза (используется во время обновлений).