模型给了个概率:这个数在工程里能拿来做什么 说明本文讨论的是模型输出端自带的概率与不确定性信号在工程里怎么用、不能怎么用属于模型能力承接话题不涉及具体模型版本与价格。AI 领域版本迭代极快凡涉及版本号、价格、可用性请以你阅读时的官方页面为准。文中代码为结构示意请按自己的技术栈调整后再上生产。一、这个数是从哪来的三个层次的信号名字像但不是一回事2026 年中一部分厂商的推理接口开始在响应里带回概率字段。工程侧拿到之后最容易被统一叫成「置信度」的其实混着三个不同层次的东西口径完全不能互换。单 token 概率解码时每一步在词表上有一个分布接口通常只回 top-k 个候选及各自的 logprob。它描述的是「在这个位置上下一个词是什么」。序列对数似然把整段输出里每个已选 token 的 logprob 加总是求和不是平均。它描述的是「这一整串 token 在这段上下文里被生成的可能性」。多次采样分布对同一输入跑 N 次统计答案是否稳定。它不是从模型内部读出来的而是你在外面把同一件事问很多遍制造出来的。这三个层次的区别是这篇文章后面所有结论的地基。第一个是逐词的粒度在一句话里的某个位置第二个是整段的把逐词的量累起来第三个是跨样本的根本不在模型内部而在你的调用侧。用逐词的信号去回答「这个答案可靠吗」等于拿一个词的把握去替整段话背书。层次从哪来单位直接回答的问题粒度单 token 概率解码时词表分布取所选 token 的 logprob每个位置一个数这个词在这里常不常见位置级序列对数似然各 token logprob 累加整段一个数这串字面输出有多「顺」序列级多次采样分布同一输入跑 N 次后聚类统计一组样本的占比换个随机种子答案还一样吗跨样本级对数似然为什么不能直接读成百分比logprob 是负数因为 log 的输入落在 0 到 1 之间。想让单个 token 变成「概率多少」做一次 exp 就恢复了exp(-0.1) ≈ 0.905也就是九成左右。问题出在序列层。整段的对数似然是求和长度越长和越负。一段两百来个 token 的输出即便每个位置的概率都在 0.9 上下累加起来也会到 -20 这个量级exp(-20)已经小到没有直觉含义——它是个 2.06e-9 的数但你不能因此说「这段输出只有十亿分之二的可能」。这个数字只反映「长度」不反映「对错」。所以序列层有两个可行的读法要么只看同一批样本之间的相对大小要么先除以 token 数做长度归一把「每 token 平均对数似然」当指标。归一之后同一批样本之间才有可比性但即便如此它也仍然不能读成百分比。还有一层更容易被忽略的错位这个数衡量的是「这串字面输出被生成的概率」不是「这句话为真的概率」。模型完全可以用很高的概率生成一句错误但极其流畅的话。把前者当成后者是后面所有翻车的源头。⚠️ 代码待验证importmathdefseq_logprob(token_logprobs:list[float])-dict:# token_logprobs 是这一段里每个已选 token 的 logprob负数rawsum(token_logprobs)# 求和口径长度越长越负nmax(len(token_logprobs),1)return{seq_logprob:round(raw,4),# 整段对数似然不能读成百分比seq_logprob_norm:round(raw/n,4),# 除以 token 数用于同批比较token_count:n,min_token_logprob:round(min(token_logprobs),4),# 最犹豫的位置}single_probmath.exp(-0.1)# 单 token 概率 ≈ 0.905序列层不要这么读再往下看接口实际返回的结构就能对上号了。⚠️ 代码待验证{choice:{message:{role:assistant,content:北京的常住人口约两千万},logprobs:{content:[{token:北京,logprob:-0.021,top_logprobs:[{token:北京,logprob:-0.021},{token:中国,logprob:-4.10}]},{token:的,logprob:-0.004,top_logprobs:[{token:的,logprob:-0.004}]},{token:常住,logprob:-0.610,top_logprobs:[{token:常住,logprob:-0.610},{token:户籍,logprob:-0.910}]}]}}}这份结构里要盯的不是content里的文本而是每个位置上的logprob和top_logprobs。前者是「选了这一 个」的对数概率后者是「当时前几个候选分别多大」。两者的差值往往比绝对值更有信息量——它告诉你模型在这个位置上犹豫没犹豫。二、它不是校准过的置信度校准是什么「校准」这个词有一个非常具体的定义不是感觉上的「准」。做法是把所有预测概率为 p 的样本归到一堆看它们里面真正正确的比例是不是也接近 p。把 p 当横轴、实际正确率当纵轴画出来理想情况是一条对角线这条线叫可靠性图。一个信号只有在过完这道工序之后才配叫「置信度」。模型带回的 logprob 没有经过这道工序。它是训练目标和采样过程的副产物不是被校准过的置信度。这两者之间隔着一次完整的再校准——需要人工标注一批样本、看偏差曲线、再决定怎么变换。模型为什么天然不校准原因有两条都很实在。第一条在预训练目标里。模型被优化的是「最大化观测序列的似然」也就是让训练数据里出现过的话概率更高。这个目标和「让输出的概率与事实正确率对齐」是两件事。前者只关心文本像不像真的被这样写过不关心文本描述的事是不是真的。第二条在对齐阶段。对齐过程偏好确定、流畅、不犹豫的表达——含糊其辞、反复自我怀疑的答案会被打低分。于是模型被塑造得「听起来很确定」。而这份确定是和语气绑定的与对错无关。两条合起来结果是一个稳定的系统性偏差模型在它答错的地方往往也给很高的 token 概率。幻觉内容尤其如此因为它恰好是被流畅地生成出来的——不流畅的那部分反而常常是模型在犹豫。典型翻车高置信但全错拿 logprob 直接当阈值卡最先撞上的就是这类样本。场景表面上看到的实际发生了什么根因事实问答关键 token 概率很高答案是编的但句式极其顺流畅度与真值无关数值抽取数字位置 logprob 不低数字来自邻近文本被错位引用局部概率不携带校验分类打标标签 token 概率 0.9 以上长尾类别被系统性误判类别先验没有反映在 logprob 里跨配置对比A 配置平均分高于 B只是温度不同分布整体偏移数字口径不带采样参数这几行的共性是一样的你看到的「高」是字面生成的高概率你想要的「可靠」是对真实世界的判断力。中间的桥——校准——没有搭。三、能用它的三个地方复核分流拿它排序不拿它定论这是三个用法里最先落地、收益也最直接的一个。落点是把一批候选输出按信号强度排个序信号弱的那一档进人工复核队列信号强的那一档直接过。它把有限的复核人力压在最可能出问题的那部分样本上而不是均匀抽查。边界要卡死两条。第一它只决定「谁先被看」不决定「谁对谁错」。分流的结果永远是「需要人看一眼」不是「这条不可信」。第二阈值只用来控分流比例不要用来解释单条样本——「这条分数低所以必然错」这句话是不成立的。多次采样一致性一个可以自己造的信号如果接口不回 logprob或者回了但你不敢用还有一条路同一输入跑 N 次温度设大一点让每次采样有差异然后把答案做规范化再聚类看主簇占比。这是个特别有用的信号因为它是你自己造的口径完全可控也不依赖某家厂商是否暴露内部字段。落点有三给答案配一句「多数采样结果一致」给不一致的样本挂复核标记或者拿一致性本身当异常输入探测器的输入。但边界同样硬一致性不等于正确性。如果模型对某个问题有稳定的错误偏见N 次采样可能全部落在同一个错误答案上此时一致性是满分而答案是错的。一致性回答的是「稳不稳定」不是「对不对」。这两者只有在模型没有系统性偏差时才近似重合。异常预警看信号分布的整体位移第三个用法不是看单条而是看一群。落点是把一段时间的信号值画成分位数曲线或者把一致性比例的日曲线画出来。如果分布整体左移、或者长尾明显变厚说明这段时间的输入在变——可能是用户提问的领域变了、可能是上游检索给的上下文质量降了、也可能是某类新输入把模型逼到了不擅长的区域。边界是这是分布层面的信号不能下推到单条样本。看到「今天的平均对数似然比上周低」正确的动作是去查输入分布而不是去挑出今天最低的那几条逐条问责。个体噪声会淹没在分布里单点解读必然过拟合。用法落点能回答不能回答复核分流按信号排序弱的进人工队列谁先被看这条是否真的错多次采样一致性同输入跑 N 次聚类看主簇占比答案稳不稳定答案对不对异常预警看信号分布的分位数与长尾变化输入分布有没有变具体哪条出了问题⚠️ 代码待验证fromcollectionsimportCounterimportredefnormalize(text:str)-str:# 规范化必须和业务口径一致大小写、空白、标点、数字格式texttext.strip().lower()textre.sub(r\s,,text)textre.sub(r[。、,.!?],,text)returntextdefconsistency(samples:list[str])-dict:# samples 是同一输入在温度 0 下跑 N 次的输出counterCounter(normalize(s)forsinsamples)top_answer,top_countcounter.most_common(1)[0]return{n:len(samples),top_answer:top_answer,top_ratio:round(top_count/len(samples),4),# 主簇占比distinct:len(counter),# 不同答案个数}这段里最容易做错的地方在normalize。规范化口径必须和业务对齐否则两次表述不同、实际同义的答案会被算成两个簇一致性被低估。数字格式尤其明显——「两千万」和「20,000,000」该不该归一取决于你的业务怎么判断相等。四、不能用的三个地方当质量阈值卡发布最常见的误用是给发布流程加一道门平均对数似然低于某个值就不放行。这样做有两处硬伤。一是信号不校准阈值没有绝对含义同一个数在两类任务上的实际错误率可以差很远。二是这个信号对「任务难度」极度敏感——简单任务整体分数高难任务整体分数低。用一条横线去卡结果是难的、真正需要谨慎的改动被拦得最多简单的、风险不大的反而全过。要卡发布用的是一个经过人工标注、能在固定任务集上复现的评测基线而不是这个原始信号。跨模型或跨配置比较数字「A 模型的平均对数似然是 -0.3B 是 -0.5所以 A 更好」——这个推理每一步都站不住。原因在于这个数高度依赖几件事分词器怎么切、词表多大、温度与 top-p 设成多少、输出长度分布。换个分词器同样的文本切出来的 token 数就不一样序列对数似然的量级直接变。换个温度同一模型的分布也跟着变。跨这些边界比数字比的是配置差异不是能力差异。拿它给成本或风险定档第三种用法更隐蔽有人拿平均信令值去给一批请求分档档位决定「多花算力」还是「少花算力」或者决定风险等级。这个用法错在把「模型自己的把握」当成了「客观风险」。前面已经说了高把握加错误是完全可能的组合。用它来分配算力最坏的情况是把算力从真正高风险的请求上挪走了——因为那些请求恰好是模型很流利地答错的类型信号反而好看。不能用处想达到的目的为什么不行该用什么替代当质量阈值卡发布拦住质量下降的版本信号不校准且随任务难度漂移固定任务集上的人工标注评测基线跨模型/跨配置比较选出更强的模型数字受分词器、长度、采样参数影响同一输入同配置的成对人工比较给成本或风险定档按把握分配资源高把握加错误是常见组合业务侧的明确风险规则五、工程落点字段怎么随响应传回来信号的第一个工程问题是它在链路上怎么流动。多数接口把它放在响应体的logprobs位置只回 top-k不会回整个词表分布——完整分布既大又没太大用。落地时有三个取舍点要定死回不回、回多少、回哪一段。回不回决定你付不付这份带宽与解析成本回多少决定你是 top-1 还是 top-5影响你能不能算「候选之间的差值」回哪一段决定你只看关键字段还是整段——抽取类任务通常只回结果字段那部分比整段回省得多。日志里留什么、存多久⚠️ 代码待验证uncertainty:seq_logprob:-3.91# 序列对数似然求和口径seq_logprob_norm:-0.31# 除以 token 数后的值用于同批比较token_count:12.6min_token_logprob:-2.30# 关键字段里最低的那个位置sampling:# 没有它上面的数字没有意义temperature:0.7top_p:0.9n_samples:5consistency:top_ratio:0.8# 多次采样的主簇占比distinct:2route:review# 分流结果用于事后回看阈值准不准schema_version:3# 字段口径本身也要版本化三个字段值得单独说。seq_logprob_norm是给同批比较用的绝对量级没有意义。sampling必须和上面的数字绑在一起存——脱开采样参数谈信号值等于脱开单位谈长度。schema_version容易漏但一旦你改了归一或长度口径历史数据就没法直接和新数据比了版本号是唯一能救回来的东西。存多久取决于用途。用来做异常预警需要足够长的时间窗画分位数一般按周或按月留用来做阈值复盘和回归样本需要长期留低分样本但可以只留分位低的那部分不必全量存。与采样参数的耦合必须一起记录这张表是第五章的核心把它单独列出来。采样参数变化后信号怎么变跨配置比较还有效吗temperature 升高分布被抹平所选 token 的 logprob 普遍走低无效temperature 降到接近 0分布尖锐logprob 普遍走高但多样性丢失无效top-p 收紧候选被截断长尾被砍掉分布偏尖无效n_samples 变化一致性统计的稳定度变样本少时抖动大无效结论就一句任何两次信号值的比较必须建立在采样配置一致的前提上。配置不一致时先对齐配置再谈数字。完整版资料清单本文用到的信号字段对照表与阈值复盘记录都整理在里面了扫码即可获取六、阈值怎么才算有依据先有一批带人工标注的样本阈值不能拍脑袋第一件事是攒样本。做法是从真实流量里抽一批几百到几千条覆盖主要任务类型对每条输出做人工判定「正确 / 错误」同时把当时的不确定信号值一起记下来。这里有一个必须坚持的细节人工标注的判据要和业务口径一致而不是标注者个人的宽容度。否则你标出来的「错」是标注者的口味用它调出来的阈值也只是在拟合这份口味。同一批样本最好有两个人独立标、再看一致性不一致的那部分单独讨论或剔除。扫一遍阈值看的是分流比例和错分代价有了样本和标注就可以扫阈值了。对每一个候选阈值算两件事一是这个阈值会把多大比例的样本推进复核队列分流比例二是被放进不同档的样本里判错各有多少错分代价。注意这里看的不是「准确率」一个数。分流是个取舍问题阈值放松复核队列变小但漏掉的错误变多阈值收紧抓到的错误变多但人力成本上升。你要找的是这两者之间业务能接受的那个点不是一个理论上的最优点。⚠️ 代码待验证defsweep(samples:list[dict],thresholds:list[float])-list[dict]:# samples: [{score: float, correct: bool}, ...]# score 越低表示信号越弱越低越该进复核rows[]total_wrongsum(1forsinsamplesifnots[correct])fortinthresholds:routed[sforsinsamplesifs[score]t]caughtsum(1forsinroutedifnots[correct])rows.append({threshold:t,route_ratio:round(len(routed)/len(samples),4),# 分流比例wrong_caught:caught,# 抓到的错wrong_missed:total_wrong-caught,# 漏掉的错precision_in_route:round(caught/max(len(routed),1),4),})returnrows跑完这张表把route_ratio要花多少人和wrong_missed会漏多少错并排看。业务方认的不是「最优点」而是「我们的人力能覆盖 X% 的分流、同时能接受漏 Y% 的错」这个组合。别拍整数最容易犯的毛病是直接取 0.9 或 -1.0 这种整数当阈值。信号的分布不会刚好在整数处有拐点拍整数等于放弃了一次寻优。阈值应该来自上面那张表取一个在「分流比例」和「漏错数」之间变化开始变陡的位置——再往下调分流比例涨得快但抓到的错涨得慢说明边际收益在下降。除此之外还要做两件事一是记住这个阈值是在哪批样本、哪个 schema 版本下调出来的二是给它留一个回滚位——标注口径一变它就该重调。这三档之间的取舍是单调的阈值越松分流比例越小、复核越省人力但漏掉的错越多阈值越紧则反过来抓得全可复核队列会先撑不住。业务方要选的从来不是「哪档最好」而是「我们的人力能覆盖多大的分流比例、同时能接受漏掉多少错」。七、落地顺序与观测先在只读的复核链路上用新信号别一上来就挂动作。第一条落地链路应该是只读的信号只用来排序、打标、写日志不触发任何写操作、不改变任何输出。这样做的理由很实在——信号不校准初期阈值一定是偏的。如果它直接连着「拦截」「重试」「转人工」这些动作标定期的误判就是真实的业务损失。放在只读链路上跑一两周你才有数据去校阈值此时误判的代价只是「复核队列排得不够好」。稳定后再和动作挂钩只读跑稳之后再逐步挂动作而且顺序要从小到大先挂「打标」——给输出附一个内部标记不改用户可见内容再挂「排序」——用它决定复核队列的先后最后才挂「分流」——真正让一部分样本走人工。每挂一级都要回头看一眼上一节的表对应不上实际分流比例和当初预算的差多少漏掉的错有没有变多。差得远就先退回去别硬上。观测三件事落地之后日常要盯的就是三件事。观测项看什么异常意味着什么信号分布分位数的日曲线整体位移提示输入分布变了分流比例实际队列占比 vs 预算占比持续偏离说明阈值或采样配置被改过阈值命中率卡在阈值边缘的样本量边缘堆太多说明阈值该重扫了第三项容易被忽略但最有用。如果大量样本的分数正好挤在阈值附近说明这个阈值卡在分布最密的地方一点噪声就会让它们来回跳档——此时应该重扫一次阈值找一个密度低的落点。三件事都要连着采样配置的变更记录一起看任何一次温度或 top-p 的调整都会让上面的曲线一起动不把配置当变量记下来就会把配置变更误读成模型行为变化。完整版资料清单本文用到的信号字段对照表与阈值复盘记录都整理在里面了扫码即可获取附表 A关键取舍一览取舍本文结论判断依据位置三个层次的信号能不能混用不能token / 序列 / 采样各说各的单位与粒度完全不同第一章序列对数似然怎么读只看相对值或长度归一不读成百分比求和口径随长度无限变负第一章能不能当置信度用不能它是训练副产物不是校准值模型在答错处也给高概率第二章幻觉信号的成因流畅度与真值无关语气由对齐过程塑造第二章复核分流用它做什么排序不做单条定论阈值只控比例第三章多次采样一致性看稳定性不看正确性系统性偏差会让采样一致地错第三章异常预警的粒度分布级不下推到单条个体噪声会淹没在分布里第三章用它卡发布不可改用人工标注的评测基线信号随任务难度漂移第四章跨模型比数字不可比的是配置差异分词器、长度、采样参数都在干扰第四章用它给成本或风险定档不可高把握加错误是常见组合第四章日志里必须记什么归一值 采样参数 schema 版本脱开配置数字没意义第五章信号存多久预警按周月样本只留低分档按用途决定留存策略第五章阈值怎么定人工标注样本 扫描分流与漏错拍整数等于放弃寻优第六章落地第一步只读链路不挂动作标定期误判会有真实代价第七章挂动作的顺序打标 → 排序 → 分流逐级上每级都要回看实际与预算的差第七章日常观测盯什么信号分布、分流比例、阈值命中率三者要连着采样配置变更一起看第七章附表 B术语速查表术语含义单 token 概率解码某一步在词表上所选 token 的概率接口常以 logprob 形式返回序列对数似然整段输出各 token logprob 的累加值随长度变负长度归一序列对数似然除以 token 数用于同批样本之间比较多次采样一致性同一输入跑 N 次后主簇答案的占比衡量稳定性校准使「预测概率为 p 的样本」中真正正确的比例接近 p 的过程可靠性图以预测概率为横轴、实际正确率为纵轴的偏差曲线logprob对数概率取值为负单个 token 做 exp 可恢复为概率top_logprobs某个位置上候选 token 及其对数概率的列表复核分流按信号强度排序把弱信号样本送人工复核的机制采样参数温度、top-p、采样次数等直接改变概率分布的形状schema 版本信号字段口径的版本号口径一改历史数据就不可直接比较阈值扫描在一批标注样本上遍历候选阈值观察分流比例与错分代价只读链路信号仅用于排序与打标、不触发写动作的落地方式阈值命中率分数落在阈值附近的样本占比过高说明阈值卡在分布密集区写在最后这篇用到的资料写这篇文章时我把几个模型的不确定信号文档、字段表和实测记录都对了一遍顺手整理成几份配套的东西大模型学习路线图从 LLM 基础到 Agent 开发各阶段该学什么、用什么资料大模型全套教程按主题分好的视频与文档清单大模型实战好书24 本附每本适合的阶段资料是我自己整理的放在下面这个码上扫码即可获取添加时备注「大模型」优先通过。拿到之后建议先看学习路线图那一份先定位自己在哪个阶段再决定学什么比一上来就啃框架效率高得多。