Skip to content

消息路由

消息路由层负责将 Agent 的回复通过正确的通道、以正确的格式、在正确的限制内投递给用户。

出站投递流程

Source: src/infra/outbound/deliver.ts

Channel Handler

typescript
// src/infra/outbound/deliver.ts (simplified)
interface OutboundSendDeps {
  sendWhatsApp: SendFunction;
  sendTelegram: SendFunction;
  sendDiscord: SendFunction;
  sendSlack: SendFunction;
  sendSignal: SendFunction;
  sendIMessage: SendFunction;
  // ...
}

function createChannelHandler(channel: string) {
  return {
    chunker: resolveChunker(channel),        // Text splitting function
    chunkerMode: resolveChunkerMode(channel), // "text" | "markdown"
    textChunkLimit: resolveChunkLimit(channel),
    sendText: (opts) => /* ... */,
    sendMedia: (opts) => /* ... */,
    sendPayload: (opts) => /* ... */,
  };
}

每个通道的 handler 包含:

属性说明
chunker文本分块函数(null = 不分块)
chunkerMode分块模式:"text""markdown"
textChunkLimit单条消息最大字符数
sendText()发送纯文本
sendMedia()发送媒体附件
sendPayload()发送结构化内容

分块策略

Source: src/auto-reply/chunk.ts

长消息需要拆分成多条发送。分块策略分两种:

文本分块

typescript
// Simple text chunking
function chunkByParagraph(text: string, limit: number): string[] {
  // Split by paragraphs
  // Respect limit per chunk
  // Avoid splitting mid-sentence
}

Markdown 分块

typescript
// Markdown-aware chunking
function chunkMarkdownTextWithMode(
  text: string,
  limit: number,
  mode: "text" | "markdown"
): string[] {
  // Preserve code blocks across chunks
  // Keep link syntax intact
  // Maintain list structure
  // Handle heading boundaries
}

Markdown 分块更智能,会:

  • 不在 ``` 代码块中间断开
  • 不在 [link](url) 中间断开
  • 在段落或标题边界处优先断开
  • 保持列表的连续性

各通道限制

通道文本限制分块模式
Telegram4000 字符markdown
Discord2000 字符markdown
WhatsApp因平台而异text
Slack因配置而异markdown
Signal因平台而异text
iMessage因平台而异text

幂等性

Source: src/gateway/server-methods/send.ts

消息发送支持幂等性去重,防止网络重试导致的重复消息:

typescript
// Idempotency deduplication
if (idempotencyKey && seen.has(idempotencyKey)) {
  return respond(true, seen.get(idempotencyKey));
}

出站依赖桥接

Source: src/cli/deps.ts

CLI 层的依赖通过 createOutboundSendDeps() 桥接到基础设施层:

typescript
// src/cli/deps.ts (simplified)
interface CliDeps {
  sendMessageWhatsApp: SendFunction;
  sendMessageTelegram: SendFunction;
  sendMessageDiscord: SendFunction;
  sendMessageSlack: SendFunction;
  sendMessageSignal: SendFunction;
  sendMessageIMessage: SendFunction;
}

function createDefaultDeps(): CliDeps {
  // Create concrete implementations
}

function createOutboundSendDeps(deps: CliDeps): OutboundSendDeps {
  // Bridge CLI deps → infra OutboundSendDeps
}

这种桥接模式让 CLI 和基础设施层可以独立测试。

小结

  • deliverOutboundPayloads() 是出站投递的统一入口
  • 每个通道通过 createChannelHandler() 提供发送函数和分块策略
  • 分块策略分 textmarkdown 两种模式,Markdown 模式保持语法完整性
  • 幂等性去重 通过 idempotencyKey 缓存防止重复消息
  • CLI 依赖通过 桥接模式 连接到基础设施层

下一章:Agent 概述

OpenClaw 源码学习教程