跳到主要内容

网关内部机制

消息网关是一个长期运行的过程,通过统一的架构将 Hermes 连接到 14 个以上的外部消息平台。

关键文件

文件目的
gateway/run.pygateway/run.py 网关Runner — 主循环、斜杠命令、消息调度(约 9,000 行)
gateway/session.pySessionStore — 会话持久性和会话密钥构建
gateway/delivery.pygateway/delivery.py出站消息传送到目标平台/渠道
gateway/pairing.pygateway/pairing.py用户授权的DM配对流程
gateway/channel_directory.pygateway/channel_directory.py将聊天 ID 映射到人类可读的名称以进行 cron 交付
gateway/hooks.pygateway/hooks.py钩子发现、加载和生命周期事件调度
gateway/mirror.pygateway/mirror.py send_message 的跨会话消息镜像
gateway/status.pygateway/status.py配置文件范围内的网关实例的令牌锁管理
gateway/builtin_hooks/gateway/builtin_hooks/始终注册的挂钩(例如,BOOT.md 系统提示挂钩)
gateway/platforms/平台适配器(每个消息传递平台一个)

架构概述

┌─────────────────────────────────────────────────┐
│ 网关Runner │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Telegram │ │ Discord │ │ Slack │ │
│ │ Adapter │ │ Adapter │ │ Adapter │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ │ │ │ │
│ └─────────────┼─────────────┘ │
│ ▼ │
│ _handle_message() │
│ │ │
│ ┌───────────┼───────────┐ │
│ ▼ ▼ ▼ │
│ Slash command AIAgent Queue/BG │
│ dispatch creation sessions │
│ │ │
│ ▼ │
│ SessionStore │
│ (SQLite persistence) │
└─────────────────────────────────────────────────┘

消息流

当消息从任何平台到达时:

  1. 平台适配器接收原始事件,将其规范化为 MessageEvent
  2. 基本适配器 检查活动会话防护:
  • 如果代理正在为此会话运行→队列消息,设置中断事件
  • 如果 /approve/deny/stop → 绕过防护(内联调度)
  1. **网关Runner._handle_message()**接收事件:
  • 通过 _session_key_for_source() 解析会话密钥(格式:agent:main:{platform}:{chat_type}:{chat_id}
  • 检查授权(请参阅下面的授权)
  • 检查是否是斜杠命令 → 分派到命令处理程序
  • 检查代理是否已经在运行 → 拦截诸如 /stop/status 之类的命令
  • 否则 → 创建 AIAgent 实例并运行对话
  1. 响应通过平台适配器发回

会话密钥格式

会话密钥对完整的路由上下文进行编码:

agent:main:{platform}:{chat_type}:{chat_id}

例如:agent:main:telegram:private:123456789

线程感知平台(Telegram 论坛主题、Discord 线程、Slack 线程)可能会在 chat_id 部分中包含线程 ID。 切勿手动构建会话密钥 — 始终使用 gateway/session.py 中的 build_session_key()

两级消息防护

当代理主动运行时,传入消息会通过两个顺序防护:

  1. 级别 1 — 基本适配器 (gateway/platforms/base.py):检查 _active_sessions。如果会话处于活动状态,则将消息放入 _pending_messages 中排队并设置中断事件。这会在消息到达网关运行器之前捕获消息。

  2. 2 级 — 网关运行程序 (gateway/run.py):检查 _running_agents。拦截特定命令(/stop/new/queue/status/approve/deny)并适当地路由它们。其他一切都会触发 running_agent.interrupt()

在代理被阻止时必须到达运行程序的命令(例如 /approve)通过 await self._message_handler(event) 内联分派 - 它们绕过后台任务系统以避免竞争条件。

授权

网关使用多层授权检查,按顺序评估:

  1. 每平台允许所有标志(例如,TELEGRAM_ALLOW_ALL_USERS) - 如果设置,则该平台上的所有用户都被授权
  2. 平台白名单(例如,TELEGRAM_ALLOWED_USERS)— 以逗号分隔的用户 ID
  3. DM 配对 — 经过身份验证的用户可以通过配对码与新用户配对
  4. 全局允许所有 (GATEWAY_ALLOW_ALL_USERS) — 如果设置,则所有平台上的所有用户都将获得授权
  5. 默认:拒绝 — 拒绝未经授权的用户

DM 配对流程

Admin: /pair
网关: "Pairing code: ABC123. Share with the user."
New user: ABC123
网关: "Paired! You're now authorized."

配对状态保留在 gateway/pairing.py 中,并且在重新启动后仍然有效。

Slash 命令调度

网关中的所有斜杠命令都流经相同的解析管道:

  1. hermes_cli/commands.py 中的 resolve_command() 将输入映射到规范名称(处理别名、前缀匹配)
  2. 根据 GATEWAY_KNOWN_COMMANDS 检查规范名称
  3. _handle_message() 中的处理程序根据规范名称进行调度
  4. 一些命令在配置上门控(gateway_config_gate on 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~/.hermes/.env API 密钥、机器人令牌、平台凭证
~/.hermes/config.yaml~/.hermes/config.yaml模型设置、工具配置、显示选项
环境变量覆盖以上任何一项

与 CLI(使用带有硬编码默认值的 load_cli_config())不同,网关直接通过 YAML 加载器读取 config.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

令牌锁

使用唯一凭据连接的适配器在 connect() 中调用 acquire_scoped_lock() ,在 disconnect() 中调用 release_scoped_lock() 。这可以防止两个配置文件同时使用相同的机器人令牌。

交付路径

外发交货 (gateway/delivery.py) 句柄:

  • 直接回复 — 将回复发送回原始聊天
  • 家庭频道交付 — 将 cron 作业输出和后台结果路由到配置的家庭频道
  • 显式目标传递send_message 工具指定 telegram:-1001234567890
  • 跨平台传递 — 传递到与原始消息不同的平台

Cron 作业交付不会镜像到网关会话历史记录中 — 它们仅存在于自己的 cron 会话中。这是一个有意的设计选择,以避免消息交替违规。

钩子

网关挂钩是响应生命周期事件的 Python 模块:

网关挂钩事件

活动被解雇时
gateway:startupgateway:startup网关进程启动
session:startsession:start新的对话会话开始
session:endsession:end会话完成或超时
session:resetsession:reset用户使用 /new 重置会话
agent:startagent:start代理开始处理消息
agent:stepagent:step代理完成一次工具调用迭代
agent:endagent:end代理完成并返回响应
command:*command:*执行任何斜杠命令

钩子是从 gateway/builtin_hooks/ (始终处于活动状态)和 ~/.hermes/hooks/ (用户安装)中发现的。每个钩子都是一个带有 HOOK.yaml 清单和 handler.py 的目录。

内存提供商集成

当启用内存大模型提供商(provider)插件(例如 Honcho)时:

  1. 网关使用会话 ID 创建每条消息的 AIAgent
  2. MemoryManager 使用会话上下文初始化大模型提供商(provider)
  3. 大模型提供商(provider)工具(例如,honcho_profileviking_search)通过:
AIAgent._invoke_tool()
→ self._memory_manager.handle_tool_call(name, args)
→ provider.handle_tool_call(name, args)
  1. 在会话结束/重置时,触发 on_session_end() 进行清理和最终数据刷新

内存刷新生命周期

当会话重置、恢复或过期时: 1.内置内存刷新到磁盘 2. 内存大模型提供商(provider)的 on_session_end() 钩子触发 3. 临时 AIAgent 运行仅内存对话轮 4. 上下文随后被丢弃或存档

后台维护

网关在消息处理的同时进行定期维护:

  • Cron 滴答 — 检查作业计划并解雇到期作业
  • 会话过期 — 超时后清理废弃的会话
  • 内存刷新 — 在会话到期之前主动刷新内存
  • 缓存刷新 — 刷新模型列表和大模型提供商(provider)状态

流程管理

网关作为长期进程运行,通过以下方式管理:

  • hermes gateway start / hermes gateway stop — 手动控制
  • systemctl (Linux) 或 launchctl (macOS) — 服务管理
  • ~/.hermes/gateway.pid 处的 PID 文件 — 配置文件范围内的进程跟踪

配置文件范围与全局start_gateway() 使用配置文件范围的 PID 文件。 hermes gateway stop 仅停止当前配置文件的网关。 hermes gateway stop --all 使用全局 ps aux 扫描来终止所有网关进程(在更新期间使用)。

相关文档