OpenClaw 架构中消息流入中枢 —— monitor-inbox.ts 如何解析、去重与防抖
关键词:消息中枢|去重|防抖|媒体下载|时间窗口|幂等性|ACP 入口
在多渠道(WhatsApp、Web、Telegram 等)接入的 OpenClaw 架构中,原始消息如潮水般涌入:用户可能连发三条“重启服务”,或上传一张截图并附带说明。若直接将这些原始数据喂给 LLM,将导致:
- 重复执行(同一命令触发三次)
- 上下文碎片化(图片与文字分离)
- 资源浪费(高频请求压垮模型)
为此,OpenClaw 设计了 src/core/monitor-inbox.ts —— 一个智能消息流入中枢。它位于渠道插件与 AI 核心之间,负责对原始消息进行解析、清洗、聚合与标准化,确保每一条进入 LLM 的请求都是必要、完整且唯一的。
本文将详解其三大核心机制:
- 消息去重:基于 ID + 时间窗口的幂等保障
- 防抖合并:同一用户的连续消息智能聚合
- 媒体下载:图片/语音自动本地化存储
一、整体架构:消息流经 monitor-inbox.ts

所有渠道通过 ACP 发送 chat.rawMessage 事件至 monitor-inbox.ts,后者处理后转发 chat.processedMessage。
二、机制一:消息去重 —— ID + 时间窗口双重保障
问题
- WhatsApp 在网络不稳时可能重复推送同一条消息
- 用户快速点击发送按钮,产生重复请求
解法:两级去重策略
1. 精确去重(Exact Deduplication)
利用渠道提供的唯一消息 ID(如 WhatsApp 的 messageId):
// monitor-inbox.ts
const seenMessageIds = new Set<string>();
function isDuplicate(message: RawMessage): boolean {
if (message.id && seenMessageIds.has(message.id)) {
return true;
}
if (message.id) {
seenMessageIds.add(message.id);
// 5 分钟后自动清理(防止内存无限增长)
setTimeout(() => seenMessageIds.delete(message.id), 300_000);
}
return false;
}
2. 模糊去重(Fuzzy Deduplication)
对于无 ID 渠道(如 CLI),使用 内容哈希 + 时间窗口:
const recentHashes = new Map<string, number>(); // hash → timestamp
function isFuzzyDuplicate(content: string, sessionKey: string): boolean {
const hash = md5(`${sessionKey}:${content}`);
const now = Date.now();
// 清理 2 秒前的记录
for (const [h, ts] of recentHashes.entries()) {
if (now - ts > 2000) recentHashes.delete(h);
}
if (recentHashes.has(hash)) {
return true; // 2 秒内相同内容
}
recentHashes.set(hash, now);
return false;
}
无论渠道是否提供 ID,都能有效防重。
三、机制二:防抖合并 —— 同一用户的连续消息聚合
场景
用户分多条发送:
- “帮我查一下”
- “订单 ORD-123”
- “的状态”
理想行为:合并为一条“帮我查一下订单 ORD-123 的状态”。
实现:会话级防抖缓冲区
// monitor-inbox.ts
interface DebounceBuffer {
messages: string[];
lastActivity: number;
timeoutId: NodeJS.Timeout | null;
}
const debounceBuffers = new Map<string, DebounceBuffer>(); // sessionKey → buffer
const DEBOUNCE_WINDOW = 1500; // 1.5 秒
function enqueueMessage(sessionKey: string, text: string) {
let buf = debounceBuffers.get(sessionKey);
if (!buf) {
buf = { messages: [], lastActivity: 0, timeoutId: null };
debounceBuffers.set(sessionKey, buf);
}
buf.messages.push(text);
buf.lastActivity = Date.now();
// 清除旧定时器
if (buf.timeoutId) clearTimeout(buf.timeoutId);
// 设置新定时器
buf.timeoutId = setTimeout(() => {
flushBuffer(sessionKey);
debounceBuffers.delete(sessionKey);
}, DEBOUNCE_WINDOW);
}
合并策略
- 纯文本:用空格连接(
messages.join(' ')) - 含媒体:保留第一条媒体 + 合并文本
- 超过 3 条:强制 flush(防无限等待)
让碎片输入,变成完整意图。
四、机制三:媒体下载 —— 图片/语音自动本地化
当用户发送非文本内容(如截图、语音指令),AI 需要访问这些文件才能理解。
1. 媒体类型识别
// monitor-inbox.ts
type MediaType = 'image' | 'audio' | 'video' | 'document';
function detectMediaType(mime: string): MediaType | null {
if (mime.startsWith('image/')) return 'image';
if (mime.startsWith('audio/')) return 'audio';
// ...
return null;
}
2. 自动下载与存储
async function handleMedia(message: RawMessage) {
if (!message.mediaUrl) return;
const mediaType = detectMediaType(message.mimeType);
if (!mediaType) return;
// 生成安全路径
const ext = mime.getExtension(message.mimeType) || 'bin';
const filename = `${nanoid()}.${ext}`;
const localPath = path.join(
process.env.MEDIA_DIR || './media',
message.sessionKey,
filename
);
// 下载(带超时与重试)
await downloadWithRetry(message.mediaUrl, localPath, {
timeout: 30_000,
retries: 3
});
// 替换原始消息中的 URL 为本地路径
message.localMediaPath = localPath;
}
3. 安全与清理
- 沙箱目录:所有媒体存于
media/,禁止遍历上级目录 - 自动清理:7 天未访问的媒体自动删除
- 大小限制:单文件 ≤ 20MB(可配置)
外部媒体 → 可控本地资产。
五、输出标准化:生成统一 ACP 事件
处理完成后,monitor-inbox.ts 发出结构化事件:
emitACPEvent('chat.processedMessage', {
sessionKey: 'wa:+1234567890',
content: '帮我查一下订单 ORD-123 的状态',
media: [
{ type: 'image', path: '/media/wa_+1234567890/abc123.jpg' }
],
sourceChannel: 'whatsapp',
timestamp: 1710234567
});
此事件被 agent-core.ts 监听,作为 LLM 的唯一输入源。
六、性能与可观测性
内存控制
seenMessageIds和recentHashes使用 TTL 自动清理- 防抖缓冲区最多缓存 5 条消息
监控指标
openclaw_inbox_messages_total{channel="whatsapp", action="deduped"} 12
openclaw_inbox_messages_total{channel="web", action="debounced"} 8
openclaw_inbox_media_downloaded_bytes_total 4567890
日志示例
[INFO] Merged 3 messages from wa:+1234567890 → "重启数据库并检查日志"
[DEBUG] Downloaded image to /media/wa_+1234567890/img_a1b2c3.jpg
[WARN] Duplicate message id=msg_abc123 ignored
结语:干净的输入,是智能的起点
monitor-inbox.ts 的存在,体现了 OpenClaw 的工程哲学:在 AI 接触世界之前,先为它过滤噪音、整理秩序。去重防止混乱,防抖还原意图,媒体本地化保障安全——这一切,让用户无需“适应 AI”,而是让 AI 更好地理解人。
这不仅是预处理管道,更是人机交互的第一道礼仪。
在下一篇中,我们将探讨 OpenClaw 的部署模型演进:从单机 Docker 到 Kubernetes Operator。
下一篇预告:
第 17 篇:OpenClaw 架构中的聊天 RPC 接口 —— chat.ts 中的历史查询、发送与中止逻辑
您的 AI 助手,从此由您定义。若感兴趣可以浏览本书其他章节内容:
浙公网安备 33010602011771号