首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >多轮 RAG 为什么越聊越蠢?

多轮 RAG 为什么越聊越蠢?

作者头像
java金融
发布2026-05-22 18:57:28
发布2026-05-22 18:57:28
620
举报
文章被收录于专栏:java金融java金融

多轮 RAG 为什么越聊越蠢

事情是这样的。

有个团队做了一个企业知识库问答机器人,接了向量库,也接了公司内部文档。

第一轮用户问:

代码语言:javascript
复制
我们公司的报销流程是什么?

机器人答得还不错,列了发票、审批、付款几个步骤。

第二轮用户又问:

代码语言:javascript
复制
那差旅报销呢?

机器人也能接住,补充了出差申请、酒店标准、交通票据。

到了第三轮,用户说:

代码语言:javascript
复制
我刚才说的那种情况,如果发票丢了怎么办?

机器人开始一本正经地胡说八道。

它先说可以走遗失发票证明,然后又说要找行政补开,最后甚至引用了一段根本不属于财务制度的办公用品采购文档。

你看,问题就来了。

明明接了知识库,为什么越聊越不准?

明明上下文越来越多,为什么系统反而越来越糊涂?

很多多轮 RAG 项目,demo 第一轮很亮眼,真实用户多问几句就开始变形。

不是模型突然变笨了。

更常见的情况是:系统从第二轮开始,就已经不知道自己到底在检索什么了。

真正的问题,不是多轮,而是信息越来越脏

单轮 RAG 的路径很简单。

用户问一个问题,系统拿这个问题去检索,再把检索结果塞给模型回答。

大概是这样:

代码语言:javascript
复制
def answer(question):
    docs = vector_db.search(question)
    prompt = build_prompt(question, docs)
    return llm.generate(prompt)

这段代码在单轮场景里看起来没什么问题。

但一旦进入多轮对话,它就开始危险了。

因为用户不会每一轮都把完整问题重新说一遍。

用户会说:

  • 这个怎么处理
  • 刚才那种情况呢
  • 如果是外地员工呢
  • 那审批人不在怎么办
  • 这种是不是也要走同一个流程

这些问题单独拿出来,几乎没有任何检索价值。

向量库看到「这个」「刚才」「那种情况」,并不知道用户指的是差旅报销、发票丢失,还是审批流程。

所以多轮 RAG 越聊越蠢,第一层原因通常不是生成问题。

而是检索问题已经坏了。

第一层,检索根本不知道「这个」是谁

很多系统的第一版实现都很朴素。

代码语言:javascript
复制
def chat(question, history):
    docs = vector_db.search(question)
    prompt = build_prompt(history, docs, question)
    return llm.generate(prompt)

注意这个细节。

history 只进了 prompt,没有参与检索。

也就是说,模型回答时能看到历史,但向量库检索时看不到历史。

这就会导致一个很典型的问题:

用户轮次

用户问题

检索系统实际看到的问题

第一轮

差旅报销流程是什么

差旅报销流程是什么

第二轮

如果发票丢了怎么办

如果发票丢了怎么办

第三轮

那审批人不在呢

那审批人不在呢

第三轮的问题,对人来说很清楚。

因为人知道「审批人不在」说的是差旅报销里的审批环节。

但检索系统不知道。

它可能检索到:

  • 请假审批人不在怎么办
  • 采购审批人不在怎么办
  • 合同审批人不在怎么办
  • OA 系统代理审批设置

这些文档看起来都和「审批人不在」相似。

但它们不一定和当前对话相关。

向量相似,不等于对话相关。

这句话是多轮 RAG 里很容易被忽略的坑。

第二层,history 越长,prompt 越乱

有人会说,那我把历史对话全部塞进 prompt 不就行了?

听起来合理。

但线上系统最怕的就是这种「看起来合理」。

假设 prompt 是这么拼的:

代码语言:javascript
复制
def build_prompt(history, docs, question):
    return f"""
你是企业知识库助手,请根据资料回答用户问题。

历史对话:
{history}

参考资料:
{docs}

用户问题:
{question}
"""

第一轮没问题。

第二轮还行。

第六轮、第八轮、第十轮以后,问题开始变复杂。

因为 history 里面会混进很多低密度信息:

  • 用户试探性问法
  • 模型之前的错误回答
  • 已经被否定的方案
  • 不相关的上下文
  • 重复解释
  • 过期结论

这些东西如果不清理,会一起进入 prompt。

模型看到的不是一个干净的问题,而是一堆杂乱的对话残片。

更麻烦的是,很多系统没有标记信息来源。

代码语言:javascript
复制
历史对话:
用户:差旅报销怎么走?
助手:需要先提交申请,再上传发票。
用户:发票丢了呢?
助手:可以提交遗失说明。

参考资料:
1. 办公用品采购管理办法
2. 差旅费用报销制度
3. 固定资产申请流程

如果没有来源、时间、轮次、可信度标记,模型很难判断:

  • 哪些是用户真实问题
  • 哪些是模型上一轮生成的内容
  • 哪些资料是高可信制度
  • 哪些资料只是相似但不相关
  • 哪个信息应该优先

所以多轮 RAG 不是简单的「上下文越多越好」。

很多时候,上下文越长,噪声越多,答案越飘。

第三层,Rerank 不做,多轮候选文档会越来越偏

向量检索的目标不是理解业务。

它只是找语义上相近的文本。

在单轮问题里,这个误差还可以接受。

但多轮对话里,用户的问题越来越短,指代越来越多,向量检索的误差会被放大。

比如第三轮用户问:

代码语言:javascript
复制
那主管不批怎么办?

向量库可能召回这些文档:

候选文档

相似原因

是否适合回答

差旅报销审批规则

包含主管、审批、报销

适合

请假审批异常处理

包含主管、不批、流程

不适合

采购付款审批制度

包含审批、主管、付款

不适合

合同用印审批说明

包含审批、驳回、主管

不适合

如果系统直接把这些候选文档塞给模型,模型就会被污染。

它不是不知道怎么回答。

它是被迫在一堆半相关资料里猜。

比较稳的做法,是在向量召回后增加 rerank。

代码语言:javascript
复制
def retrieve(question, history_summary):
    rewritten_query = rewrite_query(question, history_summary)
    candidates = vector_db.search(rewritten_query, top_k=20)
    docs = reranker.rank(
        query=rewritten_query,
        candidates=candidates,
        top_k=5
    )
    return docs

这里有两个关键点。

第一,先把当前问题改写成独立问题。

代码语言:javascript
复制
原问题:
那主管不批怎么办?

改写后:
差旅报销流程中,如果直属主管不审批或审批被拒绝,员工应该如何处理?

第二,rerank 不能只看当前问题,最好还要看历史摘要和候选文档。

否则系统依然可能把「请假审批」这种半相关文档排到前面。

第四层,Memory 只 append,不治理

很多多轮系统里的 memory,本质上就是一个不断追加的数组。

代码语言:javascript
复制
messages.append({"role": "user", "content": question})
messages.append({"role": "assistant", "content": answer})

这很容易写。

也很容易坏。

因为真实对话不是所有内容都同样重要。

多轮 RAG 至少要把 memory 分成几类:

记忆类型

保存内容

用途

最近对话

最近 3 到 5 轮原文

保留语气、指代、连续追问

任务状态

当前用户正在问什么业务问题

帮助 query rewrite

关键事实

已确认条件,比如员工类型、报销类型、地区

防止重复追问

长期摘要

更早对话的压缩总结

控制 prompt 长度

无效信息

被否定、过期、低价值内容

不进入主 prompt

如果所有历史都平等进入 prompt,模型就会把垃圾信息也当成上下文。

这就是为什么有些系统聊得越久越离谱。

不是它没有记忆。

而是它什么都记。

第五层,没有评估,多轮质量衰减只能靠感觉

多轮 RAG 最危险的一点是,它不是一开始就坏。

它通常是慢慢坏。

第一轮准确率 90%。

第三轮掉到 75%。

第五轮只剩 55%。

第八轮开始出现自信胡说。

如果没有评估,你只能靠用户反馈和肉眼感觉。

多轮 RAG 至少要记录这些指标:

指标

观察什么

价值

query rewrite 准确率

改写后的问题是否保留原意

判断检索入口是否正确

检索命中率

top_k 里是否有正确文档

判断知识库召回质量

rerank 命中率

正确文档是否排在前面

判断排序是否有效

引用覆盖率

回答是否基于命中文档

判断模型有没有脱离资料

拒答率

资料不足时是否拒答

判断系统是否过度自信

轮次质量曲线

第几轮开始明显变差

定位多轮退化点

这里最关键的是轮次质量曲线。

因为单轮测试好,不代表多轮可用。

你要专门构造这种测试集:

代码语言:javascript
复制
第 1 轮:差旅报销流程是什么?
第 2 轮:如果发票丢了怎么办?
第 3 轮:那主管不批呢?
第 4 轮:如果我是外地员工呢?
第 5 轮:这种情况财务会退回吗?

然后观察每一轮:

  • 改写后的 query 是否正确
  • 召回文档是否还在同一业务域
  • 回答有没有引用制度依据
  • 模型是否混入其他流程
  • 是否在资料不足时强行回答

没有这套评估,多轮 RAG 的优化很容易变成玄学。

工程上怎么落地

多轮 RAG 不要指望一个技巧解决。

更稳的做法是组合设计。

1. 先做 Query Rewrite

把用户当前问题改写成一个独立可检索问题。

代码语言:javascript
复制
历史摘要:
用户正在咨询差旅报销流程,已确认问题涉及发票丢失和主管审批。

当前问题:
那主管不批怎么办?

改写后问题:
差旅报销中,如果主管不审批或审批被拒绝,员工应该如何处理?

但它有副作用。

Query Rewrite 可能改错用户意图。

所以改写结果最好可观测、可回放,并且在高风险场景里保留原问题一起检索。

2. 用 Hybrid Search 托底

只靠向量检索容易召回语义相似但业务不相关的内容。

更稳的是混合检索:

  • 向量检索负责语义相近
  • 关键词检索负责业务词命中
  • 规则过滤负责权限、部门、时间和文档类型

比如「差旅报销」这种业务词,不应该在多轮里丢掉。

3. Rerank 不能省

召回 top 20,不代表都能塞进 prompt。

多轮场景里,rerank 至少要看三样东西:

  • 改写后的当前问题
  • 对话历史摘要
  • 候选文档内容

这样才能把「差旅报销审批规则」排在「请假审批规则」前面。

4. Memory 要分层

不要把 history 当垃圾桶。

可以按这个结构做:

代码语言:javascript
复制
{
  "recent_turns": "最近 3 轮完整对话",
  "task_state": "用户正在咨询差旅报销异常处理",
  "confirmed_facts": ["发票丢失", "主管未审批"],
  "long_summary": "用户之前询问了差旅报销基础流程和票据要求",
  "discarded": ["已经被否定的采购流程资料"]
}

这不是为了炫技。

而是为了让 prompt 里的每一段信息都有身份。

5. Prompt 里要标元数据

不要把所有资料混成一坨。

参考资料最好带上来源、时间、可信度和业务域。

代码语言:javascript
复制
[资料 1]
来源:财务制度中心
业务域:差旅报销
更新时间:2025-12-10
可信度:高
内容:……

[资料 2]
来源:OA 帮助文档
业务域:审批代理
更新时间:2024-09-18
可信度:中
内容:……

模型不一定真的理解你的公司制度。

但你至少要把信息优先级摆清楚。

6. Eval 要按轮次做

多轮 RAG 的评估不能只看最终回答。

要看链路上的每一步:

  • query 是否改写正确
  • 召回是否命中正确文档
  • rerank 是否把正确文档排前面
  • prompt 是否污染
  • 回答是否引用资料
  • 资料不足时是否拒答

只看答案对不对,定位不了问题。

链路拆开,才知道是检索坏了、排序坏了、记忆坏了,还是模型生成坏了。

面试怎么答

如果面试官问你:

代码语言:javascript
复制
多轮 RAG 为什么会越聊越差?你会怎么优化?

不要只回答「加长上下文」「优化 prompt」「换更好的模型」。

可以这么说。

代码语言:javascript
复制
我会先把问题拆成四层。

第一层看 query。
多轮里用户经常使用「这个」「刚才那个」这种指代,如果不做 query rewrite,检索系统根本不知道当前问题的完整语义。

第二层看 retrieval 和 rerank。
向量相似不等于对话相关,所以召回后要结合历史摘要和当前问题做二次排序,避免半相关文档污染 prompt。

第三层看 memory。
history 不能一直 append,要分成最近对话、任务状态、关键事实和长期摘要。否则上下文越长,噪声越多。

第四层看 eval。
多轮质量不是一下子崩的,要按轮次观察 query rewrite、检索命中率、引用覆盖率和拒答率,找到第几轮开始衰减。

如果想再补一句工程判断,可以这么说:

代码语言:javascript
复制
我不会直接认为是模型能力不够。

我会先看系统有没有把当前问题变成一个可检索、可排序、可约束的问题。

很多多轮 RAG 的失败,本质上不是回答失败,而是上下文治理失败。

这个回答比背概念更像真实做过项目。

因为它不是把 RAG 当成一个框架名,而是把它拆成了一条工程链路。

最后回到开头那个客服机器人

用户问「我刚才说的那种情况,如果发票丢了怎么办」。

人能听懂。

因为人会自动补全上下文。

但系统不会自动懂。

如果你没有 query rewrite,它不知道「那种情况」是什么。

如果你没有 rerank,它会把半相关文档塞进 prompt。

如果你没有 memory 分层,它会把历史里的噪声一起喂给模型。

如果你没有多轮 eval,你甚至不知道系统是从第几轮开始变差的。

所以多轮 RAG 越聊越蠢,通常不是因为模型突然不聪明。

而是你的系统没有认真管理三件事:

  • 当前问题到底是什么
  • 哪些资料真的相关
  • 哪些历史应该留下

把这三件事做好,多轮 RAG 才有机会从 demo 走向生产。

否则它第一轮像专家,第三轮像猜谜,第五轮就开始一本正经地乱答。

这不是智能不够。

这是工程没收口。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-05-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 java金融 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 多轮 RAG 为什么越聊越蠢
    • 真正的问题,不是多轮,而是信息越来越脏
    • 第一层,检索根本不知道「这个」是谁
    • 第二层,history 越长,prompt 越乱
    • 第三层,Rerank 不做,多轮候选文档会越来越偏
    • 第四层,Memory 只 append,不治理
    • 第五层,没有评估,多轮质量衰减只能靠感觉
    • 工程上怎么落地
      • 1. 先做 Query Rewrite
      • 2. 用 Hybrid Search 托底
      • 3. Rerank 不能省
      • 4. Memory 要分层
      • 5. Prompt 里要标元数据
      • 6. Eval 要按轮次做
    • 面试怎么答
    • 最后回到开头那个客服机器人
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档