找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

4466

积分

0

好友

584

主题
发表于 1 小时前 | 查看: 1| 回复: 0

前几天我一个同事去面三面,被问到一个看起来挺基础的问题:
面试官让他说说,RAG 在单轮问答和多轮问答里面,推理过程有什么区别?  

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

面试官追问多轮RAG面试者仅回答拼接历史

面试完他跟我吐槽,越想越不甘心,我俩干脆就把这块知识彻底啃了一遍,整理出来,也算是帮大家避个坑。如果面试官问到这个问题,千万别只回答“拼接历史对话”——这个答案说出来,基本等于交白卷。

真正的答案是什么呢?多轮 RAG 不是单轮 RAG 的简单叠加,而是从“无状态的一次性任务”变成了“有状态的序列决策问题”。 下面就把这中间的差异掰开揉碎来讲清楚。

单轮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 里最容易翻车的环节。

查询重写示例:原始query到重写后query

✦ ✦ ✦

二、检索必要性判断:不是每轮都要查

看个例子:

轮1 用户:"介绍一下Transformer的注意力机制"
轮1 助手:(检索+生成,介绍了Q/K/V计算方式)
轮2 用户:"能不能用更通俗的话再讲一遍?"

轮2 这种“换个说法重讲”的追问,其实不需要重新检索。直接基于轮1 已经检索到的文档(或轮1 的回答)做二次生成就行。重新检索反而可能召回不相关内容,还浪费开销。

但下面这种情况就必须重新检索:

轮2 用户:"那BERT和它有什么区别?"

这里引入了新实体“BERT”,原来检出的结果里大概率没有,所以必须触发新一轮检索。

因此,多轮 RAG 系统里通常会加一个路由判断步骤(可以用规则,也可以让 LLM 做个二分类):这一轮到底需要检索新的知识,还是复用已有上下文就够了?

✦ ✦ ✦

三、上下文管理:历史会“发胖”

假设对话到了第 10 轮,直接把 10 轮的原始问答加上每轮检索到的文档全部塞进 prompt,很容易超出上下文窗口;大量无关信息还会稀释模型注意力,导致生成质量下降——这就是常说的 “lost in the middle” 问题。

常见有三种处理方式:

  1. 滑动窗口 – 只保留最近 N 轮的原始对话,更早的直接丢弃。  
  2. 摘要压缩 – 用 LLM 把历史对话做成摘要,2000 字压成 100 字左右:
原始历史(10轮,2000字)
    ↓ LLM摘要
"用户先了解了RAG检索器和生成器的基本模型选型,
 目前正在讨论BGE和E5的优劣对比"(100字)
  1. 检索历史本身 – 把每一轮的问答也存进向量库,新问题来了不是无脑塞入全部历史,而是也去“检索”历史中相关的几轮。即使对话很长,也只取相关片段。这本质上是套娃——既检索文档库,也检索对话历史库。

对话历史膨胀与三种应对方式

✦ ✦ ✦

四、生成阶段:多了“一致性检查”

单轮的坑只管这一次答案是否准确、有没有幻觉;多轮额外多一个坑:前后矛盾

轮2 助手:"BGE模型是智源研究院发布的..."
...
轮7 用户:"你之前说的那个模型是谁发布的?"
轮7 助手(若重新检索命中了不同版本的文档):"E5模型是微软发布的..."

如果轮7 的检索命中了 E5 的文档而不是 BGE 的(比如查询改写出错,“那个模型”被消解错了指代对象),就会答非所问,还和自己历史发言矛盾。用户体验比单轮一次错误更糟,因为看起来像“AI 记性差、前后不一致”。

解决办法通常是在生成时将历史回答也作为约束条件放进 prompt,明确要求:如果本轮问题与历史提到的实体相关,要保持指代和结论的一致性。

✦ ✦ ✦

五、误差传播:滚雪球效应

这是多轮独有的、也是最麻烦的问题。完整看一条链路:

轮1 用户:"李四是哪年出生的?"
      → 检索准确,回答"1985年"
轮2 用户:"那他是哪里人?"
      → 查询重写错误,"他"被消解成了另一上下文中偶然出现的人名
      → 检索到错误文档
      → 回答"张三,山东人"(答非所问且实体错乱)
轮3 用户:"他现在多大了?"
      → 基于轮2已经错误的上下文继续推理
      → 错误被放大:可能给出张三的年龄,也可能把两人信息混在一起

单轮系统里一次查询错了,用户重新问一次就能纠正,互不影响。但在多轮系统里,上一轮的错误会成为下一轮改写和检索的输入。没有纠错机制的话,错误就如滚雪球般越滚越大。

多轮RAG错误滚雪球效应:从轮1小错误到轮3崩了

因此,较严谨的多轮 RAG 系统会加两道保险:  

  • 置信度检测:如果检索结果和改写后的 query 相关性得分很低,就主动澄清而不是硬答,例如反问“您是指李四还是张三?”  
  • 每轮独立验证:生成答案后,用检索到的文档反向验证答案里的实体、事实是否真的在文档中有支撑,相当于轻量级 fact-checking。

✦ ✦ ✦

写在最后

回头看那场面试,“拼接历史对话”这个回答本身没错,但它只说对了最表层的操作,完全没有触及问题核心。真正能体现你有没有多轮 RAG 工程实践经验的,是下面这几点:

  • 有没有意识到查询需要重写,而不是直接拿原始问题去检索  
  • 知不知道不是每一轮都要重新检索,判断检索必要性能省掉大量无效开销  
  • 会不会处理历史发胖的问题,是用滑动窗口、摘要压缩还是二次检索  
  • 有没有考虑过生成结果的跨轮一致性,避免前后矛盾  
  • 知不知道多轮里的错误会滚雪球式传播,要不要加纠错和置信度检测机制  

用一句话总结:单轮 RAG 做的是“静态的语义理解”,多轮 RAG 做的是“动态的、有状态的语义理解”。
查询改写、检索路由、上下文压缩、一致性约束、误差纠正——这五道工序才是真正拉开工程难度差距的地方,也是面试官真正想听到的答案。

多轮 RAG 的难点需要在工程实践中反复打磨,欢迎来 云栈社区 与更多开发者交流讨论。




上一篇:AI 写代码后的 Code Review:别再盯命名和格式,请审查这 7 个维度
下一篇:LLM Gateway 设计与演进:从自研轻量网关到生产级模型调用治理
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-8-6 07:28 , Processed in 1.029308 second(s), 41 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表