[控场AI]
· 8 分钟阅读· 4,373 字

模型自报置信度不是测量:可校准概率的工程审计指南

模型自报置信度不是测量:可校准概率的工程审计指南

模型自报的置信度是生成出来的 token,唯有记账 join 结果才能成为可分支的测量值。

文章指出,模型在提示词里自报的置信度数字与答案出自同一次前向传播,本质上是续写句子的产物,而非经过校准的概率。这类「装饰性数字」无法在生成时查阅结果,改变提示词只能挪动数字分布,改变不了第二列缺失的事实。判别方法是看分布形状:真测量的数字在势均力敌的问题上会变低,装饰性数字则始终停在 0.8-0.9。解法是把判断从提示词搬进带契约的组件,用返回的 job_id 作为记账第一列,先记每次判断的行,再 join 到结果计算分档命中率。只有当这一步在你自己的流量上跑过,那个数字才是测量,否则只是文风。

那个被误用的 if 分支

几乎每个接入大模型的系统都会写这样一句提示词:

回答问题,并以 0 到 1 之间的数字报告你的置信度。

模型照办,返回 {"answer": "...", "confidence": 0.92},然后下游代码做了件看起来理所当然的事:

if result["confidence"] > 0.8:
    apply(result["answer"])          # auto-accept
else:
    queue_for_human(result["answer"])

这个 if 在替你做真实决策,而它依赖的那个 token,之所以出现在那里,只是因为它续写句子续得顺。模型自报的置信度与经过校准的概率,根本不是同一类对象——差别不在哲学层面,而在于"这个数字被写下来之前,必须先存在什么"。

rss source: A confidence number your model reports is not a measurement

生成出来的数字 vs 测量出来的数字

生成出来的数字与答案出自同一次前向传播。模型见过成千上万句"答案是 X,我相当有把握",能把这个句子续得最顺的数字,就是一个高数字。这个过程中没有任何环节去查阅结果,因为结果尚不存在:选择还没做出,结果还没发生,无所谓对错。

测量出来的数字则由记账产生。有人把数字存下来,之后又把实际发生的事存下来,再把两者放进同一张表。校准只能来自这个 join——这是唯一的来源。没有这个 join 的数字不是概率,只是一个长得像概率的词。

这也解释了为什么"让模型校准得更好一点"行不通。改措辞只会改变所报数字的分布,改变不了第二列缺失的事实。你可以通过提示词让模型在同一输入上把 0.95 改成 0.6,但你没法通过提示词让它记录下 0.6 的那些判断是不是十次对了六次——只有你的系统能在时间维度上做到这件事。

校准(calibration) 是概率预测领域的标准概念,指一个预报系统在长期使用中,所报概率与实际发生频率的一致程度。例如若系统对所有「我给出 0.7 把握」的判断统计下来,实际正确率大致为 70%,就说它在 0.7 这个档上是校准的。校准只对可重复的预测才有意义,且必须通过预测—结果的配对样本估计,不能从单次输出读出。需要注意的是,一个系统可以很准确(整体正确率高)但完全没校准,也可以校准良好但仍有用;校准与判别力(discrimination,能否区分难易)是两个独立维度。Brier 分数与可靠性图是最常用的校准度量工具,它们都要求存在一张「预测值—真实结果」的表,这也正是文章反复强调的那一次 join。

前向传播(forward pass) 是神经网络执行一次推理时从输入层到输出层的完整计算过程。模型在这个过程里逐层做矩阵乘法与非线性变换,最终在输出层得到下一个 token 的概率分布并采样。关键在于,一次前向传播是自包含的:它只能访问当前上下文里的内容,无法查询外部数据库、无法知道未来结果,也无法回忆自己上一次在类似输入上是否答对。因此当模型在同一个前向传播中「顺便」吐出置信度数字时,这个数字与答案共享同一套上下文与同一批权重,它反映的是该 token 在当前语境下出现的自然程度,而非对答案正确性的独立评估。要让数字成为测量,必须在传播之外引入第二个信息源,也就是后续发生的结果。

识别技巧:看分布的形状

有个便宜的办法能判断你手上是哪种数字:看它在问题变难时会不会动。

装饰性数字又高又窄,几乎不随难度变化。让模型在一对势均力敌的选项上给个数字,再让它在一个明显有标准答案的案例上给个数字,两次都会落在 0.8 出头或 0.9 出头。它追踪的是答案听起来有多笃定,而不是证据有多接近。

真正的近距离决策产生的数字看起来完全不同。据该服务公开的记录:27 个逐字记录的日常判断,两个选项都站得住脚,所报分离度落在 27.3% 到 88.3%,中位数 74.0%,没有一个超过 90%。这个形状本身就是重点——在真正接近的问题上,一个经过结果核对的数字是"被允许"变低的,所以它就低了。在这类问题上报告 90 多分的服务,报告的不是概率,是热情。

装饰性数字测量性数字
来源与答案同一次生成中采样出的 token一条记录:现在存数字,之后存结果,再对比分档
响应什么答案听起来有多笃定证据实际有多接近
遇到更难输入几乎不变下降,常低于 0.7
能算出什么什么都算不出,无法 join每档命中率,以及分档是否诚实
作为分支依据的失败模式带自信标签的抛硬币,生产环境里不可见错误归你,但你看得见,也能修分档
最省事的区分办法同一问题换两种问法,看数字追不追问法直接查日志里到底有没有结果那一列

分箱/分档(binning) 是把连续的所报数值切成若干区间(如 [0,0.2)、[0.2,0.4)…)后逐桶统计实际命中率的做法,是可靠性图与分档准确率表的基础。分档的关键纪律是先定桶、后看数:桶的边界必须在观察到结果之前确定,否则容易把边界拟合到数据上,制造出看似校准的假象,这就是文章说的「把分档拟合到数据上,然后管这叫校准」。常见的坑还包括每档样本过少导致命中率噪声极大,以及档位与动作映射脱节——分档的目的是驱动「哪些判断可以自动执行、哪些必须交给人」的决策,而不是产出一张好看的表。

把判断从提示词里搬进组件

解法不是更好的提示词——那只会挪动数字。解法是把判断变成一次带契约的调用,并像系统里其他调用一样记录:选择、所报分离度、理由,挂在一个之后能取回的 id 下。

curl -s -X POST "https://mcp.turingcorp.net/mcp" \
  -H 'content-type: application/json' \
  -H 'accept: application/json, text/event-stream' \
  -H "authorization: Bearer $AGENT_PASS" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"decide","arguments":{
        "task":"Which draft of the outage notice do we send to customers?",
        "optionA":"Short: what broke, what we did, when it was fixed.",
        "optionB":"Longer: the same content plus what we changed so it cannot recur."
      }}}'

返回结果里带着 job_id,与决策和理由并列。把这个 id 当作记账表的第一列,而不是错误处理的细节:没回来的调用按 id 取回而不是重跑;回来了的调用就是一行之后可以 join 的记录。

然后是两个动作,都枯燥,都强制:

  1. 把每次判断记成一行——id、所报分离度、输入、时间戳,以及你的系统据此做了什么。
  2. 之后把这些行 join 到结果上——按档分桶,计算每档命中率。

在第 2 步在自己的流量上至少跑过一次之前,那个数字不是测量,是文风。

MCP(Model Context Protocol) 是一套让模型通过标准化 JSON-RPC 接口调用外部工具与数据源的开放协议,文章示例里的 tools/call 正是该协议规定的工具调用方法。把「判断」从提示词搬到 MCP 工具的实质意义在于:提示词里的数字只存在于模型的自述文本中,随对话结束而蒸发;而工具调用的返回值是你系统自己产出并落库的结构化对象,可以带上 job_id、输入快照、时间戳等字段,成为记账表里可查询、可关联的一行记录。这样一来,模型不再负责「报告概率」,它只负责产生一次可被回填结果的决策事件,而校准所需的第二列数据则由你的运行时在将来补齐。

可直接复制的检查清单

把下面这段贴进任何把关提示词变更的评审环节:

  1. 不要在一个你从未计算过分档准确率的数字上做分支。
  2. 记录每次判断:id、数字、输入、时间戳、采取的动作。
  3. 在看结果之前先定义分档。否则你会把分档拟合到数据上,然后管这叫校准。
  4. 把日志 join 到结果,计算每档命中率,把这张表放在团队看得见的地方。
  5. 当数字和结果打架时,改的是"档到动作"的映射,而不是重写提示词——后者只会挪动数字。

两个推论随之而来。其一,分档表是唯一能告诉你 0.74 在你自己场景下意味着什么的东西:在该服务的使用场景里,低数字意味着两个选项很接近、选择应留给人,而这个说法之所以站得住脚,是因为分档准确率是被测量并公开的,而不是被声称的。其二,数字一旦成为组件就变得可测试:你能对它做回归测试,能改阈值并看什么会崩,能在不靠猜的情况下回答"它多久错一次"。

一个数字之所以是测量,是因为模型之外有东西核对过它。其余的都只是碰巧含数字的句子——而如果你的控制流在读那个句子,模型就在替你选阈值。

常见问答

问:既然不能信,那还让模型报置信度干什么? 答:报出来的数字本身有价值,前提是它进入你系统里可追踪、可 join 的记账流程,成为一行用 id 标识的判断记录,而不是直接拿来做控制流分支。

问:多写几轮提示词能不能改善校准? 答:不能。措辞只改变所报数字的分布,模型无法记录"0.6 的那些判断是否十次对了六次",只有你的系统在时间维度上做得到。

问:没有历史流量数据,怎么开始? 答:先看分布形状。同一问题换两种问法,如果数字追着问法走,就是装饰性数字;然后给分档定义和结果日志建好结构,等第一次 join 跑通。

#模型置信度#置信度校准#概率校准#LLM置信度#AI决策系统#分档命中率#模型可靠性#置信度日志记录
分享:

相关推荐