把 subagent 放回主循环
上个月我把手上一个 agent 应用里的三个 specialist 删了。
不是为了省 token,而是那套 orchestrator 加三个 subagent 的结构,每加一个功能我要改四个地方:路由的分类规则、subagent 自己的 prompt、主 agent 的 prompt,还有交接时那份字段约定。改完还得赌两边对“用户到底想要什么”的理解是一致的。
删完跑我们那套没几个 case 的回归,任务完成率没掉,端到端延迟掉了将近一半。这个数字是拍脑袋得来的,并没有做 A/B,后面会提到别人认真的测量,结论差不多。
Anthropic 的 Applied AI 团队在 10 月 22 号发了一篇文章,Building agents: When to fold subagents into your main agent loop。我第一反应不是“又多一个方法论”,而是“原来不只是我们这么干,而且他们把原因写清楚了”。
他们看到的和我看到的是同一件事
文章的论点一句话能说完:少用点 subagent,把上下文和工具改成按需加载,质量往往更好,成本和延迟还更低。这个判断未必对所有场景都成立,后面我会说哪些地方不买账。
这跟我原来的直觉相反。我一开始做这套东西的时候,subagent 在我脑子里就是解耦的同义词:把复杂的系统拆成职责单一的专家模块,每个模块有自己的 prompt、自己的工具、自己的 owner,多干净。这听起来像软件工程讲了三十年的东西。
问题在于,我们习惯的那套拆法假设模块之间传的是精确的东西:函数签名、结构化的数据、明确的接口契约。而 agent 之间传的是自然语言摘要,A 干完活总结一段话给 B,B 基于这段总结再往下走。每一次交接都是一次有损压缩,损失多少根本看不见,它不报错,也不抛异常,只是让最后的结果变差了一点。
Anthropic 管这个叫 telephone game,传话游戏,这个说法挺准。一个中间人说“用户反映最近两天订单状态没更新”,传到第三个 agent 耳朵里可能就变成了“用户要查订单”,“最近两天”和“状态没更新”之间的因果全没了。
为什么企业里会长出一堆 subagent
那为什么企业里会长出这么多 subagent?这篇文章里我读得最认真的是失败模式那一段,因为它完全是从组织角度说的,不是技术角度。
一个大公司想做一个统一的 agent 入口,接住所有产品能力。但每个能力归不同的团队,于是很自然的一条路是:各团队先各自做一个 agent,平台团队在上面盖一个 orchestrator 把大家串起来。
看起来最省事,实际上立刻埋下了几个问题。
最先出问题的是上下文管理,得先决定 agent 之间传什么,传的过程中细节就丢了。接下来是 subagent 自己会泛滥,每个团队都发一个,最后几百个功能重叠,没标准,也没明确 owner。再往上就堆到 system prompt,每个团队往里加一句自己的规则,加着加着开始互相打架,语气也不统一,文章把这个叫 Frankenprompt。
eval 也跟着散掉,每个团队加一套针对自己子任务的评测,都过分优化了自己那一小块,老板最后看着一堆被稀释过的分数做决策。最实打实的是延迟,每多一层嵌套就多一次模型调用,这部分成本并没有多少优化空间。
这几个问题并不是彼此独立的。上下文丢了细节,团队就想在别处补回来。各团队倾向于把信息塞进 prompt 里补;prompt 越塞越厚,就有了 Frankenprompt;Frankenprompt 又让各自的 eval 只在自己那一小块上变好,整体反而更难评估。
文里有个数字:一家客户把多 agent 的 harness 换成单循环之后,运行时快了大概两倍。跟我上面说的将近一半差不多是一回事,只不过他们是认真的 A/B,我是拍脑袋。
文章把根因点得很直接,Conway’s law,agent 架构长成了组织架构图的样子。这话说起来不太体面,但我在好几个项目里确实见过:拆分的边界不是按上下文怎么流动画的,多半是按谁向谁汇报画的。
现在能放回去,是因为三件事变了
我一直觉得要不要用 subagent 这个问题并没有普适答案,得看是什么年代问的。
两年前不行,那时候上下文窗口就那么点,一个 agent 塞不下七八个领域的知识,塞进去就开始注意力涣散、选错工具。所以当年拆 subagent 是被迫的,并不是因为架构更优雅,而是因为上下文确实装不下。
现在能放回去,作者给了三个理由,我基本认同,但三条的份量并不一样。
共享上下文确实能出更好的答案。长任务里,前面几步的原始结果都躺在同一个窗口里,模型自己就能把 A 的发现和 C 的发现连起来。subagent 模式下,主 agent 手里只有一堆二手摘要,这种跨步骤的联想能力基本被削掉了。这点我做 RAG 类的 agent 时感受很明显:把召回结果原样留在上下文里,模型经常能自己发现一些没让它找的东西。
渐进披露,也就是 progressive disclosure,把“上下文装不下”这个前提消掉了。这是我觉得整篇文章技术含量最高的一处。思路很朴素:agent 一开始只看到一堆 skill 和 tool 的短描述,需要哪一个才把全文加载进来。上下文里常驻的东西从“所有领域知识”变成了“所有领域知识的目录”,差了一到两个数量级。
文章里有个数字我看了两遍:靠这个,包括 progressively disclosing skills 和 tools,他们把 Claude Code 在较新模型上的 system prompt 砍掉了 80% 以上。
最后一条是模型本身变强了。从 Opus 4.5 开始,模型在单个循环里能扛的事情,比大多数团队预期的要多。而且新模型会自己叙述进度、自己回头检查结果,很多我们以前写死在 harness 里的编排逻辑,现在纯属多余。
这一条我觉得比前两条都重要,也最容易被忽略。它意味着 harness 的复杂度应该跟着模型能力往下走,而不是往上加。文章里有句话我很喜欢:僵化的多 agent 结构,往往是新模型发挥不出来的真正原因。如果想让模型多做点,结果它被一堆写死的路由和交接协议卡住了。
但别急着把 subagent 全删了
这是我看完最想强调的一点,因为网上转这篇的标题大多挺耸动,容易让人读成“subagent 该淘汰了”。
那什么时候还该用 subagent?文章明确列了三条条件,而且都是可测量的,不是审美判断:单个循环因为上下文膨胀出现了可测量的性能下降;多个任务需要并行,而且用完的上下文可以整个丢掉;单个 agent 选不准工具,或者 system prompt 里存在互相冲突的指令。
我自己的经验是第二条最实在,也最不容易被误读。上个月删的那三个 specialist 都是串行使用的:用户先要查订单,查到订单才可能问物流,问完物流才可能问退款。这种链式的东西放在一个循环里,上下文全是增益,删了当然好。
但另一个项目里我留了一个 subagent 没动,因为它干的是“把三十多份财报 PDF 全读一遍然后给我一个结论”。主 agent 根本不需要那三十份 PDF 的任何一页事后还留在上下文里,它只需要那一段结论。这种读得远多于用得上的活,subagent 是天然合适的,它存在的意义就是把一大坨上下文用完之后扔掉。
这也是文章里“按上下文切分边界”那个说法:分给每个 subagent 的工作,应该是几乎不需要系统其余部分的那种,交回来一个紧凑的结果。符合这个条件的分法是好的,不符合的,比如按问题类型分、按团队分,就是传话游戏。
放回的路径,以及一个容易被忽略的细节
文章给的迁移路径很务实,没有要求一次重构完。
先有 eval,这个绕不过去,最少 10 条 golden examples 就能起步。中央团队维护一套端到端的,各团队给自己的 skill 带一套小的。还在用 orchestrator 的时候额外看 routing accuracy,放回之后改看有没有加载对的 skill、有没有调对的 tool。
文章给了三种做法,代价依次变重。
合并是最省事的一种,把经常一起被调用的专家合成一个。判断依据是生产流量,如果大量请求都是 A 之后紧跟 B,那这就是一对紧耦合,可以直接并。
当工具调则是保留 subagent,但主 agent 用 tool call 的方式调它,对话还握在主 agent 手里。好处是零改动就能拿到 baseline。代价是那些老问题还在:每个专家还是有自己的 prompt 和 owner,每次调用还是一次有损摘要加一层嵌套循环。
真正放回代价最大,要把 prompt 变成 skill,把 API 调用变成 tool,一次一个,前后对比 eval。
放回这一步有个注意事项,我读完到现在还记得。
文章说,对比的时候不能只看总分,还要看哪些任务翻转了。他们测样例客服 agent 的时候,放回之后延迟降了,但有个原本一直通过的流程挂了。原因是主 agent 手里本来就有订单查询工具,它自己查完就答了,没去问物流专家要送达日期。
这个 bug 很典型。它不崩溃,也不报错,只是 agent 变得更高效了,自己把活干完了,少问了一句本来必须问的话;而这句话正是流程一直依赖的判定条件。如果只看总分,很可能整体还涨了两个点,于是这次改动就被合了进去。
还有一处更麻烦:这个回归两次里只出现了一次。
所以他们的建议是,动手之前给生产里会一起跑的 agent 链和那些不能破的规则都准备评测任务,而且每个任务要跑不止一遍。我之前那套回归是每个 case 跑一次就完事,现在想想纯粹是运气好。
多团队协作的部分,其实是这篇文章的暗线
这篇表面上在讲架构,我觉得它真正想说的是分工,而且后者的阻力更大。
原来的分工是什么?每个领域团队做一个 agent,agent 是他们的交付物,也是他们的地盘。
文章建议的新分工是:团队交付的不再是 agent,而是 skill 和 tool。中央留一个小团队,端到端质量和 system prompt 归它管,所有新的 skill、tool、eval 上线前要经过它验一遍;领域团队继续拥有自己的 skill,也继续有自己的 DRI 保证质量。
这个转变的难点大概不在技术,在所有权。一个团队从“我有个 agent”变成“我有一段 prompt 和几个工具函数”,听起来是降级了。文章没怎么回避这一点,它给的办法是让维护 skill 的成本本身降下来,也就是用 skill 去生成、测试、修剪 skill,这个思路未必能完全抵消掉那种降级感。/skill-creator 起草和审计,/skill-doctor 看每个 skill 到底花多少钱、被用了几次,然后把没人用的删掉。
关于工具那块有个数字挺有用:超过 30 到 50 个工具定义,模型选工具就开始退化。所以更适合按用户意图分组,而不是一个 API 对应一个 tool。
还有个设计我觉得很值得抄:把权限校验放到 tool 内部。每个 tool 自己判断调用者能看什么、能改什么,不行就返回一句人话给模型。这样一个循环就能服务不同权限的用户,不需要一档权限一个 agent。这个坑我们踩过,早期按角色复制了三份 agent,后面每改一次逻辑要改三遍。
我不太买账的地方
有一个我觉得被轻描淡写的前提:任务大部分是串行的,而且共享上下文。这个前提在很多团队并不成立。如果场景本来就是要同时铺开很多路,比如一次扫上百个文件、并行验证十个假设、跑一堆互不依赖的子任务,那么 subagent 的价值不是摘要,是吞吐。文章里也承认了这一点,用的是 research frontier 的说法,但措辞很轻。我觉得这可能会让一批人误伤自己的系统,而代价要过很久才显现。
渐进披露同样有代价,只是这个代价来得比较慢。skill 描述写得不好,模型就不知道该加载哪个。这时候省了上下文,换来的是选错 skill。文章承认这需要纪律,但描述要能触发这件事本身很难评测,比传统 prompt 的调优更玄;而且这条没有任何现成的度量可以抄。我的实际体会是,skill 的描述要写得像“什么时候该用我”,而不是“我是什么”,这个差别对触发率影响很大。
至于 eval,文章说得轻巧。可是没有它,前面三个理由都不成立。它自己也承认,在内部系统难以回放、用户行为难以模拟的环境里,做 task-completion eval 非常麻烦。它给的办法是记录关键事件的时间戳,事后回放。这个技巧是实用的,但它意味着得先把埋点做起来,而埋点往往要在系统还没重构的时候就有。
也就是说,要迁移到单循环,得先有能力度量现在的系统。这个前置条件的门槛,比放回 subagent 本身高得多。
我见过不少团队卡在这儿:架构的道理都懂,但拿不出一个有说服力的数字,于是讨论就变成了品味之争。
写在最后
文章结尾那段话我觉得比正文更值得记住:
As models evolve, our understanding of how to best use them and guidance around best practice evolve as well. That makes any one architecture less important than the principle behind it: measure, change one thing at a time, and keep what holds up.
(随着模型进化,我们对怎么用它的理解也在变。这意味着任何单一架构都没有背后的原则重要:测量,一次只改一件事,保留经得起验证的东西。)
我把它贴在了自己的备忘里。要不要用 subagent 这个问题,大概还会被反复问,我的判断也未必是最后一次。
顺便说,他们放了个配套的参考实现 anthropics/single-agent-loop-kit,把审计、baseline、合并、放回、当工具调、日常维护串成一个 Claude Code 的 skill,每一步都停下来给出结论,问过一遍再继续。里面还有个十分钟能跑完的客服 agent 样例,用 KIT_MODEL=fake 可以不花一分钱先验证管道通不通。
如果手上还没有 subagent,这个 kit 其实用不太上,直接从一个循环开始写更省事。这句话是它 README 自己写的。