京东面试官皱眉:"检索到了正确的文档,大模型还是在胡说八道,你怎么解决的?" 我哑口无言…
大家好,我是吴师兄。
前几篇文章里我们拆了 RAG 系统的召回优化、知识库构建和响应速度三个模块,后台收到最多的一条留言是:”师兄,我按你说的把检索做好了,召回的文档确实是对的,但大模型生成的回答还是有问题啊——它会往里面掺自己编的东西,怎么办?”
巧了,上周正好有个训练营学员面试也被问到了这个。
面试官先问的是检索方案,他把之前文章里讲的混合检索加 Rerank 那套答了一遍,面试官点头认可。然后话锋一转:”你检索到的文档是对的,但模型生成的回答里出现了文档中根本不存在的内容,你遇到过这种情况吗?怎么解决的?”
他说在 Prompt 里加了一句”请根据提供的资料回答”。
面试官没说话,继续追:”你们系统支持多轮对话吗?用户第一轮问了车险保障范围,第二轮追问’这个怎么申请’,你的系统能理解’这个’指的是什么吗?”
他摇了摇头。
面试官最后问:”你的回答有标注信息来源吗?用户怎么知道答案是从哪份文档里来的?”
三连追问,全部哑火。
这就是今天要聊的——RAG 系统的最后一公里问题。前面三篇我们解决了”能不能找到”和”找得快不快”,今天解决”找到了之后,能不能说对”。检索做得再好,生成阶段翻车了,用户看到的还是一坨垃圾。
这篇文章,我把 RAG 生成阶段的核心问题和优化方案系统拆解一遍,也算是把这个系列的最后一块拼图补上。
一、幻觉问题:检索对了,回答还是瞎编
这是 RAG 生成阶段最致命的问题。你明明检索到了正确的文档片段,喂给了大模型,但模型生成的回答里居然出现了文档中根本不存在的信息。
为什么会这样?因为大模型的本质是”续写”——它会基于自己在预训练阶段学到的知识来补全内容。当检索到的上下文不够明确、或者模型对某个话题有”先入为主”的知识时,它就可能混入自己编造的内容,而不是老老实实只用你提供的资料。
在我们训练营的实战项目中,金融保险场景下这个问题尤其严重。用户问”ABC寿险的保障范围”,检索到的文档明确写了”涵盖身故、全残保障,附加重大疾病险”,但模型有时会自行补充一句”还包含住院津贴保障”——这个内容根本不在检索到的文档里,纯粹是模型自己编的。在金融场景下,这种幻觉可能导致严重的合规风险。这也是我在训练营里反复提醒的:幻觉不是小概率事件,而是 LLM 的天性,必须主动压制。
怎么压制幻觉?
第一层:Prompt 层面的硬约束。 在 Prompt 中明确指示模型”仅依据提供的资料回答,如果资料中没有相关信息,请坦诚说明无法回答”。这句话看起来简单,但很多人要么没加,要么措辞太弱。关键词是”仅依据”和”坦诚说明”,要让模型知道”不知道”是一个被允许的回答,而不是逼着它必须给出一个答案。
一个在实战项目中效果不错的 Prompt 模板是这样的:
请根据以下提供的资料回答用户问题。如资料不足,请坦诚说明。资料:[1] {文档1标题} {文本片段...}[2] {文档2标题} {文本片段...}问题: {用户问题}回答:
注意每个片段前面加了编号标记 [1]、[2],这不仅方便后续做引用标注,也在暗示模型”你的信息来源就是这几条,别从别处找”。
第二层:Few-shot 示例引导。 在 Prompt 中加入 1-2 个示例问答对,示范”如何根据给定资料回答并引用来源”。模型看到示例中有根据资料回答且标注了 [1]、[2] 这样的引用,就更可能模仿这个模式,而不是自由发挥。
第三层:后处理过滤。 Prompt 层面的约束再强也不能 100% 杜绝幻觉。所以还需要在模型输出后做一层后处理——用规则或分类模型检测答案中是否包含检索文档中不存在的关键断言。如果检测到可疑内容,可以拦截并替换为更保守的回答,或者触发二次检索验证。在金融场景下,还需要加风控检测层,过滤掉不当内容。

二、多轮对话:一到第二轮就跑偏
单轮问答好做,多轮对话才是真正考验 RAG 系统的地方。
在实战项目中有一个典型场景:用户第一轮问”ABC 寿险的保障范围是什么?”,系统基于检索到的产品手册正确回答了”涵盖身故、全残保障,附加重大疾病险”。然后用户第二轮追问:”这个怎么申请?”
问题来了——”这个”指的是什么?对人类来说很显然是指”ABC 寿险”,但对 RAG 系统来说,如果不做任何处理,第二轮的检索 query 就是”这个怎么申请”这六个字,检索系统根本不知道”这个”是什么,大概率召回一堆不相关的内容,模型也就跟着胡说八道了。
怎么解决多轮断裂?
核心思路是对话上下文融合 + 查询重写。
第一步:检测是否为跟进问题。 当用户新一轮提问包含代词(”这个””它””那种”)或省略主语时,系统判定这是一个跟进问题,需要结合上文理解。
第二步:查询重写。 把用户的新问题和上一轮的对话主题合并,重写成一个完整的、自包含的查询。比如把”这个怎么申请”重写为”ABC 寿险怎么申请”。这个重写后的 query 再送入检索模块,就能精准召回申请流程的相关文档了。
查询重写可以用规则做(把上一轮的主题关键词直接拼接到当前 query 前面),也可以用 LLM 来做(让大模型结合对话历史把当前问题补全)。实战中我们用的是两者结合——简单的指代消解用规则快速处理,复杂的语义省略用 LLM 补全。如果你看过这个系列第一篇关于 Query 模块的内容,会发现思路是一脉相承的:规则兜底保速度,模型补位保效果。
第三步:维护对话历史状态。 系统需要维护一个对话记忆,记录每一轮的问题、回答和主题关键词。这样不仅第二轮能用,第三轮、第四轮也能持续追溯上文。
但这里有一个工程上的坑——对话历史会挤占 Prompt 长度。如果用户聊了十几轮,把所有历史都塞进 Prompt,上下文窗口就没空间放检索到的文档了。常见策略是对最近几轮对话做总结提炼,只保留关键信息,或者只取最近 3-5 轮的历史。
三、Prompt 构建:不是简单拼接就完事
很多人觉得 Prompt 构建就是把检索到的文档片段和用户问题拼在一起,格式化一下就行了。但这里面其实有不少学问。
上下文的数量和顺序很关键
给 LLM 的上下文不是越多越好。过多无关片段会占用上下文窗口,还可能触发 “Lost in the Middle” 问题——研究表明,大模型更容易关注上下文开头和结尾的内容,中间部分的信息容易被忽略。
所以实践中的做法是:通过 Rerank 精排后只取 Top 5-10 个最相关的片段,并且把相关度最高的片段放在最前面。如果片段来自不同文档,在 Prompt 中用标题或编号区分清楚,帮助模型理解”这是来自不同来源的不同信息”。
答案形式要在 Prompt 中明确约束
不同场景下,答案的形式差异很大。客服场景要求简明扼要,知识问答场景希望详尽解释并引用来源。这些要求都需要在 Prompt 中明确指定,否则模型就按自己的默认风格来,往往不是你想要的。
比如可以在 Prompt 中加上”请用三句话总结”或”请用专业术语回答,目标读者是金融从业人员”。这些约束看起来简单,但对输出质量的影响非常大。
冲突信息的处理策略
当不同检索片段给出的信息互相矛盾时(比如旧版流程和新版流程都被召回了),模型可能会把两个版本混在一起回答,让用户一头雾水。
优化方案有两个层次:一是在 Prompt 中加入指导,比如”如果资料之间存在矛盾,请优先参考编号靠前的来源”(因为 Rerank 后相关度高的排在前面);二是在检索阶段就过滤掉低可信度或过时的片段,从源头减少冲突。
四、引用标注:让回答可溯源
在金融保险这类对准确性要求极高的场景下,用户不仅需要一个回答,还需要知道这个回答从哪来的。如果系统只给了一个结论,不说出处,用户的信任度会大打折扣。
怎么实现引用标注?
在我们的实战项目中,引用标注的实现分三步:
第一步:检索片段带上元数据。 每个召回的文档片段都带有来源信息——文档名称、页码、幻灯片编号、甚至视频时间戳。这些信息在离线阶段就已经存好了。
第二步:Prompt 中给片段编号。 把检索到的片段按 [1]、[2]、[3] 编号传给 LLM,并在 Prompt 中要求模型”在引用对应信息时标注来源编号”。
第三步:后处理映射。 模型输出的 [1]、[2] 标记是编号,系统再把这些编号映射回实际的文档名称和页码,最终展示给用户的是类似”【来源: 寿险ABC产品手册 第10页】”这样的格式。用户可以点击查看原文验证。
这个机制的好处不仅是增加用户信任,还有一个隐性收益——它能反向约束幻觉。因为模型被要求必须标注出处,当它想编造一个信息时,会发现没有对应的来源编号可以标注,这会在一定程度上抑制它的”创作欲望”。我私下管这个叫”引用即约束”——看似是给用户看的功能,实际上是给模型戴的紧箍咒。

五、反馈与自检:给回答加一道安全网
除了上面的优化手段,还有一个进阶策略——让模型对自己的回答做一次自检。
具体做法是在生成完初步回答后,再追加一轮 Prompt,让模型检查自己的答案是否充分利用了提供的资料、是否有未经支持的断言。如果发现有不确定的部分,就让模型修正答案或者在末尾补充说明”此部分信息未在提供的资料中找到”。
这个机制可以在 Prompt 内通过多轮对话实现(第一轮生成答案,第二轮让模型自查),也可以用一个小型验证模型来判别回答是否与检索到的文档一致。代价是多一次 LLM 调用,延迟会增加,所以通常只在对准确性要求极高的场景下才启用(比如金融合规问答)。
更进一步,在高级应用中,如果生成模块检测到自己”答不上来”——比如模型输出了”无法从资料中找到答案”——系统可以自动触发一次新的检索(放宽条件或换一种检索策略),拿到新的结果后再让模型重新生成。这就是所谓的反馈式检索,让生成反哺检索,形成闭环。
面试怎么聊这个话题?
如果面试官问”检索到了正确的文档,模型还是在胡说八道,你怎么解决”,可以这样回答:
先定义问题。 这是 RAG 系统的幻觉问题,即使提供了正确的上下文,LLM 仍可能混入预训练知识中的错误信息。
再讲三层防线。 Prompt 层通过硬约束和 Few-shot 示例引导模型严格依据资料作答;后处理层通过幻觉检测和风控过滤拦截不可信内容;引用标注机制反向约束模型不编造没有出处的信息。
然后展开多轮对话和 Prompt 构建。 多轮场景下通过查询重写做指代消解,维护对话状态避免上下文断裂;Prompt 构建时控制上下文数量和排序,避免 Lost in the Middle 问题,同时对冲突信息给出优先级指导。
最后提自检和反馈闭环。 对高风险场景启用模型自检机制,当模型回答”不知道”时自动触发二次检索,形成生成反哺检索的闭环。
这套回答从问题定义、解决方案到工程落地层层递进,面试官一听就知道你是真干过项目的。结合前几篇文章里讲的召回优化和知识库构建,四个模块的面试回答框架就齐了。

写在最后
到这里,这个 RAG 面试系列四篇文章就全部写完了。从召回质量、知识库构建、响应速度,一路拆到今天的生成阶段,四个模块刚好对应 RAG 系统的四大核心环节。
如果你是跟着这个系列一路看过来的,现在回头看应该能感受到一件事——RAG 的优化不是某一个 trick,而是一整套系统工程。Query 理解影响检索方向,离线解析决定知识质量,在线召回决定上下文覆盖,生成阶段决定最终输出。任何一个环节掉链子,用户看到的都不会是满意的答案。
很多人觉得”检索做好了,生成自然就好了”。这句话对了一半——检索好是必要条件,但不是充分条件。幻觉、多轮断裂、缺乏引用,这些都是生成端独有的问题,需要独立的优化策略来解决。
如果这个系列对你有帮助,欢迎转发给同样在做 RAG 项目或者正在准备面试的朋友。也欢迎在评论区聊聊你在实际项目中还踩过哪些 RAG 的坑,后续我可以专门写文章来拆解。
我是吴师兄,我们下篇文章见。
本文内容基于吴师兄大模型训练营 RAG 实战系列课程整理。系列往期文章可在主页查看。
夜雨聆风
