1. 为什么 Channel 子系统值得单独抽一层OpenClaw 的 Channel 子系统解决的是一个很具体的问题当你的智能体同时挂在 Slack、Discord、Telegram、飞书、钉钉、企业微信等 10 多个消息渠道上时如果每个渠道都单独写一套收发逻辑代码会迅速变成一团乱麻。Channel 子系统做的事情就是把这些渠道的差异全部收进适配器里上层只面向一个统一接口编程。你可以把 Channel 理解成 OpenClaw 的“感官层”。用户从哪个 IM 发消息进来、OpenClaw 把结果发回哪里全部由 Channel 负责。它对外暴露的核心能力就三件事接收消息、解析成内部统一格式、把回复发出去。至于底层是 WebSocket 长连接、Webhook 回调还是 HTTP 轮询上层完全不关心。这套设计适合谁适合需要把同一个智能体部署到多个团队沟通工具里的开发者尤其是那种“今天接 Slack、明天接 Discord、后天老板要求接企业微信”的场景。一次把适配器骨架搭好后面新增渠道就是填一个文件加一段配置的事。我试过在同一个实例里挂 6 个渠道如果没有适配器模式光是消息格式转换就能写到你怀疑人生。下面直接给可复制的配置骨架和验证步骤。2. TaoToken 前置统一 Key 与 API 通道在动手配 Channel 之前先把模型调用通道固定下来。OpenClaw 的 Channel 只负责消息进出真正生成回复的是背后的模型。如果你每个渠道都配一套不同的 Key后面排障会非常痛苦。建议统一走 TaoToken 的 API 通道一个 Key 覆盖多个模型。TaoToken 的 API 地址是https://taotoken.net/api兼容 OpenAI 风格的接口调用。你需要在控制台创建一个 API Key然后把它写进 OpenClaw 的模型配置里。这样无论消息从 Slack 还是 Telegram 进来最终都走同一条模型通道日志和用量也集中在一处。具体操作路径进入控制台创建 Key拿到形如sk-xxxx的凭证。如果你用的是 Claude 系列模型做 Agent 推理可以参考 ClaudeCodeAnthropic 的接入方式如果只是普通对话补全直接用标准 API 通道即可。Key 创建后不要硬编码在代码里放到环境变量或独立的 secrets 文件Channel 配置文件里只引用变量名。这一步的意义在于Channel 适配器只管消息模型通道只管推理两者解耦。后面新增渠道时你不需要再碰模型配置。3. 可复制的 Channel 适配器配置骨架OpenClaw 的渠道配置集中在一个文件里通常是~/.openclaw/config/channels.toml。下面这份骨架可以直接复制按需删减渠道。# ~/.openclaw/config/channels.toml [model] provider taotoken api_base https://taotoken.net/api api_key_env TAOTOKEN_API_KEY default_model gpt-4o-mini [channels.slack] enabled true type slack bot_token_env SLACK_BOT_TOKEN app_token_env SLACK_APP_TOKEN priority 2 [channels.slack.permissions] allow_all false allow_users [U0123ABCD] [channels.discord] enabled true type discord bot_token_env DISCORD_BOT_TOKEN priority 2 [channels.discord.permissions] allow_all true [channels.telegram] enabled true type telegram bot_token_env TELEGRAM_BOT_TOKEN priority 3 [channels.telegram.permissions] allow_users [123456789] [channels.web] enabled true type web port 18789 cors_origin * priority 1这份配置里有几个关键点。type字段决定 ChannelManager 创建哪个适配器实例新增渠道时这里必须和适配器注册表里的名字一致。priority数值越小优先级越高Web 控制台设为 1IM 渠道设为 2 或 3这样紧急指令从控制台发出时能优先处理。permissions是渠道级访问控制生产环境不要图省事开allow_all。环境变量建议统一放在~/.openclaw/.envTAOTOKEN_API_KEYsk-你的key SLACK_BOT_TOKENxoxb-xxx SLACK_APP_TOKENxapp-xxx DISCORD_BOT_TOKENxxx TELEGRAM_BOT_TOKEN123456:ABC-xxx配置加载顺序是先读.env注入环境变量再解析channels.toml最后按enabled true的渠道逐个初始化适配器。任何一个渠道初始化失败不会阻塞其他渠道但会在日志里打出channel_init_failed排障时先看这个。4. 新增一个渠道适配器的完整步骤假设现在要新增一个企业微信群机器人渠道走一遍完整流程。适配器模式的好处在这里体现得最明显你只需要写一个文件、注册一个类型、加一段配置。第一步创建适配器文件src/channel/adapters/wecom.adapter.tsimport axios from axios; import { Channel, ChannelConfig, IncomingMessage, OutgoingMessage, } from ../interfaces/channel.interface; interface WecomConfig extends ChannelConfig { webhook: string; } export class WecomChannel implements Channel { id: string; type wecom; config: WecomConfig; constructor(id: string, config: WecomConfig) { this.id id; this.config config; } async start(): Promisevoid { console.log([wecom] channel ${this.id} started); } async stop(): Promisevoid { console.log([wecom] channel ${this.id} stopped); } async send(message: OutgoingMessage): Promiseboolean { try { await axios.post(this.config.webhook, { msgtype: markdown, markdown: { content: message.content }, }); return true; } catch (err) { console.error([wecom] send failed, err); return false; } } async *receive(): AsyncGeneratorIncomingMessage { // 群机器人被动接收双向通信需走应用回调 yield* []; } parse(raw: any): IncomingMessage { return { channelId: this.id, sender: raw.sender, content: raw.text, timestamp: new Date(), raw, }; } }第二步在适配器注册表里加一行。找到channel-manager.ts的createAdapter方法private createAdapter(type: string, id: string, config: any): Channel { switch (type) { case slack: return new SlackChannel(id, config); case discord: return new DiscordChannel(id, config); case telegram: return new TelegramChannel(id, config); case wecom: return new WecomChannel(id, config); default: throw new Error(unknown channel type: ${type}); } }第三步在channels.toml里追加配置[channels.wecom_group] enabled true type wecom webhook https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key你的key priority 3 [channels.wecom_group.permissions] allow_all true第四步重启 OpenClaw观察启动日志里是否出现[wecom] channel wecom_group started。出现即表示适配器加载成功。5. 验证请求与消息收发自检配置写完不代表通了必须做一次端到端自检。分两个方向验证出站发送和入站接收。出站发送验证写一个最小测试脚本import { WecomChannel } from ./src/channel/adapters/wecom.adapter; async function main() { const ch new WecomChannel(wecom_group, { webhook: process.env.WECOM_WEBHOOK!, } as any); await ch.start(); const ok await ch.send({ content: OpenClaw channel self-check: outbound OK, } as any); console.log(send result:, ok); } main();运行后企业微信群里应该收到一条 markdown 消息。如果返回true但群里没消息检查 webhook 的 key 是否完整、机器人是否被移出群。入站接收验证针对支持回调的渠道Slack、Discord、Telegram用 curl 模拟一次事件推送curl -X POST http://localhost:18789/webhook/slack \ -H Content-Type: application/json \ -d {type:event_callback,event:{type:message,user:U0123ABCD,text:ping}}预期结果是 OpenClaw 日志里出现message_received channelslack并且模型通道被调用一次。如果日志里只有message_received但没有模型调用说明权限校验把消息拦了检查allow_users是否包含该用户 ID。模型通道自检直接打一次 TaoToken 的 APIcurl https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d {model:gpt-4o-mini,messages:[{role:user,content:ping}]}返回正常补全结果说明模型通道没问题。这一步和 Channel 解耦单独验证能快速定位是渠道问题还是模型问题。6. 本篇常见错排查报错一unknown channel type: wecom适配器文件写了但没注册。检查createAdapter的 switch 里是否加了对应 case以及 import 路径是否正确。TypeScript 编译后如果没报错但运行时报这个多半是注册表没更新。报错二channel_init_failed: missing env SLACK_BOT_TOKEN环境变量没注入。确认.env文件在 OpenClaw 启动目录下或者用export显式导出。TOML 里写的是bot_token_env SLACK_BOT_TOKEN它读的是环境变量名不是值本身。报错三消息发出去了但模型没回复先看权限配置。allow_all false且allow_users为空时所有消息都会被丢弃。再看 priority 队列是否堵塞如果某个渠道的消息处理卡住检查该适配器的receive()是否抛异常导致生成器中断。报错四TaoToken 返回 401Key 无效或没带上。确认api_key_env指向的环境变量确实存在且请求头是Authorization: Bearer sk-xxx。如果用的是 Claude 系列模型确认接入方式参考的是 ClaudeCodeAnthropic 而不是标准补全接口。报错五多渠道消息重复处理同一个用户从两个渠道发了相同内容去重逻辑没生效。检查dedupCache的指纹生成是否包含channelId如果两个渠道的消息指纹算出来一样会被误判为重复。指纹里必须带渠道标识。排障时优先看 OpenClaw 的启动日志和message_received日志90% 的问题在这两处能定位。如果确认是接入配置问题去 API Keys 页面核对 Key 状态再对照接入文档检查参数格式。需要快速验证模型是否正常直接用模型对话发一条测试消息最省事。长期跑编码类 Agent 或多渠道自动化任务建议用 Coding Plan 把用量和渠道统一管理起来避免每个渠道单独计费对不上账。