为问答 Agent 添加短期记忆(ConversationBufferMemory)
在构建问答Agent时,多轮对话的上下文记忆是核心需求——让Agent能记住历史对话内容,结合「历史问题+历史回答+当前问题」给出连贯回复,而非孤立回答每个问题。 LangChain中的 本文将基于LangChain框架,从核心原理、完整可运行代码、关键细节、进阶优化四个维度,教你为问答Agent集成 安装LangChain核心依赖+大模型适配依赖(以OpenAI/通义千问为例,二选一即可): 替换上述步骤2和步骤3即可,其余代码完全不变,适配性拉满: 开启 比如 默认 实际场景中,问答Agent通常需要结合私有知识库(如PDF/文档),此时将 解决方案:使用 当提示模板需要更精细的对话格式时,将 为问答Agent添加ConversationBufferMemory是轻量、易用的短期记忆组件,核心作用是按顺序缓存对话历史,并将历史内容注入到模型的输入提示中,实现问答Agent的短期记忆能力,适合中小长度的多轮对话场景。ConversationBufferMemory,支持OpenAI/国产大模型(通义千问/文心一言),代码可直接复用。一、核心概念铺垫
1.1 ConversationBufferMemory 核心作用
1.2 核心搭配
ConversationBufferMemory通常与ConversationChain(通用对话链)/RetrievalQA(知识库问答链)搭配使用,本文先实现基础问答Agent(基于ConversationChain),后续补充带知识库的问答Agent优化方案。1.3 关键参数
参数名 作用 常用值 memory_key记忆在提示模板中的变量名(需与提示模板一致) chat_history(推荐)return_messages记忆返回格式: True返回消息对象(HumanMessage/AIMessage),False返回拼接字符串False(基础场景)/True(复杂场景)input_key输入问题的变量名 input(默认,无需修改)output_key输出回答的变量名 output(默认,无需修改)二、环境准备
# 核心依赖:LangChain核心+社区组件
pip install langchain-core langchain-community -i https://pypi.tuna.tsinghua.edu.cn/simple
# 可选1:OpenAI模型依赖(GPT-3.5/GPT-4)
pip install langchain-openai -i https://pypi.tuna.tsinghua.edu.cn/simple
# 可选2:国产大模型依赖(通义千问/文心一言/智谱清言)
pip install langchain-qianfan langchain-dashscope -i https://pypi.tuna.tsinghua.edu.cn/simple三、完整实现:基础问答Agent+短期记忆
3.1 方案1:基于OpenAI模型(GPT-3.5/GPT-4)
# 1. 导入核心模块
from langchain_openai import ChatOpenAI
from langchain.chains import ConversationChain
from langchain.memory import ConversationBufferMemory
from langchain.prompts import PromptTemplate
import os
# 2. 配置环境(OpenAI API密钥)
# 国内用户需配置代理:os.environ["HTTP_PROXY"] = "http://127.0.0.1:7890"
os.environ["OPENAI_API_KEY"] = "你的OpenAI API密钥"
# 3. 初始化LLM模型
llm = ChatOpenAI(
model="gpt-3.5-turbo", # 推荐gpt-3.5-turbo,性价比高
temperature=0.1, # 越低回答越稳定,适合问答场景
max_tokens=2048
)
# 4. 初始化ConversationBufferMemory(核心:短期记忆)
memory = ConversationBufferMemory(
memory_key="chat_history", # 记忆变量名,需与提示模板中的{chat_history}一致
return_messages=False, # 返回字符串格式的对话历史,适合基础场景
input_key="input" # 输入问题的变量名,默认input即可
)
# 5. 自定义带记忆的提示模板(必须包含{chat_history}和{input})
# 模板说明:chat_history=历史对话,input=当前问题,让模型结合两者回答
prompt = PromptTemplate(
input_variables=["chat_history", "input"], # 必须包含记忆变量和输入变量
template="""你是一个专业的问答助手,善于结合历史对话内容回答当前问题。
历史对话:{chat_history}
当前问题:{input}
请简洁、准确地回答当前问题,无需额外赘述。"""
)
# 6. 构建带记忆的问答链(核心:将LLM、记忆、提示模板绑定)
conversation_chain = ConversationChain(
llm=llm,
memory=memory,
prompt=prompt,
verbose=True # 开启详细日志,可查看输入的提示内容(含历史对话)
)
# 7. 测试多轮问答(验证记忆效果)
if __name__ == "__main__":
# 第一轮问答
print("===== 第一轮 =====")
res1 = conversation_chain.invoke({"input": "什么是大语言模型?"})
print("回答:", res1["output"], "\n")
# 第二轮问答(结合历史:问大语言模型的核心优势)
print("===== 第二轮 =====")
res2 = conversation_chain.invoke({"input": "它的核心优势是什么?"})
print("回答:", res2["output"], "\n")
# 第三轮问答(结合历史:问该优势的应用场景)
print("===== 第三轮 =====")
res3 = conversation_chain.invoke({"input": "这些优势能用到哪些领域?"})
print("回答:", res3["output"], "\n")
# 手动查看记忆中的对话历史
print("===== 查看短期记忆 =====")
print(memory.load_memory_variables({}))
# 清空记忆(可选)
# memory.clear()
# print("清空记忆后:", memory.load_memory_variables({}))3.2 方案2:基于国产模型(通义千问,国内用户推荐)
# 2. 配置环境(通义千问API密钥,从阿里云DashScope获取)
os.environ["DASHSCOPE_API_KEY"] = "你的通义千问API密钥"
# 3. 初始化通义千问模型(替换OpenAI)
from langchain_dashscope import ChatDashScope
llm = ChatDashScope(
model="qwen-plus", # 通义千问轻量版,免费额度足够测试
temperature=0.1,
max_tokens=2048
)3.3 运行结果与关键日志
核心输出(记忆生效)
===== 第一轮 =====
回答: 大语言模型是基于大尺度语料训练、具备强大自然语言理解与生成能力的人工智能模型,能完成文本创作、问答、翻译等多种自然语言处理任务。
===== 第二轮 =====
回答: 大语言模型的核心优势包括:1. 强大的上下文理解与语义分析能力;2. 灵活的自然语言生成能力,可输出流畅、贴合语境的文本;3. 泛化能力强,能处理未见过的新问题;4. 多任务适配,无需单独训练即可完成多种NLP任务。
===== 第三轮 =====
回答: 这些优势可应用在智能客服、内容创作、教育辅导、代码开发、数据分析、机器翻译、智能助手等领域,覆盖互联网、教育、金融、制造业等多个行业。
===== 查看短期记忆 =====
{'chat_history': 'Human: 什么是大语言模型?\nAI: 大语言模型是基于大尺度语料训练、具备强大自然语言理解与生成能力的人工智能模型,能完成文本创作、问答、翻译等多种自然语言处理任务。\nHuman: 它的核心优势是什么?\nAI: 大语言模型的核心优势包括:1. 强大的上下文理解与语义分析能力;2. 灵活的自然语言生成能力,可输出流畅、贴合语境的文本;3. 泛化能力强,能处理未见过的新问题;4. 多任务适配,无需单独训练即可完成多种NLP任务。\nHuman: 这些优势能用到哪些领域?\nAI: 这些优势可应用在智能客服、内容创作、教育辅导、代码开发、数据分析、机器翻译、智能助手等领域,覆盖互联网、教育、金融、制造业等多个行业。'}Verbose日志(关键:验证历史对话注入)
verbose=True后,可看到模型的实际输入提示包含了历史对话,这是记忆生效的核心:> Entering new ConversationChain chain...
Prompt after formatting:
你是一个专业的问答助手,善于结合历史对话内容回答当前问题。
历史对话:Human: 什么是大语言模型?
AI: 大语言模型是基于大尺度语料训练、具备强大自然语言理解与生成能力的人工智能模型,能完成文本创作、问答、翻译等多种自然语言处理任务。
当前问题:它的核心优势是什么?
请简洁、准确地回答当前问题,无需额外赘述。
> Finished chain.四、关键细节:避免记忆失效的核心要点
ConversationBufferMemory使用简单,但容易因参数不匹配、提示模板错误导致记忆失效,以下是必须遵守的3条铁律:4.1 提示模板必须包含
memory_key指定的变量memory_key="chat_history",则提示模板中必须有{chat_history},且输入变量列表要包含该变量:# 正确:input_variables包含chat_history和input
prompt = PromptTemplate(
input_variables=["chat_history", "input"],
template="历史对话:{chat_history} 当前问题:{input}"
)
# 错误:缺少chat_history,记忆无法注入
prompt = PromptTemplate(
input_variables=["input"],
template="当前问题:{input}"
)4.2
invoke入参必须是字典,且键为input_keyinput_key="input",因此调用时必须传{"input": "你的问题"},而非直接传字符串:# 正确
conversation_chain.invoke({"input": "什么是大语言模型?"})
# 错误:入参不是字典,记忆无法关联当前问题
conversation_chain.invoke("什么是大语言模型?")4.3 避免手动修改对话历史(除非特殊需求)
ConversationBufferMemory会自动追加每次的input和output到记忆中,无需手动修改:# 自动追加:无需干预
conversation_chain.invoke({"input": "问题1"}) # 记忆中添加问题1+回答1
conversation_chain.invoke({"input": "问题2"}) # 记忆中追加问题2+回答2
# 手动修改(特殊需求时使用)
memory.save_context(
inputs={"input": "手动添加的问题"},
outputs={"output": "手动添加的回答"}
)五、进阶优化:适配更复杂的问答场景
5.1 优化1:带知识库的问答Agent+短期记忆
ConversationChain替换为RetrievalQA,并搭配ConversationBufferMemory即可实现「知识库+多轮记忆」的问答能力:# 新增:导入知识库相关模块
from langchain_community.document_loaders import TextLoader
from langchain_community.vectorstores import FAISS
from langchain_text_splitters import CharacterTextSplitter
from langchain_openai import OpenAIEmbeddings
from langchain.chains import RetrievalQA
# 1. 加载知识库(以文本文件为例,可替换为PDF/Word加载器)
loader = TextLoader("your_knowledge_base.txt") # 你的知识库文件
documents = loader.load()
# 分割文本为小片段
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=100)
texts = text_splitter.split_documents(documents)
# 构建向量库
embeddings = OpenAIEmbeddings()
db = FAISS.from_documents(texts, embeddings)
retriever = db.as_retriever(search_kwargs={"k": 3}) # 每次检索3个相关片段
# 2. 初始化记忆(与基础版一致)
memory = ConversationBufferMemory(
memory_key="chat_history",
return_messages=False,
input_key="question" # 注意:RetrievalQA的默认输入键是question,需修改
)
# 3. 构建带记忆的知识库问答链
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff", # 适合小片段文本,简单高效
retriever=retriever,
memory=memory,
chain_type_kwargs={
"prompt": PromptTemplate(
input_variables=["chat_history", "context", "question"],
template="""结合历史对话和知识库内容回答当前问题,若知识库无相关内容,仅结合历史对话回答。
历史对话:{chat_history}
知识库内容:{context}
当前问题:{question}
回答要求:简洁、准确,基于知识库内容,不要编造。"""
)
},
verbose=True
)
# 4. 测试:结合知识库+历史对话的多轮问答
qa_chain.invoke({"question": "知识库中提到的大语言模型有哪些应用?"})
qa_chain.invoke({"question": "这些应用中,哪个在教育领域的落地效果最好?"}) # 结合历史5.2 优化2:限制记忆长度(避免历史对话过长)
ConversationBufferMemory会无限制追加对话历史,当对话轮数过多时,会导致提示词过长、推理成本增加、模型注意力分散。ConversationBufferWindowMemory(窗口记忆),仅保留最近N轮对话,本质是ConversationBufferMemory的进阶版,参数完全兼容:from langchain.memory import ConversationBufferWindowMemory
# 仅保留最近2轮对话,超出的自动丢弃
memory = ConversationBufferWindowMemory(
memory_key="chat_history",
k=2, # 核心参数:保留最近k轮对话
return_messages=False
)
# 用法与ConversationBufferMemory完全一致,无需修改其他代码
conversation_chain = ConversationChain(llm=llm, memory=memory, prompt=prompt)5.3 优化3:记忆格式为消息对象(适合复杂提示)
return_messages=True,记忆会返回HumanMessage/AIMessage对象,而非拼接字符串,便于灵活格式化:# 初始化记忆:返回消息对象
memory = ConversationBufferMemory(
memory_key="chat_history",
return_messages=True, # 核心:返回消息对象
input_key="input"
)
# 自定义提示模板:遍历消息对象格式化
from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder
# 使用MessagesPlaceholder接收消息对象,无需手动拼接
prompt = ChatPromptTemplate.from_messages([
("system", "你是专业的问答助手,结合历史对话回答问题。"),
MessagesPlaceholder(variable_name="chat_history"), # 匹配memory_key
("human", "{input}") # 匹配input_key
])
# 构建链(用法不变)
conversation_chain = ConversationChain(llm=llm, memory=memory, prompt=prompt)六、常用操作:记忆的增删改查
ConversationBufferMemory提供了便捷的方法操作记忆,满足个性化需求:# 1. 查看记忆内容
memory.load_memory_variables({}) # 返回字典,键为memory_key
# 2. 清空记忆(会话结束/切换用户时使用)
memory.clear()
# 3. 手动添加记忆
memory.save_context(
inputs={"input": "手动添加的问题"},
outputs={"output": "手动添加的回答"}
)
# 4. 手动删除记忆(需先获取记忆,再修改,最后重新保存)
# 步骤1:获取记忆内容
chat_history = memory.load_memory_variables({})["chat_history"]
# 步骤2:修改/删除内容(如删除最后一行)
chat_history = "\n".join(chat_history.split("\n")[:-2])
# 步骤3:重新保存
memory.chat_memory.add_user_message("") # 清空原有记忆
memory.chat_memory.add_ai_message("")
memory.save_context(inputs={"input": ""}, outputs={"output": chat_history})七、总结
7.1 核心流程回顾
ConversationBufferMemory的核心步骤仅5步:ConversationBufferMemory,指定memory_key;invoke实现多轮问答。7.2 记忆组件选型建议
记忆组件 核心特点 适用场景 ConversationBufferMemory无限制保存所有对话历史,轻量易用 短对话、测试场景 ConversationBufferWindowMemory保留最近N轮对话,限制长度 常规多轮问答场景(推荐) ConversationSummaryMemory对长对话历史做摘要压缩,节省令牌 超长对话、高成本模型场景 VectorStoreRetrieverMemory将对话历史存入向量库,按需检索相关历史 需精准匹配历史对话的复杂场景 7.3 性能优化建议
ConversationBufferWindowMemory,并设置合理的k值(如3-5轮);max_tokens,避免无意义的长回答;verbose=False(生产环境),减少日志开销;八、常见问题排查
问题1:记忆失效,模型不结合历史对话回答
memory_key变量,或input_variables未包含该变量;{chat_history}(或自定义的memory_key),且input_variables列表包含该变量。问题2:调用时提示「key error: input」
invoke入参不是字典,或键与input_key不匹配;{"input": "你的问题"}(默认input_key="input"),若修改了input_key,则传对应键。问题3:知识库问答Agent记忆失效
RetrievalQA的默认输入键是question,而非input,记忆的input_key不匹配;input_key="question"。问题4:对话历史过长,模型推理变慢
ConversationBufferMemory无限制追加历史;ConversationBufferWindowMemory,设置k值限制轮数。

















