子Agent、Fork还是协调者——多Agent模式怎么选,关键看还要不要中间输出

原型做到现在,主 Agent 身上的活越堆越多:查知识库、总结会话、提取用户信息、回复用户,全挤在一个上下文里。

结果上下文涨得飞快。查一次知识库,几十段检索结果原样留在对话历史里,后面每一轮都要带着它们。更麻烦的是,总结、提取这类本来不着急的活,也在主流程里同步跑,用户等回复的时间被白白拉长。

是时候把活拆出去了。但拆成几个 agent、怎么拆、拆完怎么协作,又是一个新问题。于是去看了看 Claude Code 是怎么做多 agent 的。

一、为什么要多 Agent

单个 agent 有两个硬伤:上下文窗口有限,而且没法并行。

而很多任务天然适合分而治之。比如调研一个问题,可以拆成几个独立的子问题同时查;比如一个大的改动,可以一边改一边让另一个 agent 去验证。

不过要先泼一盆冷水。Anthropic 在《Building effective agents》里反复强调的是:先用最简单的方案,确实需要了再加复杂度。多 agent 意味着更多的 token、更难排查的问题,不是 agent 越多越好。

Claude Code 的做法也是这样:核心是一个单体的 Agent Loop,多 agent 是按需叠加上去的。它通过同一个 Agent 工具,提供了三种由轻到重的模式。

二、三种模式

先看一张对比表:

维度子 AgentFork协调者
上下文全新的对话完整继承父级每个 Worker 各自独立
系统提示词子 Agent 自己的继承父级协调者专用
缓存不共享共享父级缓存不共享
能否再派生可以禁止Worker 不能
适用独立的小任务需要上下文的并行探索复杂的多步骤项目

1. 子 Agent:刚走进房间的聪明同事

子 Agent 拿到的是一个全新的对话,除了你给它的那段 prompt,它什么都不知道。

所以 Claude Code 对写 prompt 的要求是:像给一个刚走进房间的聪明同事交代工作一样。他很能干,但他没参加刚才的会,你得把背景、目标、约束都讲清楚。

好处是主上下文保持干净。子 Agent 在自己的上下文里读了二十个文件、跑了十条命令,最后只给主 Agent 交回一份摘要,那些中间过程全都留在它自己那边。

2. Fork:继承上下文,共享缓存

有时候子任务需要知道前面聊过什么,重新交代一遍成本太高。这时候用 Fork:完整继承父级的上下文,相当于把当前的自己复制一份出去干活。

Fork 有一个很巧的设计:几个 fork 同时出去,它们的请求前缀是逐字节相同的,只有最后追加的那一小段指令不一样。这样所有 fork 都能命中父级的 prompt cache,复制上下文几乎不花钱。

为了保证字节一致,fork 的工具列表和父级完全一样,连"派生 agent"这个工具也在。但 fork 里不允许再 fork,否则就无限递归了。怎么办?工具留着,调用时报错;同时在 fork 的入口写一段很直白的话:

STOP. READ THIS FIRST. You are a forked worker … Your system prompt says "default to forking." IGNORE IT.

父级的系统提示词里说"默认用 fork",fork 继承了这段提示词,就会想继续 fork。所以这里承认矛盾指令的存在,然后显式地覆盖它。这比假装那条指令不存在要可靠得多。

3. 协调者:理解这一步不能外包

最重的是协调者模式。主 Agent 自己不写代码,手里只有派活、发消息、停任务这几个工具,像一个项目经理。

它的工作流分四个阶段:

  1. Research:派几个 Worker 并行去调研;
  2. Synthesis:协调者自己把调研结果综合起来,形成理解;
  3. Implementation:再派 Worker 去实现;
  4. Verification:最后派 Worker 去验证。

关键在第 2 步。Claude Code 里有一句原则:Never delegate understanding,不要把理解外包出去。

协调者给 Worker 派活时,最常见的错误写法是:

根据你的调研结果,把登录的 bug 修一下。

这等于把"理解问题"这一步也甩给了 Worker,协调者自己什么都没想。正确的写法应该是:

src/auth/session.ts 的 refreshToken() 里,过期判断用的是 < 而不是 <=,
导致 token 恰好在过期那一刻被拒绝。把比较符改掉,在 session.test.ts 里
补一个 exp === now 的单元测试,跑 npm test -- session 确认通过。

可以看到,后者把问题在哪、为什么错、改成什么、怎么验证都写清楚了。协调者真正理解了问题,Worker 才能干好活。

三、怎么选

三种模式看下来,选哪个其实只需要问一个问题:

以后我还需要这些中间输出吗?

不需要,就派出去,只拿回结果。调研的中间过程、读过的文件、试错的命令,对主 Agent 来说都是噪音,留在主上下文里只会占地方。需要,就留在自己的上下文里做。

至于派出去之后用哪种模式,看子任务需不需要现有的上下文:不需要就用子 Agent,需要就用 Fork,任务大到要分阶段、要多人协作,再上协调者。

还有一个更细的视角:委派这件事,其实可以在四个旋钮上分别裁剪:

  1. 模型:子任务简单,就换一个便宜的模型;
  2. 工具:只给它完成任务所需的工具;
  3. 回合数:限制它最多跑几轮;
  4. 上下文:给全量、给一部分,还是一点都不给。

Claude Code 里有一个 /btw 命令就是很好的例子:用户在 agent 干活的时候随口问一个问题,它会派生一个带完整上下文、但不能用任何工具、只跑一轮的查询来回答。上下文开到最大,工具和回合数拧到最小,既能答得准,又不会干扰主任务。

四、验证 Agent:专治"看起来做完了"

多 agent 里有一个角色我觉得特别实用:验证 Agent。

LLM 做验证有两个典型的毛病,Claude Code 直接在提示词里点名了:

  1. 验证回避:读读代码,描述一下"应该怎么测",就写个通过;
  2. 被前 80% 迷惑:界面挺好看,测试也过了,就没注意到一半的按钮点了没反应。

所以验证 Agent 的设定是:

  • 只读,不能改代码,但可以在临时目录写测试脚本;
  • 至少做一次对抗性的探测,比如并发、边界值、重复提交;
  • 最后必须给出结论 VERDICT: PASS、FAIL 或 PARTIAL,其中 PARTIAL 只能用于"环境不支持验证",不能用来表达"我不确定"。

最后这条很狠,等于把"含糊其辞"这条退路直接堵死了。

不指望模型不犯错,而是让它知道自己容易犯哪些错,这是一种对 LLM 固有弱点的工程补偿。

五、平级协作:共享任务图,而不是聊天

上面三种模式都是"父派生子"的层级关系。如果是多个 agent 平级协作呢?

直觉上是让它们互相发消息商量。但 Claude Code 在这类场景下的思路是:协作的核心不是聊天,而是一张共享的任务图。

每个任务是图上的一个节点,带着状态、负责人和依赖关系。agent 空闲下来就去图上领一个"没人领、依赖都完成了"的任务,领取是原子的,不会两个 agent 抢到同一个。

模型不用在自然语言里推断"现在哪个任务空着、依赖解开了没有",这件事交给代码。调度和推理分开,并行来自共享状态,而不是来自消息协商。这和我们做分布式任务调度的思路其实是一样的。

六、我们的原型怎么拆

回到开头的问题。照着"还要不要中间输出"这个标准,我把原型拆成了这样:

  1. 主对话 Agent:只负责和用户对话,上下文里只有对话本身;
  2. 知识库调研:派给子 Agent,它自己去查、去筛,只把整理好的结论交回来;
  3. 会话总结、用户信息提取:派给后台的子 Agent,异步执行,不阻塞回复。

第 3 条用上了四个旋钮:

@dataclass(frozen=True)
class SubAgentSpec:
    model: str  # 模型
    tools: tuple[str, ...]  # 工具
    max_turns: int  # 回合数
    context: Literal["none", "recent", "full"]  # 上下文


MEMORY_EXTRACTOR = SubAgentSpec(
    model="cheap-model",  # 提取信息不需要最强的模型
    tools=("write_memory",),  # 只能写记忆,不能调其他任何工具
    max_turns=3,  # 最多跑 3 轮
    context="recent",  # 只给最近一轮对话,不给全量历史
)

这样主 Agent 的上下文干净了,回复也不用再等后台的活跑完。

总结

  1. 多 Agent 是按需叠加的复杂度,能用单个 agent 解决的,就别急着拆;
  2. 子 Agent 拿全新上下文,适合独立任务;Fork 继承上下文并共享缓存,适合需要背景的并行探索;协调者适合分阶段的复杂项目;
  3. 选模式先问一句:以后还需要这些中间输出吗?不需要就派出去;
  4. 委派可以在模型、工具、回合数、上下文四个旋钮上分别裁剪,理解这一步永远不能外包;
  5. 验证 Agent 要只读、做对抗性探测、必须给出明确结论;平级协作靠共享任务图,调度交给代码,推理交给模型。

相关资料