代理系统里最贵的 bug 是那种没有任何报错的 bug。Worker 提出一个草稿。Verifier 用一段反馈把它驳回。Worker 修改。Verifier 再次驳回。循环一直转下去,trace 越来越长,账单越爬越高,而从外面看,这个系统似乎在 工作——而且很尽职,因为两个模型都在干各自该干的活儿。没有人定价进去的是:验证器的接受标准在不同调用之间并不固定。worker 在追的那个目标本身在动,而循环没有任何收敛保证。
你以为自己交付的是"迭代到满意为止",其实你交付的是一次对极值可能根本不存在的空间的搜索。
一张干净的架构图背后藏着的设计错误
这种模式已经常见到不再被当作一个"设计选择"了。你把一个 worker 模型和一个 verifier 配对——一个独立的模型、一个带评分量表的 judge、一个对抗式 critic,有时是同一个基础模型用不同 system prompt 的更严格版本——然后让它们互相传球,直到 verifier 签字放行。架构图就是两个方框加一根回头的箭头。它看起来优雅。它看起来正确。它编码了一个图本身从未点明的假设:验证器是一个函数。也就是说,给定同一个候选,它会返回同一个判决。它的接受集合是输出空间的一个稳定子集,而 worker 的任务就是到达那里。
验证器不是一个函数。它是一个随机策略。把同一个候选用非零温度跑五次,你会得到五个判断,而它们在边界附近常常互相不一致。把它跨天来跑,提供商在你不知情的时候调整模型,你会得到一种漂移——任何版本钉死都接不住,因为你的管线里压根没把它钉死。把它用在第一轮的某个候选上,再用在第三轮的同一个候选上(此时它已经嵌在一段更长的、带修改历史的 trace 里),你会得到不同的答案,因为上下文变了。
一个把"这个随机的、非严格单调的函数返回了 accept"当作收敛测试的精修循环,并没有在向质量收敛。它在从一个边界逐轮波动的输出空间区域里采样。worker 的修改并没有朝着一个不动点移动。它们在绕着一个不动点公转。
为什么"agent 越来越好了"是对轨迹的误读
团队会给循环加上仪表,看着 verifier 的置信度分数在迭代里一路爬升。第一轮:0.62。第二轮:0.74。第三轮:0.81。曲线单调,图表一路右上,自然的解读就是 worker 在变好、verifier 在识别这种变好。可以上线了。
这种解读漏掉了两件大概率正在发生的事。
第一,验证器正在被训练——非正式地、通过 trace 训练——去识别 worker 心目中"认真的尝试"长什么样。trace 越长,看起来就越像在用力。在一个没有真值 oracle 的 judge 看来,"用力"读起来就是"质量",于是即便底层产物没有发生实质变化,置信度分数也会上升。你测的是验证器的置信度,而不是产物的质量,而这两者已经脱钩了。
第二,worker 正在循环内部学习这个验证器的表层特征。它不是在靠近一个高质量产物;它是在靠近这个验证器会接受的那种修辞形状。如果你的验证器喜欢含糊措辞,worker 就更多地含糊;如果你的验证器给项目列表的分比给散文的分高,项目列表就会繁殖。你看到的"收敛"是 worker 在对 judge 过拟合,这是一种和"worker 在产出更好的输出"完全不同的现象。两者在仪表盘上长得一模一样。
把这两种情况分开的诊断手段很便宜,却很少有人跑:把第五轮被"接受"的输出,扔回一个没有任何 trace 历史的全新 verifier 实例里。如果这个全新的 verifier 拒绝了它,你的循环就不是向质量收敛的。它收敛到了两次具体 roll-out 之间的局部一致,而这种一致在对话之外没有任何意义。
max-iterations 上限是一种供认
你见过的每一个生产级 verifier 循环都有一个 max_iterations 参数,通常默认三或者五。这不是"以防万一"的兜底。这是一个架构层面的供认:循环没有收敛证明。如果循环能收敛,你根本不需要上限;循环自己就会停。上限存在的原因是:搭这个系统的团队,在某个地方知道循环可能不会停,他们决定"成本封顶"比"质量保证"更重要。
这么读,含义就更尖锐了。最终落到用户那里的产物是三种东西之一。一种是 verifier 明确批准过的输出(最理想的情况,不算上前面提到的 verifier 过拟合问题)。一种是 verifier 在最后一轮允许的迭代上还在拒绝、而系统因为上限触发就那么上线了的输出(无声失败,通常是难任务上的主导情况)。一种是介于两者之间、被系统用某种 tiebreaker 选出来的输出——最先被接受的、得分最高的、最后产出的——这些里没有一个是架构当初假装提供的"收敛保证"。
第三种情况就是团队在不自知的状况下默默降低产品质量的地方。验证器循环成了一台"信心洗白机":它让团队感觉系统有质量门,而实际上系统拥有的是一个伪装成质量门的预算门。
能恢复终止性的几种模式
补救方案有一个共同形态:它们让验证器的行为独立于循环动态,这样循环搜索的就是一个固定靶,而不是一个跟着自己一起动的靶。
把验证器锁定为一次调用。 每一轮只跑一次 verifier,在 worker 看到判决和理由之前就把它们冻结,然后把这份冻结判决当作后续修改的真值。验证器没有在修改进行到一半时改主意的资格。这么做的代价是你失去了对边界拒绝"再核对一次"的能力,但收益是 worker 现在是在对着固定批评做修改,而不是在追一个移动的批评。这是最便宜的修复,而且消除了一整类病态振荡。
采样并锚定。 第一轮就把验证器并行跑 k 次去评第一稿,取多数判决和一个综合理由,把循环剩下的部分锚定到这一个判断上。你在前面付一点点 ensemble 成本,换来后续所有迭代都有一个稳定的靶。验证器的方差被吸收到第一轮里,而不是被涂抹到每一轮上。
分歧时上交裁判。 worker 和 verifier 照旧跑,但当 worker 已经修改两次而 verifier 仍然拒绝时,升级到第三个模型——一个 prompt 不同的裁判,或者一个更强的基础模型——它的判决是终审。这个裁判不在循环里。它是循环的下匝道。这把一个可能无界的振荡变成一个有界的三步流程,并且有保证的终止状态。
先把 spec 写出来再迭代。 在 worker 写任何东西之前,让 verifier 把接受标准发出来,作为一个显式的、结构化的产物——一份清单、一份评分量表、一份会触发拒绝的失败模式列表。把这份产物在整个循环里锁死。worker 现在是在对着一份稳定的 spec 做修改,而后续轮次的 verifier 是在对照它在第一轮发布过的同一份 spec 做检查,而不是每一轮重新推导一遍标准。这最接近人类 review 的工作方式:reviewer 先写需求,然后对照需求来判,而不是对照一种每轮都在更新的"感觉"。
注意这些模式有什么共同点。它们都打破了 worker 的行为和 verifier 的标准之间的逐轮耦合。它们都把验证器的随机性变成一次性成本,而不是每次迭代都付一份。它们都给循环一个能保持静止足够久、让"满意"成为一个有意义的状态的满足曲面。
没人真的算过的那笔成本账
一个迭代上限为五、每轮拒绝率为六成的 verifier 循环,平均每个请求要花掉大约四个完整的 prompt-completion 周期,而且 prompt 每一轮都在长,因为 trace 在累积。朴素实现会在每次调用时把整个修改历史重发一遍,这意味着第五轮的输入 token 数大概是第一轮的五倍。再乘以每轮两个模型(worker 和 verifier),你为一个用户只看一次的输出付了十次模型调用,以及一个关于迭代次数二次增长的输入成本。在难任务类目下,每轮拒绝率更高,你在大部分请求上都会撞到上限,你的每个交付产物的有效成本不再是平均成本,而是上限次数乘以每轮成本。
这就是那些"自主"代理舰队周末账单 4,200 美元、月账单 87,000 美元故事背后的数学。验证器循环是今天生产代理架构里通往失控账单最干净的一条路,因为它有一个性质:系统看起来越有用(修改越多、核对越多、越尽职),它就越贵。除了迭代上限之外没有任何天然的断路器,而上限——如前所述——同时也是那个正在掩盖质量失败的东西。
成本仪表盘和质量仪表盘在告诉你关于同一个循环的不同谎言。成本仪表盘说循环昂贵但有界。质量仪表盘说置信度在上升。两者都没有告诉你:在难样例上,上限正在触发,而你在用户不知道的情况下交付了被拒绝的输出。
把收敛率当作一等公民的 eval 指标
这种立场所要求的 eval 纪律,大多数团队都跳过了。搭一个小的 worker-critic 配对基准,故意做得对抗性强:那种 worker 和 critic 对"什么算好"的先验不一样的任务,那种 critic 的评分量表欠规范到两次 roll-out 会互不一致的任务,那种 worker 的自然风格会触发 critic 挑刺的任务。把循环跑通端到端。不光测最终接受率,还要测两件事。
一:循环撞上限的频率有多高?在你真实流量分布上,撞上限率超过 10% 或 20%,就是系统在告诉你它在你输入的相当一片子集上不收敛。把那一片当作它自己的产品问题,而不是当作长尾来处理。
二:跨 roll-out,最终输出的每任务方差有多大?同一个任务跑十次。如果循环在向质量收敛,十个 final 在实质上应当相似。如果它们差别很大——不同的结论、不同的建议、不同的代码——循环就不是在收敛;它每次都在从一个不稳定的接受区域里挑一个不同的点。用户看见的产物在那个区域里实际上是随机的,而你基于单次 roll-out 的 eval 在测一个你从未刻画过的分布里的一份样本。
大多数团队这两项都不测,因为架构本身呈现出的严谨外观——我们有 worker 也有 verifier,看那张图——已经替代了证明这种严谨真能兑现的纪律。
精修循环不是一个质量原语
最深的那个架构觉悟是最难咽下去的。一个精修循环,本身,不是一个质量改进原语。它是一个没有收敛保证、由预算强制终止、接受标准跨调用漂移、最终输出由 tiebreaker 而不是由产物自身任何属性选定的搜索过程。把它当作一个质量门,和把测试运行器里一个五分钟的 timeout 当作"代码正确"的证据,是同一个错误。
只有当满足曲面独立于被评判的代理而保持稳定时,这个循环才会成为一个质量原语。那是循环周围的系统的性质——验证器冻结、spec 锁定、裁判升级、收敛率 eval——而不是循环默认就有的性质。worker-critic 循环默认的性质是:在产出进展的表象的同时消耗预算。
先把曲面建出来,再信任循环能在它上面找到不动点。一个团队上线了"迭代到满意为止"却没证明那个满足曲面是稳定的,他们交付的就是一次对极值可能根本不存在的空间的搜索——而那个上限,是阻止这次搜索永远跑下去的唯一东西,这意味着上限,而不是 verifier,才是真正决定你用户最后拿到什么的仲裁者。
会员专享
余下内容仅对会员开放。
会员可读完整内容 —— 每一个公开发布的观点背后,那些框架、决策与推理的全貌。
- —完整文章,包含未公开存档的部分
- —可落地的工作框架,附带权衡与决策依据
- —新文章抢先看,先于公开发布
随时取消 · 一次订阅,畅读全部