前几天我一个同事去面三面,被问到一个看起来挺基础的问题:
面试官让他说说,RAG 在单轮问答和多轮问答里面,推理过程有什么区别?
他当时脑子里的第一反应就是:多轮不就是把历史对话拼接到 prompt 里面,然后再走一遍“检索→生成”嘛。于是就这么回答了。面试官笑了笑,说“嗯,这是最表层的理解”,然后紧接着又追问了三四个问题,才让他发现自己漏掉的东西实在太多了——查询改写、检索必要性判断、历史管理、答案一致性……这些根本不是“拼历史”三个字能概括的。

面试完他跟我吐槽,越想越不甘心,我俩干脆就把这块知识彻底啃了一遍,整理出来,也算是帮大家避个坑。如果面试官问到这个问题,千万别只回答“拼接历史对话”——这个答案说出来,基本等于交白卷。
真正的答案是什么呢?多轮 RAG 不是单轮 RAG 的简单叠加,而是从“无状态的一次性任务”变成了“有状态的序列决策问题”。 下面就把这中间的差异掰开揉碎来讲清楚。

先说结论
单轮 RAG 的推理链是一条直线:
问题 → 检索 → 生成 → 回答
多轮 RAG 则要多绕好几道弯:
问题 + 历史对话
↓
是否需要重写?(指代消解/补全信息)
↓
改写后的独立查询
↓
是否需要重新检索?
↓ 是 ↓ 否
检索 复用历史检索结果/上文
↓ ↓
└──────────┬────────────────┘
↓
生成(带一致性约束)
↓
更新对话历史/记忆
↓
回答
多出来的这几道工序——查询改写、检索必要性判断、上下文管理、一致性维护——才是多轮 RAG 真正的难点。下面逐个拆解。
✦ ✦ ✦
一、查询理解:从“直接用”到“先重构”
在单轮场景下,问题本身是完整、自包含的,拿原始 query 直接检索就好。比如用户问:“RAG 中的检索器一般用什么模型?”语义完整,没有歧义。
但多轮就不一样了:
轮1 用户:"RAG中的检索器一般用什么模型?"
轮1 助手:"常用的有BGE、E5、GTE等双塔embedding模型..."
轮2 用户:"那生成端呢?"
轮3 用户:"它和第一种比有什么优势?"
轮2 的“那生成端呢”,字面搜“生成端”几乎查不到有效内容,因为它省略了主语和真实意图。必须先把它重写成 “RAG 中的生成端一般用什么模型?”
轮3 更麻烦:“它”和“第一种”都是指代,需要结合前两轮才能重写成 “BGE 模型和 E5 模型相比有什么优势?”
这一步通常叫查询重写(Query Rewriting),有两种做法:
- 用规则或小模型做指代消解,轻量但遇到复杂指代容易出错。
- 让 LLM 来做 query rewriting,把完整对话历史喂给它,输出独立完整的查询。效果更好,但多一次 LLM 调用的延迟和成本。
这一步如果做不好,后面的检索就全盘皆输——这是多轮 RAG 里最容易翻车的环节。

✦ ✦ ✦
二、检索必要性判断:不是每轮都要查
看个例子:
轮1 用户:"介绍一下Transformer的注意力机制"
轮1 助手:(检索+生成,介绍了Q/K/V计算方式)
轮2 用户:"能不能用更通俗的话再讲一遍?"
轮2 这种“换个说法重讲”的追问,其实不需要重新检索。直接基于轮1 已经检索到的文档(或轮1 的回答)做二次生成就行。重新检索反而可能召回不相关内容,还浪费开销。
但下面这种情况就必须重新检索:
轮2 用户:"那BERT和它有什么区别?"
这里引入了新实体“BERT”,原来检出的结果里大概率没有,所以必须触发新一轮检索。
因此,多轮 RAG 系统里通常会加一个路由判断步骤(可以用规则,也可以让 LLM 做个二分类):这一轮到底需要检索新的知识,还是复用已有上下文就够了?
✦ ✦ ✦
三、上下文管理:历史会“发胖”
假设对话到了第 10 轮,直接把 10 轮的原始问答加上每轮检索到的文档全部塞进 prompt,很容易超出上下文窗口;大量无关信息还会稀释模型注意力,导致生成质量下降——这就是常说的 “lost in the middle” 问题。
常见有三种处理方式:
- 滑动窗口 – 只保留最近 N 轮的原始对话,更早的直接丢弃。
- 摘要压缩 – 用 LLM 把历史对话做成摘要,2000 字压成 100 字左右:
原始历史(10轮,2000字)
↓ LLM摘要
"用户先了解了RAG检索器和生成器的基本模型选型,
目前正在讨论BGE和E5的优劣对比"(100字)
- 检索历史本身 – 把每一轮的问答也存进向量库,新问题来了不是无脑塞入全部历史,而是也去“检索”历史中相关的几轮。即使对话很长,也只取相关片段。这本质上是套娃——既检索文档库,也检索对话历史库。

✦ ✦ ✦
四、生成阶段:多了“一致性检查”
单轮的坑只管这一次答案是否准确、有没有幻觉;多轮额外多一个坑:前后矛盾。
轮2 助手:"BGE模型是智源研究院发布的..."
...
轮7 用户:"你之前说的那个模型是谁发布的?"
轮7 助手(若重新检索命中了不同版本的文档):"E5模型是微软发布的..."
如果轮7 的检索命中了 E5 的文档而不是 BGE 的(比如查询改写出错,“那个模型”被消解错了指代对象),就会答非所问,还和自己历史发言矛盾。用户体验比单轮一次错误更糟,因为看起来像“AI 记性差、前后不一致”。
解决办法通常是在生成时将历史回答也作为约束条件放进 prompt,明确要求:如果本轮问题与历史提到的实体相关,要保持指代和结论的一致性。
✦ ✦ ✦
五、误差传播:滚雪球效应
这是多轮独有的、也是最麻烦的问题。完整看一条链路:
轮1 用户:"李四是哪年出生的?"
→ 检索准确,回答"1985年"
轮2 用户:"那他是哪里人?"
→ 查询重写错误,"他"被消解成了另一上下文中偶然出现的人名
→ 检索到错误文档
→ 回答"张三,山东人"(答非所问且实体错乱)
轮3 用户:"他现在多大了?"
→ 基于轮2已经错误的上下文继续推理
→ 错误被放大:可能给出张三的年龄,也可能把两人信息混在一起
单轮系统里一次查询错了,用户重新问一次就能纠正,互不影响。但在多轮系统里,上一轮的错误会成为下一轮改写和检索的输入。没有纠错机制的话,错误就如滚雪球般越滚越大。

因此,较严谨的多轮 RAG 系统会加两道保险:
- 置信度检测:如果检索结果和改写后的 query 相关性得分很低,就主动澄清而不是硬答,例如反问“您是指李四还是张三?”
- 每轮独立验证:生成答案后,用检索到的文档反向验证答案里的实体、事实是否真的在文档中有支撑,相当于轻量级 fact-checking。
✦ ✦ ✦
写在最后
回头看那场面试,“拼接历史对话”这个回答本身没错,但它只说对了最表层的操作,完全没有触及问题核心。真正能体现你有没有多轮 RAG 工程实践经验的,是下面这几点:
- 有没有意识到查询需要重写,而不是直接拿原始问题去检索
- 知不知道不是每一轮都要重新检索,判断检索必要性能省掉大量无效开销
- 会不会处理历史发胖的问题,是用滑动窗口、摘要压缩还是二次检索
- 有没有考虑过生成结果的跨轮一致性,避免前后矛盾
- 知不知道多轮里的错误会滚雪球式传播,要不要加纠错和置信度检测机制
用一句话总结:单轮 RAG 做的是“静态的语义理解”,多轮 RAG 做的是“动态的、有状态的语义理解”。
查询改写、检索路由、上下文压缩、一致性约束、误差纠正——这五道工序才是真正拉开工程难度差距的地方,也是面试官真正想听到的答案。
多轮 RAG 的难点需要在工程实践中反复打磨,欢迎来 云栈社区 与更多开发者交流讨论。