功劳分配:你到底该改哪个智能体?
一次失败的多智能体运行交给你的是一个数字和八个组件,而那个数字里没有梯度——运行级别的分数不指向任何一个组件。于是团队伸手去拿逐智能体评审,因为它便宜、可并行、还能产出一张令人满意的条形图,然后就把结果当作因果去行动。它不是因果。可用的估计量有三个,它们回答的是三个不同的问题,而挑了一个跟你的决定对不上的,正是一个团队花一个季度去调那个从来不是问题的智能体的原因。
先把问题的名字叫对:这是功劳分配。
强化学习给它起过名字,并围着它疼了一个世纪。你在一幕结束时观测到一个回报,然后必须把责任分摊到促成它的那些决策上——跨步骤的时序功劳分配,以及在多智能体设定下跨行动者的结构功劳分配。一个主管拆解了任务,三个工人执行,一个批评者复核,最终答案错了。这五个决策里,该改的是哪一个?
强化学习用数量和算法解决它:数百万幕、一个价值函数、一条学到的基线。而你有四十条轨迹、一个在读它们的人,和一个截止日期。这个不对称就是"实践看起来完全不像理论"的全部原因,也是为什么这里诚实的方法都是"带有已知偏差的估计量",而不是"解法"。
有两条性质让多智能体的情形比单智能体轨迹分析更难。其一,失败常常在耦合处而不在任何一个组件里——一个工人完全正确地做了被要求做的事,而那个要求本身是错的。其二,组件之间会相互作用,所以责任不可加:如果智能体 C 一直在默默补偿 B 的输出,那么单独修好 B 反而会让整体变差。任何产出逐智能体分数的方法,都在隐含地主张一种它撑不住的可加性。
把这些方法区分开的诊断性问题是:你问的是这份输出好不好、这个智能体有没有贡献,还是这份输出是不是导致了失败?这是三个问题。多数团队问的是第三个,量的是第一个。
估计量之一:逐智能体评审。便宜、可并行,但作用域是局部的。
把每个智能体的输出隔离出来,对照它自己的任务简报打分。评审看到它被分派的子任务和它产出的东西,然后给个分。这是默认做法,因为它跟你已有的一切都能拼上:一份评审配置、跑 N 次、无需重新执行、几秒出结果。
它量的是局部质量:以这个智能体收到的输入为条件,它的输出好不好?这是一个真实且有用的量。它能抓住产出格式错乱的工人、只会盖章的批评者、把警示语丢掉的摘要器。
它看不见的是接缝。想想那个典型的协作失败:主管把一项研究任务拆成三个子问题,而没有一个是用户真正问的那个问题。每个工人都把自己的子问题答得很好。每一个逐智能体评审都返回高分。系统的答案毫无用处,而你的归因说一切正常——这个方法系统性地为一次发生在组件之间的失败,赦免了全部五个组件。这与"逐组件健康检查对错误传播毫无用处"是同一个盲区,只是这次它从评测那扇门进来。
还有第二个更微妙的偏差。逐智能体评审是以"收到的输入"为条件打分的,所以一个被上游智能体喂了污染输入的智能体,被评的是它把垃圾处理得好不好。如果它优雅地处理了垃圾,它得分很高——而这次运行依然失败了。局部质量与系统贡献可以朝相反的方向移动,而那个分数不会告诉你自己身处哪一种情形。
就把它当成它本来的样子用:一个快速筛出明显坏掉的组件的初筛步骤。不要让一张逐智能体分数的条形图,去驱动"该移除或重写哪个智能体"的决定。
估计量之二:消融。有因果性、代价高,而且淹没在方差里。
拿掉一个智能体——或者把它换成一个平凡的直通——重跑整套评测,测量端到端成功率的变化。这是一次真正的干预,它回答的问题也值得问:这个组件有没有贡献?
两笔代价。第一笔很明显:每个组件都要重跑一整套评测,八个智能体的系统就是八套,而如果你还想看两两交互,情况会更糟。第二笔才是毁掉结论的那一笔。
智能体运行是高方差的。同一份配置在同一个输入上会产出不同的轨迹,而四十项任务的端到端成功率带着很宽的置信区间。在噪声底为八个百分点的情况下,四个百分点的消融差值什么也没告诉你——而它照样会被写成"智能体 C 贡献了 4%"。跑消融之前,先做功效计算——为了让你愿意据以行动的最小差值超出噪声,需要多少任务、多少次重复——完全按评测方差与统计功效里写的来。多数团队会发现答案是他们现有评测集的好几倍。
这里还有一个解释上的陷阱。消融量的是在当前配置下的边际贡献,所以一个职责被下游智能体的鲁棒性完全吸收掉的智能体,消融差值会是零,看起来冗余。有时候这是一个真实的发现,你应该删掉它。有时候你只是量到了另一个组件在默默干两份活、并且拿 token 在为此买单——那是另一个事实,需要另一种修法。
# ablation is only informative once the delta clears the noise floor delta = success_with - success_without if abs(delta) < noise_floor(n_tasks, n_reps): report("inconclusive") # not "no contribution"
把消融留给结构性决定——这个智能体该不该存在、这套拓扑要不要塌缩成单个智能体——那里的答案配得上八次全套重跑,而且你关心的差值本来就大。它是何时该走多智能体所问问题的正确工具,也是调试某一次具体失败的错误工具。
估计量之三:反事实回放。信息量最高的那个,也是没人去建的那个。
取一条失败运行的录制轨迹。把恰好一个智能体的输出替换成一份已知良好的替代物——一份人写的答案、一个 oracle 结果,或者一个更强模型的输出——然后在其余一切保持不变的前提下,从那一点继续执行。如果这次运行成功了,那份输出就是导致失败的原因。如果它依然失败,那就不是,你该往上游看。
这直接回答了第三个问题:这份具体的输出,是不是导致了这次具体的失败?三个估计量里只有它能回答,而且它每次运行所携带的信息量远超消融,因为它把轨迹的其余全部按住不动,而不是重新掷一次骰子。
它之所以罕见,原因在工程而不在理论。它要求轨迹存下每一条智能体间消息,且保真到足以从任意一点恢复——这意味着你的链路追踪得是一份重放日志,而不是一件调试辅助。它要求环境可恢复——对纯推理管线是小事,但智能体一旦写过数据库或调过付费 API 就变难。它还要求有一份已知良好的替代物,而对许多子任务而言这意味着一个人要下场,所以这个方法无法覆盖每一次失败。
它回本的地方是反复出现的失败模式。当同一类失败出现到第五次时,为那条路径建回放的代价,已经低于关于"这是谁的锅"的第五次争论,而且它是用一次实验、而不是用一次解读来了结这个问题。它也是那个能延伸成训练数据的技术:一次隔离出单个坏步骤的回放,恰恰就是过程奖励模型想要的监督信号。
三个问题、三件工具,一张值得贴在墙上的表。
- "这个智能体的输出好不好?"→ 逐智能体评审。几秒钟、无需重新执行、能筛出坏掉的组件。对协作失败与继承来的垃圾都是瞎的。永远不是因果。
- "这个智能体有没有贡献?"→ 消融。有因果性、需要 N 次全套重跑、在做功效计算之前毫无意义。回答的是关于拓扑的结构性问题,不是关于某个具体 bug 的问题。
- "这份输出是不是导致了这次失败?"→ 反事实回放。既有因果性又具体,需要可回放的轨迹和一份已知良好的替代物。建一次很贵、每次用很便宜,只对反复出现的模式才值得。
失效模式是:跑了最便宜的那个,却当成跑了最具体的那个来汇报。"我们的归因显示检索智能体是短板"——这句话几乎总是意味着逐智能体评审给它打了最低分,而这与"检索智能体被一个没人去量的主管递了一个糟糕的查询"完全相容——恰恰在这种情形下,改检索智能体不会有用,还会吃掉一个迭代周期。
也要注意三者都做不到的事:在单个智能体内部沿时间归因。它们都把一个智能体当成一个原子。如果你的失败是某个工人前九步都好、第十步开始漂移,那你需要的是步级轨迹分析——那是轨迹与过程评测接手的话题。
在四十条轨迹这个量级上,真正管用的流程。
在多数团队实际所处的规模上——是几十条轨迹,不是几百万条——收益最高的方法并不光鲜,而且是手工的,它每次都该是第一步。
- 手读二十条失败轨迹,标出第一处偏离。对每一条,找到系统状态最早不再处在通往目标的路径上的那一点。什么都别打分;只记下步骤序号和行动者。在 N=20 这个量级上,一个人读轨迹胜过所有可用的自动归因方法,代价是一个下午。第一处偏离的分布,就是你真正的优先级清单。
- 在动手修之前先给偏离聚类。二十条里有十二条落在主管的任务拆解上,那是一个设计发现,不是一个 prompt 调优发现。一个在所有智能体上都摊平的分布,通常意味着失败在它们之间的协议里。
- 修掉最大的那一簇,然后重跑同样的二十条。同样的轨迹、同样的任务——你要看的是偏离点有没有移动,在这个样本量上,这是一个比总体成功率灵敏得多的信号。
- 只为你现在真正持有的那个问题才升级到估计量。如果问题变成了"我们到底需不需要这个智能体",就用足够的重复次数去跑消融。如果变成了"这一次交接一直在污染数据",就为那条路径建回放。如果两者都不是,那你本就不需要估计量。
- 然后才去自动化。等你手上有了二十条带标注的样本可供校准,再把"第一处偏离"的判断编码成一个评审——并且用与任何其他评审相同的标准要求它:一致性要对着你的标注去量,而不是假定。
接受一条规则,上面大半的事就会自己就位:永远不要把一个逐智能体分数当作因果去行动。逐智能体评审用来初筛;消融回答结构性问题;回放回答具体问题;而在你实际拥有的样本量上,手工标注二十条轨迹的第一处偏离胜过这三者。会去读自己轨迹的团队会去修主管。只读条形图的团队,会用一个季度去改进一个完全照吩咐办事的工人。