Skip to content

04 通道适配器框架与账号生命周期(重点)

这章到底在讲什么

一句话:OpenClaw 把每个平台(Telegram/Discord/Signal 等)都抽象成同一套 ChannelPlugin 适配器合同。
所以核心系统不需要知道“某个平台的私有细节”,只要按统一接口调用即可。

如果你要做类似项目,这一章就是“多通道架构”的核心。

步骤一:先看全链路(实现入口)

  1. 通道接口合同定义:src/channels/plugins/types.plugin.ts
  2. 插件注册通道能力:src/plugins/registry.ts
  3. 网关启动/停止通道:src/gateway/server-channels.ts
  4. 入站消息路由到 agent:src/routing/resolve-route.ts
  5. 出站消息走统一适配器:src/infra/outbound/deliver.ts
  6. 通道状态探测与体检:src/gateway/server-methods/channels.ts

步骤二:逐层拆开讲(小白可落地)

模块 1:统一接口合同(最关键)

src/channels/plugins/types.plugin.ts 里,核心类型是:

ts
export type ChannelPlugin = {
  id: ChannelId
  meta: ChannelMeta
  capabilities: ChannelCapabilities
  config: ChannelConfigAdapter
  pairing?: ChannelPairingAdapter
  outbound?: ChannelOutboundAdapter
  status?: ChannelStatusAdapter
  gateway?: ChannelGatewayAdapter
  auth?: ChannelAuthAdapter
}

你可以把它理解成“通道驱动程序接口”:

  1. config:怎么从配置里读账号、解析账号、判断是否配置完成。
  2. gateway:网关启动时,怎么拉起这个通道账号(如 webhook、长连接、SDK client)。
  3. outbound:收到 AI 回复后,怎么把文本/媒体发回该平台。
  4. statuschannels status --probe 时怎么体检。
  5. auth:怎么登录、登出、二维码流程等。

模块 2:插件如何注册“我是一个通道”

插件入口通常像这样(例如 extensions/telegram/index.ts):

ts
register(api) {
  setTelegramRuntime(api.runtime)
  api.registerChannel({ plugin: telegramPlugin })
}

然后 src/plugins/registry.tsregisterChannel(...) 会把它放进 registry.channels
从这一步开始,网关、CLI、路由层都能看到这个通道。

模块 3:账号生命周期由 ChannelManager 统一管理

src/gateway/server-channels.tscreateChannelManager(...) 提供:

  1. startChannels():启动所有已配置通道。
  2. startChannel(channel, accountId?):按通道/账号启动。
  3. stopChannel(channel, accountId?):按通道/账号停止。
  4. markChannelLoggedOut(...):标记账号登出状态。
  5. getRuntimeSnapshot():汇总运行态(running/lastError/lastStartAt 等)。

核心设计点:账号是一级实体
同一个通道可有多个 accountId,每个账号有独立运行状态与停止控制,故障隔离更好。

模块 4:入站消息怎么找到对应 agent

通道收到消息后,会调用路由层(src/routing/resolve-route.ts):

  1. 输入维度:channel + accountId + peer(群/私聊) + teamId/guildId
  2. 匹配 bindings
  3. 命中后返回 agentId + sessionKey
  4. 没命中走默认 agent

这就是“同一网关可同时托管多业务 agent”的基础。

模块 5:出站消息怎么统一发送

src/infra/outbound/deliver.ts 做统一发送编排:

  1. 先拿到目标通道插件 getChannelPlugin(channel)
  2. 检查该插件是否实现 outbound.sendText/sendMedia
  3. 统一做切片/分段(超长文本)
  4. 最终调用具体通道的发送实现

所以“AI 回复生成”和“平台消息发送”被彻底解耦。

模块 6:账号生命周期(你问的重点)

“账号生命周期”不是空话,具体就是:

  1. 配置账号(channels.<id>.accounts.<accountId>
  2. 登录账号(plugin.auth.login
  3. 网关启动账号(plugin.gateway.startAccount
  4. 运行中探测(plugin.status.probeAccount
  5. 错误恢复/重启(manager stop/start)
  6. 登出账号(plugin.auth.logoutAccount + markChannelLoggedOut

你可以在 extensions/discord/src/channel.tsextensions/signal/src/channel.ts 对照看到这整套都在用。

步骤三:如果你要复刻,最小实现清单

先实现 6 个最小点:

  1. ChannelPlugin.id/meta/capabilities/config
  2. gateway.startAccount(至少能拉起监听)
  3. outbound.sendText(先只支持文本)
  4. status.probeAccount(先返回通/不通)
  5. auth.login(哪怕先做 token 配置模式)
  6. 插件入口 api.registerChannel({ plugin })

只要这 6 点跑通,你就有了一个“可用的通道适配器框架 MVP”。

本章易混淆点(直接纠正)

  1. “通道插件”不是可有可无:它是平台协议适配层。
  2. “账号生命周期”不是概念词:它是账号从配置、登录、运行、探测到登出的完整状态机。
  3. 核心网关不直接写 Telegram/Discord 细节:全部下沉到通道适配器。

用工程视角拆解 AI 智能体框架