Langchain ConversationalRetrievalChain无法兼顾上下文与对话记忆问题
问题描述
我要做一个基于PDF上下文的HR领域ConversationalRetrievalChain聊天机器人,遇到两个矛盾问题:
- 版本V1:能正常调用vectorstore里的上下文回答问题,但对话记忆失效,问「我上一个问题是什么」时返回「我不知道」。原因是每次循环都重新创建memory和conv_chain,之前的对话记录被覆盖。
- 版本V2:把memory和conv_chain移到函数外后,对话记忆生效了,但机器人没法再用vectorstore的上下文回答问题。
问题根源
- V1中每次调用
querying_V1都重新初始化ConversationBufferMemory和ConversationalRetrievalChain,对话记忆无法持久化。 - V2中同时手动维护
chat_history列表和使用内置memory,两者冲突;且get_chat_history的lambda函数未正确格式化对话历史,导致prompt无法读取有效历史内容。 - 手动传入
chat_history参数会覆盖memory中的内容,导致chain无法同时利用vectorstore上下文和对话历史。
解决方案
核心思路:让ConversationalRetrievalChain的内置memory自动管理对话历史,无需手动维护chat_history;同时确保prompt能正确读取context和chat_history变量。
修正后的代码
from langchain.memory import ConversationBufferMemory from langchain.chains import ConversationalRetrievalChain from langchain.prompts import PromptTemplate import sys # 调整prompt模板,适配对话历史的格式化输出 template2 = """ Your name is Bot. You are a chatbot specialized in human resources. Use the following context (delimited by <ctx></ctx>) to answer the questions. Use the previous conversation history (delimited by <hs></hs>) to understand context and answer follow-up questions. If you don't know the answer just answer that you don't know. ------ <ctx> {context} </ctx> ------ <hs> {chat_history} </hs> ------ Question: {question} """ prompt2 = PromptTemplate( template=template2, input_variables=["context", "chat_history", "question"] ) # 将memory和conv_chain初始化放在循环外部,避免每次重置 memory = ConversationBufferMemory( memory_key="chat_history", return_messages=True, output_key='answer' ) conv_chain = ConversationalRetrievalChain.from_llm( llm=llm, chain_type="stuff", retriever=vectorstore.as_retriever(search_kwargs={"k": 2}), memory=memory, combine_docs_chain_kwargs={"prompt": prompt2}, output_key='answer', # 自定义历史格式化函数,把Message对象转换成易读的对话格式 get_chat_history=lambda h: "\n".join([f"Human: {msg.content}" if msg.type == "human" else f"Bot: {msg.content}" for msg in h]), verbose=False ) def querying_V3(query: str): # 无需手动传入chat_history,memory会自动处理历史记录 result = conv_chain({"question": query}) return result["answer"].strip() # 主循环:不再手动维护chat_history列表 while True: query = input("Prompt: ") if query == "q": sys.exit() result = querying_V3(query) print("\n" + result)
关键修正点
- Memory持久化:把
memory和conv_chain的初始化放到循环外部,确保对话历史不会每次被重置。 - 移除手动历史维护:取消手动创建和append
chat_history列表,让ConversationBufferMemory自动管理对话记录。 - 历史格式适配:自定义
get_chat_history函数,将memory存储的Message对象转换成「Human: XXX\nBot: XXX」的字符串格式,让prompt能正确识别对话历史。 - prompt逻辑优化:调整模板中对对话历史的描述,明确告知LLM要结合历史和上下文回答问题。
验证效果
- 先问一个基于PDF的问题(比如「公司的年假政策是什么?」),机器人会调用vectorstore上下文给出答案。
- 再问「我刚才问了什么?」,机器人能正确回忆起上一个问题,对话记忆生效。
- 后续跟进问题(比如「这个政策适用于新员工吗?」)会同时结合之前的对话历史和vectorstore上下文回答。
内容的提问来源于stack exchange,提问作者Jorge Mendes
相关产品推荐
相关产品推荐

