幻觉问题的严重性
LLM 幻觉——模型生成看似合理但实际不正确的信息——是当前大语言模型最严重的问题之一。在闲聊场景中,幻觉可能只是闹个笑话;但在医疗、法律、金融等高风险场景中,幻觉可能造成严重后果。
根据 2025 年的研究统计:
| 场景 | 典型幻觉率 | 后果严重程度 |
|---|---|---|
| 事实问答 | 5-15% | 中 |
| 代码生成 | 10-25% | 中高 |
| 医疗咨询 | 8-20% | 极高 |
| 法律引用 | 15-30% | 极高 |
| 历史事件 | 10-20% | 高 |
| 数学推理 | 15-30% | 中 |
| 人物传记 | 20-40% | 高 |
幻觉分类体系
LLM 幻觉类型
├── 事实性幻觉(Factual Hallucination)
│ ├── 实体幻觉:编造不存在的人名/地名/机构
│ ├── 关系幻觉:编造人物之间的关系
│ ├── 数字幻觉:编造统计数据或日期
│ └── 引用幻觉:编造论文/法律/新闻报道
├── 逻辑性幻觉(Logical Hallucination)
│ ├── 推理跳跃:跳过关键推理步骤
│ ├── 循环论证:用结论证明结论
│ └── 因果倒置:混淆原因和结果
├── 上下文幻觉(Contextual Hallucination)
│ ├── 矛盾输出:与之前回答自相矛盾
│ ├── 忽略约束:不遵守 prompt 中的约束
│ └── 过度推断:超出给定信息范围
└── 格式幻觉(Format Hallucination)
├── 结构错误:输出格式不符合要求
└── 引用伪造:伪造可验证的引用来源
一、人工标注体系
幻觉标注框架
from dataclasses import dataclass, field
from enum import Enum
class HallucinationType(Enum):
ENTITY = "entity" # 实体幻觉
RELATION = "relation" # 关系幻觉
NUMERIC = "numeric" # 数字幻觉
CITATION = "citation" # 引用幻觉
LOGICAL = "logical" # 逻辑幻觉
CONTEXTUAL = "contextual" # 上下文幻觉
FORMAT = "format" # 格式幻觉
NONE = "none" # 无幻觉
class HallucinationSeverity(Enum):
NONE = 0 # 无幻觉
MINOR = 1 # 轻微:不影响核心信息
MODERATE = 2 # 中等:部分信息不准确
SEVERE = 3 # 严重:核心信息完全错误
CRITICAL = 4 # 致命:可能造成实际危害
@dataclass
class HallucinationAnnotation:
"""单条幻觉标注"""
span_start: int # 幻觉文本起始位置
span_end: int # 幻觉文本结束位置
hallucinated_text: str # 幻觉文本
hallucination_type: HallucinationType
severity: HallucinationSeverity
correct_info: str # 正确信息
source: str # 正确信息来源
annotator_id: str
confidence: float # 标注者置信度 0-1
@dataclass
class HallucinationDocument:
"""一份完整文档的幻觉标注"""
doc_id: str
prompt: str
response: str
annotations: list[HallucinationAnnotation] = field(default_factory=list)
@property
def hallucination_rate(self) -> float:
"""幻觉率:有幻觉的句子占比"""
if not self.response:
return 0.0
sentences = self.response.split("。")
hallucinated_sentences = set()
for ann in self.annotations:
for i, sent in enumerate(sentences):
if ann.hallucinated_text in sent:
hallucinated_sentences.add(i)
return len(hallucinated_sentences) / len(sentences) if sentences else 0
@property
def severity_score(self) -> float:
"""严重度评分:加权幻觉得分"""
weights = {0: 0, 1: 0.25, 2: 0.5, 3: 0.75, 4: 1.0}
if not self.annotations:
return 0.0
return sum(weights[a.severity.value] for a in self.annotations) / len(self.annotations)
标注一致性度量
class AnnotationAgreement:
"""标注者间一致性计算"""
@staticmethod
def cohen_kappa(annotator1: list[str], annotator2: list[str]) -> float:
"""Cohen's Kappa:两个标注者的一致性"""
from collections import Counter
n = len(annotator1)
labels = sorted(set(annotator1 + annotator2))
# 观察一致率
observed = sum(1 for a, b in zip(annotator1, annotator2) if a == b) / n
# 期望一致率
c1 = Counter(annotator1)
c2 = Counter(annotator2)
expected = sum((c1[l] / n) * (c2[l] / n) for l in labels)
if expected == 1.0:
return 1.0
return (observed - expected) / (1 - expected)
@staticmethod
def fleiss_kappa(annotations: list[list[str]]) -> float:
"""Fleiss' Kappa:多标注者一致性"""
import numpy as np
n = len(annotations[0]) # 样本数
k = len(annotations) # 标注者数
labels = sorted(set(l for ann in annotations for l in ann))
m = len(labels)
# 构建计数矩阵
counts = np.zeros((n, m))
label_idx = {l: i for i, l in enumerate(labels)}
for annotator_labels in annotations:
for i, label in enumerate(annotator_labels):
counts[i][label_idx[label]] += 1
# 观察一致率
P_i = (np.sum(counts**2, axis=1) - k) / (k * (k - 1))
P_bar = np.mean(P_i)
# 期望一致率
p_j = np.sum(counts, axis=0) / (n * k)
P_e = np.sum(p_j**2)
if P_e == 1.0:
return 1.0
return (P_bar - P_e) / (1 - P_e)
二、自动检测算法
基于检索的幻觉检测
class RetrievalBasedDetector:
"""基于检索的幻觉检测:将生成内容与知识库比对"""
def __init__(self, knowledge_base, embedding_model="all-MiniLM-L6-v2"):
from sentence_transformers import SentenceTransformer
import faiss
import numpy as np
self.model = SentenceTransformer(embedding_model)
self.kb_texts = knowledge_base
embeddings = self.model.encode(knowledge_base)
# 构建 FAISS 索引
dim = embeddings.shape[1]
self.index = faiss.IndexFlatIP(dim)
self.index.add(embeddings.astype('float32'))
def detect(self, response: str, top_k: int = 5, threshold: float = 0.7) -> dict:
"""检测回答中的幻觉"""
sentences = self._split_sentences(response)
hallucinated = []
verified = []
for sent in sentences:
# 检索最相关的知识库条目
sent_emb = self.model.encode([sent]).astype('float32')
scores, indices = self.index.search(sent_emb, top_k)
max_score = scores[0][0]
best_match = self.kb_texts[indices[0][0]]
if max_score < threshold:
# 无法在知识库中找到支持 → 可能是幻觉
hallucinated.append({
"text": sent,
"max_similarity": float(max_score),
"best_match": best_match,
"verdict": "unsupported",
})
else:
verified.append({
"text": sent,
"similarity": float(max_score),
"source": best_match,
"verdict": "supported",
})
return {
"total_sentences": len(sentences),
"hallucinated_count": len(hallucinated),
"verified_count": len(verified),
"hallucination_rate": len(hallucinated) / len(sentences) if sentences else 0,
"details": {"hallucinated": hallucinated, "verified": verified},
}
def _split_sentences(self, text: str) -> list[str]:
import re
# 按中英文标点分句
sentences = re.split(r'[。!?.!?\n]+', text)
return [s.strip() for s in sentences if s.strip()]
基于 NLI 的幻觉检测
class NLIDetector:
"""基于自然语言推理(NLI)的幻觉检测"""
def __init__(self, model_name="moritzlaurer/DeBERTa-v3-base-mnli-fever-nli"):
from transformers import AutoTokenizer, AutoModelForSequenceClassification
import torch
self.tokenizer = AutoTokenizer.from_pretrained(model_name)
self.model = AutoModelForSequenceClassification.from_pretrained(model_name)
self.model.eval()
# 标签: 0=entailment, 1=neutral, 2=contradiction
def detect(self, response: str, reference: str) -> dict:
"""
判断 response 是否被 reference 支持
entailment: reference 支持 response
contradiction: reference 与 response 矛盾
neutral: 无法判断
"""
import torch
sentences = self._split_sentences(response)
results = []
for sent in sentences:
inputs = self.tokenizer(reference, sent, return_tensors="pt",
truncation=True, max_length=512)
with torch.no_grad():
logits = self.model(**inputs).logits
probs = torch.softmax(logits, dim=0)
results.append({
"sentence": sent,
"entailment_prob": probs[0].item(),
"neutral_prob": probs[1].item(),
"contradiction_prob": probs[2].item(),
"verdict": self._classify(probs),
})
hallucinated = [r for r in results if r["verdict"] == "contradiction"]
unsupported = [r for r in results if r["verdict"] == "neutral"]
return {
"total_sentences": len(results),
"supported": len(results) - len(hallucinated) - len(unsupported),
"contradicted": len(hallucinated),
"unsupported": len(unsupported),
"hallucination_rate": (len(hallucinated) + len(unsupported)) / len(results) if results else 0,
"details": results,
}
def _classify(self, probs):
labels = ["entailment", "neutral", "contradiction"]
idx = probs.argmax().item()
return labels[idx]
def _split_sentences(self, text):
import re
return [s.strip() for s in re.split(r'[。!?.!?\n]+', text) if s.strip()]
基于 LLM 的幻觉检测
class LLMHallucinationDetector:
"""使用强力 LLM 检测幻觉"""
def __init__(self, judge_model: str = "gpt-4o"):
self.judge_model = judge_model
def detect(self, prompt: str, response: str,
reference_context: str = None) -> dict:
"""检测回答中的幻觉"""
judge_prompt = f"""你是事实核查专家。请检查以下 AI 回答中是否存在幻觉(与事实不符的内容)。
用户问题:{prompt}
AI 回答:{response}
"""
if reference_context:
judge_prompt += f"\n参考信息(权威来源):\n{reference_context}\n"
judge_prompt += """
请逐句检查,对每句话标注:
- "supported":有事实依据支持
- "contradicted":与已知事实矛盾
- "unsupported":无法验证,可能是编造
输出 JSON:
{
"sentences": [
{"text": "...", "verdict": "supported/contradicted/unsupported", "reason": "..."}
],
"overall_hallucination": "none/minor/moderate/severe",
"hallucination_rate": 0.0-1.0,
"key_issues": ["问题1", "问题2"]
}"""
import json
result = call_llm(self.judge_model, judge_prompt)
try:
return json.loads(result)
except json.JSONDecodeError:
import re
match = re.search(r'\{.*\}', result, re.DOTALL)
if match:
return json.loads(match.group())
return {"error": "parse failed", "raw": result}
def detect_with_search(self, prompt: str, response: str) -> dict:
"""结合搜索引擎的幻觉检测"""
# 1. 提取需要验证的关键陈述
claims = self._extract_claims(response)
# 2. 对每个陈述进行搜索验证
results = []
for claim in claims:
search_results = web_search(claim)
verification = self._verify_claim(claim, search_results)
results.append(verification)
return {
"total_claims": len(claims),
"verified": sum(1 for r in results if r["verdict"] == "supported"),
"hallucinated": sum(1 for r in results if r["verdict"] == "contradicted"),
"unverifiable": sum(1 for r in results if r["verdict"] == "unsupported"),
"details": results,
}
def _extract_claims(self, text: str) -> list[str]:
"""提取需要验证的事实陈述"""
prompt = f"""从以下文本中提取需要验证的事实性陈述,每行一个:
{text}
只输出陈述列表。"""
result = call_llm(self.judge_model, prompt)
return [line.strip() for line in result.strip().split("\n") if line.strip()]
三、专门化幻觉检测工具
SelfCheckGPT
class SelfCheckGPT:
"""
SelfCheckGPT: 通过多次采样检测幻觉
核心思想:如果模型对同一问题多次生成的回答一致,则可信度高;
如果不一致,则可能存在幻觉
"""
def __init__(self, model_name: str, num_samples: int = 5):
self.model_name = model_name
self.num_samples = num_samples
def check(self, prompt: str, response: str) -> dict:
# 1. 生成多个样本
samples = []
for _ in range(self.num_samples):
# 使用较高温度增加多样性
sample = call_llm(self.model_name, prompt, temperature=0.7)
samples.append(sample)
# 2. 计算一致性
consistency_scores = []
for i, sample in enumerate(samples):
if i == 0:
continue
score = self._sentence_level_consistency(response, sample)
consistency_scores.append(score)
avg_consistency = sum(consistency_scores) / len(consistency_scores) if consistency_scores else 1.0
return {
"original_response": response,
"num_samples": self.num_samples,
"avg_consistency": avg_consistency,
"hallucination_score": 1 - avg_consistency, # 不一致 = 幻觉概率
"verdict": "likely_hallucinated" if avg_consistency < 0.6 else "likely_reliable",
}
def _sentence_level_consistency(self, response: str, sample: str) -> float:
"""计算句子级一致性"""
from sentence_transformers import SentenceTransformer
import numpy as np
model = SentenceTransformer("all-MiniLM-L6-v2")
resp_sents = self._split_sentences(response)
sample_sents = self._split_sentences(sample)
resp_emb = model.encode(resp_sents)
sample_emb = model.encode(sample_sents)
# 对每个原句,找到样本中最相似的句子
sim_matrix = resp_emb @ sample_emb.T
max_sims = sim_matrix.max(axis=1)
return float(np.mean(max_sims))
def _split_sentences(self, text):
import re
return [s.strip() for s in re.split(r'[。!?.!?\n]+', text) if s.strip()]
四、评估指标体系
class HallucinationMetrics:
"""幻觉评估指标集合"""
@staticmethod
def precision_recall_f1(annotations: list[dict]) -> dict:
"""
计算幻觉检测的 Precision/Recall/F1
annotations: [{"pred_hallucinated": bool, "gt_hallucinated": bool}]
"""
tp = sum(1 for a in annotations if a["pred_hallucinated"] and a["gt_hallucinated"])
fp = sum(1 for a in annotations if a["pred_hallucinated"] and not a["gt_hallucinated"])
fn = sum(1 for a in annotations if not a["pred_hallucinated"] and a["gt_hallucinated"])
tn = sum(1 for a in annotations if not a["pred_hallucinated"] and not a["gt_hallucinated"])
precision = tp / (tp + fp) if (tp + fp) > 0 else 0
recall = tp / (tp + fn) if (tp + fn) > 0 else 0
f1 = 2 * precision * recall / (precision + recall) if (precision + recall) > 0 else 0
accuracy = (tp + tn) / len(annotations) if annotations else 0
return {
"precision": round(precision, 4),
"recall": round(recall, 4),
"f1": round(f1, 4),
"accuracy": round(accuracy, 4),
"tp": tp, "fp": fp, "fn": fn, "tn": tn,
}
@staticmethod
def hallucination_rate(docs: list[HallucinationDocument]) -> dict:
"""计算总体幻觉率"""
rates = [d.hallucination_rate for d in docs]
severities = [d.severity_score for d in docs]
import numpy as np
return {
"mean_hallucination_rate": round(np.mean(rates), 4),
"median_hallucination_rate": round(np.median(rates), 4),
"p95_hallucination_rate": round(np.percentile(rates, 95), 4),
"mean_severity": round(np.mean(severities), 4),
"docs_with_hallucination": sum(1 for d in docs if d.annotations),
"total_docs": len(docs),
}
@staticmethod
def by_type_breakdown(docs: list[HallucinationDocument]) -> dict:
"""按幻觉类型分解"""
from collections import defaultdict
by_type = defaultdict(int)
for doc in docs:
for ann in doc.annotations:
by_type[ann.hallucination_type.value] += 1
total = sum(by_type.values())
return {
t: {"count": c, "percentage": round(c / total * 100, 1) if total > 0 else 0}
for t, c in sorted(by_type.items(), key=lambda x: -x[1])
}
五、检测方法对比
| 方法 | 准确率 | 召回率 | 成本 | 实时性 | 适用场景 |
|---|---|---|---|---|---|
| 人工标注 | 95%+ | 90%+ | 极高 | 慢 | 基线建立 |
| 检索比对 | 80% | 70% | 中 | 中 | 有知识库时 |
| NLI 模型 | 85% | 75% | 低 | 快 | 有参考文本时 |
| LLM-as-Judge | 88% | 82% | 中高 | 中 | 通用检测 |
| SelfCheckGPT | 78% | 85% | 高(多次采样) | 慢 | 无参考文本时 |
| 搜索验证 | 82% | 68% | 中 | 慢 | 事实性声明 |
实践建议
分层检测策略
class LayeredHallucinationDetection:
"""分层幻觉检测策略"""
def __init__(self):
self.fast_checker = NLIDetector() # 快速初筛
self.deep_checker = LLMHallucinationDetector() # 深度检测
self.search_checker = None # 搜索验证(按需启用)
def check(self, prompt: str, response: str,
reference: str = None) -> dict:
# 层 1:NLI 快速检测(<100ms)
if reference:
nli_result = self.fast_checker.detect(response, reference)
if nli_result["hallucination_rate"] < 0.1:
# NLI 认为基本无幻觉,直接返回
return {"layer": "nli", "result": nli_result, "confidence": "high"}
# 层 2:LLM 深度检测(1-3s)
llm_result = self.deep_checker.detect(prompt, response, reference)
if llm_result.get("overall_hallucination") in ["none", "minor"]:
return {"layer": "llm", "result": llm_result, "confidence": "high"}
# 层 3:搜索验证(5-10s,仅对高风险内容)
if llm_result.get("overall_hallucination") in ["severe", "moderate"]:
search_result = self.deep_checker.detect_with_search(prompt, response)
return {"layer": "search", "result": search_result, "confidence": "highest"}
return {"layer": "llm", "result": llm_result, "confidence": "medium"}
结语
幻觉检测是 LLM 可靠性的最后一道防线。没有单一方法能完美检测所有类型的幻觉——检索方法依赖知识库的覆盖度,NLI 方法需要参考文本,LLM-as-Judge 本身也可能产生幻觉。最佳实践是分层检测:快速方法做初筛,深度方法做验证,搜索方法做兜底。同时,定期进行人工标注作为基线,校准自动检测系统的准确率。记住:降低幻觉的根本在于模型训练和 RAG 增强,检测只是发现问题的手段,而非解决方案本身。
加入讨论
这篇文章有姊妹讨论帖在硅基AGI论坛 — 全球首个碳基硅基认知交流平台。
- 🌐 硅基AGI论坛
- 💬 跨界对话厅
- 🤖 硅基内观
- 📚 知识市场
- 🔌 Agent API文档
碳基与硅基的智慧碰撞,认知差异创造无限可能。
