第2176篇:模型置信度与不确定性量化——让AI知道自己不知道什么
2026/4/30大约 7 分钟
第2176篇:模型置信度与不确定性量化——让AI知道自己不知道什么
适读人群:希望构建可靠AI系统的工程师 | 阅读时长:约16分钟 | 核心价值:用工程方法量化LLM的不确定性,避免高置信错误答案带来的严重后果
"我已确认,张总的会议在明天下午三点。"
这是我们的日程管理AI助手的一次回答。问题是,它完全是凭空捏造的——系统里根本没有张总的这条会议记录,只是语气格外笃定,让用户信以为真,没有追问。
这类错误比"我不知道"危险十倍。AI不知道的事情,它用同样的语气说出来——这就是"过度自信"问题。
不确定性量化(Uncertainty Quantification,UQ)解决的正是这个问题:让模型在不确定时表现出不确定性,而不是用流利的语言掩盖认知空白。
LLM不确定性的来源
LLM不确定性的两类来源:
认识论不确定性(Epistemic Uncertainty)
由于知识不足导致的不确定性,原则上可以通过更多数据解决
├── 训练数据截止之后的事件(模型不知道)
├── 领域专业知识不足(模型训练数据少)
└── 私有数据查询无结果(RAG没检索到相关文档)
随机不确定性(Aleatoric Uncertainty)
由于问题本身的模糊性,即使有足够数据也无法消除
├── 用户问题表述模糊
├── 答案本身存在争议(如价值判断、预测未来)
└── 上下文信息不足以做出单一正确判断
工程意义:
认识论不确定性 → 可以通过RAG补充、知识库更新来改善
随机不确定性 → 应该给出多种可能答案,而不是强行给一个不确定性量化的核心方法
/**
* LLM不确定性量化服务
*
* 三种互补的估计方法:
* 1. 采样一致性(多次采样,看结果是否一致)
* 2. 语言置信度提取(让模型自我评估)
* 3. 检索支持度(RAG场景下,检索内容覆盖程度)
*/
@Service
@RequiredArgsConstructor
@Slf4j
public class UncertaintyQuantificationService {
private final ChatClient chatClient;
private final RetrievalService retrievalService;
private final SemanticSimilarityService semanticService;
/**
* 计算综合不确定性分数
*/
public UncertaintyResult quantify(String query, String context) {
// 方法1:采样一致性(最可靠但成本最高)
SamplingConsistencyResult samplingResult = computeSamplingConsistency(query, context);
// 方法2:语言置信度(快速但不够准确)
LinguisticConfidenceResult linguisticResult = extractLinguisticConfidence(query, context);
// 方法3:检索覆盖度(RAG场景专用)
RetrievalCoverageResult retrievalResult = assessRetrievalCoverage(query);
// 综合三种方法
double finalUncertainty = combineUncertaintyEstimates(
samplingResult, linguisticResult, retrievalResult);
return UncertaintyResult.builder()
.uncertaintyScore(finalUncertainty)
.confidenceLevel(mapToConfidenceLevel(finalUncertainty))
.samplingConsistency(samplingResult.getConsistencyScore())
.linguisticConfidence(linguisticResult.getConfidenceScore())
.retrievalCoverage(retrievalResult.getCoverageScore())
.estimatedMethod(determineMethod(samplingResult, retrievalResult))
.shouldDefer(finalUncertainty > 0.6) // 不确定性高时建议转人工
.build();
}
/**
* 采样一致性:多次采样同一问题,统计答案的一致程度
*
* 如果5次采样的答案都差不多 → 模型对这个问题有把握
* 如果5次采样差异很大 → 模型不确定,在瞎猜
*/
private SamplingConsistencyResult computeSamplingConsistency(
String query, String context) {
int sampleCount = 5;
List<String> samples = new ArrayList<>();
for (int i = 0; i < sampleCount; i++) {
String sample = chatClient.prompt()
.system(context)
.user(query)
.options(ChatOptionsBuilder.builder()
.withTemperature(0.7) // 用非零温度增加采样多样性
.build())
.call()
.content();
samples.add(sample);
}
// 计算样本间的语义相似度
double avgSimilarity = computeAveragePairwiseSimilarity(samples);
// 相似度高 = 一致性高 = 不确定性低
double consistencyScore = avgSimilarity;
log.debug("采样一致性: sampleCount={}, avgSimilarity={:.3f}",
sampleCount, avgSimilarity);
return new SamplingConsistencyResult(
consistencyScore, samples, avgSimilarity);
}
/**
* 语言置信度提取:让模型自己评估回答的可靠性
*/
private LinguisticConfidenceResult extractLinguisticConfidence(
String query, String context) {
String prompt = String.format("""
问题:%s
请回答这个问题,并在答案末尾按以下格式评估你的置信度:
[回答内容]
---
置信度评估:
- 我对这个回答有多确定?(1-10分,10=非常确定)
- 主要不确定因素是什么?(一句话)
- 这个回答是否需要用户进一步核实?(是/否)
""", query);
String response = chatClient.prompt()
.system(context)
.user(prompt)
.call()
.content();
return parseConfidenceFromResponse(response);
}
/**
* 检索覆盖度:检索到的文档是否充分覆盖了问题
*/
private RetrievalCoverageResult assessRetrievalCoverage(String query) {
List<RetrievedDocument> docs = retrievalService.retrieve(query);
if (docs.isEmpty()) {
return new RetrievalCoverageResult(0.0, "无相关文档");
}
// 最高相关性文档的分数
double topRelevanceScore = docs.get(0).getRelevanceScore();
// 文档覆盖查询不同方面的程度
double queryCoverage = computeQueryCoverage(query, docs);
// 文档新鲜度(过时文档的覆盖度应该打折)
double freshnessPenalty = computeFreshnessPenalty(docs);
double coverageScore = (topRelevanceScore * 0.5 +
queryCoverage * 0.4 -
freshnessPenalty * 0.1);
return new RetrievalCoverageResult(
Math.max(0.0, Math.min(1.0, coverageScore)),
String.format("检索到%d个相关文档,最高相关性%.2f",
docs.size(), topRelevanceScore));
}
private double combineUncertaintyEstimates(
SamplingConsistencyResult sampling,
LinguisticConfidenceResult linguistic,
RetrievalCoverageResult retrieval) {
// 一致性越高,不确定性越低
double samplingUncertainty = 1.0 - sampling.getConsistencyScore();
double linguisticUncertainty = 1.0 - linguistic.getConfidenceScore();
double retrievalUncertainty = 1.0 - retrieval.getCoverageScore();
// 加权平均:采样结果最可信,语言自评最不可信(模型容易过度自信)
return samplingUncertainty * 0.5 +
retrievalUncertainty * 0.35 +
linguisticUncertainty * 0.15;
}
private double computeAveragePairwiseSimilarity(List<String> samples) {
List<Double> similarities = new ArrayList<>();
for (int i = 0; i < samples.size(); i++) {
for (int j = i + 1; j < samples.size(); j++) {
similarities.add(
semanticService.computeSimilarity(samples.get(i), samples.get(j)));
}
}
return similarities.stream().mapToDouble(Double::doubleValue).average().orElse(0.0);
}
}把不确定性嵌入响应生成
/**
* 带不确定性感知的响应生成器
*
* 根据不确定性等级,采用不同的响应策略
*/
@Service
@RequiredArgsConstructor
public class UncertaintyAwareResponseGenerator {
private final UncertaintyQuantificationService uqService;
private final ChatClient chatClient;
private final EscalationService escalationService;
/**
* 主响应入口
*/
public UncertaintyAwareResponse generate(
String query,
String systemContext,
EscalationPolicy escalationPolicy) {
// 先量化不确定性
UncertaintyResult uncertainty = uqService.quantify(query, systemContext);
return switch (uncertainty.getConfidenceLevel()) {
case HIGH -> generateConfidentResponse(query, systemContext, uncertainty);
case MEDIUM -> generateHedgedResponse(query, systemContext, uncertainty);
case LOW -> generateDeferralOrLimitedResponse(
query, systemContext, uncertainty, escalationPolicy);
};
}
/**
* 高置信:正常回答
*/
private UncertaintyAwareResponse generateConfidentResponse(
String query, String context, UncertaintyResult uncertainty) {
String answer = chatClient.prompt()
.system(context)
.user(query)
.call()
.content();
return UncertaintyAwareResponse.builder()
.answer(answer)
.confidenceLevel(ConfidenceLevel.HIGH)
.confidenceIndicator("") // 不显示置信度标签,不干扰用户
.requiresVerification(false)
.build();
}
/**
* 中等置信:带hedge语言回答
*
* 通过Prompt控制语气,让模型用适当的不确定性表达
*/
private UncertaintyAwareResponse generateHedgedResponse(
String query, String context, UncertaintyResult uncertainty) {
String hedgedPrompt = String.format("""
%s
请注意:对于你不完全确定的内容,请使用适当的不确定性表达,如:
"根据我的了解..."、"通常情况下..."、"你可能需要进一步核实..."
不要用过于确定的语气表达不确定的信息。
""", context);
String answer = chatClient.prompt()
.system(hedgedPrompt)
.user(query)
.call()
.content();
return UncertaintyAwareResponse.builder()
.answer(answer)
.confidenceLevel(ConfidenceLevel.MEDIUM)
.confidenceIndicator("⚠️ 建议核实关键信息")
.requiresVerification(true)
.verificationSuggestion(generateVerificationSuggestion(query))
.build();
}
/**
* 低置信:拒绝回答或触发升级
*/
private UncertaintyAwareResponse generateDeferralOrLimitedResponse(
String query, String context,
UncertaintyResult uncertainty,
EscalationPolicy policy) {
if (policy == EscalationPolicy.ESCALATE_TO_HUMAN) {
// 转人工
escalationService.escalate(query, "AI置信度不足,需要人工处理");
return UncertaintyAwareResponse.escalated(
"这个问题需要更专业的判断,正在为您转接专业人员...");
}
// 说明局限性
String limitedAnswer = String.format("""
关于这个问题,我目前的信息可能不够准确。
我能告诉你的是:%s
但以下方面我不确定:%s
建议您:%s
""",
generatePartialAnswer(query, context),
uncertainty.getUncertaintyReasons(),
generateVerificationSuggestion(query));
return UncertaintyAwareResponse.builder()
.answer(limitedAnswer)
.confidenceLevel(ConfidenceLevel.LOW)
.confidenceIndicator("⛔ 信息可能不完整,强烈建议核实")
.requiresVerification(true)
.build();
}
}业务场景中的不确定性阈值设置
不同业务场景对不确定性的容忍度完全不同:
/**
* 场景化的不确定性阈值配置
*/
@Configuration
public class UncertaintyThresholdConfig {
@Bean
public Map<BusinessScenario, UncertaintyThresholds> thresholds() {
return Map.of(
BusinessScenario.CASUAL_CHAT, UncertaintyThresholds.builder()
.highConfidenceThreshold(0.3) // 低要求:不确定性<30%就正常回答
.escalationThreshold(0.85) // 不确定性>85%才转人工
.build(),
BusinessScenario.CUSTOMER_SERVICE, UncertaintyThresholds.builder()
.highConfidenceThreshold(0.2)
.escalationThreshold(0.6) // 较低阈值:有疑问就转人工
.build(),
BusinessScenario.MEDICAL_ADVICE, UncertaintyThresholds.builder()
.highConfidenceThreshold(0.05) // 极高要求:几乎完全确定才直接回答
.escalationThreshold(0.2) // 任何不确定都要提示找医生
.mandatoryDisclaimer(true) // 始终添加免责声明
.build(),
BusinessScenario.LEGAL_ADVISORY, UncertaintyThresholds.builder()
.highConfidenceThreshold(0.05)
.escalationThreshold(0.15)
.mandatoryDisclaimer(true)
.requireHumanReviewForHighStake(true) // 高风险情况必须人工复核
.build()
);
}
}核心洞察:不确定性是诚实的工程表达
开头那个日程助手的问题,上了不确定性量化后有了明显改善。当系统检索不到相关日程记录时,采样一致性和检索覆盖度都会给出很低的分数,触发hedged响应策略。现在的回答变成了:"我在系统中没有找到张总会议的相关记录,可能信息还未同步或您需要手动查询一下。"
没有了那种虚假的笃定。
几个工程上的反思:
采样一致性是目前最可靠的UQ方法,但成本是单次调用的5倍。需要根据场景决定是否使用——高风险场景值得,日常对话可以跳过。
不要依赖模型自我评估置信度。模型对自己错误的估计通常不准确,尤其是在它完全不知道某件事的时候,往往反而语气更确定。
"我不确定"本身也是一个有价值的答案。让AI说"我不知道"需要Prompt设计和阈值设置,不是模型自然会做的事情。
把不确定性日志化。记录每次的UQ分数,能帮你发现哪类问题是系统性的弱点,指导RAG知识库建设或模型微调方向。
