文档导航

代码持有阈值,模型只给概率

如果把几百个真实项目放在一起看,最稳定的一条共同点是:

模型只负责回答「有多可能」,代码负责回答「够了没有」。

这不是风格偏好。它是从模型自报的置信度不可信这个事实里推出来的唯一稳妥做法 —— 而这一点在置信度到底可不可信那篇里有独立的实测证据。

具体长什么样

Choice 与 Score 返回答案时附带一个 confidence,Noul 直接返回 0 到 1 的概率。 接口本身不替你做任何判断,阈值是你的。真实项目里这句话落实成了很具体的形态:

  • 一个跑在 Cloudflare Worker 上的脏话过滤器,阈值、max() 的聚合策略、以及 对外的 OpenAPI schema 全都写在 Worker 代码里,模型只是其中一个被调用的函数。
  • 一个浏览器扩展用它判断评论是否要遮起来,扩展自己钉着 0.85 / 0.7 / 0.5 三档, 并且检查失败时保持遮住(宁可多遮,不可漏遮)—— 这也是阈值策略的一部分。
  • 一个工单告警分流器把「要 page」定义成 P(SEV1) + P(SEV2) ≥ 0.80, 这个 0.80 写在它自己的配置里,不在模型里。

三者的共同点是同一个:换模型时阈值不动,换阈值时模型不动。 把阈值交给模型去「决定」的话,这两件事就绑在一起了 —— 而你要调阈值的时候, 会发现自己在改一个提示词。

为什么这条原则这么重要

因为它把两种失败分开了:

  • 模型回答得不好 —— 去改问题、改 state、换模型。
  • 阈值定得不好 —— 去改代码里那个数。

混在一起之后,一个「准确率不够」的问题既可能是模型的问题也可能是阈值的问题, 而你没有任何办法把它们分开。上一条的告警分流器之所以敢把 0.80 直接写进配置, 正是因为那个数可以被单独拿出来复盘、单独拿出来调。

反例:置信度不总是它看起来的那个东西

这条原则有一个必须知道的前提:你拿到的那个数,未必是校准过的概率。

有独立复算发现,Score 题返回的 confidence 经常正好等于分数的小数部分, 而且这类输出高度集中(在 121 个这样的输出里)。这不是「模型很自信」, 更像这个字段是从分数本身推导出来的一个值。

区别很大:

  • 如果它是校准过的概率,那么 0.85 可以用在「85% 的情况下我会是对的」这种推理里。
  • 如果它是分数的函数,那么 0.85 只表示「这一题得了高分」,和「我有多大概率对」 没有关系 —— 拿它去定阈值,是在给一个没有概率含义的数设门槛。

同样地,有一篇独立的测量报告直接去核对模型自报的字段与真值是否相符, 结论是那些字段的取值远没有描述里写的那么细(详见公开质疑)。

怎么办

实践里的做法可以归纳成三步,而且顺序不能反:

  1. 先用阈值做事,别信它的含义。 0.8 这个数是不是「80% 正确」不重要 —— 重要的是在你的数据上,0.8 以上的那些是不是明显更准。这是可以用留出集验的。
  2. 用你自己的标注拟合阈值。 有工具专门做这件事:拿你自己的标注数据, 为每个问题拟合出「达到目标准确率所需的阈值」,用留出集验证, 并且当模型更新破坏了已锁定的阈值时让 CI 失败。
  3. 把阈值锁进代码,并给它一个复核日期。 阈值会随着模型版本过期 —— 和价格一样,是要有人定期回头看的东西,不是一次定终身。

一句话

模型的可信度应该体现在你的阈值上,而不是体现在它的自报数字上。 你可以在自己的数据上度量前者,没法度量后者。