H繁中版
文件開發者指南gateway internals
<!-- Source: https://hermesbible.com/docs/developer-guide/gateway-internals -->

訊息閘道器是一個長駐程序,透過統一架構將 Hermes 連接到 20 多個外部訊息平台。

關鍵檔案

檔案用途
gateway/run.pyGatewayRunner — 主迴圈、斜線命令、訊息分派(大型檔案;請查看 git 取得目前行數)
gateway/session.pySessionStore — 對話持久化和會話金鑰建構
gateway/delivery.py外發訊息遞送到目標平台/頻道
gateway/pairing.pyDM 配對流程用於使用者授權
gateway/channel_directory.py將聊天 ID 映射為人類可讀的名稱,用於排程遞送
gateway/hooks.pyHook 發現、載入和生命週期事件分派
gateway/mirror.py用於 send_message 的跨會話訊息鏡像
gateway/status.py用於 profile 範圍閘道器實例的 Token 鎖管理
gateway/builtin_hooks/永遠註冊的 hook 擴充點(目前無內建)
gateway/platforms/平台適配器(每個訊息平台一個)

架構概覽

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

訊息流程

當訊息從任何平台抵達時:

  1. 平台適配器收到原始事件,將其正規化為 MessageEvent
  2. 基礎適配器檢查活動會話守衛:
    • 如果 Agent 正在為此會話執行 → 排隊訊息,設定中斷事件
    • 如果是 /approve/deny/stop → 繞過守衛(直接分派)
  3. **GatewayRunner._handle_message()**接收事件:
    • 透過 _session_key_for_source() 解析會話金鑰(格式:agent:main:{platform}:{chat_type}:{chat_id}
    • 檢查授權(見下方授權)
    • 檢查是否為斜線命令 → 分派到命令處理器
    • 檢查 Agent 是否已在執行 → 攔截 /stop/status 等命令
    • 否則 → 建立 AIAgent 實例並執行對話
  4. 回應透過平台適配器送回

會話金鑰格式

會話金鑰編碼完整的路由上下文:

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 正在活躍執行時,傳入訊息通過兩個順序守衛:

  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()

在 Agent 被阻塞時必須到達運行器的命令(如 /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
Gateway: "Pairing code: ABC123. Share with the user."
New user: ABC123
Gateway: "Paired! You're now authorized."

配對狀態在 gateway/pairing.py 中持久化,並在重啟後存活。

斜線命令分派

閘道器中的所有斜線命令都通過相同的解析管線:

  1. hermes_cli/commands.py 中的 resolve_command() 將輸入映射為標準名稱(處理別名、前綴匹配)
  2. 標準名稱針對 GATEWAY_KNOWN_COMMANDS 進行檢查
  3. _handle_message() 中的處理器根據標準名稱分派
  4. 部分命令受設定閘控(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)有特殊處理。

設定來源

閘道器從多個來源讀取設定:

來源提供的內容
~/.hermes/.envAPI 金鑰、機器人權杖、平台憑證
~/.hermes/config.yaml模型設定、工具設定、顯示選項
環境變數覆寫上述任何設定

與 CLI(使用 load_cli_config() 搭配硬編碼預設值)不同,閘道器直接透過 YAML 載入器讀取 config.yaml。這意味著存在於 CLI 預設字典中但不在使用者設定檔中的設定鍵,在 CLI 和閘道器之間可能有不同的行為。

平台適配器

每個訊息平台在 gateway/platforms/ 中有一個適配器:

gateway/platforms/
├── base.py              # BaseAdapter——所有平台共用的邏輯
├── telegram.py          # Telegram Bot API(長輪詢或 webhook)
├── discord.py           # 透過 discord.py 的 Discord 機器人
├── slack.py             # Slack Socket Mode
├── whatsapp.py          # WhatsApp Business Cloud API
├── signal.py            # 透過 signal-cli REST API 的 Signal
├── matrix.py            # 透過 mautrix 的 Matrix(可選 E2EE)
├── mattermost.py        # Mattermost WebSocket API
├── email.py             # 透過 IMAP/SMTP 的 Email
├── sms.py               # 透過 Twilio 的 SMS
├── dingtalk.py          # DingTalk WebSocket
├── feishu.py            # Feishu/Lark WebSocket 或 webhook
├── wecom.py             # WeCom(企業微信)callback
├── weixin.py            # Weixin(個人微信)透過 iLink Bot API
├── bluebubbles.py       # 透過 BlueBubbles macOS 伺服器的 Apple iMessage
├── qqbot/               # QQ Bot(騰訊 QQ)透過 Official API v2(子套件:adapter.py、crypto.py、keyboards.py、…)
├── yuanbao.py           # Yuanbao(騰訊)DM/群組適配器
├── feishu_comment.py    # Feishu 文件/雲端硬碟留言回覆處理器
├── msgraph_webhook.py   # Microsoft Graph 變更通知 webhook(Teams、Outlook 等)
├── webhook.py           # 入站/出站 webhook 適配器
├── api_server.py        # REST API 伺服器適配器
└── homeassistant.py     # Home Assistant 對話整合

適配器實作共用介面:

  • connect() / disconnect() — 生命週期管理
  • send_message() — 外發訊息遞送
  • on_message() — 入站訊息正規化 → MessageEvent

Token 鎖

使用唯一憑證連接的適配器在 connect() 中呼叫 acquire_scoped_lock(),在 disconnect() 中呼叫 release_scoped_lock()。這防止兩個 profile 同時使用相同的機器人權杖。

遞送路徑

外發遞送(gateway/delivery.py)處理:

  • 直接回覆 — 將回應送回原始聊天
  • 主頻道遞送 — 將排程任務輸出和背景結果路由到設定的主頻道
  • 明確目標遞送send_message 工具指定 telegram:-1001234567890,或用於 shell 腳本的 hermes send CLI 包裝相同工具
  • 跨平台遞送 — 遞送到與原始訊息不同的平台

排程任務遞送不會被鏡像到閘道器會話歷史中——它們只存在於自己的排程會話中。這是刻意的設計選擇,以避免訊息交替違反。

Hooks

閘道器 hooks 是回應生命週期事件的 Python 模組:

閘道器 Hook 事件

事件觸發時機
gateway:startup閘道器程序啟動
session:start新對話會話開始
session:end會話完成或逾時
session:reset使用者透過 /new 重置會話
agent:startAgent 開始處理訊息
agent:stepAgent 完成一次工具呼叫迭代
agent:endAgent 結束並回傳回應
command:*任何斜線命令被執行

Hooks 從 gateway/builtin_hooks/(一個擴充點——目前在發行版中為空;_register_builtin_hooks() 是一個無操作的 stub)和 ~/.hermes/hooks/(使用者安裝)發現。每個 hook 是一個包含 HOOK.yaml 設定檔和 handler.py 的目錄。

Memory Provider 整合

當 memory provider 外掛(例如 Honcho)啟用時:

  1. 閘道器為每則訊息建立帶有 session 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. Memory provider 的 on_session_end() hook 觸發
  3. 臨時 AIAgent 執行一個僅記憶的對話輪次
  4. 上下文然後被捨棄或歸檔

背景維護

閘道器在訊息處理的同時執行週期性維護:

  • 排程心跳 — 檢查任務排程並觸發到期任務
  • 會話過期 — 在逾時後清理被放棄的會話
  • 記憶刷新 — 在會話過期前主動刷新記憶
  • 快取刷新 — 刷新模型列表和供應商狀態

程序管理

閘道器作為長駐程序運行,透過以下方式管理:

  • hermes gateway start / hermes gateway stop — 手動控制
  • systemctl(Linux)或 launchctl(macOS)— 服務管理
  • 位於 ~/.hermes/gateway.pid 的 PID 檔案 — profile 範圍的程序追蹤

Profile 範圍 vs 全域start_gateway() 使用 profile 範圍的 PID 檔案。hermes gateway stop 只停止當前 profile 的閘道器。hermes gateway stop --all 使用全域 ps aux 掃描來終止所有閘道器程序(在更新期間使用)。

相關文件



會話儲存