LangChain Memory
本文介绍了如何使用LangChain的ConversationBufferMemory API实现对话记忆功能。通过初始化memory对象存储历史对话,使用save_context()方法保存对话内容,并结合ChatPromptTemplate和MessagesPlaceholder将历史对话注入到提示模板中。演示代码展示了如何通过加载历史对话实现连续问答,使AI能记住用户姓名和职业等上下文信息,
环境
- Anaconda:v25.5.1
- Python:v3.13.9
- LangChain:v1.0.3
- langchain-openai:v1.0.2
手动构造带有”记忆“的链
之前的文章中我们跟模型的交互都是一次性的,无论是做文本翻译还是小红书文案生成,我们都是把完整指令给模型,然后模型生成相应结果,这个过程中并没有实现像 ChatGPT 那样带有上下文的对话,因为模型本身是不直接具备上下文记忆的,比如我们先问 AI ”周杰伦是谁?“,然后在第二个请求中问”他是哪国人?“,这个时候 AI 是不知道第二个问题中的”他“到底是谁,所以输出的结果自然就不是准确的,如果想解决这个问题的话也不难,我们通过之前介绍的小样本提示的思路,可以将第一次的对话在第二次请求时塞入消息列表,让模型具有之前的对话历史,第三次请求时又将前两次的对话一并塞入消息列表……如此反复,模型就具备历史对话的记忆功能。
LangChain 框架中提供了一个 ConversationBufferMemory API 专门用来做此事,它可以储存历史对话,我们将每一轮的对话都可以存储到这里面,下面演示一下这个 API 的使用。
新建一个 py 文件,然后将下面代码拷贝:
from langchain_classic.memory import ConversationBufferMemory
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_openai import ChatOpenAI
memory = ConversationBufferMemory(return_messages=True)
print(memory.load_memory_variables({}))
memory.save_context({ "input": "我的名字是张三"}, { "output": "你好,张三"})
print(memory.load_memory_variables({}))
memory.save_context({ "input": "我是一名程序员"}, { "output": "好的,我记住了"})
print(memory.load_memory_variables({}))
prompt = ChatPromptTemplate.from_messages([
("system", "你是一个乐于助人的助手"),
MessagesPlaceholder(variable_name="history"),
("human", "{user_input}")
])
model = ChatOpenAI(model="gpt-4o-mini")
chain = prompt | model
user_input = "你知道我的名字吗?"
history = memory.load_memory_variables({})["history"]
result = chain.invoke({
"user_input": user_input,
"history": history
})
print(result)
memory.save_context({"input": user_input}, {"output": result.content})
print(memory.load_memory_variables({}))
user_input = "根据对话历史告诉我,我上一个问题问你的是什么?请重复一遍"
history = memory.load_memory_variables({})["history"]
result = chain.invoke({
"user_input": user_input,
'history': history
})
print(result)
上述代码中我们使用 ConversationBufferMemory API 初始化了一个用来存储历史对话的 memory 变量,然后模拟了两组对话,并通过 memory 的 save_context() 方法将每一组对话存入 memory 中,如果想看 memory 中存放的对话,可以使用 memory 的 load_memory_variables() 方法查看。有了两组模拟对话并存入 memory 之后,我们定义了一个提示模板,这个提示模板中我们通过 MessagesPlaceholder API 将 memory 中存放的历史对话加载了进来,加载的时候历史对话列表通过 history 变量接收,然后通过链调用的时候将我们的问题及带有历史对话列表的 history 值直接传入,最后可以得到如下的输出:
/Users/xxxxxx/Documents/some-test/python-project-test/course_8.py:5: LangChainDeprecationWarning: Please see the migration guide at: https://python.langchain.com/docs/versions/migrating_memory/
memory = ConversationBufferMemory(return_messages=True)
{'history': []}
{'history': [HumanMessage(content='我的名字是张三', additional_kwargs={}, response_metadata={}), AIMessage(content='你好,张三', additional_kwargs={}, response_metadata={})]}
{'history': [HumanMessage(content='我的名字是张三', additional_kwargs={}, response_metadata={}), AIMessage(content='你好,张三', additional_kwargs={}, response_metadata={}), HumanMessage(content='我是一名程序员', additional_kwargs={}, response_metadata={}), AIMessage(content='好的,我记住了', additional_kwargs={}, response_metadata={})]}
content='是的,你的名字是张三。' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 9, 'prompt_tokens': 60, 'total_tokens': 69, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_provider': 'openai', 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_560af6e559', 'id': 'chatcmpl-Cc8epmmF2zgaTcyIquYNRmZ3Xwrl4', 'service_tier': 'default', 'finish_reason': 'stop', 'logprobs': None} id='lc_run--985d03b9-eb65-4800-a6e6-d130aedf7326-0' usage_metadata={'input_tokens': 60, 'output_tokens': 9, 'total_tokens': 69, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}
{'history': [HumanMessage(content='我的名字是张三', additional_kwargs={}, response_metadata={}), AIMessage(content='你好,张三', additional_kwargs={}, response_metadata={}), HumanMessage(content='我是一名程序员', additional_kwargs={}, response_metadata={}), AIMessage(content='好的,我记住了', additional_kwargs={}, response_metadata={}), HumanMessage(content='你知道我的名字吗?', additional_kwargs={}, response_metadata={}), AIMessage(content='是的,你的名字是张三。', additional_kwargs={}, response_metadata={})]}
content='你上一个问题是:“你知道我的名字吗?”' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 12, 'prompt_tokens': 95, 'total_tokens': 107, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_provider': 'openai', 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_560af6e559', 'id': 'chatcmpl-Cc8erlKGxQCVMNLUHDJL6m19cCDFm', 'service_tier': 'default', 'finish_reason': 'stop', 'logprobs': None} id='lc_run--e6062246-7f82-4a77-b9c7-a806b2c783c5-0' usage_metadata={'input_tokens': 95, 'output_tokens': 12, 'total_tokens': 107, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}
上述输出中细心的同学会发现一个 API 弃用的告警 LangChainDeprecationWarning,这是因为在 LangChain v1.0.3 中 ConversationBufferMemory 这个 API 是已经被官方标记为弃用的,但我们暂时还是可以通过 langchain_classic.memory 这个模块导入使用,后续会专门介绍如何使用新的 API 来实现模型记忆功能。
带记忆的对话链(Conversation Chain)
上述代码中我们通过手动存储和载入历史对话的方式让模型有了记忆功能,但是很繁琐,其实在 LangChain 框架中还提供了一个专门用来做记忆链的 API —— ConversationChain,让我们来看一下示例代码:
from langchain_classic.memory import ConversationBufferMemory
from langchain_classic.chains import ConversationChain
from langchain_openai import ChatOpenAI
model = ChatOpenAI(model="gpt-4o-mini")
memory = ConversationBufferMemory(return_messages=True)
chain = ConversationChain(llm=model, memory=memory)
result1 = chain.invoke({ "input": "我的名字是张三"})
print(result1)
result2 =chain.invoke({"input": "我是谁"})
print(result2)
上述代码中我们分别初始化了一个模型、一个 memory 和一个记忆链,然后直接调用记忆链的 invoke() 方法传入参数,可以得到下面的输出:
/Users/xxxx/Documents/some-test/python-project-test/course_8-1.py:9: LangChainDeprecationWarning: Please see the migration guide at: https://python.langchain.com/docs/versions/migrating_memory/
memory = ConversationBufferMemory(return_messages=True)
/Users/xxxx/Documents/some-test/python-project-test/course_8-1.py:11: LangChainDeprecationWarning: The class `ConversationChain` was deprecated in LangChain 0.2.7 and will be removed in 1.0. Use `langchain_core.runnables.history.RunnableWithMessageHistory` instead.
chain = ConversationChain(llm=model, memory=memory)
{'input': '我的名字是张三', 'history': [HumanMessage(content='我的名字是张三', additional_kwargs={}, response_metadata={}), AIMessage(content='很高兴认识你,张三!这是一个很常见的名字,尤其在中国。请问你有什么兴趣爱好或者想聊的话题吗?', additional_kwargs={}, response_metadata={})], 'response': '很高兴认识你,张三!这是一个很常见的名字,尤其在中国。请问你有什么兴趣爱好或者想聊的话题吗?'}
{'input': '我是谁', 'history': [HumanMessage(content='我的名字是张三', additional_kwargs={}, response_metadata={}), AIMessage(content='很高兴认识你,张三!这是一个很常见的名字,尤其在中国。请问你有什么兴趣爱好或者想聊的话题吗?', additional_kwargs={}, response_metadata={}), HumanMessage(content='我是谁', additional_kwargs={}, response_metadata={}), AIMessage(content='你是张三!很高兴再次和你交流。你今天过得怎么样?有没有想聊的特别话题或者兴趣呢?', additional_kwargs={}, response_metadata={})], 'response': '你是张三!很高兴再次和你交流。你今天过得怎么样?有没有想聊的特别话题或者兴趣呢?'}
可以看到 result2 的输出中是带有上一个对话的历史消息的,而且它也准确知道上一个问题中我们指定的名字。通过记忆链的使用,我们就可以大大简化代码,省去了一系列手动构造历史对话列表的操作。
同样的,上述代码中也有相关 API 弃用的告警,我们后续专门处理、升级。
“记忆”的不同类型
LangChain 提供了四种 Memory 模块,分别适用于不同的对话场景和需求:
ConversationBufferMemory
特点:完整保存对话历史,适合需要完整上下文的场景。
应用场景:
- 客服系统:需要记录所有对话内容,便于后续查询。
- 教学对话:需要保留完整的教学记录。
ConversationBufferWindowMemory
特点:仅保存最近若干条对话,适合对历史上下文要求较低但需要近期对话信息的场景。
应用场景:
- 简单问答系统:关注用户近期输入即可。
- 快速对话场景:如天气查询或简单助手。
ConversationTokenBufferMemory
特点:基于 Token 限制存储对话历史,保证存储内容不会超过指定的 Token 数。
应用场景:
- 长文本对话:在上下文较长时,限制存储的内容大小以节省计算资源。
- 复杂对话:对上下文有一定要求,但需要控制资源开销。
ConversationSummaryMemory
特点:通过总结方式存储对话历史,浓缩对话内容,保留核心信息。
应用场景:
- 长期对话:如客户关系管理,需要记录关键事件和要点。
- 任务型对话:如会议助手,总结会议要点。
总结
本文我们通过手动构造和记忆链的方式让模型拥有了记忆,同时还介绍了 LangChain 提供的不同记忆类型,接下来我们通过一个示例来强化我们对于记忆链的理解和使用。
更多推荐



所有评论(0)