Skip to main content

多 Agent 协作:像公司一样运转

本节源码:src/utils/swarm/(多 Agent 框架,15 个文件)+ src/coordinator/coordinatorMode.ts(Coordinator 模式,369 行)

Claude Code 的多 Agent 系统可能是整个源码中最复杂的部分。它不只是 fork 几个进程并行跑,而是实现了一套完整的组织管理架构。这和一个真实公司的结构几乎一模一样——Leader 就是 tech lead,Teammate 就是开发者,Team 就是项目组。

组织结构

本节源码:src/utils/swarm/teamHelpers.ts(683 行的 Team 工具函数库)

源码的 src/utils/swarm/ 目录下实现了一个多 Agent 协作框架。核心组织模型是:

角色职责权限
Team一组 Agent 的容器,负责资源和目标管理最高
Leader分配任务、审批权限请求、合并结果
Teammate执行具体任务、报告进度受限

这和真实公司的结构几乎一模一样:Leader 就是 tech lead,Teammate 就是开发者,Team 就是项目组。

三种执行方式

本节源码:src/utils/swarm/backends/(4 个后端)+ src/utils/swarm/inProcessRunner.ts

Agent 可以通过三种方式并行运行:

同进程隔离——Agent 在同一个 Node.js 进程内运行,通过 AsyncLocalStorage(在 inProcessRunner.ts 第 5 行注释明确提到)实现上下文隔离。每个 Agent 有独立的 AbortController(Teammate 不会因 Leader 被中断而中断)、独立的状态存储、共享的 API 客户端和 MCP 连接。最轻量,零 spawn 开销,适合简单的并行搜索任务。

tmux 窗口——每个 Agent 在独立的 tmux 窗格中运行。可以看到各个 Agent 的实时输出。适合需要监控进度的场景

iTerm2 分割窗格——利用 iTerm2 的分屏能力,每个 Agent 在独立的窗格中运行。视觉效果最直观,但只在 macOS + iTerm2 环境下可用

为什么提供三种方式?因为不同的使用场景对可视性的需求不同。简单调研不需要看每个 Agent 的输出,但做复杂的多模块开发时,能同时看到所有 Agent 的进度很重要。

Git Worktree 隔离

本节源码:src/utils/swarm/teamHelpers.ts(worktree 管理代码)

这是多 Agent 架构中最关键的设计决策

当多个 Agent 同时修改代码时,最大的风险是冲突。Agent A 在改文件 X,Agent B 也在改文件 X,两个人互相覆盖。

Claude Code 的解决方案是 git worktree。每个 Agent 在自己的独立工作树中工作,有独立的文件系统副本。修改完成后,通过 git 的 merge 机制合并结果。

Git worktree 不是完整的 repo 克隆。它共享同一个 .git 目录,只是创建了一个新的工作目录和分支。创建速度快,磁盘开销小,适合短生命周期的 Agent 任务

.worktreeinclude 支持:Worktree 隔离有一个边界情况:某些文件被 gitignore 了(比如 .env 配置文件、本地密钥),但 Agent 工作时需要用到。源码实现了一个 .worktreeinclude 文件来解决这个问题——列出需要从主 repo 复制到 worktree 的 gitignored 文件。

如果 Agent 没有做任何更改,worktree 会自动清理。如果做了更改,worktree 的路径和分支名会返回给调用者,供后续合并。

邮箱通信

本节源码:src/utils/swarm/(邮箱文件系统,目录 permissions/permissionSync.ts:110 注释中定义)

Agent 之间怎么通信?不是通过 API 调用,不是通过消息队列。是通过邮箱文件

每个 Agent 有一个邮箱文件。其他 Agent 想给它发消息,就往这个文件里写。Agent 定期检查自己的邮箱,读取新消息。

这又是一个「最简单方案」的例子。文件系统是最可靠的通信基础设施,不需要启动额外的服务,不会有连接超时,重启后消息还在。缺点是延迟高(定期轮询),但对于 Agent 级别的协作(分钟级的任务),几秒的延迟完全可以接受。

权限冒泡

本节源码:src/utils/swarm/leaderPermissionBridge.ts(权限冒泡逻辑)

当 Teammate 遇到需要确认的操作(比如删除文件),权限请求不会直接弹给用户。它会冒泡给 Leader。Leader 决定是否批准。

为什么不直接问用户?因为如果 5 个 Agent 同时工作,每个都弹权限确认窗口,用户会被淹没。Leader 作为中间层,可以根据任务上下文批量处理权限请求,减少对用户的打扰。

这和真实公司的审批流程一样。开发者不会每个操作都去找 CEO 确认,而是让 tech lead 代为判断。

Coordinator Mode

本节源码:src/coordinator/coordinatorMode.ts(369 行,4 阶段表在 line 204-209)

除了基本的 Team 模式,Claude Code 还实现了一个更高级的 Coordinator Mode。四个阶段:

阶段做什么
ResearchWorker(并行)调查代码库、找文件、理解问题
SynthesisCoordinator读发现、理解问题、撰写实现规范
ImplementationWorker按规范做精准修改、提交
VerificationWorker跑测试验证修改可用

源码在 coordinatorMode.ts:200-209 行明确定义了这个表。关键设计:Synthesis 阶段由 Coordinator 自己做,不要分发给 Worker。原因:Synthesis 是把分散信息聚合成可执行规范的工作,需要全局视角和判断力,这是 Leader 的核心能力。

Coordinator 的系统提示分析

本节源码:src/coordinator/coordinatorMode.ts:120("You are a coordinator. Your job is to:" 提示词)

Coordinator Mode 的系统提示本身就是一份极有价值的文档。它不只是告诉模型「你是什么」,而是建立了一整套行为规范。源码中定义的角色描述开门见山

You are a **coordinator**. Your job is to:
- Help the user achieve their goal
- Direct workers to research, implement and verify code changes
- Synthesize results and communicate with the user
- Answer questions directly when possible — don't delegate work that you can handle without tools

最后一条很关键:Coordinator 不应该把能自己做的事 delegate 给 Worker。这避免了过度委派的问题。如果用户问「这个函数是干什么的」,Coordinator 应该自己回答,而不是 spawn 一个 worker 去搜索。

系统提示中还有一条容易被忽略但极其重要的规则

Every message you send is to the user. Worker results and system notifications are internal signals, not conversation partners — never thank or acknowledge them. Summarize new information for the user as it arrives.

Workers 的返回结果以格式插入到对话中,看起来像用户消息,但它们不是。Coordinator 必须区分两者的真正含义:来自用户的真正消息需要回应,来自 Worker 的通知只是消化、整合,然后用自然语言向用户汇报

最后一个更深层的设计约束:Workers 看不到 Coordinator 和用户之间的对话。每个发给 Worker 的 prompt 必须是自包含的,包含所有需要的上下文、文件路径、行号和具体要求。这和很多人想象中的多 Agent 系统不同。Agent 之间共享一个对话上下文听起来方便,但实施起来既不灵活也不可控

自包含 prompt 更干净、更可控。虽然写起来啰嗦(每个 Worker 都要重复任务上下文),但避免了过度委派的问题。

权限同步的文件锁设计

本节源码:src/utils/swarm/permissionSync.ts(928 行)+ src/utils/lockfile.ts

权限冒泡的概念前面已经说过了,但具体实现值得再展开。源码中的 permissionSync.ts 实现了一套基于文件系统的权限请求队列。

目录结构如下(来自 permissionSync.ts:110 的注释):

~/.claude/teams/{teamName}/permissions/
├── pending/ # 待处理的权限请求
│ └── perm-1712345-abc.json # 格式:perm-{timestamp}-{hash}.json
├── .lock # 文件级锁
├── perm-1712345-abc.json | .lock # 文件级锁
├── resolved/ # 已处理的权限请求
│ ├── perm-1712345-abc.json # 格式:perm-{timestamp}-{hash}.json

流程是这样的:Worker 遇到需要权限的操作时,创建一个 SwarmPermissionRequest 对象(含 toolNameinputdescription 等完整信息),写入 pending/ 目录。Leader 定期扫描 pending/ 目录,发现新请求后展示给用户,用户做出决定,Leader 将请求移到 resolved/ 目录。Worker 轮询 resolved/ 目录,读取结果,继续执行。

已处理的请求不会无限积累cleanupOldResolutions() 函数会清理 resolved/ 目录中超过 1 小时的过期文件(maxAgeMs = 3600000)。

这是一个典型的工程权衡:保留太久浪费磁盘,删除太快可能还没读到结果。1 小时是一个合理的中间值。对于 Agent 级别的任务,1 小时内 Worker 一定已经读到了结果。

Team 的生命周期管理

本节源码:src/utils/swarm/teamHelpers.ts:576cleanupSessionTeams 函数)

Team 不是创建了就完,它的销毁同样需要精心设计。源码中 cleanupSessionTeams() 定义了一个严格的清理顺序

  1. Kill Panes——先杀死所有 pane-backed 的 teammate 进程。调用各 backend(tmux/iTerm2)的 killPane() 方法,确保没有独立进程继续运行。

  2. Destroy Worktrees——销毁所有成员的 git worktree。先读取 team 文件获取 worktree 路径列表,然后逐一调用 git worktree remove --force。如果 git 命令失败,fallback 到直接 rm -rf

  3. 清理 Team 目录——删除 ~/.claude/teams/{team-name}/ 目录及其所有内容(包括权限请求文件、Teams 配置等)。

  4. 清理 Tasks 目录——删除 ~/.claude/tasks/{taskListId}/ 目录,并触发 notifyTasksUpdated() 通知 UI 更新。

为什么顺序很重要?如果先删目录再杀进程,进程还在运行就找不到自己的配置文件了;先杀进程能确保没有独立进程继续运行。先删 worktree 再删目录,目录会保留对已删除 worktree 的引用。

源码还区分了两种清理场景

  • 正常删除(通过 TeamDeleteTool,此时 teammate 已经优雅退出)——无需 kill panes
  • 异常清理(Leader 进程被 SIGINT/SIGTERM 杀死)——需要强制 kill panes

清理顺序是防御性编程模式:即使 TeamDelete 没被调用,Team 资源也不会泄露。Team 的注册和注销通过 registerTeamForSessionCleanup()unregisterTeamForSessionCleanup() 管理。创建 Team 时注册,正常删除时注销(防止重复清理),Session 结束时批量清理所有注册但未注销的 Team。

Verification 阶段同样有严格要求:「Verification means proving the code works, not confirming it exists」。验证者必须实际运行测试、检查类型错误、测试边界情况。不能只是看一眼代码说「看起来没问题」

Continue vs Spawn 的决策矩阵

本节源码:src/coordinator/coordinatorMode.ts(4 阶段表 + 「verifier that rubber-stamps weak work undermines everything」注释)

Coordinator Mode 的 prompt 中详细定义了什么时候继续用同一个 Worker(SendMessage),什么时候 spawn 一个新的

场景选择原因
Research 探索了恰好要编辑的文件ContinueWorker 已有文件在 context 中
Research 很广但实现很窄Spawn fresh避免探索噪声干扰精准实现
第一次实现完全错误Spawn fresh错误方法的 context 会锚定重试
验证另一个 Worker 的代码Spawn fresh验证者应以新鲜眼光看代码

Workers 的返回结果以格式插入到对话中,看起来像用户消息,但它们不是。Coordinator 必须区分两者的真正含义:来自用户的真正消息需要回应,来自 Worker 的通知只是消化、整合,然后用自然语言向用户汇报

Workers 看不到 Coordinator 和用户之间的对话。每个发给 Worker 的 prompt 必须是自包含的,包含所有需要的上下文、文件路径、行号和具体要求。这和很多人想象中的多 Agent 系统不同——Agent 之间共享一个对话上下文听起来方便,但实施起来既不灵活也不可控自包含 prompt 更干净、更可控。虽然写起来啰嗦(每个 Worker 都要重复任务上下文),但避免了过度委派的问题。

这个系统能教我们什么

本节源码:src/utils/swarm/(整体设计)

多 Agent 系统的难点不在技术实现,而在组织设计。

任务怎么拆分、信息怎么流转、冲突怎么解决、结果怎么合并、质量怎么保证——这些问题和管理真人团队完全一样。Anthropic 显然把 Agent 系统当作一个公司来设计:

  • 隔离优于共享。Git worktree 让每个 Agent 在独立的文件系统中工作,从根本上消除了并发修改的冲突问题。代价是需要额外的合并步骤,但这比处理运行时冲突简单得多。
  • 通信用最简单的方案。邮箱文件比消息队列简单 100 倍,对于 Agent 级别的协作(分钟级的任务),几秒的延迟完全可以接受。不要在通信基础设施上过度投入
  • 需要中间管理层。Leader 角色不是多余的。它处理权限冒泡、监督任务进度、协调结果合并。没有这一层,5 个 Agent 直接与用户交互会混乱不堪。
  • 防御性编程模式。即使 TeamDelete 没被调用,Team 资源也不会泄露。异常清理是必须有的——真实环境中 SIGINT/SIGTERM 是常态。
  • 可观测性高于功能。每个阶段都有清晰的所有者(Research/Synthesis/Implementation/Verification 各司其职),每个任务都有清晰的状态。没有可观测性,复杂系统就是黑盒
  • 记录成功,不只记录失败。Verification 阶段不仅记录代码是否通过测试,还要记录「proving the code works, not confirming it exists」。这和记忆系统的设计原则一致——成功的执行模式值得被记住

按约定没跑 pnpm build,请 pnpm start 验证。