OpenClaw 实战:AI Agent 异步任务追踪------为什么「说完成了」不等于完成了
📖 本文首发于微信公众号「Wesley AI 日记」,更多 AI Agent 实战系列请微信搜索关注。
背景:一个让我焦虑了两周的问题
我在用 OpenClaw 跑一个多 Agent 系统------CEO Agent 负责协调,下面挂着 8 个子 Agent(内容创作、数据分析、品牌设计......)。
每次我给 CEO 下达任务,CEO 会把子任务分发给对应的子 Agent,然后......就没有然后了。
我坐在那里等,等了 10 分钟,没动静。打开 session 一看,子 Agent 已经完成了,并且在自己的 session 里输出了一大堆"任务完成报告"。
但 CEO 不知道。我也不知道。
这就是多 Agent 系统里最典型的异步任务追踪失效问题。
问题根源:announce 机制的先天缺陷
OpenClaw 的 sessions_spawn 有一个 announce 机制,理论上子 Agent 完成后会通知父 session。但实际上这个机制极不可靠:
- 网络延迟/超时会导致通知丢失
- 父 session 如果已经 yield,通知可能打到空气里
- announce 的内容格式不标准,父 Agent 解析困难
结果就是:子 Agent 说"完成了",父 Agent 永远不知道。
这个问题在我们的系统里导致了若干次严重事故:
- CEO 以为任务还在进行,重复派发,子 Agent 跑了两遍
- CEO 等不到回应,直接向 Wesley 汇报"任务进行中",但其实早就完成了
- 内容发布任务静默失败,没有任何告警,Wesley 第二天才发现没有更新
解决方案:强制回调协议(Mandatory Callback Protocol)
我们最终设计了一套叫 MCP(Mandatory Callback Protocol) 的回调规范,强制所有子 Agent 遵守。
核心设计:两步完成机制
任务完成 = sessions_send 回调 + 写入完成日志
缺一不可。
Step 1: sessions_send 主动回调
ini
sessions_send(
target="ceo",
message="📋 任务完成报告
任务:{任务描述}
状态:done/fail
结果:{核心产出,≤300字}
备注:{如有后续/风险}"
)
这一步是推送------子 Agent 主动把结果推给 CEO,不依赖 announce 机制。
Step 2: 写入完成日志
bash
echo "$(date -Iseconds) COMPLETE {agent-id} {done/fail} {结果摘要50字}" \
>> /shared/task-completions.log
这一步是持久化------即使 CEO session 已经关闭,日志还在,可以事后追查。
架构图:异步任务追踪全链路
c
Wesley
│
▼
CEO Agent
│ ① 派发任务
│ ─────────────────────────────────► 子 Agent
│ │
│ ③ sessions_send 回调 │ ② 执行任务
│ ◄───────────────────────────────── │
│ │ ④ 写入 task-completions.log
│
▼
通知 Wesley (message → Feishu)
关键点:回调方向是子 Agent → CEO,而不是 CEO 轮询子 Agent。
这是 Push 模型 vs Pull 模型的本质区别:
- Pull:CEO 每隔几分钟查一次子 Agent 状态 → 浪费算力,延迟高
- Push:子 Agent 完成即时回调 → 零延迟,零轮询开销
CEO 的兜底机制:Watchdog
光靠子 Agent 自觉回调是不够的------子 Agent 本身也可能崩溃、超时、卡死。
所以 CEO 这边需要一个 Watchdog 机制:
派发时记录任务台账
bash
echo "$(date -Iseconds) DISPATCH ceo {agent-id} {任务描述} ETA:{预期完成时间}" \
>> /shared/task-dispatches.log
Cron 定期扫描超时任务
c
每5分钟:扫描 task-dispatches.log,对比 task-completions.log
→ 找出超过 ETA 仍未完成的任务
→ 检查对应 session 状态
→ 超时 → 告警 Wesley
这套机制让 CEO 从被动等待 变成主动监控。
实战踩坑记录
坑 1:回调目标写错
子 Agent 回调时 target="ceo" 是 label,不是 session key。如果 CEO session 重启了,label 对应的 session key 会变。
解法:CEO 在派发任务时,把自己的 session key 一起传给子 Agent:
ini
"请完成任务后用 sessions_send(sessionKey='xxx') 回调"
坑 2:回调内容太长导致解析失败
子 Agent 有时候把整篇文章塞进回调消息,CEO 处理时 token 爆了。
解法:规范回调格式,结果摘要不超过 300 字,完整产物写文件,回调里只传文件路径。
坑 3:失败任务沉默
子 Agent 失败时往往不发回调,直接退出。CEO 永远等不到消息。
解法:在子 Agent 的 system prompt 里明确规定:
任务失败/超时/阻塞 → 同样必须发回调,说明失败原因,否则视为未完成任务。
失败回调和成功回调同等重要。
坑 4:重复回调
网络抖动导致子 Agent 发了两次回调,CEO 重复处理,内容发布了两遍。
解法 :给每个任务生成唯一 task_id,CEO 在处理回调时做幂等检查:
python
if task_id in processed_tasks:
skip()
else:
process()
processed_tasks.add(task_id)
完整协议规范
经过多次迭代,我们的 MCP 规范最终长这样:
markdown
## 强制回调协议(所有子 Agent 必须遵守)
任务完成后,必须依次执行:
1. sessions_send 回调给派单方
格式:
📋 任务完成报告
任务ID:{task_id}
任务:{任务描述,一句话}
状态:done | fail | partial
结果:{核心产出,≤300字}
文件:{产出文件路径,如有}
备注:{风险/后续,如有}
2. 写入完成日志
echo "$(date -Iseconds) COMPLETE {agent-id} {done/fail/partial} {task_id} {摘要50字}" \
>> /shared/task-completions.log
红线:
- 失败/超时/阻塞 → 同样必须回调,说明原因
- 沉默 = 任务未完成
- 只在 session 内输出"完成了" = CEO/Wesley 看不到
效果:从"薛定谔的任务"到"实时任务看板"
实施 MCP 后,我们的系统稳定性显著提升:
| 指标 | 实施前 | 实施后 |
|---|---|---|
| 任务静默失败率 | ~30% | ~5% |
| CEO 等待超时 | 每天 2-3 次 | 每周 0-1 次 |
| 重复任务执行 | 每周 1-2 次 | 近乎 0 |
| Wesley 手动干预频次 | 每天 | 每周 |
最重要的是,任务状态从不可见变成可观测。CEO 不再是个焦虑地等通知的管理者,而是一个真正能掌控全局的调度系统。
总结
多 Agent 系统的异步任务追踪,本质上是一个分布式系统的可靠性问题:
- 不要依赖平台机制(announce)------平台机制不可靠,自己实现回调
- Push > Pull------子 Agent 主动推送,比父 Agent 轮询效率高 10 倍
- 失败回调和成功回调同等重要------沉默是最糟糕的状态
- 日志是生命线------session 会关闭,日志不会消失
- 幂等性从一开始就设计------等到出了重复发布事故再改,成本很高
如果你也在构建多 Agent 系统,建议从第一天就把回调协议写进所有 Agent 的 system prompt。这比事后修复要省心得多。
📖 本文首发于微信公众号「Wesley AI 日记」
📚 AI Agent 实战系列(微信搜索「Wesley AI 日记」关注):
- 给 OpenClaw Agent Team 装上记忆------踩了 19 天坑
- AI Agent 说"完成了",我信了------然后被打脸了
- 实战复盘:OpenClaw 6 人 Agent Team 险些全军覆没
👆 微信搜索「Wesley AI 日记」关注,不错过每一篇更新。