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+ 小伙伴加入学习,欢迎点击围观

面试考察点

  1. 理解深度:面试官首先想确认你清楚,用了 RAG 不等于不产生幻觉。幻觉只是从 "凭空编造" 换成了 "拿错资料或漏了检索继续胡说",问题本身还在。

  2. 全链路思维:能不能从数据准备、召回调优、重排过滤、Prompt 约束、生成后校验这五个环节,分别讲出对应的应对办法。能讲清楚的,是真做过项目的,不是只背了概念。

  3. 前沿跟进:有没有了解过 Self-RAGCRAGCoVe 这些 2023 到 2025 年陆续提出的方法,以及怎么用 RAGASFaithfulness 指标去量化幻觉。

核心答案

RAG 里的幻觉,归根到底就两种来源:一是检索没找对,资料本身就有问题;二是资料是对的,但模型生成时跑偏了。应对办法概括起来也不复杂:每一环都做处理,同时允许模型直接说 "我不知道"。

具体拆成五个层面:

环节 手段 作用
数据层 文档清洗、Chunk 策略、元数据标注 提升源数据质量,从源头降噪
检索层 Hybrid Search、Query 改写、Rerank 提高 "找对资料" 的概率
过滤层 相似度阈值、相关性评估器(如 CRAG) 宁可不要,也别给错
生成层 强约束 Prompt、强制引用、低温度采样 让模型忠于上下文
校验层 Self-RAG / CoVe 自验证、RAGAS Faithfulness 生成后再检查,发现幻觉就重写或拒答

下面挨个展开。

深度解析

一、先搞清楚:RAG 为什么还是会有幻觉?

这块很多人答不清楚,我自己当年也是来回看了几遍才理顺。RAG 的幻觉大致来自下面这几种情况:

  • 检索召回调错地方:用户问 "张三的离职日期",检索回来的全是 "张三的入职流程",模型基于错的资料硬编一个日期
  • 上下文冲突:知识库里同一件事新旧两份资料都在(比如旧版手册和新版手册),模型挑了过期的那份
  • 过度推理 / 知识泄漏:检索结果里没写的事,模型自己用预训练时的旧知识补了一句,这就是常说的 "知识泄漏"(knowledge leakage)
  • 指令忽略:Prompt 里已经给了资料,模型没看,直接用自己参数化记忆里的内容作答
  • 生成时不忠实:资料是对的,但模型组织语言时偷偷加戏,比如把 "可能" 说成 "一定"

所以光说一句 "我用了 RAG",没法保证答案质量。下面这套组合拳才是生产环境里实际需要做的。

二、全链路防幻觉流程

RAG 幻觉问题处理流程图
RAG 幻觉问题处理流程图

上面这张图把整个防幻觉流程串了一遍,几个关键节点的说明如下:

  • Query 改写:用户原话往往口语化、缺主语,先用 LLM 改写成更利于检索的形态,或者拆成多个子查询,避免原句召回不准
  • Hybrid Search:向量检索擅长抓语义,关键词检索(BM25)擅长匹配产品型号、人名、编号这类精确串,两路融合后漏召能少很多
  • Rerank:用 Cross-Encoder 对初筛结果重排,开销不大但对精度提升很明显,是 RAG 调优里投入产出比很高的一步
  • 相关性评估器:借鉴 CRAG 的思路,对 Top-K 文档打分,质量不行的就别硬塞给模型
  • 强约束 Prompt + 低温度temperature 调到 0~0.3,压住模型的 "自由发挥"
  • 生成后校验Self-RAG / CoVe 路子,让模型自己核对 "这句话在资料里有没有依据",不通过就重写或者拒答

三、生成层的 Prompt 约束(最便宜也最有效)

很多团队的 RAG 幻觉问题,其实靠一个写得对的 Prompt 就能解决掉一大半。三条原则:

  1. 明确边界:告诉模型只能基于提供的资料回答
  2. 允许拒答:明确告诉模型资料里没有就说不知道,这条对降低幻觉效果最直接
  3. 强制引用:要求每个论断都标注来源 [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 AIRetrievalAugmentationAdvisorLangChain4j 的 API 都在频繁调整。动手前先翻一下当前版本的官方文档,别照着半年前的博客抄。

四、检索层和过滤层的进阶手段

单靠 Prompt 不够,还得在 "找资料" 这一步把质量提上来。

Hybrid Search(混合检索):向量加关键词两路融合。像 "SKU-12345" 这种产品型号的精确串,纯向量检索很容易漏,加一路 BM25 就稳了。Milvus 2.4+ 已经原生支持混合检索,Elasticsearch 也内置了 RRF(Reciprocal Rank Fusion)融合算法,两路召回的分数融合后一起排序。

Rerank 重排序:检索阶段用 Bi-Encoder(快但粗),Rerank 阶段换 Cross-Encoder(慢但准)。模型可以选 bge-reranker-v2-m3Cohere 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 以上,幻觉直接起飞
  • 只看召回率不看忠实度:评测时只看检索能不能找到资料,不看答案有没有忠于资料,等于白评估

面试高频追问

  1. "RAG 为什么还会有幻觉?" 答:检索没找对、生成不忠实、模型偷用预训练知识(知识泄漏)。RAG 只是把凭空编造的概率压低了,没有完全消除。

  2. "Self-RAG、CRAG、CoVe 有什么区别?"

    • Self-RAG:训练时植入反思 token,管的是生成端,需要重新训练
    • CRAG:检索后做相关性评估,管的是检索端,即插即用
    • CoVe:生成后跑一遍验证链,两端一起校验,纯 Prompt 就能用,落地最简单
  3. "怎么评估 RAG 系统的幻觉率?"RAGASFaithfulness 指标做自动评测,再配上人工抽检;要求高的场景可以叠加 TLM 这类可信度评分模型。

  4. "如果知识库里压根没有相关内容,怎么处理?" 必须设计拒答机制:Prompt 里允许回答 "不知道",检索相关性低于阈值就直接拒答,不要硬塞无关资料给模型。

常见面试变体

  • "你在项目里遇到过 RAG 幻觉的 case 吗?怎么解决的?"
  • "怎么让 RAG 系统支持引用溯源(citation grounding)?"
  • "RAG 和 Fine-tuning,哪个对降低幻觉更有效?"
  • "怎么设计一个高可用的企业知识库问答系统,让答案可信?"

记忆口诀

"五层防幻觉":数据要干净、检索要混合、结果要重排、生成要约束、出结果要校验。 再加一句:让模型敢说 "不知道",比逼它硬编答案效果好得多。

总结

RAG 幻觉问题没有银弹,思路就是每一环都做处理,再配上拒答兜底。面试时把检索前、检索中、生成中、生成后的应对手段讲清楚,再提一下 Self-RAG / CRAG / CoVeRAGAS 评估,基本就能拿到不错的分数。如果能结合项目讲讲自己是怎么调 Prompt、加 Rerank、做 Faithfulness 评估的,那就更有说服力了。