Skip to content

第 7 章 · 非确定性要用统计分层对付

pixiu 锚点:docs/lab/E3-nondeterminism-layering.mdpixiu/eval/runner.py:eval_llm_judge_statistical

开篇:单次结果不可靠

agent 工程最难接受的一件事,是 LLM 的输出是概率性的。同一个输入,跑两次,结果可能不一样。

这在传统软件里是不可想象的——你 assert add(1,2)==3,它永远是 3。但在 agent 里,你让 LLM 对同一段 signal 叙事打分,它可能第一次给 1.0,第二次给 0.3。

如果你用传统软件的思维,跑一次 eval 看通过/不通过,你会被这种飘动折磨死:同一个用例,今天过明天不过,你都不知道是代码退化了还是模型今天心情不好。

解决办法只有一个:承认单次不可靠,跑多次,看通过率。 但"跑多次"听起来简单,里面的学问比你想的大——这一章就讲这个。

实证:裁判确实会飘

我在 pixiu 的 lab 里做了个实验(课题 E3),专门验证"裁判飘不飘"。

设计很简单:拿同一段 signal 叙事,让 LLM 裁判连打 5 次分,看波动。三档叙事(好/中/坏),结果是这样:

叙事5 次打分均值标准差通过率(阈0.8)
[1.0, 1.0, 0.3, 1.0, 1.0]0.860.3180%
[0.7, 0.7, 0.67, 0.5, 0.67]0.650.080%
[0.0, 0.0, 0.0, 0.0, 0.0]0.000.000%

看"好"那一行——5 次里有 4 次打 1.0,但第 3 次突然打了 0.3。同一个输入,相邻两次差了 0.7。这就是飘。如果你只跑一次,恰好抽到 0.3 那次,你会把一个好叙事误杀掉。

反直觉发现:飘的分布是不均匀的

但实验揭示了一个反直觉的事——飘不是均匀随机的。 看标准差那一列:

  • 极差输入完全不飘(std 0.00)——坏叙事 5 次全是 0.0,没有任何争议
  • 极好输入飘得最厉害(std 0.31)——好叙事偶尔会"过度挑刺",突然打低分
  • 中等输入轻微飘(std 0.08)

这个发现推翻了一个隐含假设。很多人以为"LLM 输出的随机性是均匀的,像个骰子"。不是。 裁判在"明确的好坏"上很稳定,在"好但可能被挑刺"的边界上最不稳定。

这对 eval 设计影响很大——它意味着不同用例需要的运行次数不一样,不能一刀切。

解法:跑多次看通过率

先说基本的:既然单次不可靠,就跑多次。pixiu 在 eval/runner.py 里落地了一个函数 eval_llm_judge_statistical——跑 N 次,统计通过率,用阈值(比如 80%)判定。

为什么是"通过率 80%"而不是"单次 100%"?因为 80% 让"大概率对"的过——好叙事 5 次过 4 次(80%),即使有一次飘到 0.3,整体仍判通过。这比二元判断科学得多。

这一步,pixiu 做了一件旧手记承认没做的事。旧手记第 7 章原话:

"这个分层策略我在方法论文档里写了,但代码里还没有完全落地——目前所有用例还是统一跑 5 次。这是'理论和实践的落差',我承认。"

pixiu 把这个"落差"补上了——eval_llm_judge_statistical 真正实现了多次统计,还有配套测试 test_statistical_judge_pass_rate(用 E3 的真实数据验证)。

更进一步:运行次数要按可判定性分层

但 pixiu 的 E3 实验不止验证了"要跑多次",还修正了"怎么分层"。

旧手记的分层是按维度:代码断言维度跑 3 次、裁判维度跑 5 次。这个分法对,但不够细。E3 实证显示,裁判维度内部的稳定性还和输入可判定性相关

  • 极差/明确输入——裁判一致(std 0),少跑就行
  • 好/模糊输入——裁判飘(std 0.31),要多跑

所以修正版命题是:运行次数应按"用例的可判定性"配置,而非统一 5 次。 明确的用例少跑省时,模糊的用例多跑保可信。

这就是这一章标题里"分层"的完整含义——不只是"代码维度 vs 裁判维度"两层,而是"按可判定性"的连续谱。

这对你的 eval 意味着什么

把 pixiu 的实证翻译成可操作的建议:

  1. eval 不能跑单次。 涉及 LLM 输出的,至少跑多次看通过率。
  2. 用通过率阈值(如 80%),别用二元判断。 容忍偶发飘动,抓住大概率对。
  3. 运行次数按可判定性配,不要一刀切。 明确的用例(极好/极差)少跑,模糊的(中等、边界)多跑。
  4. 先做个稳定性探针。 像 pixiu 的 eval_e3_stability_probe.py 那样,对你关心的用例跑 5 次,看标准差——你会直观看到哪些用例飘、哪些稳,再据此配运行次数。

这一章的工具:非确定性自检

  • [ ] 你的 eval 是跑单次还是多次?涉及 LLM 输出的,单次基本不可信。
  • [ ] 你用的是二元判断(过/不过)还是通过率阈值?
  • [ ] 你有没有测过你的用例"飘不飘"?(跑 5 次看标准差)
  • [ ] 你的运行次数是一刀切,还是按用例的可判定性分层?

小结

非确定性是 agent 工程和传统软件工程的根本区别。对付它的武器是统计——跑多次、看分布、用阈值。

但 pixiu 的 E3 实验告诉我们一个更深的点:非确定性本身也是不均匀的。 极端情况稳定,边界情况才飘。所以光"跑多次"不够,还要"按可判定性分层"——这是从实践中修正出来的认知,不是纸上谈兵。

下一章讲 eval 的另一面——怎么用 eval 驱动开发,而不只是事后检验。

下一章

第 8 章 · eval 驱动开发 —— 先写 eval 再写功能,以及 eval 本身的工程卫生。