乐于分享
好东西不私藏

OpenClaw 子模块: 消息路由与多通道适配

OpenClaw 子模块: 消息路由与多通道适配
如果你把 OpenClaw 暴露到公网而不配任何路由规则,那任何人都能在你的 WhatsApp 上 @ 你的 Agent 让它执行命令——查文件、跑脚本、发消息。这显然是安全灾难。OpenClaw 的路由系统通过 allowFrom、mentionRules、groupPolicy 三层过滤,把"谁的消息能触发 Agent 推理"精确到"哪个群的哪个用户说了什么关键词"。

今天我们从路由引擎的源码出发,拆解从消息到达到 Agent 被唤醒的完整路径。


一、消息的标准化:Channel Adapter 接口

OpenClaw 接 20+ 个聊天平台,每个平台的 SDK 和消息格式完全不同——WhatsApp 用的是 Baileys WebSocket 的 protobuf 消息,Telegram 用的 grammY Bot API 的 JSON 结构,Discord 用的是 Gateway Intents 的事件对象。

Channel Adapter 的职责就是把所有平台的异构消息转成统一的内部格式:

WhatsApp protobuf → Channel Adapter → {sender, chatId, text, timestamp, platform: ”whatsapp”}Telegram JSON    → Channel Adapter → {sender, chatId, text, timestamp, platform: ”telegram”}Discord Event    → Channel Adapter → {sender, chatId, text, timestamp, platform: ”discord”}

Agent 完全不需要知道消息是从哪个平台来的——它只看到统一的 InboundMessage。回复时也一样:Agent 生成文本 → Channel Adapter 反向适配成各平台的发送格式。


二、路由三件套:allowFrom、mentionRules、groupPolicy

allowFrom:最粗粒度的白名单

{  ”channels”: {    ”whatsapp”: {      ”allowFrom”: [”+15551234567”, ”+8613800138000”]    }  }}

只允许列表里的号码给 Agent 发消息。其他人发了 → 消息被静默丢弃,Agent 不唤醒。这是第一道防线——"连门都不让进"。

mentionRules:群聊里的 @ 规则

{  ”channels”: {    ”telegram”: {      ”groups”: {        ”*”: { ”requireMention”: true },        ”-1001234567890”: { ”requireMention”: false }      }    }  }}
  • "*"
     规则:默认所有群都需要 @ 才能触发 Agent(防止群聊噪音)
  • 特定群 ID 规则:某个群不需要 @(比如你和 Agent 的私人群)
  • 还支持 mentionPatterns:自定义触发词(比如 @openclaw 或 !ai

groupPolicy:群级别的细粒度控制

每个群可以独立配置谁的消息能被处理、需要什么触发条件、是否需要审批。插件通道(比如 Discord/MSTeams)可以注册自己的 groupPolicy 实现。

三层过滤的执行顺序:allowFrom → groupPolicy → mentionRules。如果 allowFrom 不通过,后面的根本不检查——短路求值,避免浪费计算。


三、Session Key:跨通道的用户身份绑定

当同一个用户在 WhatsApp 和 Telegram 上都有消息时,怎么把它们路由到同一个 Session?

OpenClaw 用 session-key 机制解决:

{  ”routing”: {    ”sessionKey”: {      ”mode”: ”sender”,      ”bindings”: {        ”whatsapp:+15551234567”: ”user-alice”,        ”telegram:123456789”: ”user-alice”      }    }  }}

session-key 的值是"用户标识"——WhatsApp 号码 + Telegram ID 映射到同一个 user-alice,Agent 在两边看到的是同一个会话上下文。你在 WhatsApp 上聊了三轮,切到 Telegram 上接着问"刚才那个 bug 怎么样了"——Agent 记得。

默认模式 sender 让每个发送者自动获得独立 Session。不需要配绑定也能用——只是跨平台不共享。


四、消息处理的全链路时序

一条 WhatsApp 消息触发 Agent 推理的完整路径:

1. Baileys WebSocket 收到 WhatsApp 消息2. WhatsApp Channel Adapter 标准化为 InboundMessage3. Routing Engine 评估:   a. allowFrom 检查 → sender 在白名单里 ✓   b. 如果是群消息 → groupPolicy 检查 → 允许处理 ✓   c. mentionRules 检查 → 消息包含 @openclaw ✓4. Session Manager 根据 session-key 查找 Session:   a. 找到 → 追加消息到现有 Session   b. 没找到 → 创建新 Session(新 JSONL 文件)5. Agent Loop 启动:   a. 读 System Prompt(AGENTS.md + Skills)   b. 读 Session 历史(前 20 条消息 + 上下文窗口)   c. 绑定 Tools(内置 + plugin 注册)   d. 调 LLM → tool_call → 执行 → 再推理 → 生成回复6. Response 通过 Channel Adapter → Baileys → WhatsApp 发送

整个链路里,路由层的开销极低——allowFrom 是内存查表,session-key 是字符串映射,都不涉及 I/O。唯一可能慢的是 Session 文件读取(磁盘 I/O),但 JSONL 的追加写+顺序读让这个开销也几乎可以忽略。


五、核心通道 vs 插件通道:代码怎么分布?

位置
通道
原因
src/telegram/
Telegram
核心通道,编译进 Gateway 二进制
src/discord/
Discord
同上
src/slack/
Slack
同上
src/signal/
Signal
同上
src/imessage/
iMessage
同上(macOS only)
src/web/
WhatsApp/WebChat
同上
extensions/msteams/
MSTeams
插件通道,按需安装
extensions/matrix/
Matrix
同上
extensions/zalo/
Zalo
同上
extensions/line/
LINE
同上

为什么 Telegram/Discord/Slack 放在核心而不是插件?因为这些是使用量最大的通道,编译进核心减少安装步骤。而 MSTeams/Matrix/Zalo/LINE 等放在插件里,用户不需要的就不装——减少依赖树和包体积。


六、群聊消息的噪音问题:怎么不让 Agent 被群里聊天淹死?

群聊最大的问题是噪音——100 人的群每分钟几十条消息,如果每条都触发 Agent 推理,token 费用直接爆炸。

OpenClaw 的解决策略:

  1. requireMention(默认开启):只有 @Agent 的消息才触发推理
  2. mentionPatterns:自定义触发模式("!ai"、"@claw"、"hey bot")
  3. 静默丢弃:没 @ 的消息 → 不进 Agent Loop → 不计费
  4. 群摘要模式:可以配 cron 定时任务,让 Agent 定期读群聊天记录生成摘要——而不是实时响应每条消息

总结

消息路由层的设计核心:

  1. Channel Adapter 让 Agent 对平台透明:Agent 只看到统一消息格式
  2. 三层过滤(allowFrom → groupPolicy → mentionRules)短路求值:不在白名单的消息根本不进推理
  3. session-key 实现跨平台会话连续性:WhatsApp + Telegram 共享同一个 Agent 上下文
  4. 核心通道编译进二进制,小众通道插件化:减少默认安装的依赖

下一篇拆工具系统与 Skills 协同——看看 TypeBox 怎么保证工具 Schema 的类型安全,Skills 怎么在 System Prompt 里告诉模型"什么时候用哪个工具"。