之前在做一个 AI Agent 项目时,我遇到过一个很典型的问题:模型会在回答里煞有介事地引用一篇不存在的论文,不仅作者、年份齐全,连期刊卷号都编得有模有样。后来在 Show HN 上看到一句话:Life Of AI – I hallucinate. Therefore I am。这句话把 AI 幻觉提升到了“存在方式”的高度,但落到实际工程里,我们要做的并不是欣赏这种哲学趣味,而是尽快识别幻觉、评估幻觉、缓解幻觉,避免它污染用户信任。
这篇文章会从概念讲到代码,再到工程落地建议。适合正在做 AI 应用开发、AI Agent 开发,或者准备把大模型接入业务系统的开发者。学完后你可以:
- 理解大模型为什么会产生幻觉;
- 搭建一个最小实验环境,亲手触发一次可观察的幻觉;
- 掌握几种幻觉检测与评估思路;
- 知道在提示词、RAG、模型部署侧有哪些缓解手段。
我会尽量避免只讲空洞概念,尽量把关键步骤和代码都展开,方便你照着实验和改造。
1. 从“我幻觉,故我在”说起:AI 幻觉是什么
1.1 一个容易误导人的拟人化表达
“I hallucinate. Therefore I am”这句话,表面上是模仿笛卡尔的“我思故我在”,想表达“AI 会幻觉,所以 AI 有某种存在感”。说实话,这个表达在传播上很聪明,但它也很容易误导人。
人类的幻觉是感知系统在缺少外部刺激时产生了错误体验。而大模型的“幻觉”更像是一种概率补全行为:模型根据上下文,选择一个最“像样”的后续文本,它并不真的“看见”了什么,也不会“故意”撒谎。理解这一点非常重要,因为很多人把幻觉问题简单归结为“模型不够聪明”或“模型在骗人”,实际上它只是不知道什么是对的,但必须继续写下去。
1.2 什么是大模型幻觉
在技术语境里,大模型幻觉(Hallucination)通常指:模型生成的文本看似流畅、语法正确,但与事实不符,或与用户提供的上下文不一致。
举例:
用户:请介绍一下《The Art of Hallucination》这本书。 模型:《The Art of Hallucination》是 2024 年由 MIT Press 出版的著作, 作者是 John A. Smith,主要讨论了生成式模型中的事实性问题。如果这本书根本不存在,那么这段回答就是典型幻觉。它的问题不在于语言,而在于事实层完全虚构。
1.3 幻觉的两种主要类型
业界在讨论幻觉时,通常会区分两种类型:
| 类型 | 含义 | 典型表现 |
|---|---|---|
| 事实性幻觉 | 生成内容与客观事实不符 | 编造论文、虚构人物、错误年份、错误法规条款 |
| 忠实性幻觉 | 生成内容与用户上下文/检索资料不一致 | 忽略用户给的材料,自行发挥;对工具返回结果进行“改写”,导致信息失真 |
在实际项目中,处理方式不同。事实性幻觉更多靠知识库、检索和时效性管理来解决;忠实性幻觉更多靠提示词、流程设计和输出校验来解决。
2. 从原理上看:幻觉为什么难以消除
在动手实验前,我建议先理解几条底层原因,否则后面调参时很容易“蒙着调”。
2.1 语言模型是“概率补全器”而非数据库
大模型本质上是在学习文本序列的分布规律。训练时,它看到的是“给定前文,预测下一个 token”的任务。推理时,它也是在不停预测下一个最可能出现的 token。
这意味着模型擅长的是生成连贯文本,而不是查表返回事实。事实被压缩在参数里,但没有一个可靠索引告诉你“这件事是真的存在”。
2.2 训练数据有边界
训练数据有截止日期,也有覆盖盲区。2024 年的新闻、某本小众论文、企业内部系统里的数据,模型不一定见过。当它被问到“没见过”的东西时,为了维持流畅性,它不会直接停下,而是会尝试“编”一个合理回答。
所以在实际工程中,知识截止日期处理非常关键。如果你的业务涉及时效性内容,必须把大模型当成推理器,而不是知识库。
2.3 解码策略影响确定性
模型生成时,解码策略决定它是否“大胆”。常见的参数包括:
temperature:控制概率分布的平滑程度;top_p:只在累计概率达到阈值的小集合里采样;top_k:只从前 k 个 token 中采样;seed:固定随机种子,使结果可复现。
温度越高,采样越随机,越容易出现发散性内容;温度越低,输出越保守,但也不能完全消除幻觉。
2.4 上下文冲突与指令偏差
有时候模型不是“不知道”,而是被上下文干扰了。比如你在 system prompt 里写了“你是一个严谨的助手”,但在 user prompt 里又给了大量错误示例,模型可能会跟着错误示例走。这种情况属于忠实性幻觉,常见于 Agent 把工具返回结果重新“润色”后丢失了关键数字。
理解了这些原因,后面实验里的现象就更容易解释了。
3. 环境准备:搭一个幻觉实验台
我不建议直接在生产项目里反复试,最好先搭一个最小环境。下面我以 Python 为例,使用常见的openaiSDK 来调用兼容接口。如果你使用的是其他模型服务或本地部署模型,逻辑是一样的,只要把base_url和model换成你自己的配置。
3.1 运行环境与依赖
本文示例环境建议:
- Python 3.10 及以上版本;
- 一个可用的 LLM API,或本地部署的推理服务;
- 安装依赖:
openai、python-dotenv。
pip install openai python-dotenv如果你的模型是本地部署的 OpenAI 兼容服务,也可以直接使用该 SDK,只需要把base_url指向本地端口。
3.2 项目结构与配置
建议创建一个目录:
llm-hallucination-lab/ ├── .env.example ├── config.py ├── client.py ├── query_examples.py └── eval_utils.py.env.example内容如下:
LLM_API_KEY=your-api-key LLM_BASE_URL=https://api.openai.com/v1 LLM_MODEL=gpt-4o-mini TEMPERATURE=0.7如果你不使用 OpenAI 官方接口,就把LLM_BASE_URL改成你自己的网关地址。
config.py负责读取环境变量:
import os from dotenv import load_dotenv load_dotenv() LLM_API_KEY = os.getenv("LLM_API_KEY", "") LLM_BASE_URL = os.getenv("LLM_BASE_URL", "") LLM_MODEL = os.getenv("LLM_MODEL", "") TEMPERATURE = float(os.getenv("TEMPERATURE", "0.7"))3.3 统一 API 客户端
client.py封装一个简单的聊天客户端,方便后面复用:
from openai import OpenAI class LLMClient: def __init__(self, api_key: str, base_url: str, model: str): self.model = model self.client = OpenAI( api_key=api_key, base_url=base_url or None, ) def chat( self, prompt: str, system_prompt: str = "", temperature: float = 0.7, ) -> str: messages = [] if system_prompt: messages.append({"role": "system", "content": system_prompt}) messages.append({"role": "user", "content": prompt}) response = self.client.chat.completions.create( model=self.model, messages=messages, temperature=temperature, ) return response.choices[0].message.content这里需要说明:base_url为空时,OpenAI()会使用官方默认地址。如果你用的是私有化网关,必须确保base_url指向正确的服务地址。
4. 实战:让模型产生一次可观察的幻觉
这一节选一个“模型大概率会编造”的问题来实验。注意:问题本身要设计成“模型不知道但又能编出完整答案”的形态。
4.1 触发幻觉的提问设计
一个非常稳定的触发方式是:让模型介绍一本不存在的书。因为书的名称、作者、简介都是可以自由组合的文本,模型很容易生成“看似合理”的回答。
再叠加“请介绍主要结论”这类指令,会让模型进一步生成细节。
4.2 编写问答脚本
query_examples.py内容:
from config import LLM_API_KEY, LLM_BASE_URL, LLM_MODEL from client import LLMClient def ask(question: str, temperature: float = 0.7) -> str: client = LLMClient( api_key=LLM_API_KEY, base_url=LLM_BASE_URL, model=LLM_MODEL, ) prompt = ( "请回答以下问题:\n" f"{question}\n" "请只给出答案,不要多余解释。" ) return client.chat(prompt, temperature=temperature) if __name__ == "__main__": question = ( "请介绍一本由 MIT Press 出版的书《The Art of Hallucination》," "包括作者、出版年份和主要结论。" ) for t in [0.1, 0.7, 1.2]: print(f"--- temperature={t} ---") print(ask(question, temperature=t)) print()这段代码会分别用三个温度值调用模型,观察输出差异。
4.3 控制温度对比输出
运行脚本:
python query_examples.py预期输出形态可能如下(仅用于演示,实际模型输出会不同):
--- temperature=0.1 --- 《The Art of Hallucination》是一本 2023 年出版的学术著作, 作者为 Jane Doe,主要讨论了大型语言模型中的事实性问题。 --- temperature=0.7 --- 《The Art of Hallucination》由 MIT Press 于 2024 年出版, 作者是 John A. Smith,作者提出幻觉源于训练数据的不完整性…… --- temperature=1.2 --- 这本书是一部大胆的思想实验,它探讨了 AI 如何用一种后现代的方式 “想象”世界,作者可能是……我不确定。可以看到:
- 温度低时,模型给出“更像模像样”的答案,可能仍然完全虚构;
- 温度高时,答案会变得发散,有时甚至自己也会说出“我不确定”;
- 无论哪种情况,只要这本书不存在,回答就属于幻觉。
4.4 从输出中识别幻觉特征
在工程中我们不能只靠肉眼判断,但可以用几个特征做初筛:
- 包含非常具体的实体:作者名、出版社、年份、期刊卷号;
- 这些实体无法在已验证的知识源中找到;
- 回答语气非常自信,没有任何“可能”“不确定”等限定词。
这些特征可以帮助设计规则,但真正可靠的检测还是需要结合外部知识源。
5. 幻觉的检测与评估
幻觉检测是一个独立的技术方向。生产环境里,我们通常不会只靠“读一遍”来保证质量,而是用一套评估流程。
5.1 为什么需要专门评估
原因很简单:大模型每次输出可能不同,人工抽查无法覆盖所有情况。特别是 Agent 场景下,模型会调用外部工具、阅读多份资料,幻觉出现的形态更复杂。只有把评估自动化,才能在上线前和迭代中持续发现风险。
5.2 人工评估维度
即使自动化做得再好,人工评估仍然不可少。建议至少关注四个维度:
- 事实正确性:回答是否与客观事实一致;
- 上下文忠诚度:回答是否基于给定资料,而不是自行扩展;
- 信息完整性:必要信息是否被遗漏;
- 表达自然度:是否流畅、无语法问题。
这四类可以做成打分表,让评估人员给出 1-5 分,形成基线数据。
5.3 自动化校验:引用、关键词、结构化字段
最简单的自动化校验是规则检查。比如模型被要求输出引用来源时,我们可以检查引用格式是否存在、URL 是否能访问、DOI 是否存在。
eval_utils.py可以放几个通用函数:
import re def has_required_keys(output: str, required_keys: list[str]) -> dict[str, bool]: """检查输出中是否包含某些关键信息。""" output_lower = output.lower() result = {} for key in required_keys: result[key] = key.lower() in output_lower return result def extract_urls(text: str) -> list[str]: """提取文本中的 URL。""" return re.findall(r"https?://[^\s\)\]\}]+", text) def extract_citations(text: str) -> list[str]: """提取形如 [1] [2] 的引用标记。""" return re.findall(r"\[(\d+)\]", text)这类函数不能判断内容真假,但可以快速发现“模型没有按格式输出”或“引用标记缺失”等问题。
5.4 使用 NLI 模型判断“是否忠于上下文”
对于忠实性幻觉,可以用自然语言推理(NLI)模型做一部分自动化判断。思路是:
- 把原始资料看作“前提”;
- 把模型输出看作“假设”;
- 用 NLI 模型判断假设是否能从前提中推出。
代码思路如下:
# 核心片段,需要安装 transformers from transformers import pipeline nli = pipeline( "text-classification", model="facebook/bart-large-mnli", ) premise = "根据报告,2024 年公司营收为 1000 万元。" hypothesis = "2024 年公司营收为 2000 万元。" result = nli(f"premise: {premise} hypothesis: {hypothesis}") print(result)如果 NLI 模型判定为“矛盾”,就说明模型输出与原始资料不一致,存在忠实性幻觉风险。这种方法不能完全替代人工,但可以作为质量看板的一部分。
6. 工程上如何缓解 AI 幻觉
缓解幻觉没有银弹。一个稳定的方案通常是“提示词约束 + 知识库检索 + 输出校验 + 流程设计”的组合。下面展开讲。
6.1 提示词约束
最简单的手段是显式告诉模型“不知道就直说”。
system_prompt = ( "你是一个严谨的 AI 助手。" "如果用户的问题超出你的知识范围,或你无法从给定资料中确认答案," "请直接回答“我无法确认这个信息”,不要编造任何细节。" ) question = "请介绍某本不存在的书。" client = LLMClient(...) answer = client.chat(question, system_prompt=system_prompt, temperature=0.2)注意,提示词约束不是万能的。模型仍然可能“忍不住”给出一个看似合理的答案,所以还要配合其他机制。
6.2 用外部知识库约束生成(RAG)
RAG(检索增强生成)是目前最常见的缓解方案。基本流程是:
- 用户提问;
- 从知识库检索相关文本;
- 将检索结果拼入提示词;
- 要求模型只能基于检索结果回答。
下面是一个很简化的示例。knowledge_base是文档列表,simple_retrieve用关键词重叠做粗粒度检索,真实项目建议改用向量检索或 BM25。
def simple_retrieve( question: str, knowledge_base: list[str], k: int = 3, ) -> list[str]: """简单关键词检索,仅用于演示。""" question_terms = set(question.lower().split()) scored = [] for doc in knowledge_base: doc_terms = set(doc.lower().split()) score = len(question_terms & doc_terms) scored.append((score, doc)) scored.sort(key=lambda x: x[0], reverse=True) return [doc for _, doc in scored[:k]] def build_rag_prompt(question: str, contexts: list[str]) -> str: context_text = "\n\n".join(contexts) prompt = ( "请根据以下资料回答问题。" "如果资料中没有答案,请直接回答“资料中未找到相关信息”。\n\n" f"资料:\n{context_text}\n\n" f"问题:{question}\n" ) return prompt knowledge_base = [ "The company revenue in 2024 was 10 million yuan.", "The product was launched in March 2025.", "The CEO emphasized the importance of offline channels.", ] question = "What was the company revenue in 2024?" contexts = simple_retrieve(question, knowledge_base, k=2) prompt = build_rag_prompt(question, contexts) answer = client.chat(prompt, temperature=0.1)RAG 之所以有效,是因为它把“生成”从“依赖内部参数”变成了“依赖外部证据”。但要注意,知识库本身可能有脏数据,检索也可能失败,所以还需要做检索质量和答案相关性评估。
6.3 后处理与输出校验
在拿到模型输出后,可以加一层后处理校验。比如:
- 如果要求模型输出 JSON,就用 JSON Schema 校验;
- 如果要求模型附带引用,就校验引用的 URL 或 DOI;
- 如果要求模型只能回答“是/否/不确定”,就做选项白名单校验。
示例:
ALLOWED_ANSWERS = {"是", "否", "不确定"} def validate_binary_answer(answer: str) -> bool: return answer.strip() in ALLOWED_ANSWERS如果校验失败,可以触发重试、改写请求,或者进入人工处理队列。
6.4 在 Agent 场景中防止“工具结果被改写”
Agent 开发中有一个隐蔽的幻觉源:模型调用工具后,对工具结果进行了“润色”。润色过程中,数字、日期、专有名词可能被改动。
我的建议是:
- 工具返回结果尽量结构化,比如 JSON;
- 如果最终答案需要引用工具结果,优先把工具结果原样放在输出里;
- 不要在 prompt 中让模型“总结”关键数字,除非你真的需要总结;
- 对高风险字段做一致性校验,比如金额、时间、订单号。
例如模型输出中如果有订单号,可以抽出来和工具返回结果对比:
def check_order_id_consistency(answer: str, real_order_id: str) -> bool: return real_order_id in answer这个方法很朴素,但能拦住不少问题。
7. 常见问题与排查清单
7.1 高频问题表格
| 问题现象 | 可能原因 | 解决思路 |
|---|---|---|
| 模型编造论文、法规、新闻 | 训练数据中不存在该信息 | 引入知识库,限制回答范围,要求“不知道就说不知道” |
| 引用标记存在但来源链接打不开 | 模型只学会了引用格式,没有真实来源 | 输出后进行 URL/DOI 有效性校验 |
| 相同问题不同次回答不一致 | 采样随机性过高 | 降低 temperature,固定 seed,使用确定性解码 |
| 提示“不知道”后仍然编造 | 提示词约束不够强 | 在 system prompt 中重复强调,并用后处理拦截 |
| 检索到了正确资料但回答错误 | 上下文过长或无关内容干扰 | 缩小检索片段,调整 TopK,增加重排序 |
| Agent 工具结果被“润色”后失真 | 模型对工具结果重新表述 | 要求原样输出高频字段,增加一致性校验 |
7.2 排查步骤
当出现明显幻觉时,可以按以下步骤排查:
- 记录当前输入的完整 prompt、模型版本和参数;
- 用相同 prompt 和参数多次调用,确认是偶发还是稳定;
- 检查模型是否可能“见过”答案,如果知识截止日期早于问题事件,大概率会幻觉;
- 检查是否启用了 RAG,检索到的资料是否真的能支撑回答;
- 用更低温度和确定性参数重跑,观察输出是否收敛;
- 如果仍无法解决,考虑增加后处理校验或人工复核。
8. 最佳实践与工程建议
幻觉问题不能一次性“修好”,需要体系化地管理。下面几条建议来自我自己的工程实践经验,不一定全能套用,但可以作为参考。
8.1 构建幻觉评测集
不要只在网上找一两个例子来测,建议建立一个专门的评测集。可以包含:
- 不存在实体问题:例如虚构书籍、虚构人物;
- 过时事实问题:例如往年热门事件;
- 矛盾上下文问题:在上下文中故意写错误信息,看模型是否会被带偏;
- 来源歧义问题:多份资料说法不一致,看模型是否能识别冲突。
每条样本至少包含:问题、预期行为、评估标准。这样可以快速对比模型版本、提示词或 RAG 策略的改动效果。
8.2 模型部署参数建议
在 AI 模型部署时,推荐按场景决定参数:
- 知识问答:
temperature=0.1~0.3,必要时固定seed; - 创意写作:
temperature=0.7~1.0,接受更多发散; - 工具调用/信息抽取:优先低温度,并配合结构化输出;
- 日志中记录模型版本和参数,方便复盘。
同时,如果使用开源模型做私有化部署,建议评估模型本身的事实性能力。不同规模模型的幻觉概率差异很大,不要只看跑分。
8.3 安全边界与人工复核
涉及医疗、法律、金融、政务等高风险场景,不能只靠模型输出直接面向用户。哪怕是 RAG,也无法保证 100% 正确。建议:
- 对高风险答案设置“需人工复核”标志;
- 输出中明确标注信息来源;
- 提供“不确定”选项,而不是强迫模型猜答案;
- 建立举报和纠错反馈机制。
8.4 日志可追溯
每次请求至少记录:
- 输入 prompt;
- 系统提示词;
- 模型名称与版本;
- temperature、top_p 等参数;
- 是否使用 RAG,检索到了哪些片段;
- 原始输出和后处理结果;
- 最终是否被拦截或人工复核。
这些日志不仅是排错依据,还能帮助你逐步分析幻觉出现的规律。
9. 总结与下一步学习建议
本文围绕“I hallucinate. Therefore I am”这句话,展开了一个很现实的技术问题:AI 幻觉。你从概念、原理、实验、评估、缓解到工程建议,走完了一条完整的链路。
关键收获包括:
- 大模型幻觉是概率补全带来的事实错误,不是模型“故意撒谎”;
- 温度、知识截止日期、上下文冲突都会影响幻觉概率;
- 工程上常用 RAG、后处理校验、低参数采样、人工复核组合来降低风险;
- 幻觉评测集和日志追溯是长期维护质量的基础。
下一步如果继续深入,可以关注:
- 向量检索与重排序如何提升 RAG 质量;
- 结构化输出与函数调用的幻觉边界;
- 幻觉检测模型的原理与训练方法;
- 在 AI Agent 多轮任务中,如何追踪每步信息链路。
如果你要在真实项目里落地,我建议第一步不是继续调 prompt,而是先把你最担心出错的场景整理成一小组测试样例,然后用今天搭建的实验环境跑一遍。先亲手制造一次幻觉,再一步步把它拦住,这条路比单纯看书更有效。