第 27 课:Coordinator 模式 — 多 Worker 编排

5 阅读8分钟

模块九:多 Agent 编排 | 前置依赖:第 25-26 课 | 预计学习时间:75 分钟


学习目标

完成本课后,你将能够:

  1. 解释 Coordinator 模式的启用条件和运行时检测机制
  2. 描述 4 阶段工作流(Research-Synthesis-Implementation-Verification)的设计原理
  3. 分析 <task-notification> XML 通信协议的结构与用途
  4. 理解 Agent Teams/Swarm 架构中 TeamCreateTool 和 TeamDeleteTool 的生命周期管理
  5. 区分 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
}

启用条件有两道门

  1. 编译时门feature('COORDINATOR_MODE') 必须为 true(内部构建)
  2. 运行时门 — 环境变量 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                          
    (按规格修改代码)            (运行测试,                        
                                类型检查,                         
                                验证边界用例)                      
  └─────────────────┘        └─────────────────┘                  
└──────────────────────────────────────────────────────────────────┘

阶段详解

阶段执行者目的并发策略
ResearchWorkers(并行)调查代码库、查找文件、理解问题自由并行
SynthesisCoordinator阅读发现、理解问题、编写实现规格单点决策
ImplementationWorkers按规格进行代码修改、提交每组文件一个 Worker
VerificationWorkers测试、类型检查、验证可与不同文件区域的实现并行

关键设计原则

"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>

协议设计要点

  1. 以 user-role 消息到达 — 看起来像用户消息,但可以通过 <task-notification> 开头标签区分
  2. result 和 usage 是可选的 — 失败或被终止的任务可能没有结果
  3. task-id 是 agent ID — 可用于 SendMessageTool 继续该 Worker 的对话
  4. 三种状态 — 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
iterm2iTerm2 原生分屏原生体验,无需 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> XMLTeamFile + 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 三种时机),以及它们如何协同工作。