连载11- Claude code 的 Agent Teams——当子 Agent 开始互相说话

Agent Teams------当子 Agent 开始互相说话

AI Coding 系列第 11 篇 · 多 Agent 编排


上一篇文章我们说清楚了一件事:Sub-agent 是 Claude Code 里唯一一个"执行完即丢弃"的东西------独立上下文窗口,噪声进去,结论出来,主对话永远看不到中间过程。但 Sub-agent 有一个硬约束:子代理只能向主对话汇报,不能互相通信。 这篇文章讲的就是打破这个约束的东西------Agent Teams。

注意 :Agent Teams 是 Claude Code 的实验性功能,默认关闭。需要在 ~/.claude/settings.json 中设置 CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1 才能启用。生产环境请谨慎评估------每个 teammate 是独立的 Claude 进程,token 消耗远高于单会话。本文第六节有完整的使用指南,包含常见故障的排查方法。


一、问题在哪:从一次失败的 Bug 排查说起

用户报告三个症状:会话偶尔丢失、API 时快时慢、订单数据偶尔串号。你用三个 Sub-agent 分别调查 session 中间件、数据库连接池、缓存层------三个报告都回来了,结论却让人抓狂:每个单独看都没问题。

但根因不是三个独立的 bug------是级联故障链。订单查询有个 N+1 问题,高峰期吃满了连接池;连接池一满,Redis session 写入开始静默失败;session 一丢,缓存层竞态条件把用户 A 的数据写进了用户 B 的缓存 key。三个调查各自独立,谁也看不到别人的证据链,自然拼不出这条线。

Sub-agent 缺的不是能力,是一个根本性的东西------Agent 之间的通信通道。 这个通道打开之后会发生什么?下一节展开。


二、从通信拓扑到消息落地------Agent Teams 的骨架与血脉

星形 vs 网状

Sub-agent 的通信拓扑是星形:主 Agent 在中间,子代理像卫星绕行,只和中心通信,子代理之间沉默如谜。Agent Teams 的通信拓扑是网状:Team Lead 只是发起人,所有 Teammate 之间可以直接对话------Researcher 发现疑点直接告诉 Implementer,不用绕道 Leader。

区分这两种拓扑的经验法则:问自己"这些 Workers 需要互相通信吗?"不需要 → Sub-agent。需要 → Agent Teams。

理解了拓扑差异,下一个问题是:这些网状通信具体是怎么落地的?

四种投递路径

SendMessageTool 是一个看似简单但内部做了四层分支路由的模块------它的核心逻辑判断约 200 行,其余是四条路径的实现细节。当一个 Agent 调用 SendMessage 时,系统根据接收者当前状态自动选择最优路径:

  • 同进程活跃 → 内存队列。 消息直接追加到 pending 队列,零磁盘 I/O,下一轮对话自然呈现。
  • 已停止 → 自动唤醒。 不只投递,还调用 resumeAgentBackground 恢复 Agent。消息变成新 prompt,Agent 继承上下文继续干活。
  • 独立 tmux 面板 → Mailbox 文件投递。 消息写入 ~/.claude/mailboxes/{team-name}/{agent-name}/inbox.json,接收方 inbox poller 定期扫描。
  • 跨机器 → Bridge/UDS。 走 Anthropic 服务器中转或 Unix Socket 直连。硬编码安全约束:classifierApprovable: false,跨机器消息永不自动通过。

Mailbox:用文件系统做消息总线

Mailbox 是消息系统中设计密度最高的组件------它用文件系统替代了消息队列。不需要 Redis、不需要 RabbitMQ。文件系统自带分布式系统里最难解决的三个属性:天然并发写入(操作系统管)、天然持久化(不删就在)、天然可调试(cat 就能看)。对于 Agent Teams 这种团队规模最多几十人的实验性功能,引入消息中间件是过度设计。

广播实现同样克制------遍历 config.json 成员列表,跳过自己,逐个写邮箱。没有 pub-sub,没有 fanout。设计者判断广播是低频操作,"为低频操作保持简单"本身就是一个有效的工程决策。

消息怎么流转的讲完了。但通信的前提是------团队本身得先"存在"。这个"存在"在代码和磁盘上究竟长什么样?


三、团队是建在磁盘上的

Agent Teams 在源码里是通过 TeamCreateTool 创建的。但理解它的最好方式不是读工具调用流程,而是看它最终在磁盘上留下的东西:

bash 复制代码
~/.claude/
├── teams/{team-name}/
│   └── config.json          ← 团队的花名册
├── tasks/{team-name}/        ← 共享任务列表
└── mailboxes/{team-name}/    ← 消息邮箱
    ├── team-lead/inbox.json
    ├── researcher/inbox.json
    └── implementer/inbox.json

config.json 是整个团队的共享真相来源。打开它能看到:团队名、创建者、每个队员的名字/ID/模型/工作目录、谁正在干活(isActive)、Leader 批了哪些免审批路径(teamAllowedPaths)。

几个值得关注的源码设计:

Leader 的 ID 是确定性的。 格式为 team-lead@团队名,不是随机 UUID。知道团队名就能推导出 Leader 的身份,不需要中心化注册表或服务发现。

Leader 刻意不是"teammate"。 源码里 isTeammate() 对 Leader 返回 false------Leader 不用轮询邮箱、不用响应关机请求、不用在停止时发空闲通知。代码层面做了角色隔离。

一个 Leader 只能管一个团队。 不是技术做不到,是刻意为之的约束------每个会话只有一个 teamContext,多团队会导致消息路由状态爆炸。

团队建好了,消息能流转了。但一个团队不可能永远活着------怎么优雅地结束它?


四、优雅关闭:关机是一个协议握手

Agent Teams 的关闭不是 kill------是一个两阶段提交协议。

第一阶段:请求。 Leader 判断任务完成后,给每个 Teammate 发 shutdown_request,附带唯一 requestId。

第二阶段:响应。 队友收到后有两个选择:

  • 批准------发 shutdown_response(approve: true)。in-process 模式下 abort 自己的 AbortController,tmux 模式下调用 gracefulShutdown(0)(退出码 0 表示正常主动退出)。
  • 拒绝------必须附带理由。源码里明确校验:approve: false 但没有 reason → 直接返回错误。没有理由的拒绝是沉默,Leader 无法区分"队友卡住了"和"队友还在干活"。

只有所有 Teammate 都批准了,Leader 才调用 TeamDelete 清理资源。有一个人拒绝,Leader 就等。

队友离开时不拖累全局

每个 Teammate 启动时注册一个 Stop Hook。停止时自动做两件事:在 config.json 里把 isActive 标为 false;给 Leader 发一条空闲通知。Hook 有 10 秒超时,执行不阻塞 Stop 流程------一个组件的退出不应该依赖另一个组件的可用性。

协议层面的机制讲完了。现在看这些机制在实际终端里怎么用。


五、在 Claude Code CLI 中实际使用

本节假设你已启用 Agent Teams。如果还没启用,往回翻到文章开头,那里有启用步骤。

启用

~/.claude/settings.json 或项目的 .claude/settings.json 中添加:

json 复制代码
{ "env": { "CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "1" } }

重启 Claude Code。确认生效的方式:终端输入"创建一个 agent team 测试",如果 Claude 回复"Agent Teams 未启用"则说明环境变量没生效,检查 settings.json 路径和 JSON 格式。

创建团队

用自然语言告诉 Claude 建团队,说清楚团队名、几个 teammate、各自负责什么:

复制代码
创建一个 agent team 帮我调查这个 bug。
1 个 teammate 查 session 层,1 个查数据库层,1 个查缓存层。
每个 teammate 只读分析自己的模块,发现疑点后发消息告诉其他 teammate。
最后 Leader 汇总所有发现,给我一份完整调查结论。

Claude 内部生成 TeamCreate 工具调用,然后逐个用 Agent 创建 teammate。你看到的是自然语言交互,看不到底层调用。

观察和管理

bash 复制代码
# 查看团队状态
哪些队友完成了任务?还有谁在工作中?

# 给特定队友发消息
告诉 researcher 我怀疑 middleware/session.js:42 有竞态条件,让它重点看看

# 给所有队友广播
所有人暂停手上的工作,先汇报目前的发现

解散团队

复制代码
让所有 teammate 汇报最终发现,然后解散团队

Leader 逐个发 shutdown_request,等所有人批准后清理目录。如果有人拒绝(带理由,如"还有一个文件没看完"),Leader 等待它完成。

常见故障排查

队友没收到消息。 两步确诊:先看 ~/.claude/mailboxes/{team-name}/{teammate-name}/inbox.json 有没有被写入新的消息条目。有消息但 teammate 没反应 → inbox poller 可能没正常工作,重启 teammate。没有消息 → 检查你发的 teammate 名字是否和 config.json 里的一致。

团队跑一半卡住了。 最常见的原因是异步 teammate 被权限弹窗阻塞。后台 teammate 设了 shouldAvoidPermissionPrompts: true,遇到需要确认的操作会被静默拒绝,Agent 不理解"被拒绝"和"操作失败"的区别,反复重试。解决:给异步 teammate 配 permissionMode: 'dontAsk' 加明确 allowedTools,或者让它在前台运行以便处理弹窗。

解散后残留文件。 正常通过 shutdown 协议解散的团队会自动清理。如果是 Ctrl+C 强制退出 Leader,~/.claude/teams/{team-name}/~/.claude/tasks/{team-name}/ 可能残留。手动删除这些目录即可。注意先确认所有 teammate 进程已退出。

四条实战建议

  1. 给 teammate 充足的上下文。 Teammate 不继承 Leader 的对话历史------它在自己的上下文窗口里从零开始。创建时把需要的信息写进 prompt。

  2. 避免文件冲突。 两个 teammate 改同一个文件会覆盖。拆分任务时确保各自负责不同的文件集。

  3. 从只读任务开始。 审查、搜索、调研先跑通,再上改代码的任务。

  4. 不要让团队跑太久没人管。 发现 Leader 在 teammate 完成前自己就开始下一步时,直接说"等你的 teammate 都完成后再继续"。


六、三种值得注意的设计取舍

为什么 Mailbox 用文件系统而不用消息队列? 消息队列更"专业",但引入外部依赖、需要运维、增加调试难度。文件系统是任何系统的默认基础设施,零运维成本。对于一个实验性功能,这个选择务实而非简陋。

为什么 Leader 能直接写代码而 Coordinator 模式不能? Coordinator 认为管和干要分离。Swarm 的 Team Lead 既是队友又是组织者------管也可以干。两种不同的编排哲学,适用于不同的信任模型。

为什么拒绝关机必须有理由? 在分布式系统里,不可观测的状态是最危险的。沉默拒绝让 Leader 无法判断队友状态。强制理由把每次拒绝变成有效通信。


七、已知限制

Agent Teams 存在以下已知限制,生产环境需要评估:

  • 不支持 Agent 嵌套。 Teammate 不能再创建自己的子 Agent。多层编排由 Leader 负责。
  • Leader 掉线后 teammates 变孤儿。 如果 Leader 进程被 kill,teammate 进程不会自动退出,且失去协调者。需手动清理。
  • 不支持跨团队通信。 一个团队的成员不能给另一个团队的成员发消息。
  • Token 成本高。 每个 teammate 是独立的 Claude 进程,有独立上下文窗口。多 teammate 场景的 token 消耗是单会话的数倍。
  • 是实验性功能。 API 和行为可能在后续版本中变化。

本讲小结

Agent Teams 的核心设计哲学:

  1. 通信拓扑决定协作上限。 星形适合分治,网状适合涌现。
  2. 简单基础设施胜过复杂中间件。 Mailbox 用文件系统------判断是"这对当前规模已经足够好"。
  3. 优雅关闭是分布式系统的试金石。 两阶段协议、拒绝必须有理由、超时不阻塞退出。
核心机制 一句话理解
团队创建 config.json 是磁盘上的"员工花名册"
消息路由 根据目标状态自动选最快投递方式
Mailbox 用文件系统做消息总线
Shutdown 协议 两阶段握手,拒绝必须有理由
Stop Hook 队友退出时自动通知 Leader

思考题

  1. 你的项目中是否有"独立调查各模块都正常,合在一起才暴露根因"的级联故障?Agent Teams 的网状通信能带来什么 Sub-agent 做不到的排查效果?

  2. Mailbox 用文件系统做消息存储,如果有 10+ 个 teammate 同时给 Leader 发消息,潜在的并发问题是什么?你有什么防护思路?

  3. 如果把 Agent Teams 的两阶段 shutdown 协议和数据库事务的两阶段提交做对比,它们在"一致性"上的要求有什么本质不同?


本篇实践任务

任务 1:跑一次多视角排查。 找一个有三个以上模块的项目,创建一个 3 人 Agent Team 做代码质量审查。一个查安全,一个查性能,一个查可维护性。对比:如果只用三个 Sub-agent 并行审查,结果有什么不同?

任务 2:测试 shutdown 拒绝机制。 在排查过程中,当 Leader 要解散时,手工给某个 teammate 追加任务,观察它是否会拒绝 shutdown。如果拒绝------它附带理由了吗?Leader 等了多久?

任务 3:对比 token 成本。 同一个中等复杂度任务,分别用 Agent Teams 和 Sub-agent 模式各跑一次。记录 token 消耗,计算倍数。这个倍数在你的场景里值得吗?


下篇预告

第 11 篇:MCP 实战------什么时候该用,什么时候不该用

Agent Teams 讲完,多 Agent 编排主题就完整了。下一篇换赛道------MCP。Anthropic 最近的态度变了:能用 CLI 直接解决的,不一定要走 MCP。理解这个转变背后的工程原因,比学怎么配置 MCP server 更重要。下篇拆解。


AI Coding 系列持续更新。Agent Teams 不是让 AI 做更多事,是让 AI 之间能互相说话------这个通信通道打开之后,能做的事比 Sub-agent 多一个维度。

相关推荐
@我们的天空6 小时前
Claude Code + GLM-5 深度赋能测试:开发 8 大 Skill 构建 AI 测试助手集群
人工智能·python·测试工具·自动化·ai编程
unique6 小时前
QwenPaw调研分析
ai编程
潍坊老登6 小时前
关于 number类型从vue端传到golang后端是float而不是int的事
前端
茶底世界之下6 小时前
你的 Mac 里,藏着一支 AI 开发团队
前端·javascript
折哥的程序人生 · 物流技术专研6 小时前
《Java 100 天进阶之路》第35篇:Java异常处理最佳实践
java·开发语言·后端·面试·求职招聘
程序员老刘6 小时前
Dart 3.12 更新要点:乏善可陈
flutter·ai编程·dart
用户8356290780516 小时前
使用 Python 创建 PowerPoint SmartArt 图形
后端·python
DigitalOcean6 小时前
为AI编程降本!OpenCode 原生支持 DigitalOcean 推理路由器
ai编程·claude
不爱说话郭德纲6 小时前
出门在外收到任务,我用 TRAE SOLO 把电脑“叫醒”干活
前端·ai编程