---
title: "02 · 一条消息的旅程"
description: "拆解一条消息从渠道进入 OpenClaw 到 Agent 回复的完整链路，包括路由、session、队列、上下文、工具和回复分块。"
verifiedAt: "2026-05-06"
---

你在 Telegram、Discord 或 Slack 里发一句：“帮我查一下服务器状态。”

表面上看，这是一次普通聊天。实际上，这条消息进入 OpenClaw 之后，会经过渠道标准化、路由、session 定位、队列、上下文组装、模型调用、工具循环、回复投递等多个阶段。

理解这条路径的价值很直接：以后 Agent 不回复、回复重复、答非所问、群聊乱触发、长回复切坏，你知道该查哪一层，而不是只说“AI 坏了”。

> [!TIP]
> 这一篇的核心不是背流程，而是建立排错顺序：入口、权限、路由、session、队列、context，再到模型和工具。

## 1. 全链路先看一遍

OpenClaw 官方消息文档把高层流程概括成：inbound message → routing/bindings → session key → queue → agent run → outbound replies。展开后可以这样理解：

```mermaid
flowchart TD
    Inbound[外部消息] --> Channel[Channel 标准化]
    Channel --> Policy[DM / group / mention 策略]
    Policy --> Routing[Binding 路由]
    Routing --> Session[Session key]
    Session --> Queue[Queue / steer / followup]
    Queue --> Context[Context 组装]
    Context --> Model[模型调用]
    Model --> Tools{需要工具吗}
    Tools -->|是| ToolRun[工具执行]
    ToolRun --> Context
    Tools -->|否| Reply[回复生成]
    Reply --> Chunk[Streaming / chunking]
    Chunk --> Outbound[发回原渠道]
```

这条链路里，真正需要模型参与的主要是模型调用和工具循环。前面的路由、session、队列，大多是确定性系统逻辑。多数“消息没到”问题，不是模型能力问题，而是入口、路由或权限问题。

## 2. 八个阶段

| 阶段 | 系统在做什么 | 常见故障 |
| --- | --- | --- |
| Channel 标准化 | 把 Telegram、Slack、Discord 等平台消息转成内部格式 | channel auth、账号状态、平台 webhook |
| 访问策略 | 判断 DM、群组、mention、allowlist、pairing 是否允许触发 | 群聊不回、私信被拒绝 |
| Binding 路由 | 按 channel、account、peer、team、guild 等规则选 Agent | 发给了错误 Agent |
| Session 定位 | 用 session key 找到对话桶和 transcript | 上下文串台、历史丢失 |
| Queue 处理 | 当前 session 忙时决定 steer、排队、合并或中断 | 回复延迟、重复回复 |
| Context 组装 | 把系统提示、workspace、记忆、历史、当前消息拼成输入 | 答非所问、忘记规则 |
| Agent run | 模型推理，必要时调用工具，多轮循环 | 卡住、超时、工具失败 |
| Outbound 投递 | 分块、流式、线程回复、发回原渠道 | 长消息切坏、发错线程 |

新手排错时不要从第 7 阶段开始。先确认前 4 个阶段：消息有没有进来、是否允许触发、路由到哪个 Agent、session key 是哪个。

> [!IMPORTANT]
> 多数“Agent 没反应”的问题，不在模型，而在消息是否进来、是否被允许触发、是否路由到正确 Agent。

## 3. Channel 标准化：先把平台差异抹平

不同渠道的消息格式完全不同。Telegram 有 chat id、message id、topic；Slack 有 team、channel、thread；Discord 有 guild、channel、author、thread。Gateway 不能让后面的 Agent 分别理解每个平台的原始格式。

Channel 层的职责是把外部消息统一成 OpenClaw 能处理的内部消息，并保留必要的路由元数据：

- sender / user id 主要影响 DM allowlist、pairing、session key。
- group / room / channel id 主要影响群组策略、binding、session key。
- thread / topic id 主要影响线程或 topic 隔离。
- accountId 主要影响多账号隔离和默认账号选择。
- message id 主要影响去重和 reply threading。
- text / media / reply context 会进入当前 prompt、附件和引用上下文。

这一步一般不需要 AI 判断。它越确定，后面的行为越稳定。

## 4. 访问策略：不是每条消息都应该触发 Agent

消息标准化之后，系统先判断这条消息有没有资格触发 Agent：

- 私信不回：看 DM policy、pairing、allowFrom。
- 群里不回：看 group policy、group allowlist。
- 群里随便一句就触发：看 mention gating 是否配置。
- 多账号消息混乱：看 accountId 和 defaultAccount。
- 线程回复不对：看 thread / topic 解析和 replyToMode。

这一层的目标不是“尽量多回复”，而是“只在该回复的时候回复”。OpenClaw 面向的是能调用工具的 Agent，入口默认应该收紧。

## 5. Binding 路由：模型不决定消息给谁

OpenClaw 路由是确定性的。官方 channel routing 文档列出的匹配顺序包括 exact peer、parent peer、Discord guild / roles、Slack team、account、channel、default agent 等。

```mermaid
flowchart TD
    Msg[标准化消息] --> Exact[exact peer]
    Exact --> Parent[parent peer / thread]
    Parent --> Guild[Discord guild / roles]
    Guild --> Team[Slack team]
    Team --> Account[accountId]
    Account --> Channel[channel match]
    Channel --> Default[default agent]
```

| 路由条件 | 适合场景 |
| --- | --- |
| exact peer | 某个固定群、频道、私信入口进指定 Agent |
| guild / roles | Discord 服务器按角色分流 |
| teamId | Slack workspace 级分流 |
| accountId | 同一渠道多个账号分流 |
| channel match | 所有 Telegram 或 Slack 入口统一进某 Agent |
| default agent | 没匹配到规则时兜底 |

重要的是：路由不让模型自由发挥。模型不应该决定“这条消息交给谁”。如果这一步让 AI 猜，系统就不可预测。

> [!NOTE]
> 路由越确定，系统越可审计。AI 负责生成回复，配置负责决定消息归属。

## 6. Session key：找到正确的对话桶

路由选出 Agent 之后，还要决定这条消息属于哪个 session。Session 是对话上下文和并发控制的基本单位。

常见 session key 形状：

- Agent main session：`agent:{agentId}:main`。
- 群组：`agent:{agentId}:{channel}:group:{id}`。
- 频道 / 房间：`agent:{agentId}:{channel}:channel:{id}`。
- Slack / Discord thread：在基础 key 后追加 `:thread:{threadId}`。
- Telegram forum topic：在 group key 中包含 `:topic:{topicId}`。

session key 解决两个问题：

- 上下文隔离：不同人、不同群、不同线程不要混在一起。
- 并发控制：同一个 session 同一时间只允许一个 active run。

很多“Agent 怎么把 A 群的上下文带到 B 群了”这类问题，本质都要回到 session key 设计。

## 7. Inbound dedupe 和 debounce

现实里的消息入口不干净。网络重连、平台重试、用户分多条发送，都会让系统收到比你想象更多的消息。

### 7.1 去重解决重复投递

Channel 可能重复投递同一条消息。OpenClaw 会用 channel、account、peer、session、message id 等信息做短期缓存，避免同一消息触发多次 agent run。

如果用户说“怎么回了两次”，先看消息 id 是否重复进入、channel 是否重连、Gateway 日志是否显示重复 run。

### 7.2 防抖解决分条发送

用户经常这样发：

```text
帮我查一下
服务器状态
主要看 CPU 和内存
```

如果每一条都立刻触发模型，系统会浪费三次调用，还可能因为第一条意图不完整而答偏。`messages.inbound.debounceMs` 会把同一 sender 在短时间内的连续文本消息合成一次 agent turn。

相关配置先记四个点：

- `messages.inbound.debounceMs`：全局防抖窗口，默认常见写法是 2000ms。
- `messages.inbound.byChannel.whatsapp`：WhatsApp 这类分条习惯明显的平台可以更长。
- media / attachments：通常立即 flush，不和纯文本一样等待。
- control commands：通常独立处理，不应被普通消息合并。

防抖不是越长越好。太短会重复处理，太长会让用户觉得系统慢。

## 8. Queue：Agent 正忙时怎么办

同一个 session 同时跑两个 Agent turn 会破坏上下文一致性，所以 OpenClaw 用队列保证同一 session 的运行被串行管理。

官方队列文档里，当前默认模式是 `steer`。常见模式可以这样理解：

| 模式 | 行为 | 适合场景 |
| --- | --- | --- |
| `steer` | 把新消息注入当前 run 的下一个模型边界；不行就 fallback 到 followup | 用户经常中途纠正方向 |
| `followup` | 当前 run 结束后逐条处理 | 每条消息都应独立响应 |
| `collect` | 当前 run 后把等待消息合并为一次 followup | 用户会连续补充上下文 |
| `steer-backlog` | 既尝试 steer，也保留 followup | 需要实时性但要避免丢后续 |
| `interrupt` | 中断当前 run，处理最新消息 | 旧任务可抛弃的场景 |
| `queue` | legacy one-at-a-time steering | 只为兼容旧行为 |

如果你看到 Agent “等很久才回”，不一定是模型慢，也可能是 session 正在排队。看 Gateway 日志、`openclaw status` 和任务状态，比盲目调 prompt 更有用。

## 9. Context 组装：AI 看到的远不止你那句话

当消息真正进入 Agent run，系统会把多种上下文拼起来：

- system prompt 定义运行规则、工具边界、行为约束。
- workspace bootstrap 包括 `AGENTS.md`、`SOUL.md`、`TOOLS.md`、`USER.md` 等。
- memory 提供长期事实、偏好、近期记忆。
- session transcript 提供当前 session 历史。
- group pending history 提供群聊中未触发 run 但可作为背景的消息。
- current message 是用户这次真正要回复的内容。
- reply / forward metadata 提供被引用消息、转发上下文。

你发的一句话，通常只是最终输入包的一小部分。Agent 答非所问时，不要只看用户消息，要看 system prompt、workspace 文件、历史 transcript 和 memory 有没有污染或缺失。

```mermaid
flowchart TD
    System[System prompt] --> Prompt[模型输入]
    Workspace[Workspace files] --> Prompt
    Memory[Memory / notes] --> Prompt
    History[Session history] --> Prompt
    Current[Current message] --> Prompt
    Prompt --> Model[Model]
```

理解 context 组装，是理解“为什么同一句话有时快、有时慢、有时答偏”的关键。

> [!WARNING]
> Agent 答非所问时，不要只改 prompt。先看本轮 context 里混进了什么、缺了什么、历史有没有污染。

## 10. Agent run：模型、工具和多轮循环

模型第一次收到 context 后，不一定马上给最终回复。它可能决定调用工具：

1. 查状态。
2. 读取日志。
3. 根据日志再查进程。
4. 再生成结论。

每次工具结果都会回到 context，模型再决定下一步。这就是 Agent loop。

常见现象可以先这样判断：

- 一直转圈：可能是工具执行慢、provider 慢、session 队列未释放。
- 反复调用同一个工具：可能是指令不清、工具结果不足、loop detection 触发前仍在尝试。
- 工具失败但 Agent 继续：错误被当作工具结果回到模型，由模型决定替代路径。
- 上下文突然变大：可能是工具输出太长、历史太长、workspace 文件太重。

工具失败不是系统崩溃。失败信息也是上下文的一部分。问题在于 Agent 是否能从错误里找到下一步。

## 11. Streaming、chunking 和最终投递

模型生成结果后，OpenClaw 还要把回复发回原渠道。这里有两个容易混淆的概念：

- **Block streaming**：把已完成的文本块作为正常 channel message 提前发出。
- **Preview streaming**：在 Telegram、Discord、Slack 等支持的渠道上更新临时预览消息。

官方 streaming 文档强调：今天的 channel message 不是直接 token delta 流。它更像“文本块或预览消息的渐进更新”。

长回复还要做 chunking。Chunking 会尊重平台文本长度限制，并尽量不要切坏 Markdown，尤其是 fenced code。切分顺序通常从最自然到最后兜底：

1. paragraph。
2. newline。
3. sentence。
4. whitespace。
5. hard break。

如果长回复在 Discord 或 Telegram 里看起来很碎，先看 block streaming、chunk limits、coalescing 和 channel text limit，而不是先怪模型。

## 12. Silent reply：做了事但不打扰

OpenClaw 支持 silent reply。精确的 `NO_REPLY` / `no_reply` 表示不要发用户可见文本。它适合内部 orchestration、群聊静默、某些心跳或后台事件。

但 silent reply 有边界：

- direct conversation 默认不应悄悄吞掉所有回复。
- 有媒体附件时，静默文本可被去掉，但附件仍可能投递。
- group / channel 和 internal orchestration 的默认策略不同。

用户没看到回复，不一定代表 Agent 没跑。要结合 logs、tasks、session transcript 判断。

## 13. 常见故障怎么定位

- 完全没反应：优先查 Channel auth、Gateway 是否在线、DM/group policy、mention gating。
- 发给错 Agent：优先查 bindings、accountId、peer、default agent。
- 上下文串台：优先查 session key、thread/topic、dmScope、WebChat main session。
- 回复重复：优先查 inbound dedupe、平台重投递、steer-backlog、block/preview 双路径。
- 用户分三条导致三次回复：优先查 inbound debounce 配置。
- Agent 很慢：优先查 context 大小、provider、工具耗时、session queue。
- 长回复切得难看：优先查 chunk limit、block streaming、code fence、channel 限制。
- 群里不该回复却回复：优先查 group allowlist、mentionPatterns、activation gating。

按阶段定位，比调 prompt 更可靠。

## 14. 给 Agent 的实践任务

如果你已经有 OpenClaw 环境，可以让本地 Agent 帮你检查消息路径：

```text
请只读检查 OpenClaw 消息管道：
1. 查看 openclaw status 和 gateway status，确认 Gateway 是否在线。
2. 读取 openclaw.json，列出 messages.inbound、messages.queue、bindings、channels 的关键设置。
3. 不打印任何 token、botToken、secret、auth profile。
4. 解释一条 Telegram 或 Slack 消息会路由到哪个 Agent，以及 session key 大概如何形成。
5. 如果当前配置有群聊入口，检查 allowlist、mention gating 和 pairing 边界。
```

没有本地环境时，就让 Agent 读官方 messages、channel routing、queue、streaming 文档，按你的目标渠道生成一份排错表。

## 15. 本章自检

读完这一章，你应该能回答：

1. 一条消息进入 OpenClaw 后，哪几步不需要模型参与？
2. Binding 路由为什么必须是确定性的？
3. session key 解决了哪两个问题？
4. dedupe、debounce、queue 分别解决什么问题？
5. Agent 答非所问时，为什么要检查 context 而不是只改用户 prompt？
6. block streaming 和 preview streaming 有什么区别？

## 16. 接下来去哪

<Cards>
  <Card href="/docs/openclaw/understanding/03-agent-brain" title="03 · Agent 的大脑" description="继续看模型如何在推理、工具调用、观察结果和最终回复之间循环。" />
  <Card href="/docs/openclaw/understanding/01-ai-home" title="01 · 给 AI 一个家" description="回到 OpenClaw 的整体心智模型，理解 Gateway、Agent、Workspace 和 Channel 的分工。" />
  <Card href="/docs/openclaw/official/01-gateway-runtime/channels" title="官方教程：Channels" description="对照官方配置口径继续看 channel、account、routing 和消息入口。" />
</Cards>

## 17. 官方资料

- Messages：[https://docs.openclaw.ai/concepts/messages](https://docs.openclaw.ai/concepts/messages)
- Channel routing：[https://docs.openclaw.ai/channels/channel-routing](https://docs.openclaw.ai/channels/channel-routing)
- Command queue：[https://docs.openclaw.ai/concepts/queue](https://docs.openclaw.ai/concepts/queue)
- Streaming and chunking：[https://docs.openclaw.ai/concepts/streaming](https://docs.openclaw.ai/concepts/streaming)
