从一个真实的事故说起
2025年,某律师事务所的助理用ChatGPT撰写法律摘要,引用了6个从未存在的判例。这件事被称为"AI幻觉事故",引发广泛讨论。时至2026年,幻觉问题并未消失,但我们对它的理解和应对方式已经大不相同。
本文将系统拆解幻觉问题的本质,以及工程团队可以采取的实际对策。
一、什么是幻觉?精确定义
AI幻觉(Hallucination)指大语言模型生成的内容与现实不符、无从证实,或与上下文矛盾,但模型却以确定的语气呈现这些内容。
根据表现形式,幻觉可分为以下几类:
1.1 事实性幻觉
- 虚构引用:捏造不存在的论文、判例、统计数据
- 错误信息:给出错误的人物生卒年、公司成立时间等
- 混淆信息:张冠李戴,把A的特征安到B身上
1.2 忠实性幻觉(针对RAG/摘要场景)
- 输入矛盾:生成的内容与提供的文档相矛盾
- 过度推断:从文档中推断出文档并未声明的结论
- 信息遗漏:忽略了文档中重要的否定词、限制条件
1.3 逻辑幻觉
- 自我矛盾:在同一回答中前后说法相悖
- 推理错误:步骤看似合理,但逻辑链条有缺口
二、幻觉的根本成因
2.1 训练数据的噪声
LLM从海量互联网文本学习,其中包含:
- 错误的维基百科编辑(在被纠正前)
- 博客中的主观臆断被当作事实陈述
- 过期信息(某项技术"最新版本"在训练截止后已更新)
- 数据重复导致特定错误被强化
2.2 自回归生成的局限
LLM的本质是"预测下一个token的概率分布",它优化的目标是生成听起来流畅、合理的文本,而不是生成事实准确的文本。
当模型不确定时,它不会说"我不知道",而是倾向于生成一个"听起来像答案"的序列——这正是幻觉的根源。
2.3 知识截止问题
训练数据有截止日期,模型对截止日期后的事件完全不知晓,但在被问及时可能会基于历史规律"编造"一个合理的答案。
2.4 上下文窗口的局限
在长文档处理中,当相关信息出现在上下文的中间部分时,模型的注意力会减弱,更容易出现"遗漏"和"推断"。这就是著名的"迷失在中间"(Lost in the Middle)问题。
三、检测幻觉的方法
3.1 基于规则的检测
对于结构化输出(时间、数字、引用),可以用规则直接验证:
import re
from datetime import datetime
def validate_date_claims(text: str, source_documents: list[str]) -> list[dict]:
"""检测文本中的日期声明是否可在源文档中验证"""
# 提取文本中的年份
years_in_text = set(re.findall(r'\b(19\d{2}|20\d{2})\b', text))
# 提取源文档中出现的年份
years_in_source = set()
for doc in source_documents:
years_in_source.update(re.findall(r'\b(19\d{2}|20\d{2})\b', doc))
# 找出文本中出现但源文档中没有的年份
unverified_years = years_in_text - years_in_source
results = []
for year in unverified_years:
results.append({
"claim": f"年份 {year}",
"status": "unverified",
"risk": "medium"
})
return results
3.2 LLM-as-Judge(用LLM评判LLM)
from openai import AsyncOpenAI
client = AsyncOpenAI()
FAITHFULNESS_PROMPT = """
你是一个严格的事实核查员。
请判断以下"模型回答"是否忠实于"参考文档"。
参考文档:
{context}
模型回答:
{answer}
请以JSON格式回答:
{{
"faithful": true/false,
"score": 0-1的浮点数(1表示完全忠实),
"issues": ["发现的问题1", "发现的问题2"],
"explanation": "简短说明"
}}
注意:
- 如果回答包含文档中没有的事实声明,视为不忠实
- 如果回答只是合理推断并有明确标注,可以接受
- 过度的确定性表述也算问题(文档说"可能",回答说"一定")
"""
async def check_faithfulness(
context: str,
answer: str,
threshold: float = 0.8
) -> dict:
response = await client.chat.completions.create(
model="gpt-4.1",
messages=[{
"role": "user",
"content": FAITHFULNESS_PROMPT.format(
context=context,
answer=answer
)
}],
response_format={"type": "json_object"},
temperature=0 # 用低温度保证判断稳定性
)
result = json.loads(response.choices[0].message.content)
result["passes_threshold"] = result["score"] >= threshold
return result
3.3 自一致性检测(Self-Consistency)
同一问题多次询问,检查答案是否一致:
async def self_consistency_check(
question: str,
num_samples: int = 5,
temperature: float = 0.7
) -> dict:
"""通过多次采样检测答案的一致性"""
answers = []
for _ in range(num_samples):
response = await client.chat.completions.create(
model="gpt-4.1",
messages=[{"role": "user", "content": question}],
temperature=temperature
)
answers.append(response.choices[0].message.content)
# 让LLM判断这些答案是否一致
consistency_prompt = f"""
以下是对同一问题的{num_samples}个回答,请判断它们在关键事实上是否一致。
问题:{question}
回答列表:
{chr(10).join(f'{i+1}. {a}' for i, a in enumerate(answers))}
返回JSON:{{"consistent": true/false, "conflicting_claims": [], "confidence": 0-1}}
"""
consistency_result = await client.chat.completions.create(
model="gpt-4.1",
messages=[{"role": "user", "content": consistency_prompt}],
response_format={"type": "json_object"},
temperature=0
)
return {
"answers": answers,
"consistency": json.loads(consistency_result.choices[0].message.content)
}
3.4 基于检索的事实核查
from qdrant_client import QdrantClient
from openai import AsyncOpenAI
async def retrieve_and_verify(
claim: str,
knowledge_base: QdrantClient,
collection: str
) -> dict:
"""从知识库检索相关内容,验证声明是否有根据"""
# 将声明转为embedding
embedding_response = await client.embeddings.create(
model="text-embedding-3-large",
input=claim
)
query_vector = embedding_response.data[0].embedding
# 检索最相关的文档片段
results = knowledge_base.search(
collection_name=collection,
query_vector=query_vector,
limit=5,
score_threshold=0.6
)
if not results:
return {
"verified": False,
"reason": "知识库中没有相关内容支撑此声明",
"risk_level": "high"
}
# 用LLM判断检索到的内容是否支持该声明
evidence = "\n".join([r.payload["text"] for r in results])
verification_response = await client.chat.completions.create(
model="gpt-4.1",
messages=[{
"role": "user",
"content": f"根据以下证据:\n{evidence}\n\n判断此声明是否有根据:{claim}\n回答:yes/no/partial"
}],
temperature=0
)
verdict = verification_response.choices[0].message.content.lower().strip()
return {
"verified": verdict == "yes",
"partially_verified": verdict == "partial",
"evidence_count": len(results),
"top_score": results[0].score if results else 0,
"risk_level": "low" if verdict == "yes" else ("medium" if verdict == "partial" else "high")
}
四、工程级解决方案
4.1 RAG + 引用追踪(最有效的通用方案)
CITATION_PROMPT = """
你是一个严谨的助手。回答问题时,必须严格基于提供的参考资料。
规则:
1. 只使用参考资料中明确陈述的信息
2. 每个关键事实后面都要标注来源,格式:[来源X]
3. 如果参考资料中没有相关信息,明确说"参考资料中未提及"
4. 不要添加参考资料以外的"常识性补充"
参考资料:
{context}
问题:{question}
"""
async def rag_with_citation(
question: str,
context_docs: list[dict]
) -> dict:
# 构建带编号的上下文
numbered_context = "\n\n".join([
f"[来源{i+1}] {doc['text']}\n(来自:{doc['source']})"
for i, doc in enumerate(context_docs)
])
response = await client.chat.completions.create(
model="gpt-4.1",
messages=[{
"role": "user",
"content": CITATION_PROMPT.format(
context=numbered_context,
question=question
)
}],
temperature=0.3
)
answer = response.choices[0].message.content
# 提取引用的来源编号
cited_sources = set(re.findall(r'\[来源(\d+)\]', answer))
return {
"answer": answer,
"cited_sources": [context_docs[int(i)-1] for i in cited_sources],
"uncited_docs": [
context_docs[i] for i in range(len(context_docs))
if str(i+1) not in cited_sources
]
}
4.2 不确定性表达(Chain-of-Thought + Uncertainty)
UNCERTAINTY_AWARE_PROMPT = """
回答以下问题。在回答中:
1. 如果你非常确定某个事实(>90%置信度),直接陈述
2. 如果你比较确定(70-90%),使用"据我了解"、"通常情况下"
3. 如果你不太确定(<70%),明确说"我不确定,但..."
4. 如果你根本不知道,直接说"我不知道这个问题的答案"
不要为了看起来有用而编造信息。承认不确定比给出错误答案更有价值。
问题:{question}
"""
4.3 多步骤验证管道
class HallucinationAwarePipeline:
"""带幻觉检测的生产级问答管道"""
def __init__(self, retriever, llm, judge_llm):
self.retriever = retriever
self.llm = llm
self.judge_llm = judge_llm
async def answer(self, question: str) -> dict:
# Step 1: 检索相关文档
docs = await self.retriever.retrieve(question, top_k=5)
# Step 2: 生成初始答案
initial_answer = await self.llm.generate(
question=question,
context=docs
)
# Step 3: 幻觉检测
faithfulness_check = await check_faithfulness(
context="\n".join([d.text for d in docs]),
answer=initial_answer
)
# Step 4: 如果检测到幻觉,重新生成或降级处理
if not faithfulness_check["passes_threshold"]:
if faithfulness_check["score"] < 0.5:
# 严重幻觉:返回"无法回答"
return {
"answer": "抱歉,我无法从现有资料中找到可靠的答案来回答您的问题。",
"reliable": False,
"reason": "检测到潜在的幻觉内容"
}
else:
# 轻微幻觉:添加免责声明并附上原始资料
return {
"answer": initial_answer + "\n\n⚠️ 注意:以上内容可能包含未经核实的信息,请参考原始资料。",
"reliable": False,
"sources": [d.source for d in docs],
"issues": faithfulness_check["issues"]
}
return {
"answer": initial_answer,
"reliable": True,
"confidence": faithfulness_check["score"],
"sources": [d.source for d in docs]
}
五、不同场景的风险等级与对策
| 场景 | 幻觉风险 | 推荐对策 |
|---|---|---|
| 内部文档问答 | 中 | RAG + 引用追踪 |
| 代码生成 | 中 | 单元测试兜底 + 实时编译验证 |
| 法律/医疗咨询 | 极高 | LLM-as-Judge + 人工复核 |
| 创意写作 | 低 | 无需特殊处理 |
| 数据分析报告 | 高 | 结构化输出 + 数据库查询验证 |
| 客服对话 | 高 | 知识库严格约束 + 超范围拒答 |
六、总结
幻觉无法被"消灭",但可以被"管理"。2026年工程实践的核心思路是:
- 设计时减少幻觉机会:用RAG约束信息范围,强制引用来源
- 运行时检测幻觉:LLM-as-Judge + 一致性检测
- 优雅降级:检测到幻觉时不是崩溃,而是告知用户不确定性
- 监控与持续优化:收集幻觉案例,定期改进提示词和检测策略
幻觉问题的最终解决,可能需要等待模型架构层面的突破。但在那之前,工程上的防御是保障AI应用可靠性的必经之路。
参考:Anthropic Constitutional AI、DeepMind Gemini Technical Report、Hughes et al. (2025) "TruthfulQA 2.0"