大模型能力越强,越容易一本正经地胡编乱造。这不是段子,而是很多 AI 应用落地时最头疼的问题。最近看到 "Life Of AI – I hallucinate. Therefore I am" 这个标题时,感触很深,它用一种近乎自嘲的方式点破了当前生成式 AI 的核心困境:大模型之所以显得“智能”,恰恰是因为它能流畅地生成内容,而流畅生成本身就伴随着失真、虚构与幻觉。本文不讨论哲学层面的“AI 是否存在”,而是从工程视角出发,拆解 AI 幻觉的本质、产生原理、常见类型,以及我们在实际开发中如何降低幻觉带来的业务风险。
这篇文章适合正在开发 AI 应用、接入大模型 API、做 RAG 问答系统、或者对模型可信度有要求的开发者。读完你会理解:幻觉不是一个“修一下就好”的 bug,而是一个需要在系统设计、提示词、检索链路、结果校验等多个环节共同治理的问题。
1. AI 幻觉到底是什么
1.1 从一句话说起
“I hallucinate. Therefore I am”,这个标题模仿了笛卡尔的“我思故我在”。把它翻译过来,意思是:我(AI)产生幻觉,所以我存在。
为什么这句话能打动 AI 开发者?因为大模型的核心能力本质上是“流畅地生成下一个 token”。一个模型在生成一段看起来逻辑通顺、语气笃定的内容时,它并没有像人类那样经过事实核查,它只是在概率空间里选择一个最符合上下文分布的词。问题在于,这个选择过程并不保证与事实一致。
所以,AI 幻觉指的是:
大模型生成的内容与客观事实不符,或者与用户提供的上下文不一致,但生成内容在语言形式上非常自信、流畅,读者很难只靠文字本身判断它是错的。
举个典型例子:
用户问:“2025 年诺贝尔物理学奖得主是谁?”
模型答:“2025 年诺贝尔物理学奖颁发给了 David J. Thouless,以表彰他在拓扑相变领域的开创性贡献。”
这句话语言流畅、结构完整,但它其实是错的。David J. Thouless 是 2016 年的获奖者,2025 年的获奖者并不是他。这就是一个典型的事实性幻觉。
1.2 幻觉与胡说的区别
我们得区分两个概念:
- 胡说/错误回答:模型给出的答案错了,但它的输出依据本身是明确的,比如模型说“2 + 2 = 5”,这是数学错误,但模型不是在“幻觉”,它只是在做错误的计算推断。
- 幻觉:模型编造了一个它“似乎知道”的答案。它可能包含假人名、假论文标题、假链接、假新闻事件,但这些内容在它的训练数据里并不存在,或者是被错误拼接出来的。
换句话说,幻觉最危险的地方不是“答错”,而是“创造了一个看起来真实但其实不存在的内容”。医生如果被 AI 幻觉误导,患者可能延误治疗;法务人员如果被 AI 幻觉引用了假判例,就可能做出错误决策。
1.3 为什么 AI 幻觉值得专门研究
大模型现在已经被接入客服、办公、教育、编程、医疗、金融等各个领域。只要生成式 AI 进入生产环境,幻觉就是绕不开的工程问题。
从业务角度看,幻觉会造成以下几类损失:
| 影响类别 | 具体表现 |
|---|---|
| 信息可信度下降 | 用户发现 AI 回答有误,对整个系统失去信任 |
| 合规风险 | 金融、医疗、法律领域引用虚假信息可能违反监管要求 |
| 调试成本上升 | 幻觉问题随机出现,难以稳定复现 |
| 二次污染 | 用户可能把 AI 生成的错误内容传播到社交网络或内部文档中 |
| 链路放大 | 在多轮对话或 Agent 任务中,一个幻觉会被后续步骤放大成更大错误 |
所以,整个大模型应用开发链条里,幻觉治理已经成为和“模型选型”“Prompt 设计”“上下文工程”并列的核心环节。
2. AI 幻觉从哪里来
很多开发者第一次发现模型幻觉时,会觉得是“模型智力问题”或者“数据不够强”。其实,幻觉的根因隐藏在大模型的原理中。
2.1 语言模型本质是概率预测
大模型的任务,简单说就是给定一段文本,预测下一个 token 的概率分布。训练时,它看过海量语料,学习到的是 token 之间的统计相关性。到了推理阶段,它按概率采样生成文本。
也就是说,模型生成的内容“长得像”真实语料,但并不意味着它在“查询”真实世界知识库。它对世界的认识,全部压缩在参数里。这个压缩过程有损失,因此它记住的事实可能是残缺的、过时的、甚至错位的。
2.2 训练数据与知识截止时间问题
大模型的训练数据有一个截止时间。比如某个模型的知识只更新到 2024 年 6 月,你问它 2025 年发生的新闻事件,它根本没有对应的 token 序列可供参考,在无法表达“我不知道”的情况下,它就会尝试从近义词、事件模式中拼凑答案。
这就是“知识截止”带来的幻觉。很多幻觉不是模型不聪明,而是它的知识库真的没有这个信息,但它的生成机制不允许它“沉默”,于是它选择“编”。
2.3 解码策略的随机性
同样一个问题,把温度(temperature)从 0 调到 0.8,模型的输出可能截然不同。温度越高,模型采样越随机,越容易出现奇怪组合;温度越低,越倾向于输出概率最高的 token,但“概率最高”不一定等于“事实正确”。
2.4 语境污染
多轮对话中,用户上一句提供了错误信息,模型为了保持连贯,可能会顺着错误的语境继续生成。比如用户说“我们公司的服务器在火星”,模型后续回答中可能真的会基于“服务器在火星”这个前提讲解网络延迟优化方案。这种幻觉不是知识缺失,而是上下文被污染后的逻辑延伸。
2.5 幻觉的分类
在工程实践中,我习惯把幻觉分成以下几类:
- 事实性幻觉(Factual Hallucination):模型生成的内容与真实世界事实不符。常见于新闻、人物、历史、科学知识等场景。
- 忠实性幻觉(Faithfulness Hallucination):模型没有忠实于用户输入或检索到的文档内容,而是自行扩展、推断甚至篡改。常见于摘要生成、对话系统。
- 逻辑性幻觉:推理链条断裂,前一步推导出的结论和后一步不一致,但语言上依然通顺。
了解分类,有助于我们后续针对不同类型做定向缓解。事实性幻觉需要检索增强,忠实性幻觉需要约束解码,逻辑性幻觉需要强化推理框架。
3. 如何识别和评估模型幻觉
要治理幻觉,先要能识别它。但幻觉的一个难点在于:每次问的结果可能都不一样。我们无法只凭一两个例子判断模型是否“爱幻觉”。
3.1 人工评估
最直接的思路是人工评估。对于问答场景,我们可以准备一组带有标准答案的测试集,让模型生成回答,由人工标注“正确 / 错误 / 部分正确 / 无法判断”。
优点是直观、准确;缺点是成本高、速度慢,没办法大规模覆盖。
3.2 自动化指标
在研发阶段,我们一般会构建一个离线评估集,用自动化指标来近似判断模型的幻觉率。
常见的做法包括:
- ROUGE/BLEU 类指标:将模型输出和参考答案做文本重叠度计算,适合摘要任务,但没法判断语义真假。
- NLI 一致性判断:用自然语言推理模型判断“输入文档”是否蕴含“模型生成内容”。如果生成内容无法由输入文档推出,就标记为潜在幻觉。
- FactScore / FACTCORE 类方法:将生成内容拆分成原子事实,逐条与知识源比对,计算事实准确率。
- LLM 作为裁判(LLM-as-a-Judge):用另一个大模型给输出打分,判断是否存在幻觉。这个方法在工程中很好用,但要注意裁判模型本身的幻觉问题。
3.3 检索事实对比
在 RAG(检索增强生成)架构中,幻觉评估更直接:把用户问题、检索到的文档、模型生成答案三者放到一起,比对生成答案中的关键实体是否在检索文档中有依据。这个比对过程可以人工,也可以用正则表达式或语义相似度实现。
3.4 置信度与日志
生产环境里,建议在每次推理时记录以下信息:
- 每个 token 的 logprob(对数概率)
- 整个回答的平均置信度
- 温度参数
- 使用的检索文档 ID 列表
- Prompt 版本号
如果某个问题下答案的置信度很低,说明模型自己也“没把握”,系统可以降级为“抱歉,我没有找到可靠信息”。
4. 工程实战:降低幻觉的几种思路
下面进入文章的重点。从工程角度看,降低幻觉没有一个“万能开关”,但我们可以从系统不同层面下手,组合使用。
4.1 提示词层面的约束
提示词是成本最低、见效最快的幻觉治理手段。先说思路,再给示例。
思路一:允许模型说不知道。
很多模型在训练时倾向于用户满意,会硬着头皮给答案。我们在 System Prompt 里明确告诉它:“如果不确定,请说不知道,不要编造。” 这样能在一定程度上减少幻觉。
思路二:要求引用依据。
尤其在 RAG 场景下,要求模型回答必须引用文档编号或原文片段。模型一旦被要求“溯源”,生成的自由度就会降低。
思路三:使用 few-shot 示例。
给模型一两个“拒绝回答”的示例,比单纯用指令更有效。因为示例直接给出了期望的输出形式。
下面是一个可复用的 System Prompt 模板:
你是一个基于内部知识库回答问题的助手。回答时需遵守以下规则: 1. 只能使用用户提供的上下文信息作答,不得使用训练阶段记忆的常识或新闻内容。 2. 如果上下文中没有足够信息,请直接回复:“抱歉,根据已有资料,我无法确认该问题的答案。” 3. 回答必须标注引用来源,格式为:答案内容(来源:文档编号-段落编号)。 4. 不要主动推断、补全或延伸上下文之外的信息。 参考示例: 用户:公司年假政策是什么? 上下文:文档1:「员工入职满一年后可享受5天带薪年假。」 回答:员工入职满一年后可享受5天带薪年假(来源:文档1-第2段)。注意,这个提示词只是一份工程思路,你需要根据实际使用的模型和业务场景做调整。如果模型是 Agent 场景,提示词可以加入“只有调用工具获取到真实数据后才能回答”的约束。
4.2 RAG 检索增强:用外部知识锚定事实
RAG 是当前降低事实性幻觉最主流的工程方案。基本原理是:先根据用户问题检索出相关知识片段,再让模型基于这些片段生成答案,而不是凭空发挥。
下面给出一个简单的 RAG 流程拆解:
- 知识库预处理:把文档切块(chunking),并生成向量索引。
- query 处理:对用户问题进行向量化。
- 召回:在向量库中召回 top-k 相关文档块。
- 重排:可选地用 reranker 模型对召回结果二次排序。
- 生成:将召回文档和用户问题组装进 Prompt,交给 LLM 生成答案。
工程中最关键的点是第 4 步和第 5 步。如果召回的文档本身不相关,再强的模型也会答错。
一个简化版的 Python 伪代码示例(核心思路,需要按实际环境改造):
# 文件路径:rag_pipeline.py from typing import List def retrieve_documents(query: str, top_k: int = 3) -> List[str]: """ 从向量数据库检索相关文档。 实际项目中替换为你的向量数据库调用,例如 Qdrant / Milvus / Elasticsearch。 """ query_vector = embed(query) hits = vector_db.search(query_vector, top_k=top_k) return [hit["text"] for hit in hits] def build_prompt(query: str, documents: List[str]) -> str: """ 把检索结果组装成 Prompt。 强调模型只能使用下列文档内容作答,并给出引用编号。 """ context = "\n\n".join( [f"[文档{i+1}]\n{doc}" for i, doc in enumerate(documents)] ) prompt = f"""你是一个严谨的问答助手。请基于以下参考文档回答用户问题。 参考文档: {context} 用户问题:{query} 要求: 1. 只使用参考文档中的信息。 2. 如果参考文档中没有答案,回答“根据已有资料无法确认”。 3. 在答案末尾标注来源编号,例如(来源:文档1)。 回答:""" return prompt def generate_answer(query: str) -> str: docs = retrieve_documents(query) prompt = build_prompt(query, docs) return llm_call(prompt)这里需要提醒一下:纯 RAG 只能缓解“知识缺失”和“知识过时”类幻觉,不能完全消除“忠实性幻觉”。因为最终生成文本的仍然是语言模型,如果 Prompt 约束不够强,模型仍然可能脱离文档自由发挥。
4.3 解码参数调整:降低随机性
如果你的场景对事实准确性要求高,可以把 temperature 调低。比如:
- 事实问答:temperature = 0.1 或 0
- 创意写作:temperature = 0.8 ~ 1.2
- 代码生成:temperature = 0.2 ~ 0.4
使用 OpenAI 兼容接口时的简单示例:
response = client.chat.completions.create( model="your-model-name", messages=[ {"role": "system", "content": "只基于事实回答,不确定时说明不知道。"}, {"role": "user", "content": user_query} ], temperature=0.1, max_tokens=512, )注意,temperature=0 不代表绝对确定,模型解码仍有概率因素,只是随机性降低。它还可能导致重复回答,因此要结合业务场景调整。
4.4 后置校验:给模型答案“上锁”
在关键业务场景,我们不能只依赖模型“自觉”。更稳妥的做法是在生成链路后加一个校验层。
校验层可以做的事情:
- 实体抽取与知识库比对:从答案中抽取关键实体,到知识库中做精确或模糊匹配,检查是否有依据。
- 规则过滤:检测答案中是否包含“不存在的链接”“伪造的日期”“无意义的编号”。
- 模式拦截:如果模型输出包含“根据文档[99]”,但实际检索文档只有 3 个,直接拦截。
- LLM 评审:调用一个独立的评审 Prompt,传入用户问题、检索文档、模型回答,由评审模型判断是否存在“上下文不支持的表述”。
下面给一个后置校验核心代码的伪代码片段,展示思路:
# 文件路径:post_check.py from typing import Dict, List def atomic_facts(claim: str) -> List[str]: """ 将模型生成的长文本拆分为原子事实。 实际可用分句模型或简单规则,例如按句号/分号拆分。 """ return [s.strip() for s in claim.split("。") if s.strip()] def verify_by_source(claim: str, documents: List[str]) -> bool: """ 简化校验:判断原子事实中的关键信息是否在文档中出现。 生产环境建议使用语义相似度或实体匹配。 """ for fact in atomic_facts(claim): for doc in documents: if any(keyword in doc for keyword in extract_keywords(fact)): break else: # 这说明该条事实没有在任何文档中找到依据 return False return True def post_check(answer: str, retrieved_docs: List[str]) -> Dict[str, bool]: return { "passed": verify_by_source(answer, retrieved_docs), "reason": "答案中存在无法在检索文档中找到依据的表述" if not verify_by_source(answer, retrieved_docs) else "ok", }这个方案的核心思想是:模型的输出只是候选答案,必须经过证据链校验后才能展示给用户。在金融、医疗等高风险场景,这套后置校验往往比调整 Prompt 更可靠。
4.5 产品层兜底:显示不确定性
有些场景下,即使做了 RAG、调了 Prompt,模型依然可能犯错。这时候产品设计要有“兜底”心态。
推荐的做法是:
- 给回答添加来源引用:告诉用户“这一段来自哪篇文档”,让用户自行判断。
- 添加不确定性提示:如果模型置信度低,显示“该答案可能需要人工核实”。
- 提供“不支持回答”的默认行为:当问题超出知识库范围时,默认不生成回答,而是引导用户找人工客服。
- 记录用户反馈:增加“这个回答正确吗”的按钮,把用户反馈回流到评估集。
这些都不是纯技术方案,但在工程落地中往往比“用更大模型”更有效。
5. 高级场景:Agent 与多步推理中的幻觉放大器
5.1 Agent 中幻觉的串联风险
在单一问答场景,幻觉的影响范围通常局限于一条回答。但在 Agent(智能体)场景中,模型可能需要规划任务、调用工具、判断中间结果、生成最终结论。一旦中间某一步产生了幻觉,后续步骤会拿着错误信息继续运算,最终错误会被放大。
举个例子:
Agent 要完成“分析某公司近期销售趋势并生成周报”。如果第一步模型幻觉出一个不存在的“订单表”,后续的计算全部基于这个假表,最终周报可能出现完全虚构的增长曲线。
5.2 工程上如何收敛 Agent 幻觉
在 Agent 场景治理幻觉,核心原则是:
- 工具结果是唯一事实来源:不要让模型“记忆”数据,所有数据都必须通过工具调用获取,并在 Prompt 中强调“只能使用工具返回数据”。
- 中间步骤要可审计:记录每一步的 tool call 参数和返回结果,方便事后排查。
- 关键节点增加二次确认:在生成总结之前,先让模型列出“用来得出该结论的关键数据点”,再由校验组件比对工具返回值。
- 限定工具返回的字段:不要把大量无关字段都丢给模型,字段越多,模型越容易混淆。
下面是一个 Agent Prompt 片段的思路:
你是一个数据库分析助手。你必须遵守以下规则: 1. 凡涉及具体数据,必须调用 get_sales_data 工具获取,禁止猜测或推算。 2. 每个结论都必须在括号中标注数据来源,例如“本月销售额 230 万(来源:get_sales_data 返回的 total_sales 字段)”。 3. 如果工具调用失败或返回空值,请直接说明查询失败,不要尝试编造数据。 4. 最终报告只能包含工具返回中出现的数值,不得新增任何未出现字段。5.3 多模型组合与幻觉互相校验
在预算允许的情况下,可以用两个不同模型做交叉验证。例如,A 模型生成回答,B 模型独立给出判断:“根据这份文档,A 的回答中哪些内容没有依据?” 两个模型都出现同样幻觉的概率会低于单模型,但要注意,这不能完全证明回答正确,只能降低风险。
这种方法在“高成本高影响”场景(比如医疗报告初筛、法律条款解读)比较适合。
6. 常见问题与排查思路
下面整理一些我在实际项目中遇到的典型问题,给大家参考:
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| 模型回答看起来流畅但人物/事件时间错误 | 知识截止时间之外的常识型幻觉 | 接入 RAG,补充最新知识库;Prompt 中要求引用来源 |
| 模型生成了看似合理但实际不存在的论文标题 | 训练数据压缩与错误拼接 | 对关键实体做知识库比对;增加“无法确认时拒绝回答”约束 |
| 多轮对话中越聊越偏 | 历史上下文中包含错误信息,模型被语境污染 | 设置上下文窗口过滤策略,只保留关键信息;对上一轮回答做校正 |
| 加了 RAG 后依然答非所问 | 召回文档与问题无关 | 检查分块大小与检索 score 阈值;增加 reranker |
| 输出内容被后置校验拦截率过高 | 提示词约束过强或检索文档冲突 | 平衡 Prompt 约束;检查知识库是否包含矛盾内容 |
| 同一问题运行多次结果不一致 | 温度过高或采样策略太随机 | 调低 temperature,必要时固定随机种子 |
| Agent 计算出错误结论 | 中间某一步工具结果被模型篡改 | 在 Prompt 中强调“只允许使用工具返回原文”;增加数据校验层 |
排查幻觉问题,我建议按照这个顺序:
- 先复现:固定问题和 Prompt,多次运行,看是随机问题还是稳定错误。
- 再分类:判断是事实性幻觉、忠实性幻觉还是逻辑性幻觉。
- 查检索:如果是 RAG,先检查召回文档是否包含正确信息。
- 调 Prompt:确认约束是否明确,有没有给模型“自由发挥”的空间。
- 加校验:在生成链路末尾加证据比对,守住最后一道防线。
7. 工程落地最佳实践
避免幻觉不只是“调一个参数”或者“加一段提示词”,而是一个需要贯穿系统设计全过程的治理体系。以下几条实践建议,来自我自己的项目经验,供大家参考。
7.1 建立“证据可回溯”的默认机制
不管应用是否对外展示,建议在内部数据结构中保留每次问答的证据链:
{ "query": "公司年假政策是什么?", "answer": "员工入职满一年后可享受5天带薪年假", "evidence": [ { "doc_id": "doc_001", "chunk_id": "chunk_002", "text": "员工入职满一年后可享受5天带薪年假。", "score": 0.92 } ], "model": "model_name_v2", "temperature": 0.1, "prompt_version": "v3" }这个证据链不仅能用于问题排查,还能用于后续评估集建设。当用户举报答错时,你能快速定位是检索问题、模型问题还是提示词问题。
7.2 配置与版本管理
同样一套功能,不同的模型版本、不同的知识库版本、不同的提示词版本,幻觉率可能差别很大。建议把 Prompt 视为代码,纳入 Git 管理。每次调整都要记录:
- 变更原因。
- 对哪些场景有效。
- 是否引入了新的幻觉模式。
- 在评估集上的指标变化。
7.3 评估集要持续迭代
幻觉治理的长期瓶颈,往往不是模型能力,而是评估数据不够。建议从第一天起就积累一个“陷阱问题集”,专门用来测试模型在边界情况下的表现。例如:
- 知识库中不存在的问题。
- 容易混淆的相似概念。
- 含有错误前提的提问。
- 需要多篇文档联合推理的问题。
- 时效性很强的新闻类问题。
持续用这样的评估集回归,比临时发现线上问题再补救要高效得多。
7.4 安全与合规边界
在医疗、法律、金融等领域,任何 AI 生成内容都必须经过人工复核才能进入正式流程。系统设计上建议:
- 默认不让 AI 直接执行高风险操作。
- 高风险回答必须二次确认。
- 完整保留审计日志。
- 在界面展示“AI 生成内容仅供参考”的提示。
安全底线永远是先于功能上线被确认的。涉及数据权限、隐私信息时,务必遵循最小权限原则,不要把所有上下文一股脑扔给模型,既增加幻觉概率,也带来数据泄露风险。
7.5 不要期待“幻觉为零”
最后想强调一点:在生成式模型当前的范式下,除非你完全禁止模型自由生成,否则幻觉不可能被绝对消灭。我们能做的是:
- 降低幻觉出现概率。
- 让幻觉在到达用户前被拦截。
- 让幻觉出现时,系统有可回溯的机制和兜底方案。
把目标定义为“幻觉率下降 70%”比“永远不出现幻觉”更现实,也更符合工程思维。
如果你正在为 AI 应用的幻觉问题头疼,可以按下面这份检查清单做一轮自查:
- [ ] 当前应用是否依赖模型“记忆”事实?如果是,尽快补 RAG。
- [ ] System Prompt 是否明确允许模型说“不知道”?
- [ ] 回答是否展示引用来源?
- [ ] 是否有后置校验环节?
- [ ] 温度参数是否针对场景调优?
- [ ] 是否记录了每次回答的证据链和模型版本?
- [ ] 是否有持续更新的陷阱问题测试集?
把这七项做扎实,AI 幻觉对你的业务影响就会回到可控范围。剩下的,就是在一次次线上反馈中持续迭代了。