为 RAG 系统设计自适应置信度评分 —— 不用固定阈值,让数据说话
问题从哪里来
RAG 系统面临一个非常实际的问题:它给出的回答到底可不可信?
检索到相关内容了吗?回答有没有忠实引用文档?问答之间语义匹配吗?这三个问题的答案共同决定了回答的质量。
如果全部交给用户自己判断,系统就没有起到」筛选」的作用。但如果拍脑袋定一个固定阈值 —— 比如相似度大于 0.7 就算高置信 —— 今天的 0.7 和明天的 0.7 可能完全是两回事。不同知识库、不同检索模式、不同嵌入模型,分数分布天然不同,固定阈值迟早失效。
OpsMind 作为面向 IT 运维的智能问答系统,需要对自己的回答有信心判断。高置信的回答可以放心展示给用户,低置信的回答则需要提示风险甚至流转人工处理。这个判断如果出错 —— 误把胡扯当权威,或者频繁把正常回答标记为不确定 —— 系统就会失去信任。
真正卡住的是哪一步
把置信度问题拆开后,本质上有两个难题:
第一个难题:在线评分的复杂度控制。
要准确判断置信度,需要多个维度的信号 —— 检索匹配度、原文对齐度、问答语义一致性。如果每个维度都做复杂的归一化、缩放、校准,在线接口的延迟就保不住了。用户问一个问题等 3 秒,然后系统花 2 秒算置信度,这不可接受。
第二个难题:阈值的自适应性。
即使设计出了合理的评分公式,阈值怎么定?如果把所有知识库的分数混在一起看,单一阈值对不同知识库可能是灾难 —— 检索质量好的知识库普遍分数高,质量差的普遍分数低。用同一个阈值切,一边都是高置信,一边都是低置信。
核心矛盾是:需要在保证在线低延迟的前提下,实现随数据分布自适应。
我最后怎么处理
最终的方案分了两层:在线链路只算原始分,离线任务负责生成阈值。
在线链路:只算三个原始分数
不做任何归一化、不做缩放、不做统计 —— 只在回答生成后计算三个原始分数,然后把它们和加权结果一起写入数据库。
| 指标 | 计算内容 | 权重 |
|---|---|---|
| S1 | Query 与 Top1 召回文档的余弦相似度 | 0.4 |
| S2 | AI 回答与召回文档集的平均余弦相似度 | 0.4 |
| S3 | 用户问题与 AI 回答的余弦相似度 | 0.2 |
原始置信度公式:
Conf_raw = 0.4 × S1 + 0.4 × S2 + 0.2 × S3 |
S1 和 S2 各占 0.4,因为它们分别回答了」找没找对」和」答没答对」两个最关键的问题。S3 只占 0.2,因为问答语义相似虽然有一定参考价值,但高相似度不代表答案正确 —— 两个错误答案之间也可能很相似。
整个过程在回答生成的同一请求中完成,仅追加三次余弦相似度计算和一次加权求和,对延迟的影响可以忽略不计。
离线链路:用分位数自动切阈值
每日定时任务从数据库拉取近 7 天全量对话的 Conf_raw 和 S1 值,先清洗掉空回答、检索失败等脏数据,然后计算四个关键分位数:
- P70:高分分割线
- P30:低分分割线
- P10:极低分分割线(兜底用)
计算结果存入配置中心,在线接口实时读取,无需重启服务。
这里没有用固定数值,原因很简单:P70 在任何一个分布中都代表」前 30% 的好回答」,无论分数绝对值是高是低。这就是」让数据说话」—— 阈值不再是 0.7,而是」比最近 70% 的回答好」。
两套规则,各管各的
拿到阈值之后,不是简单地」高于 P70 就信、低于就怀疑」。不同场景需要不同的判断标准。
基础分级(给前端展示用):
- Conf_raw ≥ P70 → 高置信:完整输出答案,展示参考文档来源。
- P30 ≤ Conf_raw < P70 → 中置信:答案前加」匹配资料有限,内容仅供参考」。
- Conf_raw < P30 → 低置信:弱化确定性表述,不展示文档引用。
严格兜底(决定是否转人工):
只有同时满足两个条件才判为」无有效资料」并拦截回答:
- Conf_raw < P30(整体分低)
- S1 < P10_near7days(检索分也极低)
只满足条件 1 但不满足条件 2,说明」整体分虽然低,但至少检索到了东西」,可以给出答案但提示风险,不拦截。
这个双重校验设计的关键目的是 减少误判 —— 只有检索和整体都差的情况才真正转人工。单一阈值做拦截必然导致大量误报。
这件事留下的经验
自适应比绝对值可靠
用分位数做阈值的本质是承认一个事实:我们不知道」好分数」应该是多少,但知道」比大部分问答好的分数」就是好分数。这是一种务实的做法 —— 不追求绝对标准,追求相对排序。
这种做法天然适应数据漂移。如果某天知识库更新了一批高质量文档,整体分数普遍上升,P70 也会自动跟着上移。不需要任何人调参数。
在线和离线分开是一种通用模式
不止是置信度 —— 任何需要」在线快、离线准」的场景都可以参考这种分层:
- 在线链路:只做必要的原始计算,数据落库。
- 离线链路:基于历史全量数据做统计分析,结果下发到在线服务。
在线链路追求低延迟(不超过回答生成本身的时间),离线链路追求准确性(有充足时间做数据处理和统计计算),两者通过配置中心解耦。离线任务失败不影响在线服务 —— 阈值不更新就继续用上一次的,不会导致服务中断。
下一步:按知识库维度拆分
当前方案把所有知识库的分数混在一起计算分位数,这隐含了一个假设:不同知识库的检索质量分布差不多。
实际上可能不是这样。更合理的做法是按知识库维度分别计算 P30 / P70,让每个知识库有自己的自适应阈值。这需要在下一步落地时解决。