LangChain是如何实现RAG多轮问答的

目录

  • 引言
  • 一、LangChain实现RAG多轮问答核心机制
      • 1. 对话历史管理(Memory)
      • 2. 问题重写(Query Rewriting)
      • 3. 检索增强生成(RAG Core)
      • 4. 链式工作流(Chain)
  • 二、关键设计特点
  • 三、完整示例代码
  • 四、优化建议

引言

小马之所以写今天这篇文章是因为在上一篇文章《多轮问答与指代消解》中的第一章节有提到“LangChain是怎么实现的多轮问答”,但很显然没有提到它在RAG下是怎么实现的多轮问答,毕竟这两者还是有区别的。前一篇文章刚好提到自己实现RAG下的多轮问答要怎么实现,唯独没有提到LangChain在RAG下是怎么实现的多轮。
前篇文章中提到的MultiQueryRetriever小马已经在文中声明,这个并不是为了解决RAG多轮的机制。

庆幸的是,我们回过头来看LangChain实现RAG多轮问答的方式其实和我们自己实现的思路不能说一模一样,简直是毫无区别。只是LangChain封装了组件,我们直接调用就能实现了,省去了自己写工程代码来实现的过程。接下来我们有必要一起看下的,不过看过上一篇的同学其实看这篇文章就很好理解了。
在这里插入图片描述

一、LangChain实现RAG多轮问答核心机制

在 LangChain 中实现 RAG(Retrieval-Augmented Generation)的多轮问答主要通过以下核心机制完成,确保对话历史被有效利用:

1. 对话历史管理(Memory)

LangChain 使用 Memory 组件 存储和传递上下文:

  • ConversationBufferMemory
    存储完整的原始对话历史(用户输入 + AI 响应)。
  • ConversationSummaryMemory
    用 LLM 压缩历史为摘要,避免 token 超限。
  • ConversationBufferWindowMemory
    仅保留最近 K 轮对话,控制上下文长度。
from langchain.memory import ConversationBufferMemorymemory = ConversationBufferMemory(memory_key="chat_history",  # 存储对话的键名return_messages=True        # 返回消息列表而非字符串
)

2. 问题重写(Query Rewriting)

将当前问题结合历史重写为独立查询,使检索更准确:

  • ConversationalRetrievalChain 内置 condense_question 步骤:
    1. 输入:当前问题 + 对话历史
    2. 输出:重写后的独立问题(如将代词替换为具体实体)
from langchain.chains import ConversationalRetrievalChainqa_chain = ConversationalRetrievalChain.from_llm(llm=chat_model,             # 如 ChatOpenAIretriever=vectorstore.as_retriever(),  # 向量检索器memory=memory,condense_question_llm=condense_model  # 可选:专用重写模型
)

3. 检索增强生成(RAG Core)

多轮流程如下:

  1. 检索上下文
    用重写后的查询从向量库检索相关文档片段。
  2. 组装提示
    将以下内容组合为最终提示:
    • 检索到的上下文
    • 对话历史(原始或摘要)
    • 当前问题
  3. 生成答案
    LLM 基于完整上下文生成连贯回复。
提示词示例:
[检索到的文档]
对话历史:用户: 量子计算是什么?AI: 量子计算利用量子比特...
当前问题:它有什么优势?
最终答案:量子计算的优势包括...

4. 链式工作流(Chain)

ConversationalRetrievalChain 自动处理整个流程:

# 第一轮
result = qa_chain({"question": "什么是RAG?"})
print(result["answer"])# 第二轮(自动携带历史)
result = qa_chain({"question": "它如何解决幻觉问题?"})  # "它"指代RAG
print(result["answer"])

二、关键设计特点

组件作用
Memory持久化历史对话,支持多种存储策略(原始/摘要/滑动窗口)
Query Rewriting解决指代消解(如“它”指代上文的 RAG),提升检索准确性
Contextual Prompt动态组装提示,包含历史 + 检索结果 + 当前问题
检索器优化支持自定义检索器(如过滤元数据、调整相似度阈值)

三、完整示例代码

from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain.memory import ConversationBufferMemory
from langchain.chains import ConversationalRetrievalChain# 初始化向量库和模型
vectorstore = FAISS.load_local("rag_vector_db", OpenAIEmbeddings())
llm = ChatOpenAI(model="gpt-4-turbo")
memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)# 构建链
qa_chain = ConversationalRetrievalChain.from_llm(llm=llm,retriever=vectorstore.as_retriever(search_kwargs={"k": 3}),memory=memory
)# 模拟多轮对话
questions = ["LangChain是什么?","它的核心组件有哪些?",  # "它" 指代LangChain"如何用RAG实现多轮对话?"
]
for q in questions:result = qa_chain.invoke({"question": q})print(f"问题: {q}\n答案: {result['answer']}\n")

四、优化建议

  1. 历史摘要:长对话时使用 ConversationSummaryMemory 避免 token 超限。
  2. 重写模型:用轻量级模型(如 GPT-3.5)专门处理问题重写,降低成本。
  3. 检索过滤:基于元数据(如对话 ID)过滤无关文档。
  4. 提示工程:定制提示模板明确区分历史/当前问题/检索内容。

通过组合 Memory 管理智能查询重写上下文感知的 RAG 流程,LangChain 有效实现了多轮问答的连贯性与准确性。

本篇文章旨在补充上一篇文章的内容,建议结合上一篇文章食用,口感效果更加。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/918648.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/918648.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

DAY 44 预训练模型

知识点回顾: 预训练的概念常见的分类预训练模型图像预训练模型的发展史预训练的策略预训练代码实战:resnet18 一、预训练的概念 我们之前在训练中发现,准确率最开始随着epoch的增加而增加。随着循环的更新,参数在不断发生更新。 所…

Java Stream API 中常用方法复习及项目实战示例

在最近的练手项目中,对于stream流的操作愈加频繁,我也越来越感觉stream流在处理数据是的干净利落,因此写博客用来记录最近常用的方法以便于未来的复习。map() 方法map()是一个中间操作(intermediate operation)&#x…

从零开始手搓一个GPT大语言模型:从理论到实践的完整指南(一)

现在人工智能飞速发展时代,LLM绝对可以算是人工智能领域得一颗明珠,也是现在许多AI项目落地得必不可少得一个模块,可以说,不管你之前得研究领域是AI得哪个方向,现在都需要会一些LLM基础,在这个系列&#xf…

Redis ubuntu下载Redis的C++客户端

1. 安装 redis-plus-plus C 操作 Redis 的库有很多,这里选择使用 redis-plus-plus,这个库的功能强大,使用简单。 Github 地址:GitHub - sewenew/redis-plus-plus: Redis client written in C 访问不了Github 地址的可以使用Ste…

nm命令和nm -D命令参数

出现这种差异的原因在于:动态库中的符号分为两种类型: 常规符号表(regular symbol table):通常用于静态链接和调试,默认不包含在动态库中(除非显式保留)。动态符号表(dyn…

Windows下cuda的安装和配置

今天开始做一个cuda教程。由于本人主要在windows下使用visual studio进行开发,因此这里讲一下windows下的cuda开发环境。 下载cuda_toolkit 从网站https://developer.nvidia.com/cuda-toolkit中下载,先选择Download Now,然后跳转到如下页面&#xff1a…

【代码随想录day 19】 力扣 450.删除二叉搜索树中的节点

视频讲解:https://www.bilibili.com/video/BV1tP41177us/?share_sourcecopy_web&vd_sourcea935eaede74a204ec74fd041b917810c 文档讲解:https://programmercarl.com/0450.%E5%88%A0%E9%99%A4%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91%E4%B8%A…

智慧养老丨实用科普+避坑指南:科技如何让晚年生活更安全舒适?

随着老龄化社会的到来,智慧养老产品逐渐成为改善老年人生活质量的重要工具。从智能手表到便携洗浴机,科技正为老年人的健康、安全与生活便利提供创新解决方案。我们这次主要介绍四类典型智慧养老产品,结合真实体验给出选购建议,并…

系统垃圾清理批处理脚本 (BAT)

系统垃圾清理批处理脚本 (BAT) 以下是一个Windows系统垃圾清理的批处理脚本,它可以清理常见的系统临时文件、缓存和日志等: echo off title 系统垃圾清理工具 color 0a echo. echo 正在清理系统垃圾文件,请稍候... echo.:: 清理临时文件 echo…

Terraform的零基础学习教程

一、Terraform 是什么? Terraform 是由 HashiCorp 开发的开源工具,用于自动化管理云基础设施(如 AWS、Azure、GCP 等)。 核心特点: 基础设施即代码(IaC):用代码定义和管理资源。跨…

429. N 叉树的层序遍历(中等)题解

题目描述给定一个 N 叉树,返回其节点值的层序遍历。(即从左到右,逐层遍历)。树的序列化输入是用层序遍历,每组子节点都由 null 值分隔(参见示例)。示例 1:输入:root [1,…

Java 课程,每天解读一个简单Java之题目:输入一行字符,分别统计出其中英文字母、空格、数字和其它字符的个数。

package ytr250813;import java.io.IOException;public class CharacterCounter {public static void main(String[] args) throws IOException {// 初始化计数器变量int letterCount 0; // 英文字母计数器int spaceCount 0; // 空格计数器int digitCount 0; // 数字计数器i…

GitLab CI + Docker 自动构建前端项目并部署 — 完整流程文档

一、环境准备1. 服务器准备一台Linux服务器(CentOS/Ubuntu皆可),推荐至少4核8GB内存已安装 Docker(及 Docker 服务已启动)已安装 GitLab Runner2. 服务器上安装 Docker (如果没装)# CentOS9以下…

LCP 17. 速算机器人

目录 题目链接: 题目: 解题思路: 代码: 总结: 题目链接: LCP 17. 速算机器人 - 力扣(LeetCode) 题目: # LCP 17. 速算机器人 小扣在秋日市集发现了一款速算机器人。…

Spring cloud集成ElastictJob分布式定时任务完整攻略(含snakeyaml报错处理方法)

ElasticJob 是一款轻量级、可扩展的分布式定时任务解决方案,基于 Quartz 二次开发,支持任务分片、失效转移、任务追踪等功能,非常适合在 Spring Cloud 微服务场景中使用。我将带你完成 Spring Cloud 集成 ElasticJob 的全过程,并分…

了解 Linux 中的 /usr 目录以及 bin、sbin 和 lib 的演变

Linux 文件系统层次结构是一个复杂且引人入胜的体系,其根源深植于类 Unix 操作系统的历史之中。在这一结构的核心,/usr 目录是一个至关重要的组成部分,随着时间的推移,它经历了显著的演变。与此同时,/bin、/sbin、/lib…

高级IO(五种IO模型介绍)

文章目录一、IO为什么慢?一、阻塞IO二、非阻塞IO三、信号驱动IO四、IO多路复用五、异步IO一、IO为什么慢? IO操作往往都是和外设交互,比如键盘、鼠标、打印机、磁盘。而最常见的就是内存与磁盘的交互,要知道磁盘是机械设备&#…

第十二节:粒子系统:海量点渲染

第十二节:粒子系统:海量点渲染 引言 粒子系统是创造动态视觉效果的神器,从漫天繁星到熊熊火焰,从魔法特效到数据可视化,都离不开粒子技术。Three.js提供了强大的粒子渲染能力,可轻松处理百万级粒子。本文将…

LeetCode Day5 -- 二叉树

目录 1. 啥时候用二叉树? (1)典型问题 (2)核心思路 2. BFS、DFS、BST 2.1 广度优先搜索BFS (1)适用任务 (2)解决思路​​:使用队列逐层遍历 2.2 深度…

<c1:C1DateTimePicker的日期时间控件,控制日期可以修改,时间不能修改,另外控制开始时间的最大值比结束时间小一天

两个时间控件 <c1:C1DateTimePicker Width"170" EditMode"DateTime" CustomDateFormat"yyyy-MM-dd" CustomTimeFormat"HH:mm:ss" Style"{StaticResource ListSearch-DateTimePicker}" x:Name"dateTimePicker"…