基于Langchain的ChatPDF应用:历史会话恢复与上下文记忆问题
解决方案:RAG ChatPDF 会话上下文持久化与历史导航实现
一、修复现有两种方法的问题
1. 解决Prompt模板加入历史后的Missing some input keys: {'context'}错误
这个错误的核心是RAG流程中未保证每次请求都检索PDF上下文并传入Prompt。当你把历史加入Prompt后,Chain的输入变量需要包含history、question和context,但二次提问时可能遗漏了触发Retriever获取context的步骤。
修复步骤:
- 确保Prompt模板明确包含三个变量:
history(会话历史)、context(PDF检索结果)、question(当前提问) - 构建Chain时,将Retriever的输出与Memory的历史、当前提问串联,保证
context始终被传入LLM
示例Prompt模板:
from langchain.prompts import PromptTemplate prompt = PromptTemplate( input_variables=["history", "context", "question"], template=""" 你是一个基于PDF文档的问答助手,根据以下信息回答用户问题: 会话历史:{history} PDF文档上下文:{context} 当前问题:{question} 请结合会话历史和PDF上下文,给出准确的回答。 """ )
2. 解决ConversationBufferMemory重启后上下文丢失问题
ConversationBufferMemory是内存存储,重启后数据清空。需要改用持久化的ChatMessageHistory,将会话历史存储到MongoDB中,再关联到Memory类。
示例代码:
from langchain.memory import ConversationBufferMemory from langchain.mongodb.chat_message_histories import MongoDBChatMessageHistory # 初始化MongoDB聊天历史存储 chat_history = MongoDBChatMessageHistory( session_id="your_session_id", connection_string="mongodb://localhost:27017/", database_name="chatpdf_db", collection_name="chat_histories" ) # 绑定持久化历史到Memory memory = ConversationBufferMemory( chat_memory=chat_history, memory_key="history", input_key="question", return_messages=False # 按字符串返回历史,适配Prompt模板 )
二、实现会话历史导航功能
要实现类似ChatGPT的会话恢复,需要完成以下核心步骤:
1. 会话标识管理
为每个会话生成唯一的session_id(比如UUID),前端展示会话列表时,将每个会话名称与对应的session_id绑定。
2. 加载指定会话的历史
当用户选择某条历史会话时,通过session_id从MongoDB加载对应的聊天历史,重新初始化Memory和Chain:
def load_session_history(session_id: str): # 根据session_id加载MongoDB中的历史 chat_history = MongoDBChatMessageHistory( session_id=session_id, connection_string="mongodb://localhost:27017/", database_name="chatpdf_db", collection_name="chat_histories" ) # 重新初始化Memory memory = ConversationBufferMemory( chat_memory=chat_history, memory_key="history", input_key="question", return_messages=False ) return memory
3. 构建完整的RAG Chain
将Retriever、Memory、Prompt模板整合到Chain中,确保每次请求既检索PDF上下文,又加载会话历史:
from langchain.chains import RetrievalQAWithSourcesChain from langchain.llms import OpenAI from langchain.vectorstores import Chroma # 初始化Chroma向量库 chroma_db = Chroma(persist_directory="./chroma_db", embedding_function=your_embedding_func) retriever = chroma_db.as_retriever() # 加载指定会话的Memory memory = load_session_history("target_session_id") # 构建带Memory的RAG Chain chain = RetrievalQAWithSourcesChain.from_llm( llm=OpenAI(temperature=0), retriever=retriever, memory=memory, prompt=prompt, return_source_documents=True ) # 发起提问,自动携带会话历史和PDF上下文 response = chain({"question": "你的问题"})
4. 会话列表展示
从MongoDB中查询所有session_id及对应会话的第一条/最后一条消息,生成会话列表供用户选择:
from pymongo import MongoClient client = MongoClient("mongodb://localhost:27017/") db = client["chatpdf_db"] collection = db["chat_histories"] # 查询所有会话的session_id和最近一条消息 sessions = collection.aggregate([ {"$group": { "_id": "$session_id", "last_message": {"$last": "$content"}, "created_at": {"$first": "$created_at"} }}, {"$sort": {"created_at": -1}} ]) # 转换为前端可用的会话列表 session_list = [{"session_id": item["_id"], "last_msg": item["last_message"]} for item in sessions]
关键注意事项
- 确保每个会话的
session_id唯一,避免历史混淆 - MongoDB存储聊天记录时,需包含
session_id、role(user/assistant)、content、created_at等字段 - 当用户切换会话时,需重新初始化Chain,确保使用对应会话的Memory
- 大模型的上下文窗口有限,过长的会话历史可能需要做摘要处理,避免超出Token限制
内容的提问来源于stack exchange,提问作者usama hussain
相关产品推荐
相关产品推荐

