OpenClaw 源码解读(4): 多渠道接入(Channels)源码实战
深入 OpenClaw 源码,解析它如何通过适配器模式统一 20+ 通讯平台的收发接口,以及群聊中的提及门控、回复追踪和长消息智能分块策略。
在上一篇中,我们探讨了 OpenClaw 的“多重宇宙”——通过 Workspace 和路由机制,让 Gateway 知道该把消息交给哪个 Agent 处理。但是,现实世界中,用户发消息的入口千奇百怪:有人爱用 WhatsApp,有人常驻 Telegram,团队协作离不开 Slack,甚至还有 Discord、微信等。
每家平台的 API 协议、鉴权方式、数据结构都完全不同。如果把这些逻辑和 AI 核心代码耦合在一起,整个项目很快就会变成一座难以维护的“屎山”。
今天,我们就来翻一翻 OpenClaw 的源码,看看它是如何通过优雅的通道抽象层(Channels Abstraction)和群组调度逻辑,将这 20 多种不同的协议“驯服”成统一收发接口的。

化繁为简的适配器模式
OpenClaw 解决多渠道接入的核心思想非常经典:面向接口编程,底层使用适配器模式(Adapter Pattern)。
在源码中,无论底层是 WhatsApp (基于 Baileys 库)、Telegram (基于 grammY 库) 还是 Slack (基于 Bolt 框架),它们在 OpenClaw 看来,统统只是一个实现了 IChannelProvider 接口的黑盒。

我们先来看看这个抽象层的核心基类定义:
// src/core/channels/IChannelProvider.ts (源码概念简化版)
export interface IChannelProvider {
/** 渠道唯一标识 (如 'whatsapp_01', 'telegram_bot') */
channelId: string;
/** 初始化并连接平台 */
connect(): Promise<void>;
/** 统一格式的发送消息接口 */
sendMessage(to: string, content: string, options?: SendOptions): Promise<MessageReceipt>;
/** 注册消息接收的回调函数 */
onMessageReceived(handler: (msg: NormalizedMessage) => Promise<void>): void;
}
紧接着,各个平台的适配器只需要实现这个接口。以 Telegram 为例,OpenClaw 会在内部把 Telegram 的专属消息对象映射为全局通用的 NormalizedMessage。
// src/packages/channels/telegram/TelegramAdapter.ts (片段)
import { Bot } from 'grammy';
import { IChannelProvider, NormalizedMessage } from '@openclaw/core';
export class TelegramAdapter implements IChannelProvider {
private bot: Bot;
constructor(public channelId: string, token: string) {
this.bot = new Bot(token);
}
public async connect() {
this.bot.start(); // 启动长轮询或 Webhook
}
public onMessageReceived(handler: (msg: NormalizedMessage) => Promise<void>) {
this.bot.on('message:text', async (ctx) => {
// 核心动作:将 TG 特有数据「洗」成 OpenClaw 标准格式
const standardMsg: NormalizedMessage = {
id: ctx.message.message_id.toString(),
senderId: ctx.from.id.toString(),
channelId: this.channelId,
content: ctx.message.text,
isGroup: ctx.chat.type === 'group' || ctx.chat.type === 'supergroup',
rawPayload: ctx.message // 保留原始数据以备高级调用
};
await handler(standardMsg);
});
}
// ... sendMessage 实现略
}
这种设计把脏活累活都封在了各个 Adapter 包里。Gateway 的控制平面和 Agent 运行时根本不需要知道当前是在给 WhatsApp 发消息还是给 Slack 发消息,它们只需调用 sendMessage 即可。这也是为什么 OpenClaw 能够快速扩展支持 20+ 个渠道的秘诀。
Agent 如何知道该“接话”了?
单聊很简单,收到消息就回复。但在几百人的大群里,如果 Agent 对每一句话都做响应,不仅会迅速耗尽 Token 额度,还会变成彻头彻尾的“复读机群宠”。
因此,对于 Non-main sessions(群组/频道),OpenClaw 设计了非常严谨的门控与追踪机制。
@ 提及门控
这是最基础的一层防御。网关在收到群消息时,会首先通过网关拦截器进行判断:这条消息是不是专门说给 Agent 听的?
// src/core/router/MentionGating.ts
export function shouldProcessGroupMessage(msg: NormalizedMessage, botIdentity: string[]): boolean {
const text = msg.content;
// 1. 显式 @提及
const isMentioned = botIdentity.some(id => text.includes(`@${id}`));
// 2. 唤醒词匹配 (比如 "Molty, 帮我查一下...")
const hasWakeWord = botIdentity.some(word => text.toLowerCase().startsWith(word.toLowerCase()));
return isMentioned || hasWakeWord;
}
回复标签追踪
如果群友没有 @ Agent,而是**直接引用(Reply)**了 Agent 之前发的一条消息,Agent 也应该能聪明地接上话茬。
OpenClaw 维护了一个会话上下文缓存。当 Agent 发送消息时,会将消息 ID 存入该群组的活跃上下文池。当接收到新消息时,即使没有 @ 提及,只要新消息的 replyToMessageId 命中了 Agent 发过的消息,门控同样会放行。
长消息分块处理机制
用过大模型 API 的朋友都知道,LLM 动辄生成几千字的 Markdown 长文。如果直接把这几千字作为一个整块推送到 WhatsApp 或 Telegram,体验极差(部分平台甚至会直接截断报错)。人类在聊天软件里的阅读习惯是“一条一条看”。
OpenClaw 实现了一个非常巧妙的 Chunking(分块)策略,将大段文本拆解为符合人类呼吸节奏的短句。

// src/core/utils/MessageChunker.ts
export class MessageChunker {
/**
* 将长文本智能切割为消息数组
*/
public static splitIntoChunks(text: string, maxLength: number = 2000): string[] {
const chunks: string[] = [];
// 1. 优先按双换行符(段落)切割
const paragraphs = text.split('\n\n');
let currentChunk = '';
for (const p of paragraphs) {
if ((currentChunk.length + p.length) < maxLength) {
currentChunk += (currentChunk ? '\n\n' : '') + p;
} else {
// 如果当前块满了,推入数组,开启新块
chunks.push(currentChunk);
currentChunk = p;
}
}
if (currentChunk) chunks.push(currentChunk);
return chunks;
}
}
在实际的发送管道中,OpenClaw 还会在每个 Chunk 之间加入短暂的 delay(比如 1-2 秒的模拟打字停顿),甚至会在某些通道触发 typing... 状态,让 Agent 表现得像一个真实的群成员在连续发报。
总结
化繁为简,是架构设计的最高境界。
OpenClaw 通过 IChannelProvider 接口 将乱七八糟的平台协议收敛为统一的数据流;通过 Mention Gating 与回复追踪 保护了群聊环境下的计算资源与用户体验;最后通过 智能 Chunking 还原了拟真的人类交互节奏。
了解了 OpenClaw 是如何处理“文字和对话”后,下一篇,我们将进入一个更令人兴奋的领域。这只叫 OpenClaw 的龙虾不仅会聊天,它还拥有“眼睛”和“手”!老金带你看看 Agent 是如何操作界面的。我们下期见!