当 AI Agent 要同时接入 Telegram、飞书、钉钉:Channel Plugin 抽象层设计之道
当 AI Agent 要同时接入 Telegram、飞书、钉钉:Channel Plugin 抽象层设计之道
)
|
🌺The Begin🌺点点关注,收藏不迷路🌺
|
1. 引言:为什么要做这层抽象?
如果你要给一个 AI Agent 系统接入多个即时通讯渠道——比如 Telegram、飞书、钉钉、WhatsApp、Discord——你很快就发现每个平台的“脾气”完全不同:
- 消息格式:Telegram 用 JSON,飞书用事件卡片,钉钉用 Webhook 回调
- 认证方式:Telegram 用 Bot Token,WhatsApp 用 Twilio,飞书用 App Secret
- 会话 ID 规则:每个平台都有自己的用户标识格式
- 回复方式:有的支持消息编辑,有的只能发新消息,有的支持 Markdown,有的只支持纯文本
如果为每个平台单独写一套完整集成,维护成本会随着渠道数量指数级增长。 OpenClaw 的解法是:将渠道视为可插拔模块,通过统一的 Channel Plugin 接口抽象,让核心引擎与具体渠道解耦。
2. 抽象层的核心原则:“契约先行,实现自由”
OpenClaw 的 Channel Plugin 抽象设计遵循一个核心原则:Channel plugins do not need their own send/edit/react tools. OpenClaw keeps one shared message tool in core(渠道插件不需要自己的发送/编辑/回复工具。OpenClaw 在核心中保留一个共用的消息工具)。
这意味着什么?核心引擎只关心“发一条消息”这个抽象动作,至于这条消息到了 Telegram 是发文本还是卡片,到了飞书是发富文本还是消息卡片,这是插件自己的事。
2.1 插件职责划分
根据 OpenClaw 官方文档,Channel Plugin 负责以下内容:
| 职责 | 说明 |
|---|---|
| Config | 账号解析与配置向导 |
| Security | DM 政策与允许列表 |
| Pairing | DM 审批流程 |
| Session grammar | 如何将供应商特定的会话 ID 映射到基础聊天、线程 ID 与父级回退 |
| Outbound | 将文字、媒体、投票发送到平台 |
| Threading | 回复如何串接 |
| Heartbeat typing | 可选的心跳输入中/忙碌信号 |
而核心引擎(Core)则负责:共用的 message 工具、提示词装配、外层会话键结构、通用 :thread: 簿记与消息分发。
3. 多渠道设计的关键抽象机制
3.1 统一会话标识:sessionKey
不同渠道的用户 ID 格式完全不同:WhatsApp 是 +1234567890,Telegram 是数字 ID 123456789,Web 是随机字符串。OpenClaw 通过标准化 sessionKey 统一标识:
| 渠道 | sessionKey 格式 |
|---|---|
wa:+1234567890 |
|
| Telegram | tg:123456789 |
| 飞书 | feishu:ou_xxxxx |
| 钉钉 | ding:user_xxxxx |
| Web | web:abc123 |
所有会话状态、记忆、任务都按 sessionKey 存储,用户从 WhatsApp 切换到 Telegram,只要绑定同一身份,对话上下文可以无缝衔接。
3.2 统一消息模型:ACP 协议
渠道插件不直接与 AI 核心交互,而是通过 ACP(Agent Communication Protocol) 消息总线。
ACP 是渠道与智能体之间的通用语言,消息格式统一为 JSON Schema,包含 source、message_id、content、attachments、timestamp 等标准字段。
3.3 渠道发现与元数据
在插件包加载之前,OpenClaw 通过 package.json 中的 openclaw.channel 元数据完成渠道发现:
{
"openclaw": {
"channel": {
"id": "acme-chat",
"label": "Acme Chat",
"blurb": "Connect to Acme Chat messaging platform",
"docsPath": "/channels/acme-chat",
"markdownCapable": true,
"exposure": {
"configured": true,
"setup": true,
"docs": true
}
}
}
}
exposure 控制渠道在设定列表、交互式选择器和文档页面中的可见性。
4. 以 createChatChannelPlugin 为入口的插件构建
对于开发者而言,不需要手动实现复杂的底层适配器。OpenClaw 提供了 createChatChannelPlugin 构建器,传入声明式选项即可组合完整插件。
以下是一个构建自定义渠道插件的示例:
import { createChatChannelPlugin, createChannelPluginBase } from "openclaw/plugin-sdk";
// 1. 账号解析:从配置中读取该渠道的 token
function resolveAccount(cfg, accountId) {
const section = cfg.channels?.["acme-chat"];
if (!section?.token) throw new Error("acme-chat: token is required");
return { accountId, token: section.token };
}
// 2. 构建插件
export const acmeChatPlugin = createChatChannelPlugin({
base: createChannelPluginBase({
id: "acme-chat",
setup: { resolveAccount }
}),
// DM 安全策略
security: {
dm: {
channelKey: "acme-chat",
resolvePolicy: (account) => account.dmPolicy,
resolveAllowFrom: (account) => account.allowFrom,
defaultPolicy: "allowlist",
},
},
// 配对待定:新联系人需要验证
pairing: {
text: {
idLabel: "Acme Chat username",
message: "Send this code to verify your identity:",
notify: async ({ target, code }) => {
await acmeChatApi.sendDm(target, `Pairing code: ${code}`);
},
},
},
// 线程模式
threading: { topLevelReplyToMode: "reply" },
// 出站消息发送
outbound: {
attachedResults: {
sendText: async (params) => {
const result = await acmeChatApi.sendMessage(params.to, params.text);
return { messageId: result.id };
},
},
base: {
sendMedia: async (params) => {
await acmeChatApi.sendFile(params.to, params.filePath);
},
},
},
});
5. 消息生命周期:Receive → Dispatch → Send
Channel Plugin 的抽象不仅是“接口定义”,还覆盖了消息的完整生命周期:
5.1 Receive(接收)
入站消息通过 runChannelInboundEvent 处理:
- 规范化:将平台事件转为标准结构
- 去重:防止 WebSocket 重连后重复投递
- 路由:确定消息归属哪个会话
- 记录:写入会话转录
- 分发:交给 Agent 处理
接收确认策略是显式声明的:
| 政策 | 含义 |
|---|---|
after_receive_record |
入站事件完成解析并记录后即可确认 |
after_agent_dispatch |
Agent 分派被接受后才确认 |
after_durable_send |
最终递送有持久化决策后才确认 |
manual |
插件完全掌控确认流程 |
5.2 Dispatch & Send(分发与发送)
发送侧同样基于上下文:
核心是 Intent(意图)先于传输 I/O 存在:在尝试平台发送之前,出站消息意图已经持久化;成功后提交平台回执。这样即使在发送过程中崩溃,系统也具备至少一次的恢复能力。
sendDurableMessageBatch 返回明确的生命周期结果:
sent:至少有一条可见的平台消息已传递suppressed:被钩子取消,不应投递partial_failed:部分成功、部分失败failed:全部失败
6. 一张图看懂 Channel Plugin 抽象层设计
7. 结语:让渠道适配成为“可插拔”
OpenClaw Channel Plugin 接口的设计智慧可以概括为三句话:
- 契约先行,实现自由——核心只定义
ChannelPlugin接口,具体实现由插件负责 - 统一消息模型,消除协议差异——ACP 协议作为渠道与核心之间的通用语言
- 声明式构建,降低开发门槛——
createChatChannelPlugin让开发者只需配置,无需重写底层
当你需要给 Agent 系统新增一个渠道时,不需要修改一行核心代码,只需实现一个 Channel Plugin 并注册到系统中。 这正是抽象层的核心价值:把“多渠道接入”从架构负担转化为可组合能力,让开发者用最低成本接入新平台。

|
🌺The End🌺点点关注,收藏不迷路🌺
|
更多推荐


所有评论(0)