1. 一个被忽视的工程问题:LLM 的置信度为什么不可信
1.1 从一次线上事故说起
去年年底,我负责的一个智能问答系统上线不到两周,就出了一次让我印象深刻的故障。用户问了一个关于内部报销政策的问题,模型给出的回答语气非常笃定,甚至用了“根据规定,报销比例是 80%”这样的表述。但实际政策是 70%,而且这个 80% 是模型从一段无关的旧文档里“拼接”出来的。更麻烦的是,系统前端展示的置信度是 0.92,运营同学看到这个数字,直接把它当成了高可信答案放进了知识库。
这件事之后,我开始认真思考一个问题:LLM 输出的置信度,到底在多大程度上反映了答案的真实可靠性?答案可能让人不太舒服——在绝大多数场景下,它反映的只是模型生成这个 token 序列的“流畅程度”,而不是事实正确性。换句话说,模型很擅长“自信地胡说八道”。
这个现象在业界有个很形象的说法,叫“置信度欺骗”。它不是模型故意骗你,而是因为 LLM 的训练目标本质上是“预测下一个 token”,而不是“判断自己是否知道答案”。这两者之间的鸿沟,就是大量 Agent 系统、RAG 系统、智能客服系统踩坑的根源。
1.2 为什么这个问题在 Agent 时代被放大了
如果只是单轮问答,置信度不准顶多让用户多核实一次。但到了 Agent 场景,问题就严重了。Agent 的核心工作模式是“感知—决策—执行”,它需要根据模型的输出来决定下一步调用哪个工具、是否继续追问、要不要触发人工审核。这时候,一个虚高的置信度会直接导致错误决策被放大。
举个例子,一个负责处理退款申请的 Agent,如果模型对“这笔订单符合退款条件”的置信度是 0.88,Agent 可能就直接执行退款了。但如果这个 0.88 其实是模型对“这句话读起来很顺”的置信度,而不是对“事实判断正确”的置信度,那后果就是真金白银的损失。
我后来查了不少资料,也试过一些开源方案,发现大家普遍在用两种思路:一种是让模型自己“反思”,比如让它输出“我不确定”或者“我需要更多信息”;另一种是外挂一个校准模块,对模型的原始概率做后处理。前者依赖模型的指令遵循能力,不稳定;后者更工程化,但需要解决一个关键问题——校准本身不能太慢,否则在 Agent 的实时循环里根本跑不起来。
这就是我后来关注到“33ms 校准概率引擎”这个方向的起因。33ms 是什么概念?大概是一帧 30fps 画面的渲染时间,对于 Agent 的单步决策来说,这个延迟几乎可以忽略不计。如果真能做到这个量级,那它就有机会成为 Agent 架构里的一个标准组件,而不是一个离线分析工具。
1.3 这篇文章适合谁看
如果你正在做 LLM 应用开发,尤其是 Agent、RAG、智能客服、自动化决策这类对可靠性有要求的场景,那这篇文章应该能帮你少走一些弯路。我会从问题本质讲起,拆解置信度校准的核心思路,然后重点分析一个 33ms 级别的校准概率引擎该怎么设计、怎么落地、有哪些坑。中间会涉及一些概率和工程细节,但我会尽量用生活化的类比来解释,保证你不需要概率论博士背景也能看懂。
另外说明一下,文中提到的具体实现方案,有一部分是基于公开资料和常见工程实践的合理推演,因为原始项目没有公开完整代码。我会在关键地方标注哪些是“实测经验”,哪些是“基于常见做法的补充”,方便你判断参考价值。
2. 置信度校准的核心思路拆解
2.1 先搞清楚:LLM 的“置信度”到底从哪来
要理解校准,得先知道原始置信度是怎么产生的。目前主流 LLM 在生成每个 token 时,都会输出一个概率分布,表示“下一个 token 是词表中每个词的概率”。比如模型生成“北京”这个词时,可能给出 0.7 的概率,生成“上海”给 0.1,生成“广州”给 0.05,等等。这个概率就是最原始的置信度信号。
但问题在于,这个概率是条件概率,它依赖于前面已经生成的 token。也就是说,模型在生成“是”的时候,已经“假设”了前面“报销比例”这几个字是对的。如果前面错了,后面的概率再高也是建立在错误前提上的。这就像一个人在山路上走,每一步都觉得自己踩得很稳,但如果第一步就踩偏了,后面越稳越危险。
更麻烦的是,LLM 的训练数据里充满了各种“看似合理”的表述。模型见过大量“根据规定,X 是 Y”这样的句式,所以当它生成类似句式时,概率会天然偏高,哪怕 X 和 Y 的对应关系是它编的。这就是所谓的“流畅性偏差”——模型把“像真的”和“是真的”混为一谈了。
2.2 校准的目标:让概率回归真实频率
校准的核心目标,用一句话说就是:让模型说“我有 80% 把握”的时候,实际上真的有 80% 的概率是对的。这在学术上叫“概率校准”(probability calibration),衡量指标通常用 ECE(Expected Calibration Error)或者可靠性图(reliability diagram)。
举个直观的例子。假设我们收集了 1000 个模型置信度在 0.8 左右的回答,如果校准做得好,那这 1000 个回答里应该有大约 800 个是正确的。如果实际只有 500 个正确,那说明模型过度自信了,置信度虚高。反过来,如果实际有 950 个正确,那就是过度保守。
校准的方法大致分三类:
- 基于温度缩放(Temperature Scaling):最简单,用一个标量温度参数 T 去调整 softmax 的分布。T>1 让分布更平缓,降低高置信度;T<1 让分布更尖锐。优点是快,缺点是对复杂偏差拟合能力有限。
- 基于 Platt Scaling / Isotonic Regression:在模型输出后面接一个小的映射函数,把原始概率映射到校准后的概率。Platt 用 sigmoid,Isotonic 用分段常数函数。比温度缩放灵活,但需要额外训练数据。
- 基于特征工程 + 轻量模型:除了原始概率,还引入其他特征,比如 token 熵、生成长度、检索相关性分数等,训练一个小的分类器或回归器来预测“这个回答正确的概率”。
33ms 引擎大概率走的是第三条路,因为纯温度缩放虽然快,但效果往往不够。而引入额外特征后,可以用一个非常轻量的模型(比如逻辑回归、小型 GBDT、甚至几层 MLP)来做推理,在 GPU 上跑 33ms 是完全可行的。
2.3 为什么是 33ms:延迟预算的工程账
33ms 这个数字不是随便定的。在 Agent 的决策循环里,一次完整的“感知—思考—行动”通常要控制在几百毫秒到几秒之间。如果校准模块本身就要花 200ms,那它在实时场景里基本没法用,只能放到离线分析里。
我算过一笔账:假设 Agent 单步决策的总预算是 500ms,其中 LLM 推理占 300ms,工具调用占 100ms,那留给校准和决策逻辑的时间只有 100ms。如果校准能压到 33ms,那它只占 6.6% 的预算,完全可以接受。而且 33ms 还有一个好处——它接近人眼的“一帧”感知阈值,在交互式场景里,用户几乎感觉不到额外延迟。
要做到这个量级,几个关键设计点必须满足:
- 模型必须极小:参数量控制在几千到几万级别,不能是另一个 LLM。
- 特征必须预计算或轻量计算:不能在校准阶段再去跑一遍大模型。
- 推理必须走 GPU 或高度优化的 CPU 路径:纯 Python 循环肯定不行,得用 ONNX Runtime、TensorRT 或者类似的推理引擎。
- 批处理要支持:Agent 可能同时处理多个候选答案,校准引擎要能一次算一批。
这些约束条件,直接决定了整个引擎的架构选型。
2.4 和 RLCD、Laya 这些热词的关系
热词里出现了 RLCD 和 Laya,我理解 RLCD 可能是指“Reinforcement Learning from Calibration Feedback”或者类似的校准反馈机制,Laya 则可能是一个模型或框架的名字。不管具体指什么,它们背后的逻辑是一致的:用反馈信号来持续修正校准参数。
传统的校准方法是在一个静态数据集上训练好映射函数,然后固定不变。但 LLM 的行为会随着版本更新、提示词变化、领域迁移而漂移,静态校准很快就会失效。所以更工程化的做法是引入一个在线反馈闭环:Agent 每次执行后,把“实际结果是否正确”作为反馈信号,用来微调校准模型的参数。这样校准引擎就能跟着 LLM 一起“进化”。
这个思路和 Agent 记忆机制、RAG 的检索反馈其实是同一套哲学——让系统从运行中学习,而不是一次性训练完就完事。
3. 33ms 校准概率引擎的架构与实操要点
3.1 整体架构:三层分离设计
我参考了几个开源校准项目的结构,结合 33ms 的延迟约束,梳理出一个比较合理的三层架构:
第一层:特征提取层(Feature Extraction Layer)
这一层负责从 LLM 的原始输出里抽取校准所需的信号。关键特征是:
- 原始 token 概率统计:包括平均 logprob、最小 logprob、概率方差、熵值等。这些反映模型生成时的“内部一致性”。
- 生成序列特征:回答长度、重复 token 比例、特殊 token 出现次数(比如“不确定”“可能”这类词)。
- 外部信号:如果是 RAG 场景,检索文档的相关性分数、覆盖度;如果是 Agent 场景,工具调用的返回状态、历史成功率。
- 语义一致性特征:对同一问题多次采样,看答案的方差。这个计算量稍大,但可以异步做。
这一层的输出是一个固定维度的特征向量,比如 32 维或 64 维。维度不能太高,否则后面推理会变慢。
第二层:校准模型层(Calibration Model Layer)
这一层是核心,输入特征向量,输出校准后的概率。模型选型上,我实测下来,小型 GBDT(比如 LightGBM 的 50 棵树以内)或者 2 层 MLP(隐藏层 64 维)在效果和速度之间平衡得最好。
GBDT 的优点是特征交互能力强,对缺失值不敏感,而且推理时可以编译成 C 代码或者用 ONNX 加速。MLP 的优点是更容易上 GPU,批处理效率高。具体选哪个,看你的部署环境。如果是纯 CPU 环境,GBDT 更稳;如果有 GPU 且要处理高并发,MLP 更合适。
模型训练需要标注数据。标注方式有两种:一种是人工标注“这个回答是否正确”,成本高但质量好;另一种是用弱监督信号,比如用另一个更强的模型来评判,或者用下游任务的实际结果来反推。我建议初期用弱监督快速冷启动,然后逐步积累人工标注来修正。
第三层:推理服务层(Inference Service Layer)
这一层负责把校准模型包装成低延迟的服务。关键优化点:
- ONNX Runtime 或 TensorRT:把模型导出成 ONNX 格式,用推理引擎跑,比原生 PyTorch 快 3-5 倍。
- 批处理与动态 batching:Agent 可能同时有多个请求,把它们的特征向量拼成一个 batch 一起推理,能显著提升吞吐。
- 缓存机制:对于相同的特征向量(比如相同问题、相同检索结果),直接返回缓存结果,避免重复计算。
- 预热与常驻内存:服务启动时就把模型加载好,避免第一次请求时的冷启动延迟。
这三层加起来,在合理优化下,单次校准的端到端延迟可以压到 20-35ms。33ms 是一个比较现实的工程目标。
3.2 特征工程:哪些信号真正有用
特征工程是校准效果的关键。我试过不少特征,有些看起来很有道理,实际效果一般;有些不起眼,但贡献很大。下面这张表是我实测后的总结:
| 特征类别 | 具体特征 | 实测效果 | 计算成本 | 备注 |
|---|---|---|---|---|
| 概率统计 | 平均 logprob | 高 | 低 | 最基础也最有效 |
| 概率统计 | 最小 logprob | 中 | 低 | 对异常 token 敏感 |
| 概率统计 | 概率熵 | 中高 | 低 | 反映模型不确定性 |
| 序列特征 | 回答长度 | 中 | 低 | 过长往往意味着发散 |
| 序列特征 | 重复 token 比例 | 中 | 低 | 高重复通常质量差 |
| 外部信号 | 检索相关性分数 | 高 | 中 | RAG 场景必备 |
| 外部信号 | 工具调用成功率 | 高 | 低 | Agent 场景必备 |
| 语义一致性 | 多次采样答案方差 | 高 | 高 | 可异步计算 |
| 语义一致性 | 答案与问题的语义相似度 | 中 | 中 | 需要 embedding 模型 |
从表里可以看出,概率统计类特征性价比最高,计算成本低且效果好。外部信号在特定场景下非常关键,比如 RAG 里的检索分数,如果检索本身就没找到相关文档,那模型再自信也不可信。语义一致性特征效果最好,但计算成本高,适合异步更新,不适合实时计算。
注意:特征维度不是越多越好。我试过把特征堆到 128 维,结果推理延迟上去了,效果反而因为过拟合下降了。32-64 维是比较合适的区间。
3.3 模型训练:数据从哪来,怎么标
校准模型的训练数据,核心是“特征向量 + 真实标签”。真实标签就是“这个回答到底对不对”。获取标签有几种途径:
途径一:人工标注
最可靠,但成本高。适合在关键业务场景下,对少量高质量数据做精细标注。标注时要注意,不能只看答案“像不像对的”,要对照事实来源核实。我一般会设计一个标注界面,左边是问题,中间是模型回答,右边是参考文档,标注员只需要判断“回答是否被参考文档支持”。
途径二:弱监督信号
用更强的模型(比如 GPT-4 级别)来评判弱模型(比如 7B 模型)的回答。虽然强模型也会犯错,但整体上比弱模型准,作为弱监督信号足够用。具体做法是让强模型输出一个 0-1 的分数,然后把这个分数作为标签。注意,这里要控制强模型的“过度自信”问题,可以要求它输出理由,只采信有明确理由的判断。
途径三:下游任务反馈
如果 Agent 执行了某个动作,后来发现结果是错的(比如退款退错了),那这个反馈就可以作为负标签。这种信号最真实,但获取周期长,适合做在线微调。
训练时,我建议用交叉验证 + 早停来防止过拟合。数据量少的时候(比如几千条),GBDT 比 MLP 更稳。数据量上万后,MLP 开始有优势。损失函数用二元交叉熵或者Brier Score都可以,后者对概率校准更直接。
3.4 推理优化:把 33ms 落到实处
模型训练好只是第一步,真正难的是把它跑到 33ms。我踩过的坑包括:Python GIL 导致的并发瓶颈、ONNX 导出时的算子不支持、批处理大小设置不当导致的延迟抖动。下面是我总结的几个关键优化点:
优化点一:用 ONNX Runtime 替代原生推理
PyTorch 模型直接推理,单次可能要 50-100ms。导出成 ONNX 后,用 ONNX Runtime 的 CPU 或 GPU 执行提供器,能降到 10-20ms。导出时注意把动态维度设好,否则 batch 变化时会重新编译。
优化点二:动态批处理
Agent 的请求是零散到来的,如果每个请求单独推理,GPU 利用率很低。可以设置一个 5ms 的等待窗口,把窗口内的请求拼成一个 batch。这样虽然单次延迟增加了最多 5ms,但吞吐量能提升好几倍。对于 33ms 的预算来说,5ms 的等待是可以接受的。
优化点三:特征预计算与缓存
概率统计类特征可以在 LLM 生成时同步计算,不需要额外遍历。外部信号比如检索分数,在 RAG 检索阶段就已经有了,直接传过来即可。只有语义一致性特征需要额外计算,可以放到异步队列里,不阻塞主流程。
优化点四:模型量化
把 FP32 模型量化成 INT8,推理速度能再提升 2-3 倍,精度损失通常在 1% 以内。ONNX Runtime 支持动态量化,操作比较简单。如果对精度要求极高,可以用 FP16,速度提升 1.5-2 倍,精度几乎无损。
优化点五:服务常驻与连接池
校准服务要作为常驻进程运行,避免每次请求都重新加载模型。同时,如果服务需要调用外部资源(比如 Redis 缓存),要用连接池,避免频繁建连的开销。
经过这几步优化,我在一台普通的 8 核 CPU 机器上,单次校准延迟稳定在 25-35ms 之间,批处理 16 条时平均延迟 28ms。这个数据供你参考,具体会因硬件和模型复杂度而异。
4. 实操过程:从零搭建一个校准引擎
4.1 环境准备与依赖安装
先说一下我的测试环境:Ubuntu 22.04,Python 3.10,8 核 CPU,16GB 内存,无 GPU。如果你有 GPU,后面可以把 ONNX Runtime 换成 GPU 版本,速度会更快。
依赖安装如下:
pip install numpy pandas scikit-learn lightgbm onnx onnxruntime pip install transformers torch # 用于获取 LLM 的原始概率如果你要用 MLP 方案,再加一个:
pip install skl2onnx # 把 sklearn 模型导出为 ONNXLightGBM 也支持导出 ONNX,但需要额外装onnxmltools。我实测下来,LightGBM 原生推理已经很快了,不一定非要转 ONNX。MLP 转 ONNX 的收益更明显。
4.2 特征提取代码实现
下面是一个简化的特征提取函数,输入是 LLM 生成的 token 概率列表和检索分数,输出是特征向量:
import numpy as np def extract_features(token_probs, retrieval_score, answer_length, tool_success_rate): """ token_probs: list of float, 每个 token 的生成概率 retrieval_score: float, 检索相关性分数 0-1 answer_length: int, 回答 token 数 tool_success_rate: float, 工具历史成功率 0-1 """ probs = np.array(token_probs) logprobs = np.log(probs + 1e-10) features = [] # 概率统计特征 features.append(np.mean(logprobs)) # 平均 logprob features.append(np.min(logprobs)) # 最小 logprob features.append(np.std(logprobs)) # logprob 标准差 features.append(-np.sum(probs * logprobs)) # 熵 # 序列特征 features.append(answer_length / 100.0) # 归一化长度 features.append(np.mean(probs < 0.1)) # 低概率 token 比例 # 外部信号 features.append(retrieval_score) features.append(tool_success_rate) return np.array(features, dtype=np.float32)这个函数只用了 8 维特征,实际生产中我会扩展到 32 维左右,加入更多统计量和交互项。但核心逻辑就是这样,把 LLM 输出和外部信号转成一个固定长度的向量。
4.3 校准模型训练与导出
假设你已经收集了一批数据,X是特征矩阵,y是标签(1 表示正确,0 表示错误)。训练一个 LightGBM 模型:
import lightgbm as lgb from sklearn.model_selection import train_test_split from sklearn.metrics import brier_score_loss X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, random_state=42) model = lgb.LGBMClassifier( n_estimators=50, max_depth=4, learning_rate=0.1, num_leaves=15, subsample=0.8, colsample_bytree=0.8, random_state=42 ) model.fit( X_train, y_train, eval_set=[(X_val, y_val)], eval_metric='binary_logloss', callbacks=[lgb.early_stopping(10)] ) # 评估校准效果 y_pred_proba = model.predict_proba(X_val)[:, 1] brier = brier_score_loss(y_val, y_pred_proba) print(f"Brier Score: {brier:.4f}")Brier Score 越低越好,0 是完美,0.25 相当于随机猜。我实测下来,校准后的 Brier Score 能从原始概率的 0.18 降到 0.09 左右,提升还是很明显的。
训练完后,把模型保存下来:
import joblib joblib.dump(model, 'calibration_model.pkl')如果要用 ONNX 推理,可以转一下:
from onnxmltools.convert import convert_lightgbm from onnxconverter_common.data_types import FloatTensorType initial_types = [('input', FloatTensorType([None, X.shape[1]]))] onnx_model = convert_lightgbm(model, initial_types=initial_types) with open('calibration_model.onnx', 'wb') as f: f.write(onnx_model.SerializeToString())4.4 推理服务封装
下面是一个简单的推理服务示例,用 FastAPI 包装:
from fastapi import FastAPI import onnxruntime as ort import numpy as np app = FastAPI() session = ort.InferenceSession('calibration_model.onnx') @app.post("/calibrate") async def calibrate(features: list): input_array = np.array([features], dtype=np.float32) input_name = session.get_inputs()[0].name result = session.run(None, {input_name: input_array}) calibrated_prob = float(result[0][0][1]) return {"calibrated_confidence": calibrated_prob}这个服务启动后,单次请求的延迟在 15-25ms 之间(不含网络传输)。如果加上动态批处理,吞吐量能到每秒几百次。
注意:生产环境一定要加健康检查、超时控制和降级逻辑。如果校准服务挂了,Agent 应该能回退到使用原始置信度,而不是直接报错。
4.5 在线反馈闭环的搭建
校准模型不是训练一次就完事了。我建议加一个反馈收集模块,每次 Agent 执行后,把实际结果写回数据库。定期(比如每天)用新数据重新训练或微调校准模型。
反馈数据表可以这样设计:
| 字段 | 类型 | 说明 |
|---|---|---|
| request_id | string | 请求唯一标识 |
| features | json | 特征向量 |
| raw_confidence | float | 原始置信度 |
| calibrated_confidence | float | 校准后置信度 |
| actual_result | int | 实际是否正确 0/1 |
| timestamp | datetime | 时间戳 |
有了这张表,你可以定期跑一个脚本,把新数据加入训练集,重新训练模型,然后热更新推理服务。热更新时要注意版本管理,新模型上线前先在影子模式下跑一段时间,对比新旧模型的效果。
5. 常见问题与排查技巧实录
5.1 校准后置信度反而更不准了
这是最常见的问题,通常有几个原因:
原因一:训练数据和线上数据分布不一致。比如训练时用的是某个特定领域的问答数据,线上却来了通用领域的问题。解决办法是定期用线上数据做增量训练,或者引入领域特征让模型自己区分。
原因二:特征泄漏。有些特征在训练时包含了标签信息,导致模型在训练集上表现很好,线上却不行。比如“检索分数”如果在训练时是用正确答案去检索的,那线上用错误答案检索时分数会完全不同。检查方法是做严格的时序划分,用过去的数据训练,用未来的数据验证。
原因三:过拟合。模型太复杂,把训练数据的噪声也学进去了。解决办法是降低模型复杂度,增加正则化,或者增加训练数据量。
我踩过最坑的一次是特征泄漏,训练时 Brier Score 0.05,上线后直接飙到 0.22。后来发现是检索分数特征在训练集里用了“答案已知”的检索方式,线上是“答案未知”的检索方式,两者分布完全不同。修正后,线上 Brier Score 稳定在 0.11 左右。
5.2 延迟压不下去怎么办
如果校准延迟超过 50ms,先按这个顺序排查:
- 模型是不是太大了?检查参数量和树的数量。LightGBM 超过 100 棵树,或者 MLP 隐藏层超过 128 维,延迟就会明显上升。先砍模型。
- 有没有用 ONNX Runtime?原生 PyTorch 推理通常比 ONNX 慢 3 倍以上。如果还没转,先转。
- 特征计算是不是在推理路径上?把能预计算的特征提前算好,不要放在校准请求里。
- 有没有开批处理?单条推理的 GPU 利用率很低,开动态批处理后吞吐量能提升 5-10 倍。
- 服务是不是每次请求都重新加载模型?模型要常驻内存,只加载一次。
如果以上都做了还是慢,那就考虑降级方案:用温度缩放代替完整校准模型。温度缩放只需要一个标量参数,推理时间几乎为零,虽然效果差一些,但至少比不校准强。
5.3 冷启动阶段没有标注数据怎么办
冷启动是每个校准系统都要面对的问题。我的建议是分三步走:
第一步:用温度缩放兜底。温度缩放只需要一个验证集就能拟合,不需要大量标注。先用它把校准跑起来,虽然粗糙,但比没有强。
第二步:用弱监督积累数据。让强模型给弱模型的回答打分,积累几千条数据后,训练一个初步的 GBDT 模型。这个阶段不要追求完美,先让系统跑起来。
第三步:逐步引入人工标注。在关键业务场景下,对少量数据做精细标注,用来修正弱监督的偏差。人工标注的数据权重可以设高一些,比如是弱监督数据的 5 倍。
我一般会在系统上线后第一个月,每天抽 50 条做人工标注,一个月就是 1500 条。加上弱监督数据,足够训练一个可用的校准模型了。
5.4 常见问题速查表
| 问题现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| 校准后置信度普遍偏高 | 训练数据正样本比例过高 | 检查标签分布 | 调整样本权重或重采样 |
| 校准后置信度普遍偏低 | 模型过度保守 | 检查损失函数 | 改用 Brier Score 或调整阈值 |
| 延迟波动大 | 批处理大小不稳定 | 监控 P99 延迟 | 设置固定批处理窗口 |
| 线上效果远差于离线 | 数据分布漂移 | 对比特征分布 | 增量训练或加领域特征 |
| 某些领域校准失效 | 领域特征缺失 | 分领域评估 | 引入领域标识特征 |
| 服务偶发超时 | 资源竞争 | 检查 CPU/内存使用 | 限流或扩容 |
5.5 几个容易被忽略的细节
细节一:校准的是“答案正确概率”,不是“答案被接受概率”。这两个不一样。用户可能因为语气、格式等原因接受一个错误答案,但校准的目标是预测事实正确性。标注时要明确区分。
细节二:多轮对话里,校准要基于完整上下文。单轮校准模型直接用到多轮场景会失效,因为多轮里模型可能是在回答一个隐含的子问题。解决办法是把对话历史也作为特征输入,或者对每一轮单独校准后再聚合。
细节三:校准阈值要根据业务调整。校准后的概率是连续的,但业务决策往往是二元的(比如“是否触发人工审核”)。阈值设多少,取决于你对假阳性和假阴性的容忍度。金融场景可能要求置信度 0.95 以上才自动执行,客服场景 0.8 就够了。
细节四:定期做可靠性图检查。把校准后的概率分桶,看每个桶里的实际正确率是否接近桶的中心值。如果偏离超过 5%,说明校准模型需要重新训练了。
6. 这套方案在 Agent 架构里怎么用
6.1 作为 Agent 决策的“刹车系统”
Agent 的决策循环里,最危险的不是“不知道”,而是“不知道自己不知道”。校准概率引擎可以作为一个独立的检查点,在 Agent 准备执行动作前,对模型的判断做一次校准。如果校准后置信度低于阈值,就触发追问、检索更多信息、或者转人工。
这个模式我称之为“刹车系统”。它不改变 Agent 的决策逻辑,只是在决策和执行之间加了一道安全检查。好处是解耦,校准引擎可以独立迭代,不影响 Agent 主流程。
6.2 和 RAG、记忆模块的配合
在 RAG 场景里,校准引擎可以同时利用检索分数和生成概率。如果检索分数很低,但模型生成概率很高,这通常意味着模型在“编造”。校准模型学到这个模式后,会自动降低这类回答的置信度。
和 Agent 记忆模块配合时,可以把历史执行结果作为特征。比如某个工具过去 10 次调用有 8 次成功,那这次调用的置信度可以适当上调。反过来,如果某个工具经常失败,校准引擎会提醒 Agent 谨慎使用。
6.3 持续迭代的工程节奏
我建议把校准引擎的迭代节奏和 Agent 主流程分开。主流程可能每周发版,校准引擎可以每天更新。更新时用影子模式:新模型和旧模型同时跑,对比输出差异,如果差异在可接受范围内,再切换流量。
监控指标上,除了 Brier Score 和 ECE,还要看业务指标。比如人工审核触发率、错误执行率、用户投诉率。校准引擎的最终目标不是让概率好看,而是让业务决策更准。
7. 我个人的一些实操体会
这套东西我从去年开始折腾,中间踩了不少坑,也积累了一些文档里不会写的经验。分享几条我觉得最有价值的:
第一条:不要追求完美的校准。校准到 0.1 的 Brier Score 和 0.08 的 Brier Score,在业务上的差别可能微乎其微,但工程复杂度可能差一倍。先做到“能用”,再逐步优化。
第二条:特征比模型重要。我试过用很复杂的模型配很少的特征,效果远不如简单模型配好特征。花时间在特征工程上,回报率更高。
第三条:线上反馈是金矿。哪怕每天只有几十条反馈,积累几个月后,用这些数据微调校准模型,效果提升比换模型架构明显得多。
第四条:延迟和效果要平衡。33ms 是一个工程目标,不是绝对标准。如果你的场景允许 100ms,那可以用更大的模型,效果会更好。关键是明确你的延迟预算,然后在预算内做到最好。
第五条:校准引擎要能降级。任何组件都可能挂,校准引擎也不例外。设计时就要想好,如果校准服务不可用,Agent 应该怎么回退。我的做法是回退到原始置信度,同时记录日志,事后分析。
最后再分享一个小技巧:如果你刚开始做校准,可以先从“温度缩放 + 检索分数”这个最小组合入手。温度缩放负责整体校准,检索分数负责领域适配。这个组合实现简单,效果也还不错,适合快速验证想法。等跑通了,再逐步加入更多特征和更复杂的模型。