hermes源码学习8--Gateway 内部机制

hermes源码学习8--Gateway 内部机制 消息 gateway 是一个长期运行的进程通过统一架构将 Hermes 连接到 20 余个外部消息平台。关键文件文件用途gateway/run.pyGatewayRunner— 主循环、斜杠命令、消息分发大文件请查看 git 获取当前行数gateway/session.pySessionStore— 会话持久化与会话键构造gateway/delivery.py向目标平台/频道投递出站消息gateway/pairing.py用于用户授权的 DM 配对流程gateway/channel_directory.py将聊天 ID 映射为可读名称用于 cron 投递gateway/hooks.pyHook钩子发现、加载与生命周期事件分发gateway/mirror.py为send_message提供跨会话消息镜像gateway/status.py面向 profile 范围的 gateway 实例的 token 锁管理gateway/builtin_hooks/始终注册的 hook 扩展点当前未内置任何 hookgateway/platforms/平台适配器每个消息平台一个架构概览┌─────────────────────────────────────────────────┐ │ GatewayRunner │ │ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ Telegram │ │ Discord │ │ Slack │ │ │ │ Adapter │ │ Adapter │ │ Adapter │ │ │ └────┬─────┘ └────┬─────┘ └────┬─────┘ │ │ │ │ │ │ │ └─────────────┼─────────────┘ │ │ ▼ │ │ _handle_message() │ │ │ │ │ ┌───────────┼───────────┐ │ │ ▼ ▼ ▼ │ │ Slash command AIAgent Queue/BG │ │ dispatch creation sessions │ │ │ │ │ ▼ │ │ SessionStore │ │ (SQLite persistence) │ └───────┴─────────────┴─────────────┴─────────────┘消息流程当消息从任意平台到达时平台适配器接收原始事件将其规范化为MessageEvent基础适配器检查活跃会话守卫若该会话的 agent 正在运行 → 将消息加入队列设置中断事件若为/approve、/deny、/stop→ 绕过守卫内联分发GatewayRunner._handle_message()接收事件通过_session_key_for_source()解析会话键格式agent:main:{platform}:{chat_type}:{chat_id}检查授权见下方授权章节检查是否为斜杠命令 → 分发至命令处理器检查 agent 是否已在运行 → 拦截/stop、/status等命令否则 → 创建AIAgent实例并运行对话响应通过平台适配器回传会话键格式会话键编码了完整的路由上下文agent:main:{platform}:{chat_type}:{chat_id}示例agent:main:telegram:private:123456789支持线程的平台Telegram 论坛话题、Discord 线程、Slack 线程可能在 chat_id 部分包含线程 ID。切勿手动构造会话键— 请始终使用gateway/session.py中的build_session_key()。两级消息守卫当 agent 正在运行时传入消息会依次经过两级守卫第一级 — 基础适配器gateway/platforms/base.py检查_active_sessions。若会话处于活跃状态将消息加入_pending_messages队列并设置中断事件。此级在消息到达 gateway runner之前进行拦截。第二级 — Gateway runnergateway/run.py检查_running_agents。拦截特定命令/stop、/new、/queue、/status、/approve、/deny并进行相应路由。其余所有消息触发running_agent.interrupt()。必须在 agent 被阻塞时到达 runner 的命令如/approve通过await self._message_handler(event)内联分发 — 绕过后台任务系统以避免竞态条件。授权Gateway 使用多层授权检查按顺序评估平台级全量放行标志如TELEGRAM_ALLOW_ALL_USERS— 若设置该平台所有用户均被授权平台白名单如TELEGRAM_ALLOWED_USERS— 逗号分隔的用户 IDDM 配对— 已认证用户可通过配对码为新用户授权全局放行标志GATEWAY_ALLOW_ALL_USERS— 若设置所有平台的所有用户均被授权默认拒绝— 未授权用户被拒绝DM 配对流程Admin: /pair Gateway: Pairing code: ABC123. Share with the user. New user: ABC123 Gateway: Paired! Youre now authorized.配对状态持久化于gateway/pairing.py重启后仍然有效。斜杠命令分发Gateway 中所有斜杠命令均经过相同的解析流程hermes_cli/commands.py中的resolve_command()将输入映射为规范名称处理别名、前缀匹配规范名称与GATEWAY_KNOWN_COMMANDS进行比对_handle_message()中的处理器根据规范名称进行分发部分命令受配置门控CommandDef上的gateway_config_gate运行中 Agent 守卫在 agent 处理消息期间不得执行的命令会被提前拒绝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具有特殊处理逻辑。配置来源Gateway 从多个来源读取配置来源提供内容~/.hermes/.envAPI 密钥、bot token、平台凭据~/.hermes/config.yaml模型设置、工具配置、显示选项环境变量覆盖上述任意配置与 CLI使用带硬编码默认值的load_cli_config()不同gateway 通过 YAML 加载器直接读取config.yaml。这意味着存在于 CLI 默认值字典但不在用户配置文件中的配置键在 CLI 和 gateway 之间可能表现不同。平台适配器每个消息平台在gateway/platforms/下均有对应适配器gateway/platforms/ ├── base.py # BaseAdapter — 所有平台的共享逻辑 ├── telegram.py # Telegram Bot API长轮询或 webhook ├── discord.py # Discord bot通过 discord.py ├── slack.py # Slack Socket Mode ├── whatsapp.py # WhatsApp Business Cloud API ├── signal.py # Signal通过 signal-cli REST API ├── matrix.py # Matrix通过 mautrix可选 E2EE ├── mattermost.py # Mattermost WebSocket API ├── email.py # 电子邮件通过 IMAP/SMTP ├── sms.py # 短信通过 Twilio ├── dingtalk.py # 钉钉 WebSocket ├── feishu.py # 飞书/Lark WebSocket 或 webhook ├── wecom.py # 企业微信WeCom回调 ├── weixin.py # 微信个人版通过 iLink Bot API ├── bluebubbles.py # Apple iMessage通过 BlueBubbles macOS 服务端 ├── qqbot/ # QQ Bot腾讯 QQ通过官方 API v2子包adapter.py、crypto.py、keyboards.py 等 ├── yuanbao.py # 元宝腾讯私信/群组适配器 ├── feishu_comment.py # 飞书文档/云盘评论回复处理器 ├── msgraph_webhook.py # Microsoft Graph 变更通知 webhookTeams、Outlook 等 ├── webhook.py # 入站/出站 webhook 适配器 ├── api_server.py # REST API 服务器适配器 └── homeassistant.py # Home Assistant 对话集成适配器实现统一接口connect()/disconnect()— 生命周期管理send_message()— 出站消息投递on_message()— 入站消息规范化 →MessageEventToken 锁使用唯一凭据连接的适配器在connect()中调用acquire_scoped_lock()在disconnect()中调用release_scoped_lock()。这可防止两个 profile 同时使用同一 bot token。投递路径出站投递gateway/delivery.py处理以下场景直接回复— 将响应发回原始聊天主频道投递— 将 cron 任务输出和后台结果路由至已配置的主频道显式目标投递—send_message工具指定telegram:-1001234567890或通过 hermes send CLI 封装同一工具供 shell 脚本使用跨平台投递— 投递至与原始消息不同的平台Cron 任务投递不会镜像到 gateway 会话历史中 — 它们仅存在于各自的 cron 会话中。这是有意为之的设计选择以避免消息交替违规。HooksGateway hook 是响应生命周期事件的 Python 模块。Gateway Hook 事件事件触发时机gateway:startupGateway 进程启动时session:start新对话会话开始时session:end会话完成或超时时session:reset用户通过/new重置会话时agent:startAgent 开始处理消息时agent:stepAgent 完成一次工具调用迭代时agent:endAgent 完成并返回响应时command:*任意斜杠命令被执行时Hook 从gateway/builtin_hooks/扩展点 — 当前发行版中为空_register_builtin_hooks()是一个空操作存根和~/.hermes/hooks/用户安装中发现。每个 hook 是一个包含HOOK.yaml清单和handler.py的目录。内存提供者集成当内存提供者插件如 Honcho启用时Gateway 为每条消息创建一个带会话 ID 的AIAgentMemoryManager使用会话上下文初始化提供者提供者工具如honcho_profile、viking_search通过以下路径路由AIAgent._invoke_tool() → self._memory_manager.handle_tool_call(name, args) → provider.handle_tool_call(name, args)会话结束/重置时on_session_end()触发以进行清理和最终数据刷写内存刷写生命周期当会话被重置、恢复或过期时内置内存刷写至磁盘内存提供者的on_session_end()hook 触发临时AIAgent运行仅含内存的对话轮次上下文随后被丢弃或归档后台维护Gateway 在处理消息的同时运行周期性维护任务Cron 计时— 检查任务计划并触发到期任务会话过期— 超时后清理废弃会话内存刷写— 在会话过期前主动刷写内存缓存刷新— 刷新模型列表和提供者状态进程管理Gateway 作为长期运行进程运行管理方式如下hermes gateway start/hermes gateway stop— 手动控制systemctlLinux或launchctlmacOS— 服务管理PID 文件位于~/.hermes/gateway.pid— 面向 profile 的进程追踪Profile 范围 vs 全局start_gateway()使用 profile 范围的 PID 文件。hermes gateway stop仅停止当前 profile 的 gateway。hermes gateway stop --all使用全局ps aux扫描来终止所有 gateway 进程用于更新时。