跳到主要内容

重跑反模式:为什么再次运行并不能发现 Bug

· 阅读需 11 分钟
Tian Pan
Software Engineer

当 AI 功能表现异常时,大多数工程师做的第一件事就是再次点击“运行(run)”。这种思路认为,模型是具有随机性的,所以这次运行可能只是运气不好。当第二次尝试产生看起来合理的结果时,工单就被关闭了。团队继续前进。而真正的 Bug——过期的工具响应、检索缺失、仅在包含特定 token 的输入时才触发的系统提示词冲突——仍然完好无损地留在生产环境中,等待下一个用户触发它。

这就是“重跑反模式(rerun antipattern)”,它是 AI 团队从聊天机器人时代继承下来的最昂贵的调试习惯。它看起来很严谨,因为模型确实是非确定性的。它看起来像是一种方差探测。但几乎没有人在重新运行之前写下假设,没有人预先决定多少次运行才算证据,也没有人考虑 token 的成本。正在发生的事情更接近于“老虎机式调试”:你不断拉动杠杆,直到红灯停止闪烁,然后你走开,并确信机器没问题。

重跑看起来像方差,但表现得像幸存者偏差

这种物理动作确实存在一个合理的版本。一个深思熟虑的 N-of-K 样本——比如在 temperature 为 0.3 的情况下运行 10 次,并带有一个书面假设,如“我预计引用步骤在合约 30% 的案例中会失败”——是一种真正的诊断技术。它测试故障模式是罕见的还是常规的,并给你一个分母。研究人员已将其正式化为“pass@k”,并表明重采样中的方差缩减是任何诚实的 LLM 评估的承重原语。《The Good, the Bad, and the Greedy》这篇论文现在已成为一个标准参考,解释了为什么在评估中忽略非确定性会产生误导性的排名。

而“绝望式重跑”从外部看完全一样,但它没有假设,没有记录 K 值,也没有决定什么才算成功的规则。它在结构上是一个幸存者偏差机器。在 10 次无声的重跑中,工程师只记住了成功的运行,最终没有发布任何修复,也没有学到任何东西。那些仅在特定形状的输入(长上下文、特定的工具输出、用户查询中的特殊标点符号)上出现的故障模式,正是重跑无法揭示的,因为循环中没有任何环节在刻意改变输入。

一个有用的测试:如果你不能回答“什么能让我相信这个 Bug 是真实的而不是抖动,以及我需要多少次运行才能确定”,那么你不是在进行方差探测。你是在拉动老虎机的杠杆。

为什么重跑掩盖了真实的 Bug

模型确实是非确定性的,而这正是该反模式躲藏的挡箭牌。但非确定性的空间比大多数工程师想象的要小得多,而它所掩盖的 Bug 通常根本不是随机的。

即使在 temperature 为 0 的情况下,相同的输入也不能保证相同的输出。GPU 归约(reductions)中的浮点数非结合性、改变内核调度的动态批处理大小、决策取决于批处理中其他序列的混合专家(MoE)路由——这些都会将熵泄漏到“确定性”推理中。Thinking Machines 最近的研究将问题专门追溯到了批处理不变性,而 Eval4NLP 2025 关于托管模型确定性的论文则量化了在所谓相同配置的运行中,准确率波动可高达 15 个百分点。所以,是的,模型确实可以为相同的输入返回不同的 token。

但这只是噪声底噪。它不是导致功能失效的原因。重跑反模式最能可靠掩盖的是那些“确定性但有条件”的 Bug:由于缓存 TTL 错误而返回过期数据的工具、在特定日期范围内有漏洞的检索索引、指令在狭窄的用户意图类别上相互矛盾的系统提示词、仅在链条深度超过三步时才将小的上游错误复合为大的下游错误的智能体循环。每当输入匹配触发条件时,这些都会失败;而每当输入不匹配时,它们都会通过。重跑不会改变输入。因此,重跑无法区分这两种情况。它只能告诉你,模型有时会走上一条不触发 Bug 的路径,而这正是用户在提交工单时就已经知道的信息。

那些拥有最强后端调试直觉的工程师往往最容易掉入这个陷阱,因为来自确定性系统的肌肉记忆——“如果它不稳定,就反复运行直到它稳定,然后查看 diff”——在底座是非确定性但 Bug 本身不是随机的情况下,会产生完全错误的行为。

先追踪,再复现

相反的立场是将单次失败的运行视为一个完全可分析的产物(artifact)。在决定 Bug 是否值得复现之前,先捕获一次端到端的追踪(trace),包括每一个提示词、每一个工具调用、每一个检索结果和每一个中间状态。这是快速调试 AI 系统的团队与那些乱撞的团队之间的分水岭。

加载中…
References:Let's stay in touch and Follow me for more thoughts and updates