RAG 中的幻觉问题怎么处理?
一则或许对你有用的小广告
欢迎加入小哈的星球,你将获得:专属的实战项目(4个项目都能学) / 1v1 提问 / 简历修改 / Java 学习路线 / 社群讨论 / 学习打卡 / 每月赠书
《Spring AI 项目实战(问答机器人、RAG 智能客服、联网搜索)》已完结,基于
Spring AI + Spring Boot 3.x + JDK 21...,查看介绍《从零手撸:仿小红书(微服务架构)》 已完结,基于
Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...,查看介绍;演示链接:http://116.62.199.48:7070/《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接:http://116.62.199.48/
新开坑项目:《从零手撸:秒杀系统高并发优化实战》 正在更新中...,查看介绍
截止目前,星球内专栏累计输出 150w+ 字,讲解图 5110+ 张,还在持续爆肝中.. 后续还会上新更多项目,已有 4700+ 小伙伴加入学习,欢迎点击围观
面试考察点
-
理解深度:面试官首先想确认你清楚,用了 RAG 不等于不产生幻觉。幻觉只是从 "凭空编造" 换成了 "拿错资料或漏了检索继续胡说",问题本身还在。
-
全链路思维:能不能从数据准备、召回调优、重排过滤、Prompt 约束、生成后校验这五个环节,分别讲出对应的应对办法。能讲清楚的,是真做过项目的,不是只背了概念。
-
前沿跟进:有没有了解过
Self-RAG、CRAG、CoVe这些 2023 到 2025 年陆续提出的方法,以及怎么用RAGAS的Faithfulness指标去量化幻觉。
核心答案
RAG 里的幻觉,归根到底就两种来源:一是检索没找对,资料本身就有问题;二是资料是对的,但模型生成时跑偏了。应对办法概括起来也不复杂:每一环都做处理,同时允许模型直接说 "我不知道"。
具体拆成五个层面:
| 环节 | 手段 | 作用 |
|---|---|---|
| 数据层 | 文档清洗、Chunk 策略、元数据标注 | 提升源数据质量,从源头降噪 |
| 检索层 | Hybrid Search、Query 改写、Rerank | 提高 "找对资料" 的概率 |
| 过滤层 | 相似度阈值、相关性评估器(如 CRAG) | 宁可不要,也别给错 |
| 生成层 | 强约束 Prompt、强制引用、低温度采样 | 让模型忠于上下文 |
| 校验层 | Self-RAG / CoVe 自验证、RAGAS Faithfulness | 生成后再检查,发现幻觉就重写或拒答 |
下面挨个展开。
深度解析
一、先搞清楚:RAG 为什么还是会有幻觉?
这块很多人答不清楚,我自己当年也是来回看了几遍才理顺。RAG 的幻觉大致来自下面这几种情况:
- 检索召回调错地方:用户问 "张三的离职日期",检索回来的全是 "张三的入职流程",模型基于错的资料硬编一个日期
- 上下文冲突:知识库里同一件事新旧两份资料都在(比如旧版手册和新版手册),模型挑了过期的那份
- 过度推理 / 知识泄漏:检索结果里没写的事,模型自己用预训练时的旧知识补了一句,这就是常说的 "知识泄漏"(knowledge leakage)
- 指令忽略:Prompt 里已经给了资料,模型没看,直接用自己参数化记忆里的内容作答
- 生成时不忠实:资料是对的,但模型组织语言时偷偷加戏,比如把 "可能" 说成 "一定"
所以光说一句 "我用了 RAG",没法保证答案质量。下面这套组合拳才是生产环境里实际需要做的。
二、全链路防幻觉流程
上面这张图把整个防幻觉流程串了一遍,几个关键节点的说明如下:
- Query 改写:用户原话往往口语化、缺主语,先用 LLM 改写成更利于检索的形态,或者拆成多个子查询,避免原句召回不准
- Hybrid Search:向量检索擅长抓语义,关键词检索(
BM25)擅长匹配产品型号、人名、编号这类精确串,两路融合后漏召能少很多 - Rerank:用 Cross-Encoder 对初筛结果重排,开销不大但对精度提升很明显,是 RAG 调优里投入产出比很高的一步
- 相关性评估器:借鉴
CRAG的思路,对 Top-K 文档打分,质量不行的就别硬塞给模型 - 强约束 Prompt + 低温度:
temperature调到 0~0.3,压住模型的 "自由发挥" - 生成后校验:
Self-RAG/CoVe路子,让模型自己核对 "这句话在资料里有没有依据",不通过就重写或者拒答
三、生成层的 Prompt 约束(最便宜也最有效)
很多团队的 RAG 幻觉问题,其实靠一个写得对的 Prompt 就能解决掉一大半。三条原则:
- 明确边界:告诉模型只能基于提供的资料回答
- 允许拒答:明确告诉模型资料里没有就说不知道,这条对降低幻觉效果最直接
- 强制引用:要求每个论断都标注来源
[doc_id],逼着模型对齐证据
下面是用 Spring AI 实现的一个强约束 RAG Advisor 示例:
// application.yml 核心配置
/*
spring:
ai:
openai:
api-key: ${OPENAI_API_KEY}
chat:
options:
model: gpt-4o-mini
temperature: 0.2 // 低温度,减少发散
top-p: 0.8
embedding:
options:
model: text-embedding-3-small
*/
@Configuration
public class RagConfig {
@Bean
public ChatClient chatClient(ChatClient.Builder builder) {
// 把强约束 Prompt 写进系统提示,模型每次生成都带着这把 "紧箍咒"
String systemPrompt = """
你是企业知识库问答助手,必须严格遵守:
1. 只能基于 <context> 标签中提供的资料回答问题;
2. 如果资料里没有相关信息,必须直接回答 "知识库中暂无相关信息",
绝不允许根据自身知识猜测或编造;
3. 每个事实性论断后必须用 [doc_id] 标注来源,
例如:张三的入职时间是 2021-03-01 [doc_3];
4. 如果多份资料冲突,请明确指出冲突,不要擅自决断。
""";
return builder
.defaultSystem(systemPrompt)
.build();
}
@Bean
public RetrievalAugmentationAdvisor ragAdvisor(VectorStore vectorStore,
ChatClient chatClient) {
// 1. Query 改写:把口语化问题改写成更利于检索的形态
QueryTransformer queryTransformer = RewriteQueryTransformer.builder()
.chatClientBuilder(ChatClient.builder(chatClient))
.build();
// 2. 检索:Top-K 取 5,宁多勿少,后面靠 Rerank 筛
// 3. similarityThreshold 相关性阈值,低于直接丢弃
return RetrievalAugmentationAdvisor.builder()
.queryTransformers(queryTransformer)
.documentRetriever(VectorStoreDocumentRetriever.builder()
.vectorStore(vectorStore)
.topK(5)
.similarityThreshold(0.65)
.build())
// ContextualQueryAugmenter:把检索结果以 <context> 形式拼到用户问题里
.queryAugmenters(ContextualQueryAugmenter.builder().build())
.build();
}
}
调用侧就比较干净了:
@Service
public class KnowledgeQaService {
private final ChatClient chatClient;
private final RetrievalAugmentationAdvisor ragAdvisor;
public KnowledgeQaService(ChatClient chatClient,
RetrievalAugmentationAdvisor ragAdvisor) {
this.chatClient = chatClient;
this.ragAdvisor = ragAdvisor;
}
public String ask(String userQuestion) {
return chatClient.prompt()
.user(userQuestion)
.advisors(ragAdvisor) // 挂上 RAG + 改写 + 重排
.call()
.content();
}
}
如果用的是 LangChain4j,思路一样,只是 API 形态不同:
// 强约束系统提示:直接写在 @SystemMessage 注解里
@dev.langchain4j.service.SystemMessage("""
你只能基于提供的资料片段回答用户问题。
若资料中未提及,请直接回答 "我不清楚",禁止编造。
每个事实论断需标注来源片段编号 [片段N]。
""")
interface KnowledgeAssistant {
@dev.langchain4j.service.UserMessage("{{question}}")
String ask(@dev.langchain4j.service.MemoryId String sessionId,
@dev.langchain4j.service.V("question") String question);
}
// 装配:带 RAG + 最低相关度过滤
KnowledgeAssistant assistant = AiServices.builder(KnowledgeAssistant.class)
.chatLanguageModel(model)
.contentRetriever(EmbeddingStoreContentRetriever.builder()
.embeddingStore(embeddingStore)
.maxResults(5)
.minScore(0.7) // 最低相关度,低于直接丢
.build())
// Rerank:LangChain4j 支持自定义 RetrievalAugmentor 扩展点做 Cross-Encoder 重排
.build();
提醒一下,Java AI 框架迭代很快,
Spring AI的RetrievalAugmentationAdvisor和LangChain4j的 API 都在频繁调整。动手前先翻一下当前版本的官方文档,别照着半年前的博客抄。
四、检索层和过滤层的进阶手段
单靠 Prompt 不够,还得在 "找资料" 这一步把质量提上来。
Hybrid Search(混合检索):向量加关键词两路融合。像 "SKU-12345" 这种产品型号的精确串,纯向量检索很容易漏,加一路 BM25 就稳了。Milvus 2.4+ 已经原生支持混合检索,Elasticsearch 也内置了 RRF(Reciprocal Rank Fusion)融合算法,两路召回的分数融合后一起排序。
Rerank 重排序:检索阶段用 Bi-Encoder(快但粗),Rerank 阶段换 Cross-Encoder(慢但准)。模型可以选 bge-reranker-v2-m3、Cohere Rerank,或者直接用 LLM 来做 Reranker。生产经验上,Rerank 这一步对 RAG 整体效果的提升非常明显。
相关性评估器(CRAG 思路):对 Top-K 的文档先用一个轻量模型打分,分三种情况处理:
| 评估结果 | 处理方式 |
|---|---|
| Correct(高分) | 做知识提炼(去噪)后用于生成 |
| Incorrect(低分) | 弃用检索结果,转 Web 搜索兜底,或直接拒答 |
| Ambiguous(中间) | 检索结果 + Web 搜索都给 |
CRAG 的好处是不用重新训练模型,拿来就能挂上用,落地成本低,工程上可以做成一个 Advisor 或 Interceptor,放在检索之后。
五、生成后校验:Self-RAG 与 CoVe
再激进一点,可以在生成完之后再查一遍。
- Self-RAG:让模型在生成过程中输出 "反思 token"(reflection tokens),自己评估每句话有没有资料支撑。需要做端到端微调,工程成本高,但效果上限也高
- Chain-of-Verification(CoVe):先生成草稿答案,模型自己列出验证问题,自己回答验证,再基于验证结果修正答案。零样本就能用,纯 Prompt 工程,生产环境里最容易落地
CoVe 的 Prompt 大概长这样:
String covePrompt = """
请按以下步骤回答用户问题:{{question}}
第一步:基于检索到的资料生成初版答案。
第二步:把初版答案中的每个关键论断拆成独立的待验证陈述。
第三步:逐条对照资料,标注 [支持] / [不支持] / [资料未提及]。
第四步:只保留 [支持] 的陈述,重新组织成最终答案。
如果没有任何陈述被 [支持],请回答 "知识库中暂无相关信息"。
""";
工程上可以把这个流程做成一个 Advisor 或 Interceptor,挂在生成之后做二次校验,发现幻觉就重写或者拒答。三种进阶方案的区别可以先记一句话:
| 方案 | 纠正对象 | 是否需训练 | 落地难度 |
|---|---|---|---|
Self-RAG |
生成端 | 需端到端微调 | 高 |
CRAG |
检索端 | 即插即用 | 低 |
CoVe |
检索 + 生成都校验 | 纯 Prompt | 中 |
六、评估:怎么知道幻觉有没有变少?
不评估就是玄学。推荐用 RAGAS 框架做自动化评测,重点看一个指标:Faithfulness(忠实度)。
- Faithfulness:答案里的每条陈述能不能在检索到的资料里找到依据,分数 0~1,越接近 1 说明越不容易出现幻觉
- Answer Relevancy:答案有没有真正回应用户的问题
- Context Precision / Recall:反映检索质量
另外提一句,Cleanlab 的基准测试里指出,RAGAS Faithfulness 在简单问答场景下效果不错,但碰到复杂推理或多跳问答就会打折扣。生产环境最好再配一个专门的可信度评分模型(比如 TLM),或者加一层人工抽检,别只盯一个指标。
七、几个常见的坑
- 盲目调大 Top-K:把 K 拉到 20 以为召回更全,结果上下文太长,中间的内容容易被模型忽略(Lost in the Middle),反而幻觉变多。一般 K=3~5 加 Rerank 是比较合适的区间
- Chunk 切太大:一个 Chunk 几千字,啥都塞进去,模型挑花眼。建议 200~500 tokens,再加一点重叠
- 温度调太高:为了让回答 "更自然" 把
temperature拉到 0.7 以上,幻觉直接起飞 - 只看召回率不看忠实度:评测时只看检索能不能找到资料,不看答案有没有忠于资料,等于白评估
面试高频追问
-
"RAG 为什么还会有幻觉?" 答:检索没找对、生成不忠实、模型偷用预训练知识(知识泄漏)。RAG 只是把凭空编造的概率压低了,没有完全消除。
-
"Self-RAG、CRAG、CoVe 有什么区别?"
Self-RAG:训练时植入反思 token,管的是生成端,需要重新训练CRAG:检索后做相关性评估,管的是检索端,即插即用CoVe:生成后跑一遍验证链,两端一起校验,纯 Prompt 就能用,落地最简单
-
"怎么评估 RAG 系统的幻觉率?" 用
RAGAS的Faithfulness指标做自动评测,再配上人工抽检;要求高的场景可以叠加TLM这类可信度评分模型。 -
"如果知识库里压根没有相关内容,怎么处理?" 必须设计拒答机制:Prompt 里允许回答 "不知道",检索相关性低于阈值就直接拒答,不要硬塞无关资料给模型。
常见面试变体
- "你在项目里遇到过 RAG 幻觉的 case 吗?怎么解决的?"
- "怎么让 RAG 系统支持引用溯源(citation grounding)?"
- "RAG 和 Fine-tuning,哪个对降低幻觉更有效?"
- "怎么设计一个高可用的企业知识库问答系统,让答案可信?"
记忆口诀
"五层防幻觉":数据要干净、检索要混合、结果要重排、生成要约束、出结果要校验。 再加一句:让模型敢说 "不知道",比逼它硬编答案效果好得多。
总结
RAG 幻觉问题没有银弹,思路就是每一环都做处理,再配上拒答兜底。面试时把检索前、检索中、生成中、生成后的应对手段讲清楚,再提一下 Self-RAG / CRAG / CoVe 和 RAGAS 评估,基本就能拿到不错的分数。如果能结合项目讲讲自己是怎么调 Prompt、加 Rerank、做 Faithfulness 评估的,那就更有说服力了。
