【LangChain】ConversationalRetrievalChain、ConversationBufferMemory 迁移至 RunnableWithMessageHistory
代码中使用 langchain.memory.ConversationBufferMemory 和 langchain.chains.ConversationalRetrievalChain 来实现带有对话历史的检索问答(RAG),会触发 LangChainDeprecationWarning,提示 ConversationBufferMemory 已废弃,并建议参考迁移langchain_cor
代码中使用了 langchain.memory.ConversationBufferMemory
和 langchain.chains.ConversationalRetrievalChain
来实现带有对话历史的检索问答(Retrieval-Augmented Generation, RAG),会触发 LangChainDeprecationWarning
,提示 ConversationBufferMemory
已废弃,并建议参考迁移指南。本文基于 LangChain 0.3.x,详细解释如何迁移到推荐的对话历史管理方式(langchain_core.runnables.history.RunnableWithMessageHistory
),并提供代码示例(结合 ChatOpenAI
和向量存储实现 RAG)。
代码废弃告警:
LangChainDeprecationWarning: Please see the migration guide at: https://python.langchain.com/docs/versions/migrating_memory/
memory = ConversationBufferMemory(memory_key = “chat_history”, return_messages = True) ……
为什么触发 LangChainDeprecationWarning
?
langchain.memory.ConversationBufferMemory
和 langchain.chains.ConversationalRetrievalChain
是 LangChain 早期(0.1.x 及之前)的 API,用于管理对话历史和构建检索问答链。然而,随着 LangChain 0.2.x 和 0.3.x 的发展,这些类被废弃,原因包括:
- 模块化重构:LangChain 引入了 LangChain Expression Language(LCEL),提供更灵活、统一的链构建方式(如
Runnable
)。 - 现代化接口:
RunnableWithMessageHistory
取代ConversationBufferMemory
,支持更强大的历史管理和 LCEL 集成。 - 性能与可维护性:旧链(如
ConversationalRetrievalChain
)代码复杂,LCEL 链更简洁且易于调试。 - 废弃计划:
ConversationBufferMemory
和ConversationalRetrievalChain
将在 LangChain 1.0 中移除。
根据迁移指南(LangChain 文档),推荐:
- 使用
langchain_core.runnables.history.RunnableWithMessageHistory
替代ConversationBufferMemory
。 - 使用 LCEL 组合
ChatPromptTemplate
、ChatOpenAI
、向量存储检索器和StrOutputParser
,替代ConversationalRetrievalChain
。
迁移目标
假如原始代码实现了一个 RAG 系统:
- 使用
ConversationBufferMemory
存储对话历史(memory_key="chat_history"
,return_messages=True
返回消息对象)。 - 使用
ConversationalRetrievalChain
结合ChatOpenAI
和向量存储检索器(vectorstore.as_retriever
),基于检索到的上下文和历史回答问题。 verbose=True
启用日志,方便调试。
迁移后,我们将:
- 替换
ConversationBufferMemory
为RunnableWithMessageHistory
和ChatMessageHistory
。 - 替换
ConversationalRetrievalChain
为 LCEL 链,结合ChatPromptTemplate
和向量存储检索器。 - 保持功能等效:支持对话历史、上下文检索和问答。
迁移步骤
以下是将代码从 ConversationBufferMemory
和 ConversationalRetrievalChain
迁移到 LCEL 和 RunnableWithMessageHistory
的详细步骤,假设你使用 OpenAI 的嵌入模型和 Chroma 向量存储。
1. 分析原始代码
代码如下:
import os
os.environ["OPENAI_API_KEY"] = "Your OpenAI API Key"
from langchain.memory import ConversationBufferMemory
from langchain.chains import ConversationalRetrievalChain
from langchain_openai import ChatOpenAI
from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings
# 初始化嵌入模型和向量存储
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = Chroma.from_texts(
["人工智能(AI)是计算机科学的一个分支,专注于创建智能系统。",
"机器学习是 AI 的子领域,涉及从数据中学习模型。"],
embeddings
)
# 初始化对话历史
memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)
# 创建 RAG 链
qa = ConversationalRetrievalChain.from_llm(
ChatOpenAI(temperature=0),
vectorstore.as_retriever(),
memory=memory,
verbose=True
)
# 调用链
result = qa({"question": "什么是人工智能?"})
print(result["answer"])
result = qa({"question": "它包含哪些子领域?"})
print(result["answer"])
输出示例:
人工智能(AI)是计算机科学的一个分支,专注于创建能够执行需要人类智能的任务的系统,例如学习、推理和决策。
人工智能包含子领域,如机器学习,涉及从数据中学习模型。
问题:
from langchain.memory import ConversationBufferMemory
已废弃,需替换为langchain_community.chat_message_histories.ChatMessageHistory
和langchain_core.runnables.history.RunnableWithMessageHistory
。from langchain.chains import ConversationalRetrievalChain
已废弃,需使用 LCEL 构建等效链。- 需要安装
langchain-community
和langchain-core
。 - 确保
OPENAI_API_KEY
有效,支持嵌入模型和 LLM。
2. 安装必要依赖
确保安装 LangChain 0.3.x 及其相关依赖:
pip install --upgrade langchain langchain-community langchain-openai langchain-core chromadb
确认 OPENAI_API_KEY
已配置,支持 text-embedding-3-small
和 gpt-4o-mini
。
3. 迁移到 LCEL 和 RunnableWithMessageHistory
以下是迁移后的代码,使用 LCEL 和 RunnableWithMessageHistory
重现原功能:
import os
os.environ["OPENAI_API_KEY"] = "Your OpenAI API Key"
from langchain_community.vectorstores import Chroma
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough, RunnableWithMessageHistory
from langchain_community.chat_message_histories import ChatMessageHistory
from operator import itemgetter
# 初始化嵌入模型和向量存储
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = Chroma.from_texts(
["人工智能(AI)是计算机科学的一个分支,专注于创建智能系统。",
"机器学习是 AI 的子领域,涉及从数据中学习模型。"],
embeddings
)
retriever = vectorstore.as_retriever(search_kwargs={"k": 2})
# 初始化 LLM
llm = ChatOpenAI(temperature=0, model="gpt-4o-mini")
# 定义提示模板
prompt = ChatPromptTemplate.from_messages([
("system", "你是一个助手,根据以下上下文和对话历史回答问题:\n上下文:{context}"),
MessagesPlaceholder(variable_name="history"),
("human", "{question}")
])
# 创建 LCEL 链
runnable = (
{
"context": itemgetter("question") | retriever, # 提取 question 并传递给 retriever
"question": itemgetter("question"), # 提取 question 传递给 prompt
"history": itemgetter("history") | RunnablePassthrough() # 传递 history
}
| prompt
| llm
| StrOutputParser()
)
# 初始化消息历史存储
store = {}
def get_session_history(session_id: str) -> ChatMessageHistory:
if session_id not in store:
store[session_id] = ChatMessageHistory()
return store[session_id]
# 创建带历史的链
qa = RunnableWithMessageHistory(
runnable,
get_session_history,
input_messages_key="question",
history_messages_key="history"
)
# 调用链
session_id = "user1"
response = qa.invoke(
{"question": "什么是人工智能?"},
config={"configurable": {"session_id": session_id}}
)
print(response)
response = qa.invoke(
{"question": "它包含哪些子领域?"},
config={"configurable": {"session_id": session_id}}
)
print(response)
输出示例:
人工智能(AI)是计算机科学的一个分支,专注于创建能够执行需要人类智能的任务的系统,例如学习、推理和决策。
人工智能包含子领域,如机器学习,涉及从数据中学习模型。
代码说明
- OpenAI API 密钥:
- 通过
os.environ["OPENAI_API_KEY"]
设置,替换为实际密钥。 - 确保密钥支持嵌入模型(
text-embedding-3-small
)和 LLM(gpt-4o-mini
)。
- 通过
- 导入替换:
- 使用
langchain_community.chat_message_histories.ChatMessageHistory
和langchain_core.runnables.history.RunnableWithMessageHistory
替代ConversationBufferMemory
。 - 使用 LCEL(
RunnablePassthrough
,ChatPromptTemplate
,StrOutputParser
)替代ConversationalRetrievalChain
。
- 使用
- 向量存储:
Chroma.from_texts
创建向量存储,retriever
搜索相关文档(k=2
返回 2 个结果)。
- 提示模板:
ChatPromptTemplate
包含系统消息(带上下文)、历史消息(MessagesPlaceholder
)和用户问题。MessagesPlaceholder(variable_name="history")
插入对话历史。
- LCEL 链:
{ "context": itemgetter("question") | retriever,"question": itemgetter("question"),"history": itemgetter("history") | RunnablePassthrough() }
并行检索上下文和传递问题。prompt | llm | StrOutputParser
生成回答并解析为字符串。
- 对话历史:
ChatMessageHistory
存储消息,store
按session_id
区分会话。RunnableWithMessageHistory
包装链,自动管理历史。
- 调用:
- 输入为字典(
{"question": "..."}
),session_id
保持会话一致。 - 输出为字符串,与原代码的
result["answer"]
等效。
- 输入为字典(
- 调试:
- 原代码的
verbose=True
效果可通过设置langchain.debug = True
或自定义日志实现:import langchain langchain.debug = True
- 原代码的
4. 功能等效性
- 对话历史:
RunnableWithMessageHistory
完全替代ConversationBufferMemory
,支持return_messages=True
的消息格式。 - RAG 功能:LCEL 链通过
retriever
和prompt
实现与ConversationalRetrievalChain
相同的检索问答逻辑。 - 输入/输出:新链接受
{"question": "..."}
输入,返回字符串输出,与原代码一致。 - 上下文管理:
MessagesPlaceholder
和ChatMessageHistory
确保历史和上下文正确传递。
5. 自动迁移(可选)
LangChain 提供 CLI 工具自动升级废弃导入和内存管理:
- 安装 LangChain CLI:
pip install langchain-cli
- 运行迁移命令:
langchain migrate
- 按提示扫描代码,工具会将
ConversationBufferMemory
和ConversationalRetrievalChain
替换为新 API。
注意:
- 迁移工具可能无法完全处理复杂链(如
ConversationalRetrievalChain
的逻辑),建议手动迁移并验证。 - 确保安装
langchain-community
和langchain-core
以支持新 API。
6. 测试与验证
- 依赖:
- 确认已安装
langchain
,langchain-community
,langchain-openai
,langchain-core
, 和chromadb
。 - 验证
OPENAI_API_KEY
有效,支持嵌入模型和 LLM。
- 确认已安装
- 运行:
- 测试向量存储创建,确认文档嵌入正确。
- 测试多轮问答,验证对话历史保留(例如第二轮问题依赖第一轮上下文)。
- 比较输出与原代码,确保功能一致。
- Chroma 配置:
- 默认使用内存存储,生产环境可设置持久化路径:
vectorstore = Chroma.from_texts(texts, embeddings, persist_directory="./chroma_db")
- 默认使用内存存储,生产环境可设置持久化路径:
注意事项
- API 密钥安全:
- 避免硬编码密钥,推荐使用
.env
文件:from dotenv import load_dotenv load_dotenv() # 加载 OPENAI_API_KEY
- 确保密钥支持嵌入模型(
text-embedding-3-small
)和 LLM(gpt-4o-mini
)。
- 避免硬编码密钥,推荐使用
- 嵌入模型选择:
text-embedding-3-small
:1536 维,成本低,适合大多数场景。text-embedding-3-large
:3072 维,精度更高,成本更高。text-embedding-ada-002
:旧模型,仍广泛使用。
- 对话历史管理:
ChatMessageHistory
是内存存储,生产环境可替换为持久化存储(如SQLChatMessageHistory
):from langchain_community.chat_message_histories import SQLChatMessageHistory def get_session_history(session_id: str) -> SQLChatMessageHistory: return SQLChatMessageHistory(session_id=session_id, connection_string="sqlite:///history.db")
- 性能:
- 检索速度依赖文档数量和 Chroma 配置,优化
search_kwargs
(如k
)可平衡精度和速度。 - LLM 调用速度依赖 API,批量处理可提高效率。
- 检索速度依赖文档数量和 Chroma 配置,优化
- Ollama 替代:
- 若想避免 OpenAI API 费用,可使用
langchain_ollama
:from langchain_ollama import ChatOllama, OllamaEmbeddings embeddings = OllamaEmbeddings(model="nomic-embed-text") llm = ChatOllama(model="qwen3:1.7b")
- 若想避免 OpenAI API 费用,可使用
常见问题
Q1:可以继续使用 ConversationBufferMemory
和 ConversationalRetrievalChain
吗?
A:可以,但不建议。它们已废弃,迁移到 LCEL 和 RunnableWithMessageHistory
更安全且现代化。
Q2:新旧 API 功能有差异吗?
A:功能等效,新 API(LCEL 和 RunnableWithMessageHistory
)更灵活,支持流式、异步和复杂链。
Q3:如何调试 LCEL 链?
A:设置 langchain.debug = True
查看详细日志,或单独调用链的组件(如 retriever.invoke()
, prompt.invoke()
)检查中间输出。
Q4:如何优化 RAG 性能?
A:调整检索器的 k
参数、使用更高效的嵌入模型(如 text-embedding-3-small
)、优化提示模板,或启用流式输出(chain.stream()
)。
总结
从 ConversationBufferMemory
和 ConversationalRetrievalChain
迁移到 LCEL 和 RunnableWithMessageHistory
的步骤包括:
- 安装
langchain
,langchain-community
,langchain-openai
,langchain-core
, 和chromadb
。 - 替换
ConversationBufferMemory
为ChatMessageHistory
和RunnableWithMessageHistory
。 - 替换
ConversationalRetrievalChain
为 LCEL 链(retriever | prompt | llm | StrOutputParser
)。 - 设置
OPENAI_API_KEY
,确保支持嵌入模型和 LLM。 - 使用
ChatPromptTemplate
和MessagesPlaceholder
管理上下文和历史。 - 可选使用 LangChain CLI(
langchain migrate
)自动升级导入。 - 可选使用
langchain_ollama
替代 OpenAI 模型。
更多推荐
所有评论(0)