第2036篇:LLM幻觉检测与处理——生产系统中的事实核查机制
2026/4/30大约 7 分钟
第2036篇:LLM幻觉检测与处理——生产系统中的事实核查机制
适读人群:需要处理LLM幻觉问题的工程师 | 阅读时长:约17分钟 | 核心价值:理解幻觉产生的原因,建立适合生产环境的幻觉检测和缓解机制
有一次我们的法律AI回答了一个关于某部法规的问题,引用了一条"第X条第Y款"。用户按照这个信息去查,结果这条法规根本没有那一款——模型编造了一个看起来很真实的引用。
这就是LLM的幻觉(Hallucination)问题:模型生成了听起来合理但实际上不正确的信息。
幻觉产生的原因
关键认知:LLM不是数据库,不是搜索引擎。它是一个统计语言模型,生成的是"听起来合理的文字序列"。
幻觉检测方法
方法1:一致性检查
多次问同一个问题,观察答案是否一致。幻觉内容通常每次都不同:
@Service
@RequiredArgsConstructor
@Slf4j
public class HallucinationDetector {
private final ChatClient chatClient;
private final EmbeddingModel embeddingModel;
/**
* 一致性检测:多次采样,检查答案是否稳定
* 稳定的答案更可能是真实的,不稳定的更可能是幻觉
*/
public ConsistencyCheckResult checkConsistency(String question, int sampleCount) {
List<String> answers = new ArrayList<>();
for (int i = 0; i < sampleCount; i++) {
String answer = chatClient.prompt()
.user(question)
.options(ChatOptions.builder().temperature(0.7).build())
.call()
.content();
answers.add(answer);
}
// 计算答案间的语义相似度
List<float[]> embeddings = answers.stream()
.map(embeddingModel::embed)
.collect(Collectors.toList());
// 计算所有答案对之间的平均相似度
double totalSimilarity = 0;
int pairCount = 0;
for (int i = 0; i < embeddings.size(); i++) {
for (int j = i + 1; j < embeddings.size(); j++) {
totalSimilarity += cosineSimilarity(embeddings.get(i), embeddings.get(j));
pairCount++;
}
}
double avgConsistency = pairCount > 0 ? totalSimilarity / pairCount : 0;
return ConsistencyCheckResult.builder()
.consistencyScore(avgConsistency)
.answers(answers)
.reliable(avgConsistency > 0.85)
.reason(avgConsistency > 0.85
? "多次采样答案一致,可信度高"
: "多次采样答案差异较大,可能存在幻觉")
.build();
}
/**
* 检测特定类型的幻觉:法律条文引用
* 对于引用特定法规条款的回答,需要特别验证
*/
public LegalCitationCheckResult checkLegalCitation(String responseText) {
// 提取法律引用(正则匹配)
Pattern legalPattern = Pattern.compile(
"(第[一二三四五六七八九十百零\\d]+条)(第[一二三四五六七八九十百零\\d]+款)?");
List<String> citations = new ArrayList<>();
java.util.regex.Matcher matcher = legalPattern.matcher(responseText);
while (matcher.find()) {
citations.add(matcher.group());
}
if (citations.isEmpty()) {
return LegalCitationCheckResult.noCitationsFound();
}
// 使用另一个LLM调用验证这些引用是否真实存在
String verificationPrompt = String.format("""
以下回答中引用了这些法律条款:%s
完整回答内容:
%s
请判断这些法律引用是否真实存在,是否准确。
如果不确定,请说明哪些引用你无法确认真实性。
""", String.join(", ", citations), responseText);
String verificationResult = chatClient.prompt()
.system("你是法律条文核实专家,请严格核实法律引用的准确性。")
.user(verificationPrompt)
.call()
.content();
return LegalCitationCheckResult.builder()
.citations(citations)
.verificationResult(verificationResult)
.requiresHumanReview(verificationResult.contains("无法确认") ||
verificationResult.contains("不确定"))
.build();
}
private double cosineSimilarity(float[] a, float[] b) {
double dot = 0, normA = 0, normB = 0;
for (int i = 0; i < a.length; i++) {
dot += a[i] * b[i];
normA += a[i] * a[i];
normB += b[i] * b[i];
}
return dot / (Math.sqrt(normA) * Math.sqrt(normB));
}
}RAG作为幻觉缓解手段
RAG(检索增强生成)是减少幻觉最有效的工程手段——让模型基于真实文档回答,而不是凭空生成:
@Service
@RequiredArgsConstructor
@Slf4j
public class GroundedResponseService {
private final VectorStoreRetriever retriever;
private final ChatClient chatClient;
/**
* 有文档支撑的回答:要求模型引用文档来源
* 如果没有相关文档,明确告知用户
*/
public GroundedResponse answer(String question) {
// 检索相关文档
List<Document> relevantDocs = retriever.retrieve(question, 3);
if (relevantDocs.isEmpty()) {
return GroundedResponse.builder()
.answer("根据当前知识库,暂无关于此问题的文档记录,建议查阅官方文档或专业人士。")
.groundingSource("无文档支撑")
.reliability(ReliabilityLevel.LOW)
.build();
}
// 构建包含来源约束的Prompt
String docContext = IntStream.range(0, relevantDocs.size())
.mapToObj(i -> String.format("[文档%d: %s]\n%s",
i+1, relevantDocs.get(i).getMetadata().get("source"),
relevantDocs.get(i).getContent()))
.collect(Collectors.joining("\n\n"));
String groundedPrompt = String.format("""
请根据以下文档内容回答问题。
重要规则:
1. 只使用文档中明确提到的信息
2. 如果文档中没有相关信息,明确说明"根据现有文档,暂无此信息"
3. 回答时注明信息来自哪个文档
4. 不要基于你自己的训练知识推测
文档内容:
%s
问题:%s
""", docContext, question);
String response = chatClient.prompt()
.user(groundedPrompt)
.call()
.content();
// 检查回答是否引用了文档
boolean hasDocReference = relevantDocs.stream()
.anyMatch(doc -> {
String source = (String) doc.getMetadata().get("source");
return response.contains(source) || response.contains("文档");
});
return GroundedResponse.builder()
.answer(response)
.groundingDocs(relevantDocs)
.groundingSource(hasDocReference ? "有文档支撑" : "未明确引用文档")
.reliability(hasDocReference ? ReliabilityLevel.HIGH : ReliabilityLevel.MEDIUM)
.build();
}
}让模型主动表达不确定性
一个容易被忽视的技巧:显式要求模型表达不确定性:
@Service
@RequiredArgsConstructor
public class UncertaintyAwareService {
private final ChatClient chatClient;
/**
* 包含不确定性表达的问答
* 让模型在不确定时明确说出来,而不是编造内容
*/
public UncertaintyAnnotatedResponse ask(String question) {
String systemPrompt = """
你是一个诚实的AI助手。
关于不确定性,你必须遵守以下规则:
1. 当你对某个事实不确定时,必须明确表达:"我不确定这一点"或"这需要核实"
2. 当问题涉及最新事件(2024年以后)时,主动说明你的知识可能不是最新的
3. 当涉及具体数字、日期、名称时,如果不是100%确定,请说明
4. 宁可说"我不知道",也不要猜测
格式要求:
- 高度确定的信息:直接陈述
- 有些不确定的信息:加上"据我所知..."或"可能是..."
- 非常不确定:明确说"这需要你核实"
""";
String response = chatClient.prompt()
.system(systemPrompt)
.user(question)
.call()
.content();
// 解析不确定性信号
boolean hasUncertaintySignal = response.contains("不确定") ||
response.contains("据我所知") ||
response.contains("需要核实") ||
response.contains("可能是") ||
response.contains("不是最新");
UncertaintyLevel level = hasUncertaintySignal
? UncertaintyLevel.MEDIUM : UncertaintyLevel.LOW;
return UncertaintyAnnotatedResponse.builder()
.content(response)
.uncertaintyLevel(level)
.requiresVerification(hasUncertaintySignal)
.build();
}
}高风险场景的人工审核机制
对于高风险场景(医疗、法律、金融),纯AI回答不够,需要人工兜底:
/**
* 高风险回答的人工审核队列
*/
@Service
@RequiredArgsConstructor
@Slf4j
public class HighRiskResponseReviewer {
private final HallucinationDetector detector;
private final HumanReviewQueue reviewQueue;
/**
* 高风险问题的处理流程:
* 1. LLM生成回答
* 2. 幻觉风险评估
* 3. 高风险进入人工审核队列
* 4. 低风险直接返回(附加免责声明)
*/
public Response handleHighRiskQuestion(String userId, String question) {
// 1. AI生成回答
String aiResponse = generateResponse(question);
// 2. 幻觉风险评估
RiskLevel riskLevel = assessHallucinationRisk(question, aiResponse);
if (riskLevel == RiskLevel.HIGH) {
// 3. 进入人工审核队列
String taskId = reviewQueue.submit(ReviewTask.builder()
.userId(userId)
.question(question)
.aiResponse(aiResponse)
.riskLevel(riskLevel)
.submittedAt(LocalDateTime.now())
.build());
log.info("高风险问题进入人工审核队列: taskId={}", taskId);
// 返回等待提示,不直接给AI回答
return Response.pending(taskId,
"您的问题涉及专业领域,正在为您安排专业人士核实,预计30分钟内回复。");
}
// 低风险:直接返回,附加免责声明
return Response.success(aiResponse +
"\n\n---\n注:以上内容由AI生成,仅供参考,如涉及专业决策建议咨询专业人士。");
}
private RiskLevel assessHallucinationRisk(String question, String response) {
// 检查高风险信号
boolean hasSpecificNumbers = response.matches(".*\\d+(\\.\\d+)?%.*") ||
response.matches(".*第\\d+条.*");
boolean hasDateClaims = response.matches(".*\\d{4}年\\d{1,2}月.*");
boolean hasPersonalInfo = question.contains("谁") && response.contains(":");
if (hasSpecificNumbers || hasDateClaims || hasPersonalInfo) {
return RiskLevel.HIGH;
}
return RiskLevel.LOW;
}
}幻觉是LLM的本质局限,不能完全消除,但可以通过工程手段显著降低其影响。
核心策略三件套:RAG让模型基于事实回答、要求模型表达不确定性、高风险场景加人工兜底。三者结合,才能在生产中安全使用LLM。
