第 15 章 Inbox 注入让 AI 队友异步通信 · Agent架构实操十五 开始测验

解密 Claude Code 协作机制:Inbox 注入让队友异步通信

单个 Agent 处理到后半程,前半程细节已离开上下文。一次性子 Agent 是「一问一答」,但有些工作需保留历史、接收补充、异步汇报、完成后继续存在。本章:持续 Teammate + 持久 Mailbox。
⏱ 约 15 分钟 🤝 spawn_teammate/send_message 📬 每消息一文件 +TEAMMATE+MAILBOX

🎯 学习目标

学完能答
  • 一次性子 Agent 和持续 Teammate 在生命周期/历史/通信上的差别?
  • 为什么不实现 MessageBus + read_text()/unlink()?崩溃窗口在哪?
  • 每消息一文件的 ready→processing→done/quarantine 解决什么?
  • spawn_teammate 和 send_message 各做什么?
  • Teammate 的四种状态(running/idle/failed/shutdown)?
  • Mailbox 消息如何回到公共 Agent Loop?

🧠 核心概念

点击展开。
1 · 一次性子 Agent vs 持续 Teammate
边界一次性子 Agent持续 Teammate
生命周期完成一次委派后返回完成后进入 idle,可接收后续消息
对话历史每次委派独立每个名字绑定独立 AgentRunner,后续复用
通信返回最终结果通过持久 Mailbox 双向发送文本消息
公开工具Lead 的 taskLead 的 spawn_teammate、send_message
状态运行或结束running、idle、failed、shutdown
2 · 为什么不实现 MessageBus + read_text/unlink

持续存在不能只靠进程内 dict。如果消息读取后直接删除,Agent 在「读到消息」和「写入历史」之间崩溃,工作就消失了。多个 writer 追加同一 .jsonl 还要解决并发写、部分行和读取游标。

崩溃窗口 read→处理→写history→unlink,任一步崩溃都会:要么消息丢失(读了就删),要么重复处理(没删就崩溃重启又读)。每消息一文件 + 状态机解决这个。
3 · 每消息一文件 + 状态机

FileMailboxStore:每消息一个文件,状态机 ready → processing → done/quarantine

  • ready:消息已持久写入,等待队友读取。
  • processing:队友已读取正在处理(原子改状态,崩溃后重启能看到 processing 重新决策)。
  • done:处理完成,消息已写入队友历史。
  • quarantine:处理异常,隔离不丢,供排查。

不读就删:只有 done/quarantine 后才清理。崩溃窗口靠状态机收窄——processing 状态明确表示「正在处理」。

4 · 两个 Lead 工具
工具必填字段可观察结果
spawn_teammatename、role、prompt创建独立 Runner,把首个 task 消息持久写入队友 Mailbox,异步启动 worker
send_messageto、content把 message 持久写入 Lead 或已存在队友的 Mailbox

Teammate 状态:running(工作中)、idle(完成一轮等消息)、failed(异常)、shutdown(优雅关闭)。idle 时新消息进入 Mailbox 会唤醒 worker 继续。idle 是被动的:它只表示 Runner 还留着、等下一条显式消息,不会自己找活干——空闲自驱认领是第 17 章的事。

6 · claim 租约与至少一次投递

claim 把消息从 ready 原子搬到 processing,等于取得租约:未确认前不会被别人拿走。双锁保护:进程内 promise 队列 + 跨进程 proper-lockfile,12 个并发 claim 拿到 12 条互不重复的消息。权威 FIFO 键是 (created_at_utc, id),不是文件名顺序。ack = processing→done;release = 退回 ready 保留重放;坏消息进 quarantine 留证据不堵队列。

投递语义是至少一次:崩溃可能重放(重启把 processing 恢复为 ready),但不会静默丢;「读+删」的替代方案是至多一次,有永久丢失窗口。重复由稳定 ID 去重。队友执行失败:输入隔离入 quarantine、状态改 failed,并向 Lead 发布可观察的失败 result。关闭顺序 Teammate → Cron → supervisor → model,单个资源关闭失败不跳过其余。

5 · 四个边界 + 回到公共 Loop
Lead 工具:spawn_teammate / send_message
    ↓
FileMailboxStore
  每消息一个文件,ready→processing→done/quarantine
    ↓
TeammateRuntime
  持有队友 Runner、状态和 worker,组合 Mailbox/Cron event
    ↓
AgentRunner.runEvents()
  把 typed message 注入 canonical history,再执行 event-only turn

Mailbox 消息通过 typed event 通道回到公共 Agent Loop:TeammateRuntime 把 ready 消息转 typed message event → EventInbox → AgentRunner.runEvents() 注入 canonical history → event-only turn。工具仍经过 Hook 与权限。不创建第二套 Loop 或第二个 EventInbox。

先入 history,再 ack:ack 失败要回滚弹出刚追加的 history,顺序反了崩在中间就永久丢工作——宁可重复不可丢失。mailbox 是唯一保留半状态(已进 history、ack 未完成)的事件类型,ack 失败重试不重复追加。sender 不是工具输入字段,由 ToolContext 注入可信身份;idempotencyKey 就是消息 UUID。

▶️ 交互演示:Lead ↔ Teammate Mailbox

点按钮看 spawn/send 消息如何在两个 Mailbox 间流转,状态从 ready→processing→done。注意崩溃窗口被状态机收窄。

🧑 Lead Mailbox

🧒 writer Mailbox

🔑 一句话总结

每消息一文件 + ready→processing→done 状态机 = 持久投递不丢不重,崩溃窗口收窄

QA 测验