Coordinator-Worker 与 Context 继承:结构化协作的工程实现

《Claude Code 架构解密》精读笔记 · 第10篇 · 第6章后半(6.6-6.12)

上一篇我们拆解了 Fork-and-Delegate 的轻量并行分叉机制。当任务复杂度升级——“先调研、再实现、后验证”——Fork 的隐式编排就力不从心了。这时需要一个"指挥者"来显式调度工作流,这就是 Coordinator-Worker 模式的用武之地。本篇将从结构化编排、异步生命周期、上下文缓存共享三个维度,解析 Claude Code 如何把多 Agent 协作从"散兵游勇"升级为"正规军"。


一、导语:从游击队到正规军

Fork-and-Delegate 像游击战——每个子 Agent 继承完整上下文、独立执行、无需协调。但"对代码库做安全审计→修复高危漏洞→验证修复"这样的多阶段任务,需要严格的阶段控制和结果聚合。

核心矛盾:Fork 的"继承式共享"带来了缓存效率和上下文连贯性,但代价是缺乏编排结构;Coordinator 的"隔离式编排"带来了结构化控制和安全性,但代价是缓存失效和额外成本。

Claude Code 的解法不是二选一,而是两种模式互斥并存——根据任务特征选型,同一时刻只有一种模式激活。


二、架构图解:七模式协作全景

第6章的七个设计模式并非孤岛,它们形成了一张协作网络:

                    ┌─────────────────────┐
                    │ Agent Type Registry  │ ← 加载 Agent 定义
                    └──────────┬──────────┘
                               │
                    ┌──────────▼──────────┐
                    │   Tool Sandboxing    │ ← 过滤工具集
                    └─────┬────────┬──────┘
                          │        │
          ┌───────────────▼┐  ┌────▼──────────────┐
          │ Fork-and-Delegate│  │ Coordinator-Worker │ ← 两种互斥编排
          └───────┬────────┘  └─────┬──────────────┘
                  │                  │
          ┌───────▼──────────────────▼──────┐
          │      Async Agent Lifecycle       │ ← 统一生命周期
          └───────┬──────────────────┬──────┘
                  │                  │
     ┌────────────▼──────┐  ┌───────▼──────────┐
     │ Context Cache      │  │  Scoped Memory    │ ← 成本优化 + 长期记忆
     │ Sharing (Fork专属) │  │  (所有Agent共享)  │
     └───────────────────┘  └──────────────────┘

协作路径解读

  1. Agent Type Registry → 提供定义
  2. Tool Sandboxing → 过滤权限
  3. Fork / Coordinator → 互斥编排
  4. Async Lifecycle → 统一生命周期管理
  5. Context Cache → Fork 专属成本优化
  6. Scoped Memory → 全局跨会话记忆

三、核心点拆解

3.1 Coordinator-Worker:编排者不执行

角色分离的极端设计:Coordinator 只有 4 个工具——Agent(启动 Worker)、TaskStop(停止 Worker)、SendMessage(继续 Worker 对话)、SyntheticOutput(结构化输出)。不能读文件、不能写文件、不能执行 Shell。

相对应地,Worker 拥有丰富的执行工具,但被禁止创建子 Agent(防嵌套递归)和向用户提问(后台无交互界面)。

四阶段工作流(软约束):

Research(调研)→ Synthesis(综合)→ Implementation(实现)→ Verification(验证)

"软约束"的精妙之处:LLM 可根据实际情况灵活调整——简单任务跳过 Research,纯分析任务跳过 Implementation。

XML 通知协议:Worker 完成时用结构化 XML 报告结果:

<task-notification>
  <task-id>{agentId}</task-id>
  <status>completed|failed|killed</status>
  <summary>{human-readable status summary}</summary>
  <result>{agent's final text response}</result>
  <usage>
    <total_tokens>N</total_tokens>
    <tool_uses>N</tool_uses>
    <duration_ms>N</duration_ms>
  </usage>
</task-notification>

为什么用 XML 不用 JSON?LLM 对 XML 标签的解析更鲁棒——JSON 一个多余逗号就崩,XML 标签的嵌套结构对 LLM 更自然。而且 <task-notification> 可以嵌入普通文本中,不需要额外的通信通道。

3.2 自包含 Prompt 与 Scratchpad 间接通信

Coordinator-Worker 与 Fork 最关键的区别:上下文隔离

维度 Fork-and-Delegate Coordinator-Worker
上下文共享 子 Agent 继承父 Agent 完整对话历史 Worker 看不到 Coordinator 的任何对话
Prompt 要求 只需简短的 Fork 指令 必须自包含所有必要上下文
缓存效率 高(Prompt Cache 共享) 低(每个 Worker 独立请求)

Coordinator 的 system prompt 有一条明确规则:"Prompts must be self-contained. Workers cannot see your conversation."启动 Worker 时必须将所有必要信息嵌入 prompt——文件路径、行号、具体需求、背景上下文。

这种隔离带来两个好处

  1. 安全隔离:Worker 看不到 Coordinator 与用户的对话,降低信息泄露风险
  2. 独立性:Worker 可以被独立测试和调试

Scratchpad 间接通信:Worker 之间无法直接通信,Coordinator 可指定一个共享目录作为"草稿纸"——Worker 写入中间结果,其他 Worker 读取。一种简单但有效的间接通信模式。

3.3 Async Agent Lifecycle:五阶段状态机

长时间运行的 Agent 不应阻塞主线程,但异步执行引入了新的复杂性——进度反馈、错误处理、资源清理、前后台切换。

五阶段生命周期

Created → Running → Completed
                   → Failed
                   → Killed

核心实现 runAsyncAgentLifecycle() 五阶段:

阶段 动作 关键点
Phase 1 初始化——创建进度追踪器 tracker 记录 toolUseCount / token 用量 / recentActivities
Phase 2 消息流处理——实时追踪进度 for await (const message of makeStream()) 逐消息更新
Phase 3 完成处理 finalizeAgentTool + completeAsyncAgent
Phase 4 安全审查 classifyHandoffIfNeeded 分类审查
Phase 5 通知 Coordinator/父 Agent enqueueAgentNotification XML 格式通知

进度追踪器的 token 统计细节:

  • 输入 token 取最新值(API 每次返回的是累计值,包含缓存命中部分)
  • 输出 token 逐轮累加(每轮输出是增量)

这个细节反映了对 LLM API 计费模型的深入理解。

3.4 前台/后台切换与自动后台化

Agent 支持前台后台动态切换。核心机制是 backgroundSignal——一个 Promise,当 Agent 被切换到后台时 resolve。Agent 执行逻辑可以 await 这个信号来调整行为:前台即时显示进度,后台只在完成时通知。

自动后台化定时器(默认 120 秒)解决了一个 UX 问题:用户启动 Agent 后可能忘记手动切后台,长时间运行的前台 Agent 会阻塞主界面。120 秒后自动后台化是合理的默认行为。

3.5 十步资源清理链:最精密的工程

异步 Agent 生命周期中最精密的部分。运行中的 Agent 可能持有 MCP 连接、Hook 注册、文件缓存、Shell 进程等多种资源。finally 块确保这些资源被可靠释放:

1. mcpCleanup()                    // 关闭 MCP 连接
2. clearSessionHooks()             // 清除 Hook 注册
3. cleanupAgentTracking()          // 清除 Cache 检测
4. readFileState.clear()           // 释放文件缓存
5. initialMessages.length = 0      // 释放消息引用
6. unregisterPerfettoAgent()       // 注销 Tracing
7. (保留位)
8. delete todos[agentId]           // 删除 Todo 条目
9. killShellTasksForAgent()        // 杀死后台 Shell
10. killMonitorMcpTasksForAgent()  // 杀死 Monitor 任务

清理顺序的设计哲学

  1. 先关闭外部连接(MCP),正在执行的工具调用可能依赖它们
  2. 再清除注册信息(Hook、Tracing),依赖于 MCP 状态
  3. 然后释放内存缓存(文件缓存、消息引用)
  4. 最后清理进程资源(Shell、Monitor),最独立的资源

第 5 步的微妙之处:为什么用 initialMessages.length = 0 而不是 initialMessages = []?因为 .length = 0原地清空数组,所有持有该数组引用的代码都能观察到清空效果。重新赋值只改变引用,旧数组仍被其他引用持有,无法被 GC 回收。在 Fork 场景中,上下文消息可能非常大,这个差异对内存管理至关重要。

3.6 通知防重复:乐观锁模式

function enqueueAgentNotification({ taskId, status, finalMessage }) {
  let shouldEnqueue = false
  updateTaskState(taskId, setAppState, task => {
    if (task.notified) return task  // 已通知过
    shouldEnqueue = true
    return { ...task, notified: true }
  })
  if (!shouldEnqueue) return
  // 构建 XML 通知...
}

在状态更新回调中设置 notified 标志,利用状态更新的原子性保证"检查+标记"不可分割。典型的乐观锁模式——不使用显式锁,而是通过原子操作保证一致性。

3.7 Retain/Evict 懒清理

UI 层面的内存管理:

  • 用户查看 Agent 面板 → retain: true,阻止驱逐
  • 用户离开面板 → 开始 30 秒倒计时(PANEL_GRACE_MS
  • 倒计时结束且任务已完成 → 驱逐面板数据释放内存

"懒清理"策略在响应速度(用户切回来时数据仍在)和内存效率(长时间不看的数据被释放)之间取得了平衡。

3.8 Context Cache Sharing:四维一致性保证

成本优化的架构洞察:假设父 Agent 有 50,000 tokens 对话历史,Fork 出 3 个子 Agent——

方案 成本
朴素方案 3 × 50,000 = 150,000 input tokens
Prompt Cache 方案 50,000 缓存写入(一次)+ 3 × 50,000 缓存读取(便宜 90%)

要利用 Prompt Cache,必须确保多个请求的前缀字节级一致——哪怕一个字节的差异都会导致缓存失效。

四维一致性保证

维度 机制 代码关键字
维度 1:统一占位符 所有 Fork 子 Agent 的 tool_result 使用同一文本 FORK_PLACEHOLDER_RESULT
维度 2:精确工具列表 useExactTools: true 跳过工具解析,直接使用父级工具列表 useExactTools
维度 3:模型继承 model: 'inherit' 避免模型切换导致缓存失效 model: 'inherit'
维度 4:文件状态缓存克隆 Fork 子 Agent 继承父 Agent 的文件读取缓存 cloneFileStateCache

架构启示:LLM 系统的成本优化不仅仅是算法问题,更是系统架构问题——成本优化是跨切面关注点(cross-cutting concern),需要在消息构建、工具解析、模型选择、缓存管理多个层面协同设计。

3.9 Fork vs Coordinator:系统性对比

维度 Fork-and-Delegate Coordinator-Worker
上下文共享 继承父 Agent 完整上下文 Worker 无父上下文,需自包含 prompt
工具权限 继承父级所有工具 Coordinator 只有编排工具,Worker 有执行工具
缓存效率 高(Prompt Cache 共享) 低(每个 Worker 独立请求)
任务编排 隐式(父 Agent 自行决定) 显式(四阶段工作流)
互动能力 无(子 Agent 静默执行) Worker 间可通过 Scratchpad 间接通信
递归深度 硬限制:禁止递归 Fork 硬限制:Worker 不可嵌套 Agent
适用场景 并行独立任务,需要共享上下文 复杂工作流,需要阶段控制
成本模型 低(缓存共享) 高(独立请求)

选择指南

  • 并行独立子任务 + 需要理解完整上下文 → Fork
  • 多阶段编排 + 子任务间有依赖 → Coordinator
  • 成本敏感 → Fork(Prompt Cache 共享可省 90% 输入成本)
  • 安全敏感 → Coordinator(Worker 看不到 Coordinator 与用户的对话)

四、横向对比:三大框架的多 Agent 编排哲学

维度 Claude Code LangGraph OpenAI Agents SDK AutoGen
编排模型 隐式(Fork)/ Prompt 引导(Coordinator) 显式有向图 函数调用交接 对话协议
灵活性 LLM 动态决定任务分解 图结构编译时确定 声明式轻量 Agent 协商
可预测性 较低(LLM 决策不确定) 较高(图结构确定) 中等 较低
并行度 真正并行(Fork/Worker) 支持并行分支 串行交接 支持并行对话
上下文传递 Fork 共享 / Worker 自包含 图边传递状态 交接时传递完整上下文 对话消息传递
复杂度 较高(7 个模式) 中等(图定义) 较低 中等
适用场景 通用 AI 编程助手 预定义工作流 单一信任域 多 Agent 研究/讨论

Claude Code 的选择逻辑

  • vs LangGraph:任务类型不可预知,需要 LLM 自主判断如何分解
  • vs Agents SDK:需要进程级隔离的生产级复杂场景,不是单一信任域内的轻量交接
  • vs AutoGen:Agent 间对话引入额外 LLM 调用成本 + 不确定性传播,不如 Coordinator 集中编排

五、实战启示:三个可复用设计模式

模式一:角色分离 + 最小权限

编排者不执行,执行者不编排

这个原则可以推广到任何多角色系统——Coordinator 只有调度工具、Worker 只有执行工具、记忆 Agent 只能编辑一个文件。每个角色的权限边界越窄,系统的安全推理越简单。

模式二:十步确定性清理链

资源清理不是 try-finally 里的三行代码,而是精心排序的多步链条:

  1. 外部连接先关(MCP)
  2. 注册信息次之(Hook、Tracing)
  3. 内存缓存随后(文件、消息)
  4. 进程资源最后(Shell、Monitor)

initialMessages.length = 0 的细节值得记住:原地清空 vs 重新赋值,在共享引用场景中有本质区别。

模式三:四维缓存一致性

在 LLM 系统中做成本优化,不是在某个组件里做缓存,而是在多个维度上协调一致

  • 消息层面:统一占位符
  • 工具层面:精确工具列表
  • 模型层面:继承父级模型
  • 缓存层面:文件状态克隆

任何一个维度的疏忽都会导致缓存前缀断裂,90% 的成本节省瞬间归零。成本优化是跨切面架构关注点,不是局部优化。


六、下期预告

第 10 篇完成了第 6 章(Agent 编排)的精读。七个模式从"轻量分叉"到"结构化编排"再到"生命周期管理",构建了一套完整的多 Agent 协作体系。

下一篇进入第 7 章——上下文管理,这是 Agent 系统的"灵魂所在":有限的上下文窗口如何装下无限的需求?

第 11 篇:Context 的生死抉择——补丁压缩、截断算法与 Session Memory

  • 7.1 上下文窗口的核心矛盾
  • 7.2 补丁压缩处理机制
  • 7.3 Session Memory——实时更新要素系统
  • 7.4 消息截断算法——在正确的地方"按一下"

本文是《Claude Code 架构解密》精读笔记系列第 10 篇,覆盖第 6 章后半(6.6-6.12)。系列共 20 篇,持续更新中。

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐