返回博客

2026.09.30

五个 agent 对一个 agent,各跑 8 小时,114 比 104:多出来的四个到底买到了什么?

同一道推理引擎优化题、同一个外部裁判、同一个模型,一组开五个 agent 在黑板上协作,一组只开一个。结果多 agent 赢了 10%,但两组的前两步是各自独立撞上的同一招,差距全来自一个单干没走到的方向;真正的天花板是那个串行排队的裁判。这篇给两条能自己算的账(评估器容量、每涨一点的 token 价),再拆一则「10 个 agent 发现新算法」的新闻。

多 agent推理引擎评测方法论

同一道题,同一个裁判,同一个模型,各跑 8 小时左右:

              评测次数   最好成绩(B=16 聚合)   相对基线
五个 agent      125         114.14 tok/s          1.71×
一个 agent       73         103.97 tok/s          1.56×
基线                         66.65 tok/s
过线要求                     132

五个的多了 10%。两边都没过线。

我原本想看的是:一群 agent 在黑板上接力,能不能把一个方向挖得比单干更深。结果两组的前两步几乎一模一样,而且是各自想出来的,单干那组根本看不到另一组的代码和留言。多出来的 10 tok/s 全部来自一个方向,单干的那个 agent 没走到那里。

还有两件更意外的事。这场实验的天花板在裁判身上,跟 agent 数量关系不大。第一轮被判了死刑的一个方向,第二轮换了个 kernel,成了全场最大的一块收益。

想直接拿公式的看第 4、5 章;想知道怎么拆「N 个 agent 协作发现了 X」这类新闻,看第 7 章。

1. 题目:让我的推理引擎一次喂饱 16 条序列

上一篇我写过这台 M1 Max 上的自研推理引擎:一次同时解 16 条序列,输出全对,聚合吞吐却是一条平线,66 到 67 tok/s。同一台机器上 MLX 能到 132。我当时给自己写了条闸门,过不了 132 就停,然后就真停了。

没过几天,我看到一则新闻:有团队让 10 个 Opus 在一个留言板上协作,做出了一个号称渐近意义上比 Dijkstra 更快的最短路算法。我想知道「多个 agent + 共享黑板 + 自动裁判」这套打法,放到我自己的难题上到底管不管用。那道停掉的题判据清楚、过线值早就写死了,拿来当考题正合适。

题目就三句:B=16 时聚合 decode 吞吐超过 132 tok/s;B=1 单条速度退化不许超过 2%;16 条序列的输出必须和各自单跑逐 token 一致。

2. 裁判放在 agent 够不着的地方

这类实验最怕 agent 自己给自己打分。模型想交差的时候,会变得非常有创意。

所以裁判是宿主机上的一个脚本。worker 只能往队列里扔 commit,碰不到裁判本身:

  • 正确性用 4 组隐藏 seed 生成的输入(B=2/4/16/16),参照答案来自冻结的基线版本,worker 看不到
  • 一片「冻结区」代码算 sha256,改一个字节直接拒收
  • B=1 回归:基线和候选交替跑,输出 md5 必须一样
  • prefill(把 prompt 一口气读进去的那一段)设了上限,防止有人把时间从 decode 挪到不计时的地方

开跑前我拿四种输入喂它:原版、故意让序列错位、改冻结区、在冻结区外伪造计时输出。原版 PASS 67.04,错位判 WRONG,后两种判 REJECT。四种全对才开跑。

编组:四个 Opus 5.5 worker,各领一个初始方向(投影、MoE、输出头加调度、集成),再加一个 GPT 当挑刺的,只审别人的 PASS,自己不交评测。

对照组就一个 Opus 5.5,同一个裁判、同一套隐藏 seed、同样的轮数上限,放在另一块看板上。它的仓库是只含 master 的干净克隆,挂载目录也分开了,连第一组 worker 跑的时候自己攒下的 3 条经验笔记我都删了。我专门查过,它看不到那边任何一个 commit。

两组共用 M1 上唯一的一块 GPU,评测只能排队串行。这条后面会变成全文最要紧的一个数。

3. 两组各自撞上了同一招

把路径摆出来:

                                   五个 agent        一个 agent
第 1 步  CPU 编码和 GPU 执行重叠      84.26             84.19
第 2 步  合并 encoder                 93 → 94           102 → 104
第 3 步  输出头批量                   负收益            跑通但只有 88,诊断说它只占一步约 4%
第 4 步  MoE / 共享专家分组调度       100.6 → 114       没走到

第 1 步是每隔几层就把攒好的 GPU 命令提交一次,CPU 编下一段的时候,GPU 已经在跑上一段。两边给这个开关起的名字都不一样,结果差 0.07。

说实话看到这里我有点泄气。四个 agent 在黑板上聊了几个小时,跟一个 agent 自言自语,想到的是同一招。

差距全在第 4 步。MoE 模型里有很多「专家」,每个 token 只叫醒其中几个;另外还有一个每个 token 都要用的「共享专家」。原来的写法是每次调用各发一次 GPU 调度,中间夹着一堆同步屏障。负责 MoE 的 worker 把这些调度按组合并,又把关键路径上每个 token 一次的调度和屏障去掉,从 100.6 做到 114。

它是叠在另一个 worker 刚交上来的「砍屏障」成果(100.6)上继续做的。这是整场实验里黑板接力真实发生的那一次。就这一次。大部分时间,是集成者或者 MoE 那位一个人在扫参数。

还有个细节挺扎心。单干那个 agent 在第 2 小时就自己算出来了:要过 132,必须让 16 条序列共享同一次权重读取。然后到截止也没开工。

4. 公式一:评估器才是天花板

125 次对 73 次,这个差距比成绩差距大得多。得先算清楚它从哪来:

评估器容量 ≈ 有效时长 ÷ 单次评测耗时

就是一个除法。我手上有一次完整评测的计时,218 秒,包括编译、四组正确性和 B=1 交替跑。五个 agent 那组有效时长约 8.1 小时(先跑了 2.6 小时,停摆后又续了 5.5 小时):

五个 agent:8.1 h × 3600 ÷ 218 s ≈ 134 次,实际 125 次,裁判忙了约 93%
一个 agent:8   h × 3600 ÷ 218 s ≈ 132 次,实际  73 次,约 55%

裁判那边几乎没空过。再加第五个、第十个 worker,评测次数也涨不上去了,队列只会越排越长。

所以在这个实验里,多 agent 能多买的只有一样东西:同一段时间里,排进队列的那些提交覆盖了更多方向。单干的那个一次只想一件事,裁判一半时间在等它。

推论很直接:开多少个 agent,先看评估器吃不吃得下。评估器一轮几分钟、能并行,那就放心多开;只有一块 GPU 串行评测,开到把裁判喂饱就够了,再多都是在排队。

5. 公式二:多出来的 10 tok/s 花了多少

token 数是按两组各自的会话记录、在各自实验时间窗里求和的,只算 Opus 的输出 token:

每涨 1 tok/s 的 token 成本 = 输出 token ÷ (最好成绩 − 基线)

一个 agent:1.33M ÷ (103.97 − 66.65) = 1.33M ÷ 37.32 ≈ 3.6 万
五个 agent:1.96M ÷ (114.14 − 66.65) = 1.96M ÷ 47.49 ≈ 4.1 万
边际:     (1.96 − 1.33)M ÷ (114.14 − 103.97) = 0.63M ÷ 10.17 ≈ 6.2 万

多出来那 10 tok/s,每一点的价格是单干平均价的 1.7 倍。这还没算缓存读取(127M 对 47M,2.7 倍),也没算那个 GPT 审查员自己吃掉的 0.59M 输入。

贵不贵要看你卡在哪。卡在「想不到下一个方向」,1.7 倍挺便宜;只想把同一个方向挖深,这钱就白花了,第 3 章已经看到,深度上两边差不多。

6. 裁判也会冤枉人

第一轮最后 6 个 WRONG,我点开一看,token 全对。

原因是 prefill 那条上限我写的是绝对值 13000 毫秒。M1 整机状态会漂:同一个基线二进制复测一次,prefill 从 13 秒涨到 15.7 秒,decode 纹丝不动。上限被机器自己顶穿了。那 6 次都是复测,不影响最好成绩,但判决是错的。

改成「同次基线的 1.5 倍」之后,第二轮又冤了 7 次。两组 B=16 的输入里,第二组 prompt 更长,基线自己就要约 13 秒,可上限是拿第一组的基线乘出来的,正好卡在它的正常值上。

更麻烦的是反方向的错:该判错的它判了 PASS。第二轮冠军有个状态标志在批量结束后没人清零,一旦从批量切回单条 decode,某一层会跳过投影、读到上一轮的陈旧结果。裁判只测「纯批量」和「纯单条」,从来没测过两者交替,于是它一路绿灯。是那个 GPT 审查员挑出来的。我补了一个交替模式的检查:故意不清零的变异版,256 个判定点错了 150 个;修好的版本错 0 个。

判据两句:WRONG 但 token 全对,先怀疑裁判;PASS 只证明裁判测过的那几条路径。裁判也要喂变异体,而且每改一次判据就重新喂一次。

7. 拆「N 个 agent 协作发现了 X」

回到那则最短路的新闻。我去翻了一手材料,按三个问题拆。

第一,有没有单 agent 对照?没有。10 个 agent 做出来了,只能说明 10 个 agent 做得出来,说明不了「多 agent」这个方法本身有用。我这边要是没开对照组,大概会把 114 全记在协作头上。可实际上从 66 到 104 那一大段,一个 agent 自己也走完了。

第二,赢在哪个区间、赢多少?新算法只在边数约等于 n·log^(3/4) n 的一条窄密度带上渐近优于 Dijkstra,优势比例是 (log n)^(1/12)。自己代个数(以 2 为底):

n = 100 万:log₂n ≈ 20,20^(1/12) ≈ 1.28
n = 10 亿: log₂n ≈ 30,30^(1/12) ≈ 1.33

十亿个节点,理论优势 1.33 倍,这还是没算常数的数。社区里有人照着写了实现,实测比 Dijkstra 慢 1.4 到 2.9 倍。

第三,裁判验证的是什么?他们用 Lean 做形式化验证当评估器,这是整套打法里最值得抄的部分:快,而且 agent 骗不过它。它能检查纸面证明有没有漏洞,管不到实现跑起来快不快。新颖性也还没有外部专家审过。

所以我现在读这类新闻只看三件事:有没有对照组;优势用什么指标、在什么区间量的;裁判验证的是不是你真正关心的那个性质。

8. 第二轮:判了死刑的方向,成了最大一块

对照做完,我决定让五个 agent 那套从 114 接着跑。判据明确为逐 token 一致,裁判的起点直接设成上一轮冠军的配置,worker 只需要交增量开关。

又跑了 8 小时,145.66,复测 145.33,过线。收益账:

线性注意力 qkv:直读 4-bit 权重的小矩阵乘   113.9 → 120.5
全注意力 q/k/v 批量                          → 123.9
线性注意力输出投影批量                       → 128.6
全注意力输出投影批量                         → 130.9
输出头跨序列批量                             → 142.6   (+11.7)
线性注意力 z 投影批量(只用于 decode)       → 145.7

几乎全是单干那位第 2 小时就算出来、却谁都没动手的那件事:让 16 条序列共享同一次权重读取。

最大的一块是输出头,+11.7。第一轮它被判了死:五个 agent 那组做出来是负收益,单干那组诊断说它只占一步约 4%。第二轮换了一个一次读权重、同时服务 16 条序列的 kernel,就成了。死的是那个实现,那个方向本身没死。我在上一篇里也写过「输出头批处理最多快 1.39 倍」,那个上限是按「每条序列各读一遍权重」算的;实现一换,字节那一项就变了。以后记死路,我会写到具体哪个 commit、哪个 kernel。

另一个 MoE 方向倒是真死:让用到同一个专家的序列共享读取,结构开销太大,最好 108,比不做(113.8)还慢。

还有个运维上的笑话。第二轮只评测了 43 次,比第一轮少一大截,很大一部分原因是 agent 自己下班了:四个 worker 里三个提前宣布完成;集成者凌晨 02:33 过线之后,把自己挂起说「等人 review」,一直停到早上截止,白白丢了 5 小时。第一轮也有一次,开跑 2.6 小时轮数上限就耗光,全员停摆,那时最好成绩才 94。

过线不是停止条件,这句话得写进任务里。不写的话,agent 一过线就急着交作业。

9. 这次哪些数不够硬

  • 每组只跑了一次。10% 的差距里有运气成分,换一批 seed 重跑一次,排名倒过来我也不会太意外。
  • 对照不完全对称。五个 agent 那组开局没有「别空轮询」这条提示,停摆后续跑才补上;单干那组一开始就有,这对单干有利。单干那组中途被我自己另一个会话重启 agent 网关打断过约 5 分钟,裁判跑在同一个进程组里,一起被带走了。
  • 第二轮没有对照组。145.66 只说明五个 agent 从 114 接着跑到了 145,说明不了单干从 114 跑不到。
  • 218 秒只测了一次,是第二轮开跑前复测冠军那一次;第一轮每次评测的耗时我没逐条拉出来。第 4 章的容量是估算,量级可信,93% 这个数别太当真。
  • 放宽判据有代价。两轮冠军换几个 seed 都会出现「近平局翻转」:参照答案里第一名和第二名的 logit 只差 0.002 到 0.011,一点数值误差就能让它翻过去。裁判那 4 个隐藏 seed 刚好没踩中。
  • 输出头那两个数我没对上。第一轮诊断说它只占一步约 4%,第二轮它让一步的耗时少了约 8%(130.9 → 142.6)。两次诊断的基准配置不同,我没拆清楚。
  • B=1 有一个罕见的非确定性。两轮里都出现过一次输出 md5 对不上,单独重跑 10 次都正常,根因还没查。
  • 单机、单模型、单题。一台 M1 Max 64GB,一个 4-bit 的 35B MoE,一个 Opus 5.5。换题换模型,数字全变。

10. 下次想开一群 agent,先过这几关

  1. 先问有没有自动裁判。没有就别开,一群 agent 只会互相附和,把错误放大。
  2. 实测一轮评测多久,算容量:时长 ÷ 单次耗时。worker 开到把裁判喂饱为止。
  3. 裁判放在 agent 碰不到的地方:隐藏 seed、冻结参照、冻结区 sha。开跑前喂变异体,全判对才开。
  4. 所有跟机器状态有关的上限,写成同次基线的倍数,并且每个 case 用各自的基线。
  5. 同时开一个单 agent 对照组,同裁判、同预算,仓库、挂载、笔记彻底隔离。没有对照组,赢了也说不清是谁赢的。
  6. 任务里写死:过线和本方向证伪都不算停止条件,截止前不许自己宣布完成,也不许挂起等 review。
  7. 裁判进程和 agent 网关分开托管,别让一次重启把裁判一起带走。
  8. 看到 WRONG 先看 token 对不对;看到 PASS 先问裁判测没测过这条路径。
  9. 记死路写到 commit 和 kernel 那一级,别只写「这个方向不行」。