多代理架构:分叉-汇聚模式的协作艺术
有一个反直觉的现象:同一个模型,拆成多个独立实例让它协作,效果往往比单个实例更好。
不是算力增加了(三个实例的总 token 消耗比一个实例多),不是能力提升了(每个实例都是同样的模型)。提升来自角色分离——当每个 Agent 只关注问题的一个方面时,它做得比"全能 Agent"更专注、更深刻。
Claude Code 的多代理架构就是建立在这个观察上的。
为什么一个 Agent 不够
单个 Agent 处理复杂任务时有一个结构性问题:角色冲突。
想象你让 Claude Code 做一件事:“分析这个项目,然后提出重构方案。” 这个任务包含两个阶段:
- 探索——读文件、理解架构、发现问题(需要广泛但浅层的扫描)
- 规划——基于探索结果设计重构方案(需要深度但聚焦的思考)
如果同一个 Agent 做这两件事,它在探索阶段收集的详细信息会占据上下文,到了规划阶段可能没空间做深度推理。更糟的是,探索时的"发现心态"和规划时的"决策心态"在认知上是不同的——同一个上下文里同时保持两种心态,模型容易混淆。
人类怎么处理这个问题?我们开会。先让调研组去收集信息,汇总后让决策组设计方案。Claude Code 的多代理架构做的是同样的事。
三种内置代理角色
Claude Code 预置了三种子代理角色,每种都有明确的职责边界:
Explore(探索者)——只读权限,快速扫描。它的任务是"尽可能多地了解项目":读文件、搜代码、列目录、看依赖。它不写代码、不改配置、不做决策。它只收集信息并生成报告。
Explore 的核心设计决策是广度优先。它不深入分析每个文件,而是快速扫描尽可能多的文件,建立项目的"地图"。输出的是一份结构化摘要:项目结构、技术栈、关键文件、潜在问题。
Plan(规划者)——基于 Explore 的报告做深度分析。它有只读权限,但关注点不同:Explore 回答"项目里有什么",Plan 回答"该怎么改"。Plan 接收 Explore 的发现,结合用户需求,生成结构化的行动计划。
Plan 的核心设计决策是深度优先。它不关心项目的全貌,只关心与当前任务相关的部分。输出的是一份可执行计划:步骤、优先级、风险评估、预估工作量。
General(通用)——全权限,执行者。它接收 Plan 的方案,具体实施。读文件、写代码、跑测试、修 bug。它是三种角色中能力最全面的,但也是约束最明确的——它不应该做探索或规划,它应该执行既定方案。
General 的核心设计决策是执行优先。它不做战略决策,它把战略决策转化为具体操作。
分叉-汇聚模式
三种角色的协作模式可以概括为分叉-汇聚(Fork-Join):
主 Agent
│
├─► Explore (分叉) ──┐
│ ├─► Plan (分叉) ──┐
│ │ ├─► General (执行)
└────────────────────┴─────────────────┴─► 结果汇聚回主 Agent主 Agent 接收用户请求,判断需要哪些子代理,按顺序 fork 出来。每个子代理在独立的上下文中运行——它有自己的对话历史、自己的工具调用、自己的 Think-Act-Observe 循环。子代理完成后,结果被汇聚回主 Agent,主 Agent 整合后回复用户。
关键设计是上下文隔离。Explore 不知道 Plan 在做什么,Plan 只看到 Explore 的报告(不是原始数据),General 只看到 Plan 的方案(不是探索过程)。每个子代理的上下文只包含它需要的信息。
上下文隔离的好处是防止污染。如果所有角色共享同一个上下文,Explore 的大量扫描数据会挤占 Plan 的推理空间。隔离后,每个子代理有自己的干净窗口,专注做一件事。
子代理的启动与终止
子代理不是永久运行的。它们是按需创建、按任务销毁的临时实例。
主 Agent 决定启动子代理时,会创建一个独立的 Agent 实例,注入特定的 system prompt(定义角色)、工具权限(定义能力边界)、任务描述(定义目标)。子代理运行自己的 Think-Act-Observe 循环,直到完成任务。完成后,子代理的输出被打包成结构化报告返回给主 Agent,子代理实例被销毁。
这意味着子代理没有持久状态。它不会"记住"上次的探索结果,不会"学习"从之前的规划中犯的错误。每次都是从零开始。
这不是缺陷,是设计选择。无状态的子代理更容易推理——你知道它的行为只取决于输入(system prompt + 任务描述),不取决于隐藏状态。
自定义代理
除了三种内置角色,Claude Code 还支持用户自定义代理。用户在 .claude/agents/ 目录下创建 Markdown 文件,定义代理的角色、能力、行为准则。
一个自定义代理文件可能长这样:
# 安全审计代理
## 角色
专门负责代码安全审计的 Agent。
## 能力
- 只读权限
- 可以读取文件和运行安全扫描工具
## 行为准则
- 重点关注注入漏洞、XSS、CSRF
- 发现漏洞时给出 CVSS 评分
- 按照 OWASP Top 10 分类自定义代理的 existence 揭示了一个更深层的设计理念:Agent 的角色应该被声明式地定义,而不是硬编码在程序里。 和 Skill 一样,代理角色是一种"可配置的专业知识"——只不过 Skill 配置的是"怎么做",代理配置的是"谁来做"。
多代理的代价
多代理不是免费的。它有明确的代价:
Token 消耗倍增。 每个子代理都有独立的 system prompt、独立的对话历史。三个子代理的 token 消耗大约是单代理的 2-3 倍(不是 3 倍,因为子代理的对话通常较短)。
延迟增加。 子代理是串行调用的(Explore → Plan → General),每个都要等前一个完成。总延迟是各子代理延迟之和。
信息损失。 每个传递环节都有信息压缩。Explore 的原始数据被压缩成报告给 Plan,Plan 的详细分析被压缩成方案给 General。最终执行者看到的可能只是原始信息的"摘要的摘要"。
多代理只在任务复杂度超过单代理处理能力时才有价值。简单的任务(“帮我改这个函数”)不需要 fork 子代理——直接让 General 干就完了。
多代理的哲学
Claude Code 的多代理架构背后有一条设计哲学:
复杂任务不应该由一个全能 Agent 处理,而应该被分解为角色明确的子任务,由专注的子代理协作完成。
这和人类组织的分工逻辑完全一致。你不会让同一个人既做市场调研又做产品设计又写代码——你让不同的人做不同的事,然后通过协调机制(会议、文档、流程)把成果整合。
多代理架构是组织理论的 Agent 版本。它不依赖模型变聪明,它依赖分工变清晰。
延伸阅读:BYF 的前台子代理与实时查看器
BYF 在 Claude Code 的多代理架构上做了两个方向上的演进。
1. 前台子代理(Foreground Sub-agent)与实时查看器。 BYF 的子代理有两种运行形态:后台(通过 /tasks 管理)和前台(阻塞父代理,用户通过 /agent 打开全屏实时查看器)。前台子代理的事件通过 routeSubagentEvent 路由到父组件的 ToolCallComponent,用户可以看到子代理的每一步——生命周期状态变化、工具活动、审批等待、错误和最终结果。这不是模型的私有思考链,而是可观察的活动记录。
我自己的一个体会是:子代理的"可观察性"远比"快"重要。当一个子代理跑起来但屏幕上一片空白时,用户不知道它是卡住了还是在干活。BYF 的实时查看器解决的是这个问题——它让子代理的工作过程变得透明。
2. TaskEntry 判别联合。 这是 BYF 的一个设计债清理案例。后台任务管理器用一个 ManagedProcess 结构同时管理两种任务:真实 OS 进程(有 pid、stdin/stdout/stderr 流)和子代理(纯 JS Promise)。为了让 Promise 任务适配,代码中出现了一处 as unknown as KaosProcess 的类型强制转换——BYF 源码中唯一的 as unknown。
BYF 通过引入 TaskEntry 判别联合(ProcessTaskEntry | PromiseTaskEntry)彻底消除了这个逃生口。现在编译器强制你在访问 entry.proc 前用 kind 守卫做类型细化。这个重构不是在"改功能",而是在"改类型"——但它让后续所有使用后台任务的代码都更加安全。
这和 Claude Code 的"三种子代理角色"是同一个思路的反面:Claude Code 用角色区分不同代理的行为,BYF 用类型区分不同任务的形态。 两种方法都指向同一个目标——让多代理系统更健壮、更可预测。
下一篇
多代理让 Agent 内部协作更高效。但 Agent 还需要和外部工具协作——数据库、API、第三方服务。下一篇文章我们要拆解 MCP 协议(Model Context Protocol):它为什么被称为"AI 工具互联的 USB-C"?JSON-RPC over stdio 的设计取舍是什么?为什么 AI 工具需要一个标准协议?
本系列基于 Claude Code 官方源码项目进行架构分析,聚焦设计思想而非代码实现。