AI 编程教程中文版
从原理到实战

02 · 一条消息的旅程

拆解一条消息从渠道进入 OpenClaw 到 Agent 回复的完整链路,包括路由、session、队列、上下文、工具和回复分块。

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

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

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

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

1. 全链路先看一遍

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

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 是哪个。

多数“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 等。

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 / rolesDiscord 服务器按角色分流
teamIdSlack workspace 级分流
accountId同一渠道多个账号分流
channel match所有 Telegram 或 Slack 入口统一进某 Agent
default agent没匹配到规则时兜底

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

路由越确定,系统越可审计。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 防抖解决分条发送

用户经常这样发:

帮我查一下
服务器状态
主要看 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,处理最新消息旧任务可抛弃的场景
queuelegacy one-at-a-time steering只为兼容旧行为

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

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

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

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

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

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 组装,是理解“为什么同一句话有时快、有时慢、有时答偏”的关键。

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 帮你检查消息路径:

请只读检查 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. 接下来去哪

17. 官方资料

本页目录