Skip to content

第六章 统一网关:一个入口,连接所有渠道

核心问题:OpenClaw 怎样把 Telegram、Discord、Slack、WhatsApp 等不同平台的消息统一接入,并在多渠道下尽量保持连续的协作体验?


上一章讲的是消息进来以后怎么排队、怎么执行、怎么不串线。

这一章要补上更前面的一层:这些消息到底从哪里来,又是怎么进入系统的。

如果 Agent 只活在一个聊天窗口里,这个问题不复杂。但 OpenClaw 面向的是现实世界里的多个渠道。平台一多,系统就不只要处理“用户说了什么”,还要处理:

  • 消息来自哪个平台
  • 这个平台怎么认证
  • 这个平台的用户 ID 怎么表示
  • 回复该发回哪里
  • 跨平台时,系统能不能认出还是同一个人
  • 不同平台能力不一样时,回复该怎么展示

所以统一网关的作用,不只是“多接几个平台”,而是把平台差异挡在外层,让 Agent 内部尽量只处理统一的“人、会话、消息”。


第一节 为什么需要统一网关

这一节只回答一个问题:为什么 OpenClaw 不能让每个平台直接连到 Agent,而必须先经过一层统一网关。

1.1 这不只是“多接几个平台”

不同平台的差异很大。下面这张表只是常见示意,实际字段名和认证方式要以当前渠道实现为准:

平台ID 例子文本字段示意常见接法
Telegram数字 IDmessage.textBot Token
DiscordSnowflake IDmessage.contentBot Token / OAuth
SlackU... 这类成员 ID事件对象里的字段App Token / OAuth
飞书open_iduser_id平台事件里的字段tenant token

除此之外,平台之间还常常有这些差别:

  • 私聊和群聊结构不同
  • 线程支持不同
  • 流式输出能力不同
  • 单条消息长度限制不同
  • 回复、编辑、附件处理方式不同

如果没有统一网关,每接一个新平台,Agent 核心里就会多一层平台判断。

短期看,这些 if-else 都能跑;长期看,系统会越来越像一团打结的线。所以统一网关的第一层意义不是“让架构显得高级”,而是:把平台差异拦在最外层,不让它们一路流进 Agent 核心。

1.2 真正麻烦的,不只是字段不同

字段不同只是工程问题。

更影响体验的是:同一个人换个平台后,系统到底认不认得出来。

这里先分清三个词:

可以先理解成什么
渠道你从哪个平台来
身份这个人到底是谁
会话当前这一段具体对话现场

这三层不是一回事。

在 OpenClaw 的现有文档里,这一块主要和 identityLinksdmScope 等配置有关。也就是说:

  • 系统要先判断“这几个平台 ID 是不是同一个人”
  • 还要再判断“这些消息要不要算同一段会话”

这两件事会互相影响,但不应该混成一件事。

一个更稳的理解是:

  • 身份是否统一,看你有没有做身份关联
  • 会话是否合并,看你的会话粒度怎么配

这里还要多加一句:identityLinks 更像给 DM session key 做 canonicalization 的辅助配置,不是自动把所有渠道上下文都焊在一起的总开关。
所以不要默认“跨平台一定合并”,也不要默认“跨平台一定分开”。这要看配置和你的真实目标。

1.3 统一网关保护的,是 Agent 核心

走到这里,就能更准确地理解 Gateway 的角色了。它不是一个“消息转发器”那么简单,更像一点的说法是:Gateway 是 Agent 的感官系统,也是外部世界和内部推理之间的翻译层。

可以想象成:

text
平台原始事件
→ Gateway 先读懂
→ 转成系统内部统一消息
→ 再交给 Agent 判断

反过来也一样:

text
Agent 先产出统一回复
→ Gateway 再按当前平台能力发回去

这样一来,Agent 核心就能长期只关心几件事:

  • 谁在找我
  • 现在是哪段会话
  • 用户说了什么
  • 下一步该怎么推进

至于“这是哪个平台”“平台支不支持线程”“要不要分块发”“要不要流式更新”,这些差异都留在网关层。


第二节 它是怎么跑起来的

这一节要回答的问题是:一条来自外部平台的消息,进入 OpenClaw 以后,到底会经历哪些步骤,最后又怎样变成一条合适的回复发回去。

2.1 第一步:先把平台原始事件转成统一消息

不同平台的原始事件长得很不一样,所以 Gateway 先做的不是路由,而是标准化

可以把这一步理解成:

text
平台原始事件
→ 渠道适配层读取并解析
→ 提取统一字段
→ 形成系统内部消息

这一章里,你可以把这层适配器统称为渠道插件。在本仓库前面的构建章节里,也会把它写成 ChannelPlugin。你可以把它理解成“平台翻译层”。

它至少要解决两件事:

  • 入站时,把平台事件翻成统一格式
  • 出站时,把统一回复翻回平台格式

2.2 第二步:再决定交给哪个 Agent

标准化之后,系统才开始路由。这里的核心问题是:这条消息该交给哪个 Agent。

在 OpenClaw 里,系统不一定只有一个 Agent。不同渠道、不同账号、不同群组,可能默认归不同 Agent。

从机制上看,这一层对应的就是路由解析逻辑。你不需要死记函数名,但最好知道它大致在做什么:系统会根据当前渠道、账号、对话对象,以及你写下的 bindings 规则,从更具体的匹配一路走到更通用的匹配,最后找到最合适的 Agent。

这一层通常和这些信息有关:

名字可以先理解成什么
channel哪个平台
account这个平台上的哪个机器人账号
peer这条消息来自哪个聊天对象
bindings“这类消息交给哪个 Agent” 的规则

可以把过程看成:

text
先看更具体的绑定
→ 再看默认归属
→ 最后落到某个 Agent

这一层的重点很朴素:先交对人,后面才有意义。
默认情况下,OpenClaw 还是为一条入站消息选一个 Agent;只有在 broadcast groups 这类特定配置下,同一条消息才会 fan-out 给多个 Agent。

2.3 第三步:再决定进哪段会话

找到 Agent 以后,还不能立刻开始处理。因为“交给哪个 Agent”和“落到哪段会话”是两件事。

这里可以把它理解成“会话键”。仓库文档里给过一些常见示意:

来源常见会话键格式
私聊默认agent:<agentId>:<mainKey>(常见示例是 agent:main:main
群聊agent:<agentId>:<channel>:group:<id>
Croncron:<job.id>

这也是为什么 Gateway 和上一章的消息循环是连着的。上一章解决的是“同一条 lane 里的消息怎么排队”;这一章在它前面补上了另一半:什么叫“同一条 lane”,是谁来决定的。

这里还有两个必须一起看的配置:

配置主要解决什么
dmScope私聊会话按多细的粒度拆开
identityLinks不同平台上的 ID 是否视为同一个人

这一点需要特别说明:

按现有使用文档,默认私聊可能共享同一条主会话;多人共用时,官方建议把 session.dmScope 设成 per-channel-peer 来隔离不同人。

同时,identityLinks 又可能让同一个人在多个平台上的 ID 关联起来。
但它只是帮助系统把 DM peer id 归一化;如果你用的是 per-channel-peerper-account-channel-peer,channel / account 这一层仍然会保留下来,所以不会因为做了身份关联就自动跨平台共用同一段上下文。

因此,最终是“合并”还是“隔离”,要看你的 dmScope、身份关联和渠道场景一起怎么配。

一句话记住:先想清楚你要不要跨平台认人,再想清楚你要不要跨平台共用上下文。

2.4 第四步:Agent 回完以后,网关再按平台发出去

OpenClaw 内部先形成统一回复,再由 Gateway 决定怎么发回当前平台。

这里最重要的原则是:优雅降级。 能展示得更好,就展示得更好;做不到,也至少保证内容送达。

仓库里的相关文档已经明确提到一些平台侧能力,例如:

  • 流式输出模式:off / partial / block / progress
  • 线程绑定:不同平台可按线程继续对话
  • 分块发送:长内容可以按块送出

但这些能力并不是每个平台、每种接法都一样。所以网关层通常要做这些判断:

  • 当前平台支不支持流式输出
  • 这段会话是不是应该回到原线程
  • 长消息要不要分段
  • 后续回复该沿哪条出站路径继续发

这里最后一点,在实现里可能有不同做法。更稳的说法是:系统通常需要记住最近一次有效的出站路径,避免后续回复跑偏。

2.5 第五步:优雅降级不只是样式问题

很多人一听“优雅降级”,会先想到富文本和纯文本。其实还不止这些。

网关层还要处理两类很现实的问题:

  1. 消息太长,一次发不下
  2. 这次发送失败了

所以一个成熟的 Gateway,通常不只是“把文本发出去”,还会先判断:

  • 当前平台单条消息大概能发多长
  • 代码块和列表要怎么切,才不难读
  • 失败是暂时的,还是结构性的
  • 值不值得重试

所以优雅降级的重点不是“好不好看”,而是:平台条件不理想时,内容也要尽量完整、稳定、可预期地送达。

2.6 第六步:为什么这层设计能持续扩展

到这里,整条链路就比较清楚了:

text
平台事件
→ 渠道适配
→ 标准化
→ 路由到 Agent
→ 归入正确会话
→ 进入消息循环
→ Agent 产出回复
→ 按平台能力发回去

最后还差一个关键问题:为什么 OpenClaw 能不断接新平台,而不是每接一个平台就得重写系统? 答案就在 ChannelPlugin 这层插件边界上。

从设计角度看,插件机制最大的价值不是“代码组织更好看”,而是:系统终于可以把‘平台特有能力’和‘Agent 通用能力’拆开演化。

对一个新平台来说,最小要做的事情通常并不多:

  1. 告诉系统自己是谁;
  2. 能把平台事件读进来;
  3. 能把系统回复发回去。

这就是很典型的工程化思路:入口门槛低,上限又足够高。也是因为有了这层插件注册和能力声明,Gateway 才能真正做到:新增平台时,主要改插件,不改 Agent 核心。

也就是说,统一网关真正带来的不是“多平台炫技”,而是长期可维护的边界。


第三节 实际使用时怎么配

前两节讲的是“为什么存在”和“它怎么运转”,现在更实际的问题来了:如果你真的要开始用 OpenClaw 的 Gateway,该怎么配,才比较稳,比较不容易掉坑。

3.1 先跑通一条最小链路

人们最容易犯的错,是一上来就把很多平台一起接上,还顺手把 streaming、线程、富文本等增强能力全开。

更稳的做法是:先选一个最熟的平台,把最小链路跑通。

所谓最小链路,至少包括四步:

  1. 能收到消息
  2. 能路由到正确的 Agent
  3. 能落到正确的会话
  4. 能把回复发回正确的地方

如果你已经在前面的安装阶段配好了默认模型,下面这份配置片段就足够你先跑通“单渠道 + 默认 Agent + 每人独立私聊上下文”:

json5
{
  agents: {
    defaults: {
      workspace: "~/.openclaw/workspace"
    }
  },
  channels: {
    telegram: {
      enabled: true,
      botToken: "${TELEGRAM_BOT_TOKEN}",
      dmPolicy: "pairing"
    }
  },
  gateway: {
    bind: "loopback",
    auth: {
      mode: "token",
      token: "dev-token"
    }
  },
  session: {
    dmScope: "per-channel-peer"
  }
}

这个起手式的重点不是“功能全”,而是“变量少”:

  • 先只接一个渠道,排掉多平台带来的干扰
  • 先用默认 Agent,不急着一开始就拆多 Agent 路由
  • per-channel-peer 会按“渠道 + 联系人”隔离私聊,最适合多人共用同一个机器人时避免串线;这也是官方安全审计对共享私聊场景给出的常见建议
  • 如果后面你要接第二个 Agent,再补 bindings 去做显式路由就行

这里顺手提醒一句:示例里的 dev-token 只适合本地试跑。只要不是纯本地临时环境,就应该换成更严格的鉴权和凭证管理方式。

如果你想把这条最小链路真正跑通,建议紧接着按下面的顺序做一次最短验证:

  1. openclaw config validate,先确认配置本身可解析;
  2. openclaw channels status --probe,确认渠道插件已经起来;
  3. 给机器人发一条私信;如果你开了 dmPolicy: "pairing",先用 openclaw pairing list telegram 看待批准请求,再用 openclaw pairing approve telegram <code> 放行;
  4. openclaw agents bindings,确认当前渠道最终命中了你预期的 Agent。

只要这四步跑顺了,这一章的大部分核心机制其实就已经活起来了。这时候你再去接第二个平台,排错会轻松很多,因为你已经知道:Agent 核心本身是通的,问题大概率出在新增渠道的适配或配置上。

所以零基础同学最好的起手式不是“全开”,而是:单渠道先通,多渠道后加。

3.2 配之前先想清楚四件事

真正开始配 Gateway 时,建议先把这四个问题写下来:

  1. 这类消息应该交给谁处理?
    这对应的是路由层。你要先明确:哪些渠道默认归哪个 Agent,哪些频道或群组要单独绑定,哪些账号只服务某一类任务。如果这一层没先想清楚,后面再怎么调显示效果都没意义。

  2. 哪些平台 ID 其实是同一个人?
    这对应的是 identityLinks。这里最关键的不是“绑得越多越好”,而是:只在你真的确定是同一人的时候才绑定。 因为一旦绑错,系统就会把本不该共享的偏好或身份信息混到一起。

  3. 哪些消息应该算同一段会话?
    这对应的是 sessionKeydmScope 相关思路。这里一定要记住一句话:同一个人,不等于同一个会话。 所以你要先判断:是希望不同渠道各自保留上下文,还是希望某些场景合并得更紧一些,私聊要拆得多细,群聊要不要按线程隔离。

  4. 哪些增强能力要先开,哪些可以后开?

阶段优先目标
第一层先保证能收、能发、不串线
第二层再去打开 streaming、threads、富文本或平台专属增强能力

因为“能用”和“好用”是两件事。先把“能用”做稳,后面才有资格讨论体验优化。

3.3 一定要分清“身份共享”和“上下文共享”

这是这一章最容易踩坑的地方。

更准确的说法是:

  • identityLinks 主要回答“这是不是同一个人”
  • dmScope 等会话配置主要回答“这些消息算不算同一段会话”

这两者有关,但不是同一个开关。

你可以用下面这张表先想清楚目标:

目标更稳的思路
只想跨平台认人先做身份关联,再谨慎看会话是否合并
想让同一个人跨平台继续同一段私聊身份关联和会话粒度要一起设计,而且要确认 dmScope 真的允许去掉 channel / account 维度
多人共用机器人但绝不串线优先用 per-channel-peer 这类更细粒度隔离
群聊和线程要保持独立继续按群或线程拆分会话

所以如果你未来要做跨平台协作,最重要的不是“有没有 identityLinks”,而是:你心里要先清楚,自己到底想共享哪一层。

可以先记两个最容易混淆、也最实用的目标:

目标配置思路结果
跨平台认人,但上下文仍分开identityLinksdmScope 保持 per-channel-peer 或更细系统知道是同一个人,但 Telegram 和 Discord 仍各走各的私聊上下文
跨平台认人,并希望继续同一段私聊identityLinks,并确认 dmScope 允许去掉 channel / account 维度只有在会话粒度也一并放宽时,跨平台才可能真正共用一段上下文

3.4 多渠道优化,顺序最好是“先保底,再增强”

Gateway 很容易让人上头,因为一旦看到 streaming、按钮、线程回复、富文本卡片、typing 指示,你会很想全部打开。但从工程上讲,更推荐的顺序其实很朴素:

text
先保证内容一定送达
→ 再保证会话一定正确
→ 再保证展示效果更好
→ 最后才追求平台特有增强能力

这背后的原因很简单:用户最先感知到的,不是“有没有流式输出”,而是:

  • 回没回我;
  • 回对地方没;
  • 会不会串对话;
  • 同一个人能不能认出来。

这些基础问题不稳时,越花哨的增强能力,越容易让你更难排障。

所以优雅降级不仅是系统内部设计原则,也应该成为你的配置原则:

先把所有平台都做成“至少稳稳能用”,再去追求“在某个平台上特别好用”。

3.5 遇到问题时,按固定顺序排错

Gateway 问题看起来很多,但通常可以按这个顺序收敛:

  1. 先看消息有没有真的进来。
    先确认渠道插件是否启动,平台事件有没有成功送到本地,对应账号是否配置正确。如果消息压根没进系统,后面就不用看了。最常用的抓手是 openclaw channels status 和渠道日志。

  2. 再看路由是不是命中了正确 Agent。
    如果消息进来了,但去了错误的 Agent,通常要优先检查:bindings 写没写对,默认账号和默认归属是不是配错,某条更具体的规则有没有把它截走。这里最直接的抓手是 openclaw agents bindings

  3. 再看会话是不是算对了。
    如果 Agent 对人是对的,但上下文明显串线或断裂,就要看:sessionKey 的粒度是不是不合适,dmScope 是否把私聊拆得太粗或太细,identityLinks 是否把不该合的人绑在一起。先回到配置里核对 session.dmScope,必要时再对照日志里的 session key。

  4. 最后再看平台能力和出站适配。
    比如,为什么没有 streaming,为什么按钮没显示,为什么长消息被切得不好看,为什么回复回到了错误渠道或错误位置。这时再去看平台能力声明、出站适配和最近一次回路信息,思路会清楚很多。

可以把这条顺序记成一句很短的话:

text
先看进没进来
→ 再看交没交对人
→ 再看会话算没算对
→ 最后再看显示和体验

这样排错,通常比一上来就翻平台细节要快得多。


本章小结

这一章表面上讲的是 Gateway,实际上讲的是:OpenClaw 怎样把外部平台整理成 Agent 能稳定处理的统一入口。

最重要的理解可以收成下面几条:

  1. 统一网关不是“多平台附件”,而是系统边界。
    它把平台差异挡在外层,避免污染 Agent 核心。

  2. 网关处理的不只是消息格式,还包括路由和会话归属。
    消息先交给正确的 Agent,再落到正确的会话。

  3. 身份统一和会话统一不是一个问题。
    identityLinksdmScope 和你的渠道策略需要一起看。

  4. 优雅降级很关键。
    平台能力强,就展示完整;平台能力弱,也要保证内容送达。

  5. 适配层让系统真正能扩展。
    新增平台主要改渠道适配,不必总去动 Agent 核心。

这就是 OpenClaw 的“感官系统”:把外面五花八门的平台世界,翻译成内部统一的协作现场。

下一章: 第七章 安全沙箱:自由与约束的平衡