04 通道适配器框架与账号生命周期(重点)
这章到底在讲什么
一句话:OpenClaw 把每个平台(Telegram/Discord/Signal 等)都抽象成同一套 ChannelPlugin 适配器合同。
所以核心系统不需要知道“某个平台的私有细节”,只要按统一接口调用即可。
如果你要做类似项目,这一章就是“多通道架构”的核心。
步骤一:先看全链路(实现入口)
- 通道接口合同定义:
src/channels/plugins/types.plugin.ts - 插件注册通道能力:
src/plugins/registry.ts - 网关启动/停止通道:
src/gateway/server-channels.ts - 入站消息路由到 agent:
src/routing/resolve-route.ts - 出站消息走统一适配器:
src/infra/outbound/deliver.ts - 通道状态探测与体检:
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
}你可以把它理解成“通道驱动程序接口”:
config:怎么从配置里读账号、解析账号、判断是否配置完成。gateway:网关启动时,怎么拉起这个通道账号(如 webhook、长连接、SDK client)。outbound:收到 AI 回复后,怎么把文本/媒体发回该平台。status:channels status --probe时怎么体检。auth:怎么登录、登出、二维码流程等。
模块 2:插件如何注册“我是一个通道”
插件入口通常像这样(例如 extensions/telegram/index.ts):
ts
register(api) {
setTelegramRuntime(api.runtime)
api.registerChannel({ plugin: telegramPlugin })
}然后 src/plugins/registry.ts 的 registerChannel(...) 会把它放进 registry.channels。
从这一步开始,网关、CLI、路由层都能看到这个通道。
模块 3:账号生命周期由 ChannelManager 统一管理
src/gateway/server-channels.ts 的 createChannelManager(...) 提供:
startChannels():启动所有已配置通道。startChannel(channel, accountId?):按通道/账号启动。stopChannel(channel, accountId?):按通道/账号停止。markChannelLoggedOut(...):标记账号登出状态。getRuntimeSnapshot():汇总运行态(running/lastError/lastStartAt 等)。
核心设计点:账号是一级实体。
同一个通道可有多个 accountId,每个账号有独立运行状态与停止控制,故障隔离更好。
模块 4:入站消息怎么找到对应 agent
通道收到消息后,会调用路由层(src/routing/resolve-route.ts):
- 输入维度:
channel + accountId + peer(群/私聊) + teamId/guildId - 匹配
bindings - 命中后返回
agentId + sessionKey - 没命中走默认 agent
这就是“同一网关可同时托管多业务 agent”的基础。
模块 5:出站消息怎么统一发送
src/infra/outbound/deliver.ts 做统一发送编排:
- 先拿到目标通道插件
getChannelPlugin(channel) - 检查该插件是否实现
outbound.sendText/sendMedia - 统一做切片/分段(超长文本)
- 最终调用具体通道的发送实现
所以“AI 回复生成”和“平台消息发送”被彻底解耦。
模块 6:账号生命周期(你问的重点)
“账号生命周期”不是空话,具体就是:
- 配置账号(
channels.<id>.accounts.<accountId>) - 登录账号(
plugin.auth.login) - 网关启动账号(
plugin.gateway.startAccount) - 运行中探测(
plugin.status.probeAccount) - 错误恢复/重启(manager stop/start)
- 登出账号(
plugin.auth.logoutAccount+markChannelLoggedOut)
你可以在 extensions/discord/src/channel.ts、extensions/signal/src/channel.ts 对照看到这整套都在用。
步骤三:如果你要复刻,最小实现清单
先实现 6 个最小点:
ChannelPlugin.id/meta/capabilities/configgateway.startAccount(至少能拉起监听)outbound.sendText(先只支持文本)status.probeAccount(先返回通/不通)auth.login(哪怕先做 token 配置模式)- 插件入口
api.registerChannel({ plugin })
只要这 6 点跑通,你就有了一个“可用的通道适配器框架 MVP”。
本章易混淆点(直接纠正)
- “通道插件”不是可有可无:它是平台协议适配层。
- “账号生命周期”不是概念词:它是账号从配置、登录、运行、探测到登出的完整状态机。
- 核心网关不直接写 Telegram/Discord 细节:全部下沉到通道适配器。