多轮对话与上下文管理 📅 2026/7/7 8:02:14 多轮对话与上下文管理单轮问答简单多轮对话就复杂了用户追问「那它的价格呢」系统得知道「它」指的是什么。这篇讲多轮对话 RAG 的上下文管理、问题改写、历史记录处理、以及怎么避免上下文越堆越长。大家好我是黒漂技术佬。单轮问答的 RAG 好做用户问一句答一句。但真实场景往往是多轮对话用户退货流程是什么 AI... 用户那需要运费吗 AI... 用户多久能到账 AI...后面的问题是承接上文的直接拿去检索肯定搜不到。「多久能到账」——什么到账没有上下文就不知道是退款到账。这篇讲多轮对话 RAG 怎么处理上下文、怎么做问题改写、怎么管理历史记录。一、多轮对话的挑战1. 指代消解「它」「这个」「那个」指代前面提到的东西脱离上下文看不懂。2. 省略补充「那需要运费吗」省略了主语「退货」。完整问题应该是「退货需要运费吗」3. 话题切换用户可能突然换话题之前的上下文不能再用了。4. 上下文膨胀对话轮数多了历史记录越来越长上下文窗口装不下还会引入噪声。5. 检索时机每一轮都检索吗还是只有新话题才检索检索太频繁浪费不检索又信息不全。二、核心思路问题改写Query Rewriting多轮对话的标准做法不直接拿当前问题检索先结合历史把问题改写成完整独立的问题再检索。历史对话 当前问题 → 改写为完整问题 → 检索 → 回答例子历史 用户退货流程是什么 AI退货需要在7天内申请寄出后填写物流单号... 当前问题需要运费吗 改写后退货需要运费吗 ← 补全了主语改写后的问题是完整的可以直接拿去检索。为什么不直接把历史都塞给大模型检索不准历史对话塞进去检索噪声大相关度低上下文浪费历史记录占 token留给参考资料的就少了效果不稳定模型有时候看历史有时候不看先改写再检索检索质量有保证回答也更准。三、问题改写的实现方式方法 1大模型改写最常用让大模型把多轮问题改写成独立问题请根据对话历史把用户的最新问题改写成一个完整、独立的问题。 要求 1. 补充省略的主语和上下文 2. 消解指代它/这个等 3. 如果用户换了话题直接保留原问题 4. 只输出改写后的问题不要其他内容 【对话历史】 {history} 【当前问题】 {question} 【改写后的问题】改写 Prompt 优化加 Few-shot 效果更稳例子1 历史退货流程是什么 → 先申请再寄回 问题需要运费吗 改写退货需要运费吗 例子2 历史这款手机续航怎么样 → 续航一天没问题 问题价格呢 改写这款手机价格是多少 例子3 历史A产品怎么安装 → 下载APP扫码绑定 问题B产品支持WiFi吗 改写B产品支持WiFi吗 话题切换直接保留方法 2规则 关键词提取简单场景可以用规则提取历史中的核心实体当前问题出现指代时替换优点快、不花钱缺点复杂对话搞不定方法 3专门的改写模型有专门做对话改写的小模型速度快成本低。大部分场景用大模型改写就够了Prompt 写好效果不错。四、历史记录怎么管理保留多少轮不是越多越好太少上下文不够指代消解不了太多噪声大、占 token、可能引入旧话题一般保留 3-5 轮就够了大部分指代和省略都能覆盖。历史记录的格式用户xxx 助手xxx 用户xxx 助手xxx ...按轮次排列用户和助手交替。只保留用户历史行不行最好用户和助手的都保留。助手的回答里也有关键信息能帮助理解上下文。太长了怎么办对话很长的时候不能全塞进去策略 1滑动窗口只保留最近 N 轮更早的丢掉。简单有效大部分场景够用。策略 2摘要压缩把早期的对话压缩成摘要保留关键信息。【历史摘要】 用户之前询问了退货流程和运费问题了解到7天内可退货运费由商家承担。 【最近对话】 用户退款多久到账 助手3个工作日原路返回。适合长对话场景但实现复杂一点。策略 3只保留相关的根据当前问题从历史里挑相关的几轮。实现复杂效果不一定比滑动窗口好多少。五、完整的多轮 RAG 流程用户新问题 ↓ 获取对话历史最近3-5轮 ↓ 问题改写结合历史生成独立完整的问题 ↓ 用改写后的问题检索相关文档 ↓ 检索结果 当前问题 简短历史 → 生成回答 ↓ 把这一轮加入历史记录 ↓ 返回答案为什么生成答案时还要带一点历史改写主要是为了检索。生成答案的时候带一点历史回答更连贯能承接上一轮的语气。但不用带太多1-2 轮就够了主要内容还是检索到的参考资料。六、话题切换检测用户突然换话题了还用上一轮的上下文改写就错了。怎么检测话题切换方法 1改写模型自己处理Prompt 里加上「如果换了话题就直接保留原问题」让大模型自己判断。大部分情况能搞定但偶尔会错。方法 2单独做分类用一个小模型或规则判断是不是新话题和上一轮语义相似度低 → 可能换话题了出现新的实体 → 可能换话题了方法 3用户明确新开用户说「换个话题」「问你点别的」直接清空上下文。话题切换了怎么办清空历史从头开始或者保留摘要不保留详细对话七、多轮对话的 Prompt 设计改写 Prompt你是一个对话问题改写助手。 根据对话历史将用户的最新问题改写为一个完整、独立、语义明确的问题。 要求 1. 补充省略的成分消解代词它/这个/那个等 2. 保持原问题的意图和语气 3. 如果用户开启了新话题直接输出原问题 4. 只输出改写后的问题不要任何解释 【对话历史】 {history} 【最新问题】 {question} 改写后的问题回答 Prompt带历史你是XX客服助手。根据参考资料回答用户问题。 注意对话上下文回答要连贯。 只用资料中的信息不知道就说暂无相关信息。 【参考资料】 {context} 【对话历史】 {short_history} 【当前问题】 {question} 【回答】回答阶段的历史不用太长1-2 轮就行保持连贯即可。八、多轮对话的其他技巧1. 缓存检索结果同一话题下多轮问题可能检索到的文档差不多。可以缓存检索结果相似问题不用重复检索。节省时间和成本。2. 追问澄清用户问题太模糊主动追问用户怎么弄 AI请问你是指退货流程、退款申请还是其他操作呢提升体验减少误解。3. 会话隔离不同用户、不同会话的历史要分开不能串了。每个 session_id 对应一份历史记录。4. 历史过期对话超过一定时间比如 30 分钟没说话清空历史算新会话。避免用户隔了半天回来还带着之前的上下文。九、工程实现要点存储历史记录存在哪里短期内存、Redis适合服务端长期数据库用户完整对话记录Session 管理每个对话一个 session_id前端传 session_id后端查对应历史新对话生成新 session_id控制 Token 数历史 问题 资料 答案不能超模型的上下文窗口。要做长度控制超了就截历史、减少检索文档数。异常处理改写失败怎么办降级直接用原问题检索历史丢失怎么办从空历史开始十、常见坑坑 1历史全塞进去检索把整个对话历史当 query 去检索噪声大结果不准。一定要先改写再检索。坑 2历史留太多轮十几轮历史全塞进去占 token 不说还容易引入旧话题干扰。3-5 轮足够。坑 3改写后语义变了大模型改写有时候会改偏原意变了。可以加个校验或者用保守的改写策略。坑 4话题切换没检测到用户换话题了还带着上一轮的上下文答非所问。Prompt 里强调话题切换的处理。坑 5回答阶段历史太多生成答案时塞了一堆历史结果模型盯着历史回答忽略了检索资料。回答阶段历史少放点重点还是参考资料。十一、本篇小结多轮对话的核心挑战指代、省略、话题切换、上下文膨胀标准做法先问题改写结合历史生成完整问题再检索再回答改写用大模型 Few-shot Prompt效果不错历史记录保留 3-5 轮滑动窗口策略简单有效回答阶段只带少量历史保持连贯重点还是检索资料要处理话题切换的情况避免上下文串了工程上注意 session 隔离、token 控制、缓存优化下一篇讲性能优化RAG 系统慢怎么办缓存、批量、并发怎么搞。我是黒漂技术佬。