Agent 编排模式
问题
如果没有刻意的编排结构,多 Agent 系统会塌缩为两种失败模式之一:一个过载的单一 Agent 串行处理所有事务并耗尽上下文,或者不受控制的扇出——每个子 Agent 都生成更多子 Agent,产生不可能跟踪或取消的递归深度。第三种失败是"懒惰委派"——将原始研究发现直接传递给实现 worker,而非综合成精确规格,这会使输出质量与任务复杂度成正比地下降。
这些问题出现在任何支持委派给子 Agent 的 Agent 运行时中——并非特定于某个具体实现。
黄金法则
综合理解,而非委派理解
编排者的工作不是将一个 worker 的原始发现转发给下一个。研究阶段完成后,编排者必须阅读结果,提取相关事实,为下一个 worker 编写自包含的规格说明。一个 worker prompt 说"根据你的发现"是在将理解委派给一个没有任何发现的进程——它从空白上下文开始。每个 worker prompt 都必须是从未见过之前对话的人也能理解的。
刻意选择委派模式;不要混合冲突的假设
委派模式(coordinator、fork、swarm/peer)解决根本不同的问题,并对上下文共享、深度和生命周期做出不同的假设。在单个会话中混合模式之前,验证它们的假设是否兼容。Coordinator worker 从空白上下文开始,与 fork 的上下文共享假设不兼容。通过共享状态协调的 swarm peer 与 coordinator 风格的分阶段排序不兼容。如果你的运行时在模式之间强制互斥,这是一个合理的简化——以灵活性为代价消除了整个类别的所有权歧义。
深度必须在设计上有界
递归委派是 Agent 编排中最危险的失败模式。每种模式都必须强制执行硬深度限制:coordinator worker 可以生成子 worker,但整棵树必须保持可管理;fork 子代不能再 fork;swarm peer 不能生成其他 peer。这些约束存在的原因是无界深度产生指数级扇出,不可能监控、取消或推理。
Worker 只获得所需的工具
继承父级完整工具集的子 Agent 可能执行编排者从未意图的操作——生成自己的后台 worker、修改编排状态,或访问任务范围之外的资源。将每个 worker 的可用工具过滤为其特定任务所需的。异步 worker 需要比同步 worker 更受限的集合,因为它们在没有直接监督的情况下运行。
适用场景
- 你的 Agent 需要在多个并发子 Agent 之间分配工作。
- 任务有必须按顺序进行的不同阶段(研究、综合、实现、验证)。
- 你需要独立子任务的并行执行,同时维护单一协调点。
- 父 Agent 已积累大量上下文,需要与 worker 共享而无需重新推导。
- 长时间运行的 peer Agent 需要在多轮中协作共享状态。
- 你需要由不共享实现者假设的 worker 对实现质量进行独立验证。
权衡
| 决策 | 收益 | 代价 |
|---|---|---|
| 三种不同的模式而非一种 | 每种模式简单且适合其场景 | 开发者必须正确选择;错误选择会降低性能 |
| Coordinator 从空白上下文启动 worker | 干净的关注点分离;过时的假设不会泄漏 | 编排者必须做真正的综合工作来产生自包含的 prompt |
| Fork 继承完整父上下文 | Worker 立即拥有所有相关背景;无需重新研究 | 上下文大小按每个子代翻倍;fork 后的父级变更对子代不可见 |
| 单层 fork 约束 | 防止指数级扇出;保持进程树浅且可取消 | 无法进一步分解 fork 子代的工作;父级必须预先规划粒度 |
| 扁平 swarm 花名册 | 每个 peer 都可见且可寻址;无隐藏子团队 | 协调复杂度随团队规模线性增长;无层级委派 |
| 模式间互斥(如果强制执行) | 无编排所有权歧义 | 无法在一个会话中结合 coordinator 的分阶段工作流和 fork 的上下文共享 |
| Worker 工具过滤 | Worker 无法在任务范围外执行意外操作 | 过滤逻辑必须随工具集演化而维护 |
| 继续 vs. 新建的决策 | 复用 worker 保留已加载上下文;新建避免假设泄漏 | 在过时上下文中继续比重新加载的代价更大 |
实现模式
- 在分派工作之前决定委派模式。如果你的运行时在模式之间强制互斥,门控检查应显式拒绝冲突的激活而非静默降级。
- 使用 coordinator 模式时,以显式阶段结构化工作:研究 worker 收集信息,coordinator 将发现综合为规格说明,实现 worker 按规格执行,验证 worker 独立确认结果。
- 将每个 worker prompt 写成自包含文档。包含具体文件路径、需要的精确更改和成功标准。永远不要引用"之前的发现"或"上面的上下文"——worker 没有先前的上下文。
- 研究 worker 汇报后,在分派实现工作之前阅读其完整结果。综合步骤是编排者增加价值的地方——跳过它会产生"懒惰委派"失败。
- fork 时,构建子代的初始消息使分叉点尽可能靠后,共享前缀尽可能长。这最大化了兄弟 worker 之间的 cache 命中。只有每个子代的任务指令应该不同。
- 在调用时而非工具定义时强制执行单层 fork 约束。从 fork 子代的 schema 中移除委派工具会更改工具集,破坏与兄弟的 cache 对齐。相反,保留工具但以清晰的错误消息拒绝调用。
- 对于 swarm peer,通过共享制品(任务列表、文件或消息总线)而非 peer 间生成来协调。花名册在 swarm 创建时定义,在执行期间不会增长。
- 根据角色过滤每个 worker 的工具集。只读研究 worker 不需要写入工具。实现 worker 不需要生成自己后台 Agent 的能力。异步 worker 获得比同步 worker 更受限的允许列表。
- 根据上下文相关性决定是继续现有 worker 还是新建一个。如果 worker 已加载的上下文与下一个任务直接重叠,继续它。如果下一个任务需要不同的视角(尤其是验证),新建以避免假设泄漏。
- 对于在隔离的文件系统副本中运行的 worker,注入通知说明父上下文中的路径指向不同的根。Worker 必须将继承的路径转换为自己的工作目录。
- 在每个 worker prompt 中包含目的声明,以便 worker 校准深度和范围。"这项研究将用于 PR 描述"与"这项研究将用于安全审计"产生不同的输出。
踩坑指南
不要跳过综合步骤。 最常见的 coordinator 失败是将研究 worker 的原始输出直接传递给实现 worker。研究 worker 优化了广度;实现 worker 需要精确、可执行的规格说明。编排者必须弥合这个差距。
验证 worker 必须从头开始。 永远不要从实现 worker 的上下文继续验证任务。实现者加载的上下文携带关于正确性的假设,这些假设会使验证者对其应该发现的缺陷视而不见。
Fork 子代不能再 fork。 不要设计指示 fork 子代通过再次 fork 来进一步分解工作的 prompt。递归守卫会在调用时拒绝该尝试,浪费一轮。在父级层面规划分解粒度。
相同的共享前缀对 cache 效率至关重要。 fork 时,共享消息槽中的占位内容必须在所有兄弟间字节级一致。在共享前缀区域中自定义每个子代的内容会摧毁证明 fork 优于 coordinator 风格委派的 cache 收益。
Swarm peer 不能生成其他 peer。 团队花名册在创建时固定。围绕共享状态而非动态团队扩展来设计协调。如果需要新 peer,由会话编排者添加——peer 不招募成员。
进程内 peer 有生命周期约束。 在 leader 进程内运行的 peer Agent 无法管理独立的后台工作,因为其生命周期与 leader 绑定。如果 peer 需要自主的长时间运行任务,它必须在自己的进程中运行(例如,独立的终端会话)。
工具过滤有多层。 不同 Agent 类型可能接收不同的工具子集。内置、自定义和异步 Agent 各自面对自己的过滤规则。自定义或信任度较低的 Agent 在基础禁止列表之上还面临额外限制。工具的信任来源——内置、用户安装或动态加载——可能决定它通过所有过滤层还是绕过某些层。
将较弱模型分配给子任务时要谨慎。 编排者无法可靠地预测子任务复杂度。将"简单"任务路由到更便宜的模型是一种虚假的节省,因为复杂度评估本身需要有能力的模型的判断力。
模式检查在调用时而非定义时进行。 编排模式门控不会从 schema 中移除工具;它们在错误模式下调用时拒绝。这意味着工具看起来可用但会失败。围绕这一点设计错误处理——不要重试同一调用期望不同结果。
Claude Code 实证
Claude Code 在单一 Agent 运行时中实现了所有三种编排模式作为互斥模式。每个会话只能有一种模式活跃——如果 coordinator 模式活跃,fork 被禁用,反之亦然。这是一个刻意的简化,以灵活性为代价消除了所有权歧义。
Coordinator 模式作为独立的操作人格。 当 coordinator 模式被激活时,Agent 接收一个完全不同的系统 prompt 来替代默认值。这个 prompt 将分阶段工作流(研究、综合、实现、验证)和"始终综合"规则编码为一等指令。Coordinator 将 worker 作为异步工具调用分派,并以结构化通知的形式注入对话接收其结果。Coordinator 通过标记结构区分这些通知和真实用户消息,防止人类输入和 worker 输出混淆。
Fork 作为带 cache 优化的上下文共享。 Claude Code 的 fork 实现将父级的完整消息历史克隆到每个子代中,然后追加一个合成轮次,其中所有工具结果槽包含相同的占位文本。只有最终的指令块按每个子代不同。这一设计是专门为最大化 prompt cache 命中而选择的——整个共享前缀(系统 prompt、消息历史、工具定义和占位结果)在兄弟间字节级一致,因此推理提供者可以从同一缓存前缀服务所有子代。单层守卫保留 fork 子代工具 schema 中的委派工具(保持字节级一致的工具定义)但在调用时以清晰的错误消息拒绝任何 fork 尝试。
Swarm 作为扁平 peer 拓扑。 Claude Code 的团队系统为每个 peer 分配一个名称并在专用进程中运行。Peer 通过共享任务列表而非消息传递或 peer 生成来协调。当队友尝试创建另一个队友时,运行时以显式拒绝消息强制执行扁平花名册约束。进程内队友面临额外的生命周期限制——它们无法生成后台 Agent,因为其执行与 leader 进程耦合——而进程隔离的队友独立管理自己的后台工作。
工具过滤作为纵深防御。 Claude Code 根据 Agent 类型应用多层工具过滤。所有子 Agent 面临一个基础禁止列表。自定义 Agent(用户定义的而非内置于运行时中的)面临额外的限制层。异步 Agent 被限制在显式允许列表而非完整的过滤池中。扩展提供的工具绕过所有过滤——这是一个刻意的设计选择,反映了扩展是用户安装并受信任的。这种分层方法意味着每个 worker 以其角色所需的最小工具面运行。
模型选择作为 coordinator 的职责。 Claude Code 的 coordinator 设计指南建议不要为个别 worker 覆盖默认模型,基于编排者无法可靠预测子任务复杂度的观察。将较弱模型指定给"简单"任务被视为虚假的节省。
继续 vs. 新建作为显式决策点。 Coordinator 模式将继续还是新建的选择作为编排流程中的一等决策。Coordinator 可以向现有 worker 发送后续消息(保留其积累的上下文)或生成新 worker(从干净的白板开始)。Claude Code 的设计指南是明确的:当现有上下文与下一个任务直接重叠时继续,不重叠时新建——尤其是验证时,因为实现 worker 的上下文携带的假设会损害独立审查。