扇出税

10 分钟读完

G12
深入解析 · 多智能体系统

扇出被当作「用 token 换挂钟时间」来卖,而这笔交易的两边都被量错了:账单的增长快于分支数,而省下的时间在大约三条之后就不再增长。

第四条并行子智能体几乎总是得不偿失,而你在动手之前就能用两个已经在手的数字预测到这一点——你那段可缓存的前缀,以及一条分支报告的大小。每条分支都要重付一段单一循环本已按十分之一价格缓存住的前缀,而每条分支的输出都会被计费两次:一次作为生成,再一次作为归并的输入;与此同时,挂钟时间的上限是最慢的那条分支,而「很多条里最慢的那条」只比「少数几条里最慢的那条」慢一点点。

步骤 1那个人人知道的倍数,掩盖了钱究竟花到了哪里。

头条数字流传已久:Anthropic 在 2025 年 6 月写下的那套多智能体研究系统,token 用量大约是一次对话交互的 15 倍,而一个普通的单智能体在 4 倍上下。同一篇文章也诚实地给出了收益——那套多智能体配置在他们内部的研究评测上比单个 Claude Opus 4 智能体高出 90.2%,而仅 token 用量一项就解释了约 80% 的表现方差——并点出了条件:只有在任务的价值撑得起这笔开销时经济性才成立,而那些需要智能体之间共享上下文的任务受益最少。

那个倍数没告诉你的,是它的形状。团队把 15× 读成一个常数,并照常数去做预算:四条分支而不是两条,同样的倍数,两倍的速度。它不是常数。它是三个随分支数以不同方式增长的项之和,而其中只有一项是你本来想做的那件事。把它们拆开,有用扇出的上限就从算术里掉出来,而不是从品味里。本页假定的是主管—工人那个形状;何时以及为何走多智能体是「到底要不要扇出」这个更前面的问题。

步骤 2第一项:每条分支都要重付一段单一循环早已缓存住的前缀。

一个串行的智能体循环便宜得很容易被忽略,因为提示缓存在默默干活。系统提示与工具定义坐在上下文最前面、跨步不变,于是它们被写进缓存一次,之后每一轮都以折扣价读回来。Anthropic 公布的倍数是:默认五分钟生命期下,缓存写入为基础输入价的 1.25 倍(一小时选项为 2 倍),缓存读取为 0.1 倍——对那些不变的字节打了九折。提示缓存讲机制,上下文缓存的经济性讲打平的算术。

一个子智能体的前缀是另一段前缀。它有自己的指令、通常有自己收窄过的工具集、以及自己那一块任务——这恰恰是子智能体继承了什么讲的事,它意味着父级建好的那条缓存条目用不上。每条分支都以一次未命中开场,并付出写入倍数:

# the prefix term, in base-input-token equivalents
prefix        = 8000            # system + tool definitions, identical for every branch
steps_each    = 6
write_mult    = 1.25            # 5-minute cache write
read_mult     = 0.10            # cache read on every later step

# one sequential loop doing all 18 steps: one write, 17 reads
seq   = prefix * write_mult + prefix * read_mult * 17        # = 23,600

# three branches of 6 steps each: three writes, 5 reads apiece
three = 3 * (prefix * write_mult + prefix * read_mult * 5)   # = 42,000

# 1.8x on the prefix alone, before any of the real work is counted.
# six branches of 3 steps: 6 * (10,000 + 1,600) = 69,600, i.e. 2.9x

这一项随分支数近乎线性增长,而每条分支的工作量在缩小,于是你每把扇面放宽一次,前缀在账单里的占比就升高一次。实际后果是给分支长度设了一个下限:一条只跑三步的分支,大部分预算花在自我介绍上。如果你的前缀很大——通常的原因是工具目录太宽——那么扇出在还什么都没做之前就已经很贵,而削减每条分支的工具集,是比任何换模型都更大的杠杆。智能体循环的 KV 缓存预算是同一套账在单一循环上的版本。

步骤 3第二项:分支的输出被计费两次,而第二次落在主管身上。

一条分支得出的任何结论都得送到主管那里,这意味着每条分支报告的每个 token 都先按输出价生成一次,然后在归并时又作为输入被读一次。这种双重计费与更少的 token vs 更便宜的 token 在单一循环内指出的是同一个机制,但扇出把它变成了结构性的:归并的输入在构造上就是所有分支输出之和,于是主管自己的上下文随分支数增长。

三条分支各返回 2,000 token 的报告,就是在主管自己的前缀之上再给它 6,000 token 要读。六条给它 12,000,而此时归并这一步成了整个任务里最贵的单次调用——也是最暴露在「有效上下文」问题之下的那一步,因为一个在六段不同叙述、一万两千 token 之上做推理的主管,正处在召回会退化的那个区间里。那就是扇出真正的天花板,而它是一个上下文天花板,不是一个成本天花板。

所以该给扇面封顶的是归并预算,而不是工人数量。先决定你愿意在多大的归并输入上做推理——比如说 8,000 token——再从你真正能强制执行的报告大小倒推分支数。如果算术说四条、而你需要十二条,答案不是十二条分支:而是一棵归约树(两两归并,每次归并产出一份同样大小的报告),或者一个共享存储——让分支往里写,主管去查询而不是整篇吞下。共享记忆与黑板是后一个选项,而塑形工具返回结果讲的是怎么把一份报告做小到值得返回。

一份并不比产出它的工作小多少的报告,说明分支选错了单位。扇出在「一条分支读很多、返回很少」时才划得来——搜索、分流、逐文件分析。当分支的任务是产出用户将要阅读的那段文字时它不划得来,因为那时什么都压缩不了,而你已经为「省下零」付了前缀与双重计费。

步骤 4第三项:分支彼此看不见,于是重叠的发现工作按分支逐份付费。

第三项正是 Anthropic 那句提醒所指的。相互独立的分支会把同样的东西重新发现一遍。两个研究相邻问题的子智能体抓取同样的三份文档、读同样的背景,而两边都为此付了全价;主管随后在归并里把两份关于它的叙述都读了一遍。架构里没有任何东西会把这件事去重,因为让并行成为可能的那种隔离,正是阻止共享的那种隔离。

这一项在 token 总数里看不见,在一条追踪里一目了然。测量很便宜:数一数跨分支去重后的检索文档数或工具调用参数数,再除以总数。比值低于约 0.7——也就是超过 30% 的分支工作是重复的——意味着你的分解切过了一条共享依赖,而不是沿着一条独立的缝切开;重新切这个任务会胜过任何程度的逐分支优化。多智能体失败模式编目了那条坏缝还会造成什么,而子智能体模式对照讲了当缝怎么切都不干净时该伸手去拿哪个形状。

一个便宜的局部修法:给分支们一个共享的、读穿式检索缓存——同一次向量或文档抓取,在工具层按键记忆化,在模型之外。它帮不了模型侧的重复(每条分支仍把那段文字读进自己的上下文),但它消掉了检索与重排的开销,而在文档密集的工作上,那经常是更大的那条线。

步骤 5延迟那一侧会饱和,因为「很多条里最慢的」只比「少数几条里最慢的」慢一点点。

这笔交易的收益那一半也被读错了。一次扇出的挂钟时间是最慢的那条分支加上归并,不是平均那条分支——而那个最大值会随你加分支而变大。对于尾部较轻的分支时长分布,k 次抽样的期望最大值大致按「均值加上一个含 ln k 的项」增长,而这有一个具体且不方便的后果:

  • 1 → 3 条分支是那次大胜。三个时长为 T 的串行子任务要花 3T;并行则大约花 1.2–1.4T 再加归并。算它两倍加速、换 1.8 倍的前缀账单——在一个延迟有价码的任务上,这笔交易是好的。
  • 3 → 6 条分支不是。最大值从大约 1.3T 移到约 1.5T,于是你什么都没省下、甚至可能亏,而前缀项从 1.8 倍跑到 2.9 倍,归并输入翻倍。

有两个真实效应让情况比这个模型更糟。厂商的速率限制会把你的一部分扇出串行化——不管你有没有这么计划——于是第六条分支可能并不在前五条启动时真的启动了,测出来的最大值于是把排队也算了进去。而智能体工作里的分支时长是右偏的、不是轻尾的:一条分支撞进重试循环或一个慢工具,就给整个任务定了钟。诚实的总结是:扇出买到的是延迟上的第一个二倍,之后就所剩无几。在为那个倍数花钱之前先给它定价,因为延迟的美元价值并不均匀——给延迟定价展示了那条成本曲线在一秒以内几乎是平的、到十秒为止是线性的、之后呈台阶状,而度量智能体延迟讲的是怎么拿到分布而不是一个均值。

步骤 6该怎么做:三个条件、一个上限、三件仪器。

三条同时成立时扇出,任何一条不成立时串行:

  • 分支足够长。步数多到足以摊平每条分支的那次前缀写入——经验法则是每条至少五到六个用到工具的步骤。比这更短,那么在单一循环里用并行工具调用就能拿到大部分并发而完全不付前缀成本,而这正是团队会跳过的那个选项,因为它不够有趣。
  • 分支相互独立。在一份带追踪的抽样上,重复工作比值高于约 0.7。低于它,就重新切这个任务。
  • 分支会压缩。一份明显小于产出它的工作量的报告,而且这个大小要写进子智能体的指令里强制执行,不是指望它自己变小。

然后按归并预算、而不是按工人数量给扇面封顶:挑一个你愿意在其上做推理的最大归并输入,除以被强制执行的报告大小,那就是你的分支数。需要更多,意味着该上归约树或共享存储,而这两样都比一个淹在六段叙述里的主管便宜。请给每条分支留一个真正的终止条件,因为一个步数上限会把一条慢分支变成一条被无声截断的分支,而归并分不出这两者的区别。

有三件仪器告诉你某一次扇出是否值回本钱,而其中没有一件是 token 总数:

  • 每条分支的缓存命中率。如果分支的命中率低于那个单一循环曾经的水平,你就在自己的追踪里找到了那个前缀项。随扇面放宽而下降的命中率,就是「扇面太宽了」的信号。
  • 归并输入占总输入的比例。它上升,意味着你在花钱读自己的输出。超过大约三分之一,就先重构,再谈优化。
  • 每多一美元换来多少秒。这笔交易真实的汇率,对照一个你仍在抽样上跑着的串行基线来测。它是唯一能回答「第四条分支该不该存在」的数字,而它几乎总是比第三条分支的更差。

在放宽一次扇出之前:测出你那段可缓存前缀与被强制执行的分支报告大小,然后为你想要的分支数算出前缀项与归并输入。只在分支足够长、相互独立且会压缩时扇出;分支很短时在单一循环里用并行工具调用,分支很多时用共享存储或归约树。按你推理得过来的归并预算给扇面封顶,而不是按你付得起的工人数量。给每条分支的缓存命中率、归并占输入的比例、以及对照串行基线「每多一美元省多少秒」上仪表——并把结果折回每成功任务成本里,因为一个更快的错答案仍然是一个失败的任务。如果只留一句话:扇出买到的是延迟上的第一个二倍,其余部分按超线性收费。