先看一段纯教学示例对话。假设用户第一轮问“意外险一般保障什么”,第二轮接着问“那摔伤算吗”,第三轮又问“这个怎么申请”。
对人来说,这几句话很好理解。第二轮的“那”延续了意外险,第三轮的“这个”可能指前面讨论的保障或理赔申请。
但如果系统直接拿“这个怎么申请”去搜索,Query 里没有产品、事项和场景,知识库很难找到正确原文。
把整段聊天历史原样塞给检索器也不稳。历史越长,无关主题越多,当前问题反而越容易被稀释。
这就是多轮 RAG 的一道高频面试题:
当前问题依赖历史时,怎样把它恢复成可以独立检索的问题?对话历史太长以后,窗口、摘要和长期记忆又该怎样分工?
只回答“把历史放进 Prompt”远远不够。面试官还会继续问:
- 怎么判断当前是追问,还是已经换了话题?
- Query 改写错了,系统怎样回退?
- 历史窗口放多少轮,为什么不是越多越好?
- 用户记忆和知识库事实冲突时,应该听谁的?
为什么直接拼历史会出问题?
多轮问答里至少有三类信息。
第一类是当前问题。它决定用户此刻真正想问什么。
第二类是最近对话中的必要上下文,比如产品名、对象、时间或上一轮确认的条件。
第三类是与当前问题无关的旧内容,比如用户之前问过另一个产品,或者已经结束的话题。
如果把三类信息不加区分地拼在一起,检索器可能同时受到多个主题影响。
在另一个教学例子里,假设用户先问意外险,后来改问报销制度,再说“这个需要哪些材料”。系统如果只看最早出现的产品名,就可能把“这个”错误改写成意外险申请材料。
历史越多,并不代表信息越充分。旧信息可能成为新的噪声。
记忆可以看成一条与静态知识检索并行的信息来源。知识库提供制度、条款和产品资料,记忆提供会话里已经确认的对象、用户补充的条件和前文主题。两者都可以进入生成上下文,但职责不同。

知识库回答“外部资料怎么规定”,记忆回答“我们刚才在谈什么、用户已经提供了什么”。
如果把用户说过的话直接当成知识库事实,用户一句错误陈述就可能污染答案。反过来,如果完全忽略记忆,系统每一轮都像第一次见面,追问就无法成立。
一次多轮检索应该怎样走?
完整链路可以拆成六步。
第一步,加载当前会话中最近的相关历史和已有摘要。
第二步,判断当前问题是话题延续、话题切换,还是信息不足无法判断。
第三步,如果是延续,就补全代词、省略的实体、时间和约束,生成独立 Query。如果是新话题,就按单轮问题处理。
第四步,同时保留原 Query 与改写 Query,检查改写有没有改变否定关系和核心意图。
第五步,用改写后的问题检索。结果不足时,按策略回退原问题、并行两路检索,或者向用户请求澄清。
第六步,把检索证据、当前问题和必要历史交给模型,生成后更新会话历史和摘要。

改写只是检索前的一步,保留原问题、检索证据和生成后的记忆更新同样重要。
最直观的做法是:先从最近历史中选出与当前轮有关的内容,让模型把当前问题改写成一个完整、独立的问题;当改写后检索不到合格内容时,再回到原始问题重新搜索。
这个回退非常重要。
Query 改写本质上也是一次模型生成。它会补信息,也可能补错信息。没有原 Query,就没有办法判断它改了什么;没有两边候选,就无法知道正确证据是被改写救回,还是被改写丢掉。
怎么判断追问还是新话题?
代词是信号,但不是结论。
“这个”“那个”“它”“那种”通常需要历史上下文。可是用户也可能不带代词继续追问,比如上一轮问“意外险保什么”,下一轮直接说“摔伤住院能赔吗”。
反过来,两个问题包含相同词,也可能已经换了意图。上一轮问“报销制度在哪里”,下一轮问“报销系统为什么打不开”,虽然都有“报销”,后者可能是故障咨询。
所以,话题判断至少可以结合三类信号。
第一类是显式语言信号,包括代词、承接词、“换个问题”等切换表达。
第二类是实体与主题。当前问题中的产品、事项和上一轮是否一致。
第三类是语义判断。用轻量分类器或模型判断它是追问、澄清还是新主题。
实现时可以先用语义相似度快速判断话题是否延续,明显相关和明显无关的样本直接分流,落在灰色区域的再交给分类模型。这种分层决策能避免每一轮都调用更重的模型,但分界仍要用当前场景中的连续对话、换题和多实体样本校准。
低置信度时,系统不应该强行选择一个历史实体。
假设历史里同时出现 A 产品和 B 产品,用户说“这个怎么申请”。最稳妥的回答可能是:“您问的是 A 产品还是 B 产品?”
澄清会多一轮交互,却比围绕错误对象生成完整流程更可靠。
Query 改写要遵守什么边界?
好的改写不是把历史重新总结一遍,而是生成一个短而完整、可以独立检索的问题。
原问题是“那摔伤算吗”,结合前文后可以改写为:“摔伤是否属于意外险的保障范围?”
这里补了对象和关系,却没有添加资料中不存在的答案。
改写时要重点保护四类信息。
第一,核心实体。产品、人群、制度或文档对象不能选错。
第二,否定关系。“不能申请了吗”不能变成“如何申请”。
第三,时间和版本。“现在”“去年”“新版”不能被删掉。
第四,用户意图。查询流程、比较差异和确认资格,可能包含相同关键词,却需要不同证据。
原 Query 和改写 Query 最好一起写入 Trace。出现错误时,能够回看模型补了哪些词、丢了哪些条件,以及两次搜索分别找到了什么。
改写结果怎样通过验收?
改写句子读起来顺,不代表它适合检索。验收时可以先把原问题和历史拆成四类要素:正在讨论的实体、用户要完成的动作、时间与版本条件,以及否定、比较、范围等关系。改写后的独立 Query 应补齐缺失要素,同时不能凭空增加答案或替用户选择尚未确认的对象。
例如,历史里出现 A、B 两个产品,用户问“这个怎么退”。改写器如果直接输出“A 产品如何退订”,语言完整,但实体选择没有依据。正确动作不是给这条改写一个较低分后继续搜索,而是把“指代对象未确认”作为明确状态,向用户澄清。
还要检查语义方向。原问题含有“不再支持”“能否免除”“除了哪些情况”等表达时,可以把否定词、比较对象和限定条件单独抽出,与改写结果做一致性核对。只比较两句话的向量相似度,可能看不出方向已经反转,因为正反陈述往往共享大量词。
改写器也不应把旧答案写进新 Query。上一轮模型回答“A 产品通常支持线上办理”,下一轮用户问“那纸质材料呢”,可补全为“A 产品线上办理是否需要纸质材料”,不能直接改成“A 产品线上办理不需要纸质材料吗”。前一句只恢复问题,后一句已经把未经当前证据确认的结论带入检索。
因此,验收结果最好不是一个单独置信度,而是实体是否唯一、约束是否完整、语义是否保持、是否引入新事实,以及下一步动作。全部通过才使用改写 Query;对象不唯一就澄清;只缺少非关键条件时可以保留原问题并行检索;语义被改变则直接回退。这样错误不会被一个看似漂亮的句子掩盖。
改写器应该读取哪些历史?
Query 改写只消费完成当前消歧所需的历史:优先读取最近原始对话,较早内容只能使用带来源的摘要;跨会话记忆必须先通过用户、任务、时间和状态过滤。它们在这里都是改写器的输入来源,不是本文要展开的存储系统。
把历史交给改写器时还要标出来源层级。当前用户原话、系统摘要和长期候选不能混成一段无标签文本;出现冲突时优先使用当前轮次,无法确认指代对象就追问,而不是让模型从多份历史里猜一个。

越靠近当前轮次,越需要保留原文;越长期的信息,越要先验证来源、作用域和有效性。
改写器不能把用户历史当成权威资料,也不能把模型自己生成的旧回答当成事实。需要长期保存什么、怎样写入、隔离、更新和删除,继续看 对话记忆管理题。本文只检查历史是否帮助当前 Query 补对实体、条件和意图。
会话状态怎样和原始历史配合?
只依赖最近几轮原文,窗口变长以后会逐渐丢掉早期条件;只依赖自由文本摘要,又可能在压缩时漏掉否定和例外。更稳的组织方式,是让近期原文、结构化会话状态和历史摘要各自承担一种职责。
近期原文保留用户实际说法,适合处理代词、纠正和语气变化。结构化状态只保存当前任务需要的槽位,例如活动话题、候选实体、已确认条件、待澄清项,以及每个字段来自哪一轮。摘要用于帮助理解更早的对话脉络,但不能覆盖用户后来明确修改的条件。
每个状态字段都要带来源和状态。用户说“我问的是 B,不是 A”时,系统应把 B 标为当前确认对象,把 A 记录为已被否定,而不是简单把两个实体都留在摘要里。模型生成的旧回答只能标作系统曾经说过的内容,不能自动升级成用户事实或知识库证据。
状态更新也需要作用域。一个会话中确认的办理地区,不应无条件写入用户的所有后续任务;一次性比较条件也不应永久变成偏好。只有确实跨会话有价值、用户允许保存且有清晰有效期的信息,才考虑进入长期记忆。查询时还要按用户、任务和时间过滤,不能只凭语义相似就取回。
这种分层不是为了保存更多内容,而是为了让每条历史信息都能回答三个问题:谁说的、在哪个任务里有效、现在是否仍然成立。改写器只读取当前问题需要的最小集合,生成结果再附上使用了哪些状态字段。出现误绑定时,就能定位是历史选择错误、状态更新错误,还是改写模型本身改变了语义。
四个 bad case,应该怎样排查?
第一种,代词绑定了错误实体
历史里先后讨论两个产品,用户问“这个怎么退”。改写模型默认选择最近出现的实体,但用户其实指前一个。
先看历史中是否存在多个候选实体,再看系统是否有足够信号消歧。没有时应该澄清,不要把“最近实体”当成必然正确。
第二种,用户已经换题,系统仍沿用旧历史
用户前面咨询保障范围,后面突然问系统登录问题。模型因为两个话题都出现同一产品名,继续把它当成保障追问。
排查话题分类结果和切换信号,同时检查生成 Prompt 是否无条件塞入旧摘要。新话题需要清理不相关历史,而不是让摘要永久跟随。
第三种,摘要丢了例外条件
原对话说“某材料通常需要,但线上办理时不需要”。摘要只留下“需要某材料”。后续回答会把例外条件抹掉。
摘要生成后要保留来源轮次,并对数字、否定、时间和条件做抽检。重要条件最好结构化保存,不只依赖自由文本摘要。
第四种,记忆和知识库发生冲突
用户记得旧流程,最新资料已经更新。系统把用户记忆和检索证据一起交给模型,却没有标明来源类型,模型混合成一个答案。
上下文中应清楚区分“用户提供的信息”和“知识库证据”。事实回答优先使用当前有效文档,冲突无法判断时明确说明并请求确认。
这些 bad case 分别来自实体消歧、话题切换、摘要压缩和事实边界。都不能用“多放几轮历史”解决。
回退怎样避免把两路噪声一起带回来?
保留原 Query 不等于每次都把原问题和改写问题的所有候选直接合并。原问题可能缺少实体,改写问题又可能补错实体;盲目合并会同时扩大两边噪声,让后续重排在互相冲突的候选中做选择。
回退可以按失败原因触发。改写后没有找到任何合格证据时,先用原 Query 重试,用来判断改写是否删掉了关键原词。两路都找到候选时,要检查它们是否围绕同一实体与意图;若改写路线找到 A 产品、原问题路线找到 B 产品,而历史无法消歧,应该澄清,不能靠综合分替用户决定。
如果两路语义一致,可以保留候选来源标签再融合。这样能看到正确证据由原问题还是改写问题找回,也能在评测中判断改写真正贡献了什么。若原 Query 已经稳定找到证据,改写没有增加覆盖却带来更多噪声,就不应只因为流程完整而强制启用改写。
重试还要有次数和策略上限。一次回退解决不了的问题,不应继续自动扩写、分解和放宽门槛,直到出现一段勉强相关的文本。对象或条件不清时,澄清是正常结果;资料里没有答案时,则进入无答案控制,而不是把“改写失败”伪装成“检索成功”。
Trace 需要记录触发回退的原因、两条 Query、各自使用的历史、候选证据、融合决策和最终动作。验证时除了看正确证据是否进入前 K 条,还要统计哪些问题被改写救回、哪些被改写破坏、澄清是否在真正歧义处触发。这样才能决定改写器适合哪些问题,而不是默认所有多轮请求都必须经过同一条路径。
怎么验证多轮改写真的有用?
评测集至少包含四类对话。
第一类是明确代词追问。检查改写是否补对实体。
第二类是不带代词的连续追问。检查系统能否识别语义延续。
第三类是明确换题。检查旧历史是否被正确隔离。
第四类是多个候选实体。检查系统是否会澄清,而不是猜测。
每条样本要标注原始对话、理想独立 Query、正确证据和期望动作。只比较改写句子像不像不够,还要看正确证据是否进入前 K 条,错误历史是否引入噪声,以及原 Query 回退能否救回失败。
可以做三组对照:只用当前问题、当前问题加全部历史、话题判断加独立 Query。三组使用同一批对话和同一份知识库,逐题比较,而不是只看最终回答的流畅度。

改写正确只是第一步,还要检查检索证据、话题隔离和失败回退。
线上 Trace 应保留会话 ID、当前 Query、改写 Query、话题判断、使用的历史片段、检索证据和最终答案。这样出现答非所问时,才能判断问题发生在历史选择、改写、检索还是生成。
面试官继续追问,怎么接?
为什么不把全部历史直接交给大模型?
全部历史会占用上下文,也会引入旧主题和错误陈述。应只保留当前问题需要的近期原文、摘要或检索到的长期记忆,并明确它们的来源。
Query 改写错了怎么办?
保存原 Query 与改写 Query,检查核心实体、否定和时间条件。改写检索失败时回退原问题,歧义明显时向用户澄清,也可以两路并行召回后再合并。
窗口应该保留几轮?
没有通用轮数。要根据任务、上下文成本和评测结果决定。重点不是轮数本身,而是正确实体和条件是否保留,无关历史是否被隔离。
长期记忆是不是再建一个向量库就行?
不是。还要决定什么值得写入、怎样更新、何时过期、如何删除,以及怎样防止相似但无关的记忆干扰当前问题。
历史和当前文档冲突时怎么办?
先区分信息来源。用户记忆用于理解上下文,当前有效知识库用于支持外部事实。无法确认时说明冲突,而不是把两边拼成一个确定结论。
多轮检索回答到这里,要守住改写与回退边界
想把这道题说完整,不妨按“识别上下文、改写与回退、记忆分层、证据验收”的顺序来组织:
多轮 RAG 首先要判断当前问题是在追问还是已经换题。追问时,我会从最近相关历史中提取实体、时间和条件,把代词或省略问题改写成可以独立检索的 Query,同时保留原始问题,检查否定关系和意图有没有改变。改写结果不可靠时,可以回退原 Query、两路并行检索,或者让用户澄清。
历史不会全部塞进 Prompt。最近轮次保留原文,较早内容压缩成可追溯摘要,跨会话且确有价值的信息才进入长期记忆并按需检索。知识库提供当前外部事实,记忆提供会话和用户上下文,发生冲突时要区分来源,不能把用户过去说过的话当成权威证据。
验证时,我会准备连续追问、话题切换、多实体歧义、摘要丢条件和知识更新等对话,标注理想独立 Query 与正确证据。除了检查改写文本,还会比较正确证据是否进入前 K 条、旧历史是否增加噪声、回退是否有效,并保存整条 Trace 便于定位。
把 history 变量放进 Prompt 只是实现动作。判断这套方案是否可靠,要看系统能否只拿需要的历史,改写失败时有没有退路,以及记忆会不会反过来污染当前事实。