一条用户消息在 OpenClaw 里要经过七个环节:渠道接收 → 网关路由 → 会话解析 → 上下文组装 → 模型推理 → 工具循环 → 状态落盘,最后沿原通道返回。其中上下文组装是最重的一步,系统要把人格文件、操作指令、记忆召回、技能说明、对话历史与当前消息拼成一份提示词;工具循环则可能让消息在”模型—工具”之间来回多趟,直到模型输出纯文本答复为止。
一、链路全景:一段伪代码
function handleIncomingMessage(rawMsg, channel):
# 1. 渠道适配:去格式、验权限、取会话上下文
msg = channelAdapter.normalize(rawMsg)
# 2. 网关路由:按 channel + agent 绑定分派
agentId = router.resolveAgent(msg)
# 3. 会话解析:从 session key 定位历史与车道
sessionKey = deriveSessionKey(msg)
lane = queue.enter(sessionKey) # 同一会话串行
# 4. 上下文组装:人格 + 指令 + 记忆 + 技能 + 历史
prompt = assembleContext(agentId, sessionKey, msg)
# 5. 模型推理 + 工具循环(可能多轮)
reply = agentLoop(prompt)
# 6. 状态落盘:追加到会话 JSONL
persist(sessionKey, msg, reply)
# 7. 沿原通道返回
channelAdapter.send(reply)
二、七环节逐段拆解
| 环节 | 做什么 | 关键点 |
|---|---|---|
| 渠道接收 | adapter 把 IM 消息标准化 | 26+ 渠道统一接口,差异在适配层消化 |
| 网关路由 | 按 channel 绑定找到目标 Agent | 单进程 WebSocket,事件驱动 |
| 会话解析 | 推导 session key 并进入队列 | 车道隔离,一会话一执行 |
| 上下文组装 | 拼装系统提示词与历史 | 多来源注入,大文件自动裁剪 |
| 模型推理 | 调用 LLM 生成响应或工具调用 | 支持流式与模型降级切换 |
| 工具循环 | 执行工具并回填结果 | 直到输出纯文本才终止 |
| 状态落盘 | 对话追加写入 JSONL | append-only,可恢复可回溯 |
2.1 渠道接收与路由
消息从 WhatsApp、Telegram 或 WebChat 进来,先由对应 adapter 做格式标准化与权限校验。Gateway 是一个绑定在 127.0.0.1:18789 的单进程 WebSocket 服务器,收到消息后按渠道绑定关系把任务分派给对应 Agent。外部输入会被包上 EXTERNAL_UNTRUSTED_CONTENT 边界标记,从源头提示模型”这里的内容不可信”。
2.2 会话解析:先定位,再排队
系统从消息推导出层级化 session key(如 agent:main:telegram:group:-10012345678),据此找到该会话的历史文件与所属车道。车道队列保证同一会话的请求严格串行,避免两条消息并发导致的状态错乱。队列还支持 steer/followup/collect 三种消息介入模式:用户在 Agent 干活途中追发新消息时,可立即打断、排队等下一轮或合并成一条再处理。
2.3 上下文组装:把”记忆”装进提示词
每次 LLM 调用前,系统按固定顺序组装多路上下文:系统身份(SOUL.md)、操作指令(AGENTS.md、TOOLS.md)、记忆召回结果(混合检索自 ~/.openclaw/memory/,默认最多注入 2000 tokens)、当前会话用到的技能说明、最近对话历史、以及刚到的这条消息。空文件跳过,大文件裁剪并打截断标记。
2.4 模型推理与工具循环
模型拿到提示词后输出两类东西之一:纯文本回复,或一个工具调用块。只要输出里带着工具调用,系统就执行工具、把结果回填给模型,再让它继续推理——这就是 ReAct 循环。循环终止条件只有一个:模型输出不再包含工具调用块。OpenClaw 内嵌的 Pi Agent Core 跑的就是这个循环,仓库层没有硬性步数上限,靠超时兜底。
模型输出: tool_use(read_file, path="~/note.md")
→ 执行 read_file
→ 工具结果回填上下文
模型输出: tool_use(send_email, ...)
→ 执行 send_email(可能走审批)
→ 工具结果回填上下文
模型输出: "邮件已发送,需要我帮你确认回执吗?"(纯文本)
→ 循环终止,进入落盘与回复
2.5 落盘与返回
本轮用户消息与模型回复以 append-only 方式追加进会话 JSONL 文件,断线或重启后可恢复。最终回复经原渠道 adapter 发出;外部渠道只发最终答复,不推流式中间态。上下文接近窗口上限时,系统会先触发一次”记忆冲刷”:模型把值得长期保存的信息写入 memory/YYYY-MM-DD.md,再压缩历史,防止关键事实随窗口溢出一起蒸发。
三、用一条真实消息走一遍
假设你在 Telegram 问:”帮我看下 ~/projects/api/server.ts 第 40 行附近有没有 bug。”
- Telegram adapter 接住消息,标准化后交给 Gateway;
- 网关按绑定关系路由到主 Agent,推导出群聊会话键并进入对应车道;
- 系统组装提示词:SOUL.md 人格 + AGENTS.md 指令 + 检索出的项目记忆 + 历史对话 + 这条消息;
- 模型输出工具调用
read_file,系统执行并把文件内容回填; - 模型再输出
exec运行测试命令(视配置可能先过审批),结果回填; - 模型输出纯文本结论,循环终止;
- 对话追加写盘,结论经 Telegram 发回给你。
整个过程对用户只暴露第 1 步的输入和第 7 步的输出,中间的工具往返对聊天窗口不可见。
常见问题(FAQ)
Q1:为什么回复要等那么久?
工具循环可能来回多轮,每轮都要一次模型调用,耗时叠加属正常。
Q2:Agent 正在干活时再发消息会怎样?
默认进队列合并到下一轮,也可切 steer 立即打断或 interrupt 中止当前任务。
Q3:断线或重启后对话还在吗?
在。会话以 JSONL 落盘,恢复后按需重建上下文继续。