OpenClaw IM 消息通道逻辑架构分析

基于 github.com/openclaw/openclaw 源码与官方文档的技术分析


目录

  1. 总体架构概览
  2. Layer 0 — 外部 IM 平台接入
  3. Layer 1 — Channel Adapter 协议适配层
  4. Layer 2 — Channel Routing 消息路由层
  5. Layer 3 — Command Queue 并发控制层
  6. Layer 4 — Gateway Core 核心控制平面
  7. Layer 5 — LLM 推理与响应组装
  8. Layer 6 — Outbound 出站投递
  9. 完整消息生命周期时序
  10. 关键设计决策总结

1. 总体架构概览

OpenClaw 的核心设计是单 Gateway 进程统一管理所有 IM 通道,形成一个严格分层的消息处理流水线。

┌─────────────────────────────────────────────────────────────────┐
│                     外部 IM 平台 (Layer 0)                       │
│  WhatsApp  Telegram  Slack  Discord  Signal  iMessage  Teams ... │
└──────────────────────────┬──────────────────────────────────────┘
                           │ 平台原生协议事件
                           ▼
┌─────────────────────────────────────────────────────────────────┐
│               Channel Adapter 适配层 (Layer 1)                   │
│          消息规范化  ·  安全门控  ·  媒体处理                     │
└──────────────────────────┬──────────────────────────────────────┘
                           │ 规范化 InboundMessage
                           ▼
┌─────────────────────────────────────────────────────────────────┐
│               Channel Routing 路由层 (Layer 2)                   │
│         Agent 匹配  ·  Session Key 生成  ·  Broadcast            │
└──────────────────────────┬──────────────────────────────────────┘
                           │ (AgentId, SessionKey, Message)
                           ▼
┌─────────────────────────────────────────────────────────────────┐
│               Command Queue 并发层 (Layer 3)                     │
│       Lane-Aware FIFO  ·  队列模式  ·  Typing Indicator          │
└──────────────────────────┬──────────────────────────────────────┘
                           │ 出队执行
                           ▼
┌─────────────────────────────────────────────────────────────────┐
│               Gateway Core 核心平面 (Layer 4)                    │
│  ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐           │
│  │  Agent   │ │ Session  │ │Streaming │ │  Tool    │           │
│  │  Loop    │ │ Manager  │ │  Engine  │ │  Engine  │           │
│  └──────────┘ └──────────┘ └──────────┘ └──────────┘           │
│       ws://127.0.0.1:18789  (WS Control Plane)                  │
└──────────┬────────────────────────────┬────────────────────────┘
           │ LLM API 请求               │ 内部 WS
           ▼                            ▼
┌──────────────────┐        ┌───────────────────────┐
│  LLM Providers   │        │  Clients / Nodes       │
│  (Layer 5)       │        │  CLI / macOS / iOS /   │
│  Anthropic/OpenAI│        │  Android / WebChat     │
└──────────┬───────┘        └───────────────────────┘
           │ 响应流
           ▼
┌─────────────────────────────────────────────────────────────────┐
│               Outbound 出站投递 (Layer 6)                        │
│       分块策略  ·  流式预览  ·  媒体处理  ·  Retry               │
└──────────────────────────┬──────────────────────────────────────┘
                           │ 回写平台
                           ▼
                  各 IM 平台 (收到回复)

2. Layer 0 — 外部 IM 平台接入

每个 IM 平台使用不同的底层协议和 SDK 接入,Gateway 对每种平台维护独立的长连接或事件监听。

┌─────────────────────────────────────────────────────────────────────┐
│                    IM 平台接入方式一览                               │
├─────────────────┬──────────────────┬──────────────────────────────┤
│ 平台            │ 接入库/方式       │ 连接模型                      │
├─────────────────┼──────────────────┼──────────────────────────────┤
│ WhatsApp        │ Baileys          │ WhatsApp Web 逆向协议,         │
│                 │                  │ messages.upsert 事件驱动       │
│                 │                  │ QR 码扫描 → creds.json 持久化  │
├─────────────────┼──────────────────┼──────────────────────────────┤
│ Telegram        │ grammY           │ Bot API 长轮询 / Webhook       │
│                 │                  │ botToken 认证                  │
├─────────────────┼──────────────────┼──────────────────────────────┤
│ Slack           │ Bolt SDK         │ Socket Mode (AppToken+BotToken)│
├─────────────────┼──────────────────┼──────────────────────────────┤
│ Discord         │ discord.js       │ Discord Gateway WebSocket      │
│                 │                  │ Bot Token, 服务器长连接         │
├─────────────────┼──────────────────┼──────────────────────────────┤
│ Signal          │ signal-cli       │ 命令行工具桥接, 隐私优先        │
├─────────────────┼──────────────────┼──────────────────────────────┤
│ BlueBubbles     │ REST + Webhook   │ macOS BlueBubbles Server       │
│ (推荐 iMessage) │                  │ 服务端 REST API + Webhook 回调  │
├─────────────────┼──────────────────┼──────────────────────────────┤
│ iMessage legacy │ imsg CLI         │ macOS 本地 Messages 集成 (弃用) │
├─────────────────┼──────────────────┼──────────────────────────────┤
│ MS Teams        │ Bot Framework    │ Azure Bot Framework HTTP        │
├─────────────────┼──────────────────┼──────────────────────────────┤
│ Google Chat     │ Chat API         │ HTTP Webhook 入站               │
├─────────────────┼──────────────────┼──────────────────────────────┤
│ Matrix          │ 插件             │ Matrix 协议 (单独安装)          │
├─────────────────┼──────────────────┼──────────────────────────────┤
│ WebChat         │ Gateway WS 直连  │ 与内部 WS 控制平面共用端口      │
├─────────────────┼──────────────────┼──────────────────────────────┤
│ 扩展通道        │ 插件机制         │ Zalo/LINE/Feishu/Mattermost/   │
│                 │                  │ Nostr/Twitch/Tlon 等            │
└─────────────────┴──────────────────┴──────────────────────────────┘

关键设计:Gateway 是唯一拥有 WhatsApp Baileys Session 的进程——同一主机只允许一个 Gateway 实例运行。


3. Layer 1 — Channel Adapter 协议适配层

所有平台消息在进入路由层前,必须经过统一的规范化和安全门控流程。

3.1 消息规范化流水线

平台原生事件
     │
     ▼
┌────────────────────────────────────────────────┐
│ Step 1: 过滤无效消息                            │
│   · 忽略 Status / Broadcast 频道消息            │
│   · 忽略自身消息回显 (避免循环)                  │
│   · 过滤 Bot 自身发送的消息                      │
└───────────────────┬────────────────────────────┘
                    │
                    ▼
┌────────────────────────────────────────────────┐
│ Step 2: DM Policy 安全门控                      │
│                                                │
│   ┌──────────┐  首次陌生发送者                  │
│   │ pairing  │─────────────────→ 发送短码(1h过期)│
│   │ (默认)   │                   消息不处理      │
│   └──────────┘                                 │
│   ┌──────────┐                                 │
│   │allowlist │──→ 仅白名单 E.164 号码通过        │
│   └──────────┘                                 │
│   ┌──────────┐                                 │
│   │  open    │──→ 需配合 allowFrom: ["*"]       │
│   └──────────┘                                 │
│   ┌──────────┐                                 │
│   │ disabled │──→ 完全关闭该通道                │
│   └──────────┘                                 │
└───────────────────┬────────────────────────────┘
                    │ 通过门控
                    ▼
┌────────────────────────────────────────────────┐
│ Step 3: 构造规范化 InboundMessage 对象          │
│                                                │
│   Body        : 消息正文                        │
│   SenderId    : E.164 电话号码 / 用户ID         │
│   ChatId      : DM JID / 群组 JID              │
│   Channel     : "whatsapp" / "telegram" / ...  │
│   AccountId   : 多账号标识                      │
│   MediaType   : text / image / audio / video   │
│   Timestamp   : Unix 毫秒时间戳                 │
└───────────────────┬────────────────────────────┘
                    │
                    ▼
┌────────────────────────────────────────────────┐
│ Step 4: 引用消息展开                            │
│                                                │
│   原始 Body: "好的"                             │
│   展开后:                                       │
│   "好的                                         │
│    [Replying to +8613812345678 id:ABC123XYZ]   │
│    你昨天说的那个方案                            │
│    [/Replying]"                                │
│                                                │
│   同时填充:                                     │
│     ReplyToId     = "ABC123XYZ"                │
│     ReplyToBody   = "你昨天说的那个方案"         │
│     ReplyToSender = "+8613812345678"           │
└───────────────────┬────────────────────────────┘
                    │
                    ▼
┌────────────────────────────────────────────────┐
│ Step 5: 媒体内容替换                            │
│                                                │
│   图片  →  <media:image>                       │
│   视频  →  <media:video>                       │
│   音频  →  <media:audio>                       │
│   文件  →  <media:document>                    │
│   贴纸  →  <media:sticker>                     │
│                                                │
│   (媒体文件同时下载到临时目录供 Agent 使用)      │
└───────────────────┬────────────────────────────┘
                    │
                    ▼
┌────────────────────────────────────────────────┐
│ Step 6: 群消息发送者标识注入                    │
│                                                │
│   DM 消息: 无需注入                             │
│   群组消息: 在 Body 末尾追加                     │
│     "[from: 张三 (+8613812345678)]"            │
│                                                │
│   群历史注入 (未处理的旧消息):                  │
│   "[Chat messages since your last reply       │
│    - for context]"                             │
│   当前消息: "[Current message - respond to    │
│    this]"                                      │
└────────────────────────────────────────────────┘

3.2 WhatsApp 专项处理

WhatsApp Baileys 事件循环
         │
         │  messages.upsert
         ▼
┌────────────────────────────┐
│  Inbox 监听器              │
│  (关闭时分离,防止          │
│   事件处理器堆积)           │
└────────────┬───────────────┘
             │
             ├── status/broadcast? ──→ 丢弃
             │
             ├── 自身消息 + selfChatMode=false? ──→ 丢弃
             │
             ├── 群组 JID? ──→ 群组策略检查
             │     └── groupPolicy: open/allowlist/disabled
             │
             └── DM E.164? ──→ DM Policy 检查
                   │
                   └── allowFrom 白名单匹配
                           │
                           ├── 命中 ──→ 进入规范化流水线
                           └── 未命中 ──→ pairing 短码响应

3.3 设备信任链 (WS 客户端接入)

新 WS 客户端连接 Gateway
         │
         ▼
┌────────────────────────────────────────────┐
│  connect 帧 (必须是第一帧)                  │
│  {                                         │
│    role: "client" | "node",               │
│    deviceId: "uuid-xxx",                  │
│    platform: "macos",                     │
│    deviceFamily: "desktop",               │
│    auth: { token: "..." },               │
│    challenge_signature: "sig_v3..."       │
│  }                                         │
└──────────────────┬─────────────────────────┘
                   │
        ┌──────────┴──────────┐
        │                     │
        ▼                     ▼
  已配对设备              新设备 ID
  (存在 device token)     (首次连接)
        │                     │
        ▼                     ▼
  验证签名 nonce        需要配对审批
  验证 platform/         openclaw pairing
  deviceFamily           approve <code>
  (元数据变更需重配对)          │
        │                     │
        └──────────┬──────────┘
                   │
                   ▼
         loopback / tailnet?
           ├── 是 ──→ 自动审批
           └── 否 ──→ 人工审批
                   │
                   ▼
            ← hello-ok { health }
            连接建立成功

4. Layer 2 — Channel Routing 消息路由层

消息路由决定哪个 Agent 处理该消息,并生成唯一的 Session Key

4.1 Agent 路由规则 (优先级降序)

收到规范化 InboundMessage
         │
         ▼
┌─────────────────────────────────────────────┐
│ 优先级 1: 精确 peer 匹配                     │
│   bindings[].match.peer.kind == "direct"    │
│   bindings[].match.peer.id == "+13125550123"│
│   → 命中 → 返回对应 agentId                 │
└──────────────────┬──────────────────────────┘
                   │ 未命中
                   ▼
┌─────────────────────────────────────────────┐
│ 优先级 2: Guild 匹配 (Discord)               │
│   bindings[].match.guildId == "12345678"    │
└──────────────────┬──────────────────────────┘
                   │ 未命中
                   ▼
┌─────────────────────────────────────────────┐
│ 优先级 3: Team 匹配 (Slack)                  │
│   bindings[].match.teamId == "T0123ABCD"    │
└──────────────────┬──────────────────────────┘
                   │ 未命中
                   ▼
┌─────────────────────────────────────────────┐
│ 优先级 4: Account 匹配                       │
│   bindings[].match.accountId == "work"      │
└──────────────────┬──────────────────────────┘
                   │ 未命中
                   ▼
┌─────────────────────────────────────────────┐
│ 优先级 5: Channel 匹配                       │
│   bindings[].match.channel == "whatsapp"    │
└──────────────────┬──────────────────────────┘
                   │ 未命中
                   ▼
┌─────────────────────────────────────────────┐
│ 优先级 6: 默认 Agent                         │
│   agents.list[].default == true             │
│   → 否则取 agents.list[0]                   │
│   → 否则 fallback "main"                    │
└──────────────────┬──────────────────────────┘
                   │
                   ▼
             确定 AgentId

4.2 Session Key 生成规则

消息类型
    │
    ├── DM (私信)
    │     │
    │     ├── dmScope=main (默认)
    │     │     └── agent:<agentId>:<mainKey>
    │     │         示例: agent:main:main
    │     │               (所有DM共享同一上下文)
    │     │
    │     ├── dmScope=per-peer
    │     │     └── agent:<agentId>:dm:<peerId>
    │     │
    │     ├── dmScope=per-channel-peer (推荐多用户)
    │     │     └── agent:<agentId>:<channel>:dm:<peerId>
    │     │         示例: agent:main:telegram:dm:123456789
    │     │
    │     └── dmScope=per-account-channel-peer (多账号)
    │           └── agent:<agentId>:<channel>:<accountId>:dm:<peerId>
    │
    ├── 群组消息
    │     └── agent:<agentId>:<channel>:group:<groupId>
    │         示例: agent:main:whatsapp:group:120363012345@g.us
    │
    ├── Telegram Topic (论坛频道)
    │     └── agent:<agentId>:telegram:group:<id>:topic:<threadId>
    │
    ├── Slack/Discord Thread
    │     └── agent:<agentId>:<channel>:channel:<id>:thread:<threadId>
    │
    ├── Cron Job
    │     └── cron:<job.id>
    │
    └── Webhook
          └── hook:<uuid>

4.3 Broadcast Groups (多 Agent 并行)

同一消息 → 匹配 broadcast 配置
                │
                ▼
┌──────────────────────────────────┐
│ broadcast:                       │
│   strategy: "parallel"           │
│   "+8613812345678":              │
│     - "alfred"                   │
│     - "logger"                   │
└──────────────────────────────────┘
                │
    ┌───────────┴───────────┐
    │                       │
    ▼                       ▼
Agent "alfred"          Agent "logger"
(独立 Session)          (独立 Session)
(独立 Workspace)        (独立 Workspace)
    │                       │
    └───────────┬───────────┘
                │ 各自独立回复
                ▼
           回写平台

5. Layer 3 — Command Queue 并发控制层

防止多条消息同时触发 Agent 运行产生竞态,同时保证用户体验无感知。

5.1 Lane 并发模型

┌────────────────────────────────────────────────────────────────┐
│                    Lane-Aware FIFO Queue                        │
├────────────────────────────────────────────────────────────────┤
│                                                                │
│  Session Lane (每 session 一个 lane)                           │
│  ┌─────────────────────────────────────────┐                  │
│  │ session:agent:main:main                 │  并发上限: 1       │
│  │ [msg_A] → [msg_B] → [msg_C]  (串行)    │                  │
│  └─────────────────────────────────────────┘                  │
│  ┌─────────────────────────────────────────┐                  │
│  │ session:agent:main:telegram:dm:123      │  并发上限: 1       │
│  │ [msg_X]                                 │                  │
│  └─────────────────────────────────────────┘                  │
│                          │                                     │
│                          ▼ 出队后进入                           │
│  Global Lane (跨 session 并行控制)                              │
│  ┌─────────────────────────────────────────┐                  │
│  │ main lane                               │  并发上限: 4       │
│  │  ├── run_A (session:main)               │                  │
│  │  ├── run_X (session:tg:dm:123)          │                  │
│  │  ├── run_Y (session:wa:group:xxx)       │                  │
│  │  └── (等待槽位...)                       │                  │
│  └─────────────────────────────────────────┘                  │
│  ┌──────────────────┐  ┌──────────────────┐                   │
│  │ subagent lane    │  │ cron lane        │                   │
│  │ 并发上限: 8      │  │ 独立运行          │                   │
│  └──────────────────┘  └──────────────────┘                   │
│                                                                │
└────────────────────────────────────────────────────────────────┘

5.2 队列模式 (Queue Mode)

新消息到达,当前 session 有正在运行的 Agent
                   │
          ┌────────┴────────┐
          │  队列模式配置    │
          └────────┬────────┘
                   │
       ┌───────────┼───────────┬───────────┐
       │           │           │           │
       ▼           ▼           ▼           ▼
   collect      steer      followup   steer-backlog
  (默认)
       │           │           │           │
  合并所有      立即注入     等当前结束   立即注入
  待处理消息    当前运行     后排队运行   + 保留
  为单次运行    (取消待执    (保证顺序)   followup
               行工具调用)              副本
       │
       ▼
  debounceMs: 1000ms  (防止连续消息各触发一次)
  cap: 20             (队列上限)
  drop: "summarize"   (溢出时压缩为摘要)

5.3 Typing Indicator 即时性保证

消息入队 (Agent 还未开始处理)
       │
       ├──→ 立即发送 Typing Indicator ← 用户无感知等待
       │    (各平台: 正在输入...)
       │
       └──→ 等待队列 drain → Agent 开始运行

6. Layer 4 — Gateway Core 核心控制平面

Gateway 是整个系统的大脑,监听 ws://127.0.0.1:18789

6.1 WebSocket 控制平面协议

┌──────────────────────────────────────────────────────────────┐
│              Gateway WebSocket 协议 (JSON over WS)            │
├──────────────────────────────────────────────────────────────┤
│                                                              │
│  握手阶段:                                                    │
│    Client → { type: "connect", role, deviceId, auth }       │
│    Gateway ← { type: "hello-ok", health: {...} }             │
│                                                              │
│  请求/响应 (RPC):                                             │
│    Client → { type: "req", id: "123", method: "agent",      │
│               params: { message, sessionKey, ... } }         │
│    Gateway ← { type: "res", id: "123", ok: true,            │
│               payload: { runId, acceptedAt } }               │
│                                                              │
│  服务端推送事件 (单向):                                       │
│    Gateway → { type: "event", event: "agent",               │
│               payload: { stream: "assistant",               │
│                          delta: "..." },                    │
│               seq: 42, stateVersion: "v7" }                 │
│                                                              │
│  支持的事件类型:                                               │
│    agent     - Agent 运行状态与输出流                         │
│    chat      - 聊天消息 delta / final                        │
│    presence  - 用户在线状态                                   │
│    health    - Gateway 健康状态                               │
│    heartbeat - 心跳保活                                       │
│    cron      - 定时任务触发                                    │
│                                                              │
│  幂等性:                                                      │
│    send / agent 方法必须携带 idempotency key                 │
│    Gateway 维护短期去重缓存 (防止重试重复执行)                 │
│                                                              │
└──────────────────────────────────────────────────────────────┘

6.2 Agent Loop (Pi Agent Core)

agent RPC 调用
      │
      ▼
┌──────────────────────────────────────────────────────┐
│ agentCommand 入口                                     │
│   1. 验证 params                                      │
│   2. 解析 sessionKey / sessionId                      │
│   3. 持久化 session 元数据                            │
│   4. 立即返回 { runId, acceptedAt }                   │
└─────────────────────┬────────────────────────────────┘
                      │ 异步
                      ▼
┌──────────────────────────────────────────────────────┐
│ runEmbeddedPiAgent                                    │
│                                                      │
│  ┌──────────────────────────────────────────────┐    │
│  │ 1. 解析 model + thinking 级别                │    │
│  │    (off/minimal/low/medium/high/xhigh)       │    │
│  │                                              │    │
│  │ 2. 加载 Skills 快照                          │    │
│  │    (~/.openclaw/workspace/skills/)           │    │
│  │                                              │    │
│  │ 3. 构建 Pi Session                           │    │
│  │    (Auth Profile 选择 + Failover 配置)       │    │
│  │                                              │    │
│  │ 4. 订阅 Pi 事件流:                           │    │
│  │    text_delta → stream: "assistant"          │    │
│  │    tool_start → stream: "tool"               │    │
│  │    tool_end   → stream: "tool"               │    │
│  │    lifecycle  → stream: "lifecycle"          │    │
│  │                                              │    │
│  │ 5. abort 保护:                               │    │
│  │    超时 600s → AbortSignal 触发               │    │
│  └──────────────────────────────────────────────┘    │
└──────────────────────────────────────────────────────┘

6.3 Session Manager (状态持久化)

Session 存储结构:
~/.openclaw/agents/<agentId>/
  sessions/
    sessions.json          ← Session 索引 (key → metadata)
    <SessionId>.jsonl      ← 完整对话 Transcript
    <SessionId>.jsonl.bak  ← 备份

sessions.json 结构:
{
  "agent:main:main": {
    "sessionId": "sess_abc123",
    "updatedAt": "2025-03-06T10:00:00Z",
    "inputTokens": 12500,
    "outputTokens": 3200,
    "contextTokens": 15700,
    "channel": "whatsapp",
    "origin": {
      "label": "张三的对话",
      "provider": "whatsapp",
      "from": "+8613812345678"
    }
  }
}

Session 生命周期管理:
┌──────────────────────────────────────────────┐
│ 重置策略                                      │
│   daily: 每天 04:00 (Gateway 所在主机本地时间) │
│   idle: idleMinutes 滑动窗口                  │
│   先触发者优先                                │
│   手动: /new 或 /reset 命令                   │
├──────────────────────────────────────────────┤
│ 维护策略 (enforce 模式)                       │
│   pruneAfter: 30d  (清理过期 Session)         │
│   maxEntries: 500  (总数上限)                 │
│   rotateBytes: 10MB (sessions.json 轮换)      │
│   maxDiskBytes: 可选硬限制                    │
├──────────────────────────────────────────────┤
│ 上下文剪枝 (LLM 调用前)                       │
│   裁剪过旧的 tool result                      │
│   不修改 JSONL 历史文件                       │
│   Auto-compaction: 上下文过长时自动压缩摘要    │
└──────────────────────────────────────────────┘

6.4 Streaming Engine (分块投递引擎)

LLM 输出流
      │
      ▼
┌──────────────────────────────────────────────────────────┐
│ EmbeddedBlockChunker                                      │
│                                                          │
│  模型输出:  "好的,我来分析一下这个问题..."                │
│                ↓ text_delta 事件                          │
│  Buffer 累积 → 检查分块条件:                               │
│                                                          │
│  低水位 (minChars): buffer < 500字 → 继续累积             │
│  高水位 (maxChars): buffer > 2000字 → 强制分块            │
│                                                          │
│  分块偏好 (breakPreference):                              │
│    paragraph > newline > sentence > whitespace > 硬切     │
│                                                          │
│  代码块保护: 不在 ``` 内部分块                             │
│             强制分块时关闭再重开 fence                     │
└──────────────────┬───────────────────────────────────────┘
                   │ 块输出
                   ▼
┌──────────────────────────────────────────────────────────┐
│ Coalescer (合并防碎片)                                    │
│   等待 idleMs 空闲间隔 → 合并多个小块                     │
│   maxChars 上限 → 超过立即发送                             │
│   Slack/Signal/Discord: 默认 minChars=1500               │
└──────────────────┬───────────────────────────────────────┘
                   │
                   ▼
┌──────────────────────────────────────────────────────────┐
│ humanDelay (可选)                                         │
│   每个块之间随机延迟 800~2500ms                           │
│   模拟人类打字节奏                                        │
└──────────────────┬───────────────────────────────────────┘
                   │
                   ▼
              出站投递 (Layer 6)

各平台预览流式模式:

┌─────────────┬────────────────────────────────────────────┐
│ 平台        │ streaming 模式                              │
├─────────────┼────────────────────────────────────────────┤
│ Telegram    │ partial: sendMessage + editMessageText      │
│             │ block: 分块 edit                            │
├─────────────┼────────────────────────────────────────────┤
│ Discord     │ partial: send + edit 预览                   │
│             │ block: append-style draft                   │
├─────────────┼────────────────────────────────────────────┤
│ Slack       │ partial: native streaming API               │
│             │   (chat.startStream / append / stop)        │
│             │ progress: 状态预览 → 最终答案               │
└─────────────┴────────────────────────────────────────────┘

6.5 Tool Engine (工具执行)

Agent 决定调用工具
       │
       ▼
┌─────────────────────────────────────────────────┐
│ Plugin Hook: before_tool_call                   │
│   可拦截/修改工具参数                            │
└──────────────────┬──────────────────────────────┘
                   │
       ┌───────────┼───────────┬────────────┐
       │           │           │            │
       ▼           ▼           ▼            ▼
  bash/exec    browser     canvas/A2UI  sessions_*
  (本地命令)   (CDP Chrome) (可视画布)   (跨Agent通信)
       │           │           │            │
       ▼           ▼           ▼            ▼
┌─────────────────────────────────────────────────┐
│ Plugin Hook: after_tool_call                    │
│   可处理/转换工具结果                            │
└──────────────────┬──────────────────────────────┘
                   │
                   ▼
┌─────────────────────────────────────────────────┐
│ tool_result_persist                             │
│   结果写入 JSONL transcript 前的最后转换机会     │
└──────────────────┬──────────────────────────────┘
                   │
                   ▼
         工具结果注入对话上下文
         → 继续 Agent Loop

6.6 Retry & Failover

出站 API 请求失败
       │
       ├── HTTP 429 (Rate Limit)
       │     └── 读取 retry_after header → 精确等待
       │         指数退避 (有 jitter 防雪崩)
       │         最多 3 次重试
       │
       ├── 网络超时 / 连接重置
       │     └── 仅 Telegram 支持重试
       │         Discord 仅重试 429
       │
       ├── Markdown 解析错误 (Telegram)
       │     └── 降级为纯文本重发 (不重试)
       │
       └── Model API 失败
             └── Model Failover:
                 Auth Profile 轮换
                 → 备用 Provider 切换
                 → 备用模型切换

默认配置:
  attempts:   3
  minDelayMs: 400  (Telegram) / 500  (Discord)
  maxDelayMs: 30000
  jitter:     0.1 (10%)

7. Layer 5 — LLM 推理与响应组装

7.1 Prompt 构建

┌──────────────────────────────────────────────────────┐
│                   System Prompt 组装                  │
│                                                      │
│  ① 基础 Prompt                                       │
│     SOUL.md     - Agent 人格/价值观                  │
│     IDENTITY.md - Agent 身份描述                     │
│     AGENTS.md   - Agent 行为规范                     │
│                                                      │
│  ② Skills Prompt                                    │
│     ~/.openclaw/workspace/skills/<skill>/SKILL.md   │
│     (自动发现并注入)                                  │
│                                                      │
│  ③ Bootstrap Context                                │
│     TOOLS.md    - 可用工具说明                       │
│     BOOTSTRAP.md - 启动上下文                        │
│     USER.md     - 用户偏好/信息                      │
│                                                      │
│  ④ Session History (JSONL transcript 剪枝后)         │
│     [对话历史 - 经过 token 预算控制]                  │
│                                                      │
│  ⑤ 当前 InboundMessage → user turn                  │
│     [群聊历史注入 (如有)]                             │
│     当前消息 Body                                    │
└──────────────────────────────────────────────────────┘

7.2 响应组装与过滤

Pi Agent Core 输出
       │
       ▼
┌───────────────────────────────────────────────────────┐
│ 响应组装 (Reply Shaping)                               │
│                                                       │
│   assistant text   ─────────────────────────────┐    │
│   reasoning text (可选)  ─────────────────────→ │    │
│   tool summaries (verbose模式) ─────────────────→│    │
│                                                  │    │
│                                              payload[] │
│                                                  │    │
│   过滤规则:                                      │    │
│   · "NO_REPLY" 令牌 → 静默丢弃                   │    │
│   · messaging tool 确认消息 → 去重               │    │
│   · 无可渲染内容 + tool 错误 → fallback 错误回复  │    │
└───────────────────────────────────────────────────────┘

8. Layer 6 — Outbound 出站投递

8.1 各平台分块策略

┌───────────────┬──────────────────────────────────────────────┐
│ 平台          │ 出站策略                                      │
├───────────────┼──────────────────────────────────────────────┤
│ WhatsApp      │ textChunkLimit: 4000字符/条                   │
│               │ chunkMode: "length" 或 "newline"(段落分割)    │
│               │ 音频: PTT 格式 (OGG/Opus)                    │
│               │ 图片: 自动压缩为 JPEG + 尺寸限制              │
│               │ 媒体上限: 5MB/条 (agents.defaults.mediaMaxMb) │
│               │ ackReaction: 收到消息立即发送 👀 表情          │
├───────────────┼──────────────────────────────────────────────┤
│ Telegram      │ edit-in-place 预览流式                        │
│               │ 429 → 读取 retry_after 精确等待              │
│               │ Markdown 失败 → 降级纯文本                   │
├───────────────┼──────────────────────────────────────────────┤
│ Slack         │ native streaming (chat.startStream)          │
│               │ coalesce minChars: 1500                      │
│               │ progress 模式: 状态 → 最终答案               │
├───────────────┼──────────────────────────────────────────────┤
│ Discord       │ maxLinesPerMessage: 17 (防 UI 截断)          │
│               │ send + edit 预览 / append-style block        │
│               │ 429 only retry                               │
├───────────────┼──────────────────────────────────────────────┤
│ Signal        │ coalesce minChars: 1500                      │
├───────────────┼──────────────────────────────────────────────┤
│ BlueBubbles   │ REST API 发送 / 支持 edit/unsend/reactions   │
└───────────────┴──────────────────────────────────────────────┘

9. 完整消息生命周期时序

以 WhatsApp 群组 @提及 消息为例:

用户在 WhatsApp 群组发送 "@OpenClaw 帮我查一下天气"
       │
       │ [T+0ms]  Baileys messages.upsert 事件触发
       ▼
Channel Adapter:
  · 解析群组 JID: "120363012345@g.us"
  · 检查 groupPolicy: allowlist → 群组在白名单
  · 检查 activation: mention → 检测到 @OpenClaw
  · 规范化 Body: "@OpenClaw 帮我查一下天气\n[from: 张三 (+8613812345678)]"
       │
       │ [T+50ms]  路由决策
       ▼
Channel Routing:
  · SessionKey = "agent:main:whatsapp:group:120363012345@g.us"
  · AgentId = "main"
       │
       │ [T+60ms]  入队
       ▼
Command Queue:
  · 消息入 session lane 队列
  · 立即发送 WhatsApp "正在输入..." 指示器 ← [用户看到]
  · 等待 debounce 1000ms (防多条合并)
       │
       │ [T+1100ms]  出队执行
       ▼
Agent Loop:
  · 加载 Skills
  · 构建 Prompt (System + History + 当前消息)
  · 调用 Anthropic Claude API (streaming)
       │
       │ [T+2000ms]  LLM 开始返回 token
       ▼
Streaming Engine:
  · text_delta 持续到来
  · Buffer 累积到 minChars (500) → 开始输出
  · 分块: "好的,我来查一下天气..."
       │
       │ [T+2500ms]  第一个块就绪
       ▼
Outbound:
  · WhatsApp 发送第一条消息  ← [用户收到第一条]
  · humanDelay: 随机等待 1200ms
       │
  Agent 继续调用 weather 工具
       │
       │ [T+5000ms]  工具返回结果
       ▼
  LLM 继续生成最终回答
       │
       │ [T+6500ms]  最终消息块
       ▼
Outbound:
  · WhatsApp 发送第二条消息 (天气详情) ← [用户收到第二条]
  · 停止 Typing Indicator
       │
Session Manager:
  · 追加对话到 JSONL transcript
  · 更新 sessions.json token 计数
  · 检查是否需要 Auto-compaction

10. 关键设计决策总结

┌───────────────────────────────────────────────────────────────────┐
│                      核心设计决策                                  │
├─────────────────────┬─────────────────────────────────────────────┤
│ 决策                │ 理由                                        │
├─────────────────────┼─────────────────────────────────────────────┤
│ 单 Gateway 进程     │ 避免多进程竞争 WhatsApp Session;              │
│                     │ 统一控制面; 简化部署                         │
├─────────────────────┼─────────────────────────────────────────────┤
│ WS 控制平面         │ 实时双向通信; 支持事件推送;                   │
│ (内部通信)          │ 与 IM 平台协议解耦                           │
├─────────────────────┼─────────────────────────────────────────────┤
│ Session Key 隔离    │ 群组/DM/账号完全独立上下文;                  │
│                     │ 防止用户信息交叉污染                         │
├─────────────────────┼─────────────────────────────────────────────┤
│ Lane-Aware Queue    │ 同 session 串行防竞态;                       │
│                     │ 跨 session 并行提升吞吐                      │
├─────────────────────┼─────────────────────────────────────────────┤
│ Typing Indicator    │ 入队即发送, 用户无感知队列等待               │
│ 即时发送            │                                             │
├─────────────────────┼─────────────────────────────────────────────┤
│ 分层分块策略        │ 适配各平台字符限制;                          │
│                     │ humanDelay 模拟自然对话节奏                  │
├─────────────────────┼─────────────────────────────────────────────┤
│ DM Pairing 机制     │ 陌生用户须配对, 防止未授权调用 AI;           │
│                     │ 保护 API 配额和隐私                          │
├─────────────────────┼─────────────────────────────────────────────┤
│ TypeBox Schema      │ 协议类型安全; 自动生成 JSON Schema;          │
│ 驱动协议            │ Swift 客户端代码自动生成                     │
├─────────────────────┼─────────────────────────────────────────────┤
│ 幂等性键            │ 网络重试不产生重复消息;                      │
│                     │ 服务端短期去重缓存兜底                       │
└─────────────────────┴─────────────────────────────────────────────┘

数据来源: github.com/openclaw/openclaw + docs.openclaw.ai

posted @ 2026-03-06 02:44  LexLuc  阅读(409)  评论(0)    收藏  举报