模块九:多 Agent 编排 | 前置依赖:第 25-26 课 | 预计学习时间:75 分钟
学习目标
完成本课后,你将能够:
- 解释 Coordinator 模式的启用条件和运行时检测机制
- 描述 4 阶段工作流(Research-Synthesis-Implementation-Verification)的设计原理
- 分析
<task-notification>XML 通信协议的结构与用途 - 理解 Agent Teams/Swarm 架构中 TeamCreateTool 和 TeamDeleteTool 的生命周期管理
- 区分 Coordinator 模式与 Swarm 模式在架构上的关键差异
27.1 Coordinator 模式概述
Claude Code 的 Agent 编排存在两条路径:
┌─────────────────────────────────────────────────────────┐
│ 多 Agent 编排 │
│ │
│ ┌─────────────────┐ ┌─────────────────────────┐ │
│ │ Coordinator 模式 │ │ Swarm/Teams 模式 │ │
│ │ (单进程内编排) │ │ (多终端窗格/多进程) │ │
│ │ │ │ │ │
│ │ coordinator/ │ │ utils/swarm/ │ │
│ │ coordinatorMode │ │ TeamCreateTool │ │
│ │ .ts │ │ TeamDeleteTool │ │
│ └─────────────────┘ └─────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
Coordinator 模式是一种纯粹的"中心化编排"方案:一个 Coordinator Agent 充当调度者,通过 AgentTool 派生多个 Worker,Worker 的结果以 <task-notification> 消息的形式回传。Coordinator 不直接操作文件系统,它的超能力是并行派发和综合决策。
Swarm/Teams 模式则是"去中心化"方案:每个 Teammate 运行在独立的终端窗格(tmux/iTerm2)或独立进程中,通过共享的 Team File 协调。
27.2 coordinatorMode.ts 核心函数
isCoordinatorMode()
export function isCoordinatorMode(): boolean {
if (feature('COORDINATOR_MODE')) {
return isEnvTruthy(process.env.CLAUDE_CODE_COORDINATOR_MODE)
}
return false
}
启用条件有两道门:
- 编译时门 —
feature('COORDINATOR_MODE')必须为 true(内部构建) - 运行时门 — 环境变量
CLAUDE_CODE_COORDINATOR_MODE必须为 truthy
这是标准的"正面三元模式"(Positive Ternary Pattern),确保外部构建中 GrowthBook 字符串字面量被完全 DCE 消除。
matchSessionMode()
当恢复(resume)一个会话时,会话可能是在 Coordinator 模式下创建的,但当前进程不在 Coordinator 模式下(或反过来)。matchSessionMode 负责对齐:
export function matchSessionMode(
sessionMode: 'coordinator' | 'normal' | undefined,
): string | undefined {
// 旧会话没有存储模式 → 不操作
if (!sessionMode) return undefined
const currentIsCoordinator = isCoordinatorMode()
const sessionIsCoordinator = sessionMode === 'coordinator'
if (currentIsCoordinator === sessionIsCoordinator) return undefined
// 翻转环境变量 — isCoordinatorMode() 实时读取,无缓存
if (sessionIsCoordinator) {
process.env.CLAUDE_CODE_COORDINATOR_MODE = '1'
} else {
delete process.env.CLAUDE_CODE_COORDINATOR_MODE
}
// 返回切换提示信息
}
关键设计:环境变量是活绑定,修改后 isCoordinatorMode() 立即生效,无需重启。
getCoordinatorUserContext()
此函数为 Coordinator 生成运行时上下文,告诉它 Worker 能访问哪些工具:
export function getCoordinatorUserContext(
mcpClients: ReadonlyArray<{ name: string }>,
scratchpadDir?: string,
): { [k: string]: string } {
if (!isCoordinatorMode()) return {}
// 简单模式 vs 完整模式
const workerTools = isEnvTruthy(process.env.CLAUDE_CODE_SIMPLE)
? [BASH_TOOL_NAME, FILE_READ_TOOL_NAME, FILE_EDIT_TOOL_NAME]
: Array.from(ASYNC_AGENT_ALLOWED_TOOLS)
.filter(name => !INTERNAL_WORKER_TOOLS.has(name))
.sort()
.join(', ')
let content = `Workers spawned via the ${AGENT_TOOL_NAME} tool have access to these tools: ${workerTools}`
// MCP 工具
if (mcpClients.length > 0) {
content += `\n\nWorkers also have access to MCP tools from connected MCP servers: ${serverNames}`
}
// Scratchpad 目录(tengu_scratch 门控)
if (scratchpadDir && isScratchpadGateEnabled()) {
content += `\n\nScratchpad directory: ${scratchpadDir}`
}
return { workerToolsContext: content }
}
INTERNAL_WORKER_TOOLS 是一个过滤集合,包含 TeamCreate、TeamDelete、SendMessage、SyntheticOutput — 这些工具是 Coordinator 专用的,Worker 不应该看到它们。
getCoordinatorSystemPrompt()
这是 Coordinator 的完整系统提示词,长度约 370 行。它定义了 Coordinator 的整个行为规范。
27.3 四阶段工作流
系统提示词中定义了一个标准的任务分解流程:
┌──────────────────────────────────────────────────────────────────┐
│ Coordinator 四阶段工作流 │
│ │
│ Phase 1: Research Phase 2: Synthesis │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Worker A │ ──→ │ Coordinator │ │
│ │ (调查代码库) │ │ (阅读发现, │ │
│ ├─────────────────┤ │ 理解问题, │ │
│ │ Worker B │ ──→ │ 编写实现规格) │ │
│ │ (研究测试) │ └────────┬────────┘ │
│ └─────────────────┘ │ │
│ ▼ │
│ Phase 3: Implementation Phase 4: Verification │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Worker C │ ──→ │ Worker D │ │
│ │ (按规格修改代码) │ │ (运行测试, │ │
│ │ │ │ 类型检查, │ │
│ │ │ │ 验证边界用例) │ │
│ └─────────────────┘ └─────────────────┘ │
└──────────────────────────────────────────────────────────────────┘
阶段详解
| 阶段 | 执行者 | 目的 | 并发策略 |
|---|---|---|---|
| Research | Workers(并行) | 调查代码库、查找文件、理解问题 | 自由并行 |
| Synthesis | Coordinator | 阅读发现、理解问题、编写实现规格 | 单点决策 |
| Implementation | Workers | 按规格进行代码修改、提交 | 每组文件一个 Worker |
| Verification | Workers | 测试、类型检查、验证 | 可与不同文件区域的实现并行 |
关键设计原则
"Parallelism is your superpower" — 系统提示词的核心理念。Coordinator 被反复提醒要最大化并行度:
Parallelism is your superpower. Workers are async. Launch independent
workers concurrently whenever possible — don't serialize work that can
run simultaneously and look for opportunities to fan out.
"Don't say based on your findings" — Synthesis 阶段的核心戒律。Coordinator 必须自己消化 Worker 的研究成果,然后用具体的文件路径、行号和修改内容编写 spec:
// 反模式 — 懒惰委托(BAD)
AgentTool({ prompt: "Based on your findings, fix the auth bug" })
// 正确 — 综合后的精确规格(GOOD)
AgentTool({
prompt: "Fix the null pointer in src/auth/validate.ts:42. " +
"The user field on Session is undefined when sessions expire " +
"but the token remains cached. Add a null check before user.id " +
"access — if null, return 401 with 'Session expired'."
})
Continue vs. Spawn 决策矩阵:
上下文重叠度
高 低
┌───────────┬───────────┐
上次结果 │ Continue │ Spawn │
成功 │ (复用上下文) │ (干净起点) │
├───────────┼───────────┤
上次结果 │ Continue │ Spawn │
失败 │ (有错误上下文)│ (避免锚定) │
└───────────┴───────────┘
27.4 task-notification XML 通信协议
Worker 完成任务后,结果以 <task-notification> XML 格式注入到 Coordinator 的消息流中:
<task-notification>
<task-id>agent-a1b</task-id>
<status>completed|failed|killed</status>
<summary>Agent "Investigate auth bug" completed</summary>
<result>Found null pointer in src/auth/validate.ts:42...</result>
<usage>
<total_tokens>15234</total_tokens>
<tool_uses>8</tool_uses>
<duration_ms>12500</duration_ms>
</usage>
</task-notification>
协议设计要点
- 以 user-role 消息到达 — 看起来像用户消息,但可以通过
<task-notification>开头标签区分 - result 和 usage 是可选的 — 失败或被终止的任务可能没有结果
- task-id 是 agent ID — 可用于
SendMessageTool继续该 Worker 的对话 - 三种状态 — completed(成功)、failed(失败)、killed(被停止)
消息流时序
Coordinator: "Let me investigate."
→ AgentTool({ description: "Investigate", prompt: "..." })
→ AgentTool({ description: "Research", prompt: "..." })
"Investigating in parallel."
[Worker A 完成]
User(实际是系统): <task-notification>...<status>completed</status>...</task-notification>
Coordinator: "Found the bug — null pointer at line 42."
→ SendMessageTool({ to: "agent-a1b", message: "Fix it..." })
"Fix is in progress."
[Worker B 完成]
User(实际是系统): <task-notification>...<status>completed</status>...</task-notification>
Coordinator: "Research confirms test gap. Spawning verifier..."
27.5 Scratchpad — 跨 Worker 持久化知识
当 tengu_scratch Feature Gate 开启时,系统会为 Coordinator 提供一个 scratchpad 目录:
if (scratchpadDir && isScratchpadGateEnabled()) {
content += `\n\nScratchpad directory: ${scratchpadDir}\n` +
`Workers can read and write here without permission prompts. ` +
`Use this for durable cross-worker knowledge — structure files ` +
`however fits the work.`
}
Scratchpad 解决了一个关键问题:Worker 之间默认是隔离的(没有共享上下文),Scratchpad 提供了一个无需权限提示的共享文件系统区域,Worker 可以在其中写入中间产物、研究笔记、代码片段等,供后续 Worker 读取。
Worker A (Research) ─── 写入 ──→ scratchpad/findings.md
Worker B (Research) ─── 写入 ──→ scratchpad/test-gaps.md
│
Coordinator (Synthesis) ←── 读取 ────────┘
│
Worker C (Implement) ←── 读取 ──── scratchpad/spec.md ←── Coordinator 写入
27.6 Agent Teams/Swarm 架构
与 Coordinator 模式的"单进程内编排"不同,Teams/Swarm 模式使用真正的多进程/多终端架构。
后端检测与选择
┌──────────────────────────────────────────────────────┐
│ Swarm Backend Registry │
│ │
│ 检测优先级: │
│ 1. iTerm2 (在 iTerm2 内 + it2 CLI 可用) │
│ 2. tmux (在 tmux 内或 tmux 可用) │
│ 3. In-Process (后备方案) │
│ │
│ tengu_amber_flint Feature Gate 控制总开关 │
│ │
│ backends/ │
│ ├── types.ts ← PaneBackend 接口 │
│ ├── detection.ts ← 环境检测 │
│ ├── registry.ts ← 后端注册与选择 │
│ ├── TmuxBackend.ts ← tmux 窗格管理 │
│ ├── ITermBackend.ts ← iTerm2 窗格管理 │
│ └── InProcessBackend.ts ← 进程内隔离执行 │
└──────────────────────────────────────────────────────┘
三种后端类型:
| 后端 | 运行方式 | 优势 | 限制 |
|---|---|---|---|
| tmux | 独立 tmux 窗格 | 广泛可用,可分屏 | 需要 tmux |
| iterm2 | iTerm2 原生分屏 | 原生体验,无需 tmux | 仅 macOS + iTerm2 |
| in-process | 同进程 AsyncLocalStorage 隔离 | 无外部依赖 | 共享进程资源 |
TeamCreateTool — 创建团队
export const TeamCreateTool: Tool<InputSchema, Output> = buildTool({
name: TEAM_CREATE_TOOL_NAME,
isEnabled() {
return isAgentSwarmsEnabled() // tengu_amber_flint 门控
},
async call(input, context) {
// 1. 检查是否已有团队(一个 leader 只能管理一个团队)
if (existingTeam) {
throw new Error(`Already leading team "${existingTeam}".`)
}
// 2. 生成唯一团队名(如果冲突则用 wordSlug)
const finalTeamName = generateUniqueTeamName(team_name)
// 3. 创建确定性的 leader agent ID
const leadAgentId = formatAgentId(TEAM_LEAD_NAME, finalTeamName)
// 4. 构建 TeamFile 并写入磁盘
const teamFile: TeamFile = {
name: finalTeamName,
leadAgentId,
leadSessionId: getSessionId(),
members: [{ agentId: leadAgentId, name: TEAM_LEAD_NAME, ... }],
}
await writeTeamFileAsync(finalTeamName, teamFile)
// 5. 注册清理(会话结束时自动清理)
registerTeamForSessionCleanup(finalTeamName)
// 6. 重置任务列表目录
await resetTaskList(taskListId)
await ensureTasksDir(taskListId)
// 7. 更新 AppState
setAppState(prev => ({
...prev,
teamContext: {
teamName: finalTeamName,
teamFilePath,
leadAgentId,
teammates: { [leadAgentId]: { ... } },
},
}))
return { data: { team_name, team_file_path, lead_agent_id } }
},
})
注意 Leader 不设置 CLAUDE_CODE_AGENT_ID 环境变量 — 这是故意的,因为 Leader 不是 "teammate",isTeammate() 对它应该返回 false。
TeamDeleteTool — 解散团队
async call(_input, context) {
// 1. 检查是否有活跃成员(不能在有人工作时解散)
const activeMembers = nonLeadMembers.filter(m => m.isActive !== false)
if (activeMembers.length > 0) {
return { success: false, message: `Cannot cleanup team with active members` }
}
// 2. 清理目录和工作树
await cleanupTeamDirectories(teamName)
unregisterTeamForSessionCleanup(teamName)
// 3. 清除颜色分配和 leader 标记
clearTeammateColors()
clearLeaderTeamName()
// 4. 清除 AppState
setAppState(prev => ({
...prev,
teamContext: undefined,
inbox: { messages: [] },
}))
}
安全设计:必须先用 requestShutdown 优雅终止所有活跃成员,才能 TeamDelete。
27.7 Coordinator vs. Swarm 对比
| 维度 | Coordinator 模式 | Swarm/Teams 模式 |
|---|---|---|
| 进程模型 | 单进程,Worker 是子 Agent | 多进程/多终端窗格 |
| 通信方式 | <task-notification> XML | TeamFile + Inbox 消息 |
| 可视化 | 统一对话流 | 分屏终端(tmux/iTerm2) |
| 启用条件 | COORDINATOR_MODE Feature + 环境变量 | tengu_amber_flint Feature Gate |
| Worker 工具集 | ASYNC_AGENT_ALLOWED_TOOLS | 完整工具集 |
| 持久化 | Scratchpad(tengu_scratch) | 共享文件系统 + TeamFile |
| 适用场景 | API/非交互式场景 | 交互式终端 |
| 编排风格 | 中心化(一个 Coordinator 决策) | 去中心化(Leader + Teammates) |
27.8 Coordinator 工具集
Coordinator 拥有的专属工具:
┌──────────────────────────────────────────────────────────┐
│ Coordinator 专属工具 │
│ │
│ AgentTool ← 派生新 Worker │
│ SendMessageTool ← 向已有 Worker 发送后续消息 │
│ TaskStopTool ← 停止一个运行中的 Worker │
│ SyntheticOutputTool ← 生成合成输出(内部) │
│ │
│ Worker 看不到的工具(INTERNAL_WORKER_TOOLS): │
│ - TeamCreateTool │
│ - TeamDeleteTool │
│ - SendMessageTool │
│ - SyntheticOutputTool │
└──────────────────────────────────────────────────────────┘
Worker 的工具集由 ASYNC_AGENT_ALLOWED_TOOLS 定义(Bash、Read、Edit 等标准工具),但排除了 INTERNAL_WORKER_TOOLS 中的编排工具。这确保了 Worker 不能"越级"创建自己的团队或直接与其他 Worker 通信。
课后练习
练习 1:模式切换分析
阅读 matchSessionMode 函数,回答:如果一个会话是在 Coordinator 模式下创建的,但当前进程没有启用 Coordinator 模式,恢复会话时会发生什么?为什么选择修改环境变量而不是 AppState 来切换模式?
练习 2:提示词设计
基于第 27.3 节的四阶段工作流,为以下场景设计 Coordinator 的完整交互流程(包括每个阶段的 Worker 提示词):"用户报告登录页面在 Safari 上白屏,Chrome 正常"。
练习 3:并发安全
分析四阶段工作流中的并发控制策略:哪些阶段可以安全并行?如果两个 Implementation Worker 修改了同一个文件会怎样?系统如何防止这种情况?
练习 4:架构比较
画出 Coordinator 模式和 Swarm 模式的架构图,标注数据流方向。分析在以下场景中哪种模式更合适:(a) CI/CD 管道中的自动化代码审查, (b) 开发者在本地终端中实时协作开发, (c) 通过 API 调用进行批量代码迁移。
本课小结
| 要点 | 内容 |
|---|---|
| 启用机制 | 编译时 Feature Gate + 运行时环境变量双重门控 |
| 工作流 | Research → Synthesis → Implementation → Verification 四阶段 |
| 通信协议 | <task-notification> XML,以 user-role 消息到达 |
| 核心原则 | 并行是超能力;不要说"基于你的发现";自己消化再指挥 |
| Swarm 后端 | tmux / iTerm2 / in-process 三种,自动检测选择 |
| 生命周期 | TeamCreateTool 创建 → 工作 → TeamDeleteTool 清理 |
下一课预告
第 28 课:插件、技能与钩子体系 — 深入 Claude Code 的三大扩展机制:Plugin 系统(安装/卸载/启用/禁用)、Skill 系统(bundled/disk/MCP 三种来源)、Hook 系统(PreToolUse/PostToolUse/Stop 三种时机),以及它们如何协同工作。