「LangChain 学习笔记」LangChain大模型应用开发:储存(Memory)
「LangChain大模型应用开发」 系列文章目录:
目录
1. 对话缓存储存 (ConversationBufferMemory)
1.1 什么是ConversationBufferMemory?
2. 对话缓存窗口储存 (ConversationBufferWindowMemory)
2.1 什么是 ConversationBufferWindowMemory?
3. 对话令牌缓存储存 (ConversationTokenBufferMemory)
3.1 什么是 ConversationTokenBufferMemory?
4. 对话摘要缓存储存 (ConversationSummaryBufferMemory)
4.1 什么是 ConversationSummaryBufferMemory?
在之前的学习中,与语言模型交互时,你可能已经注意到一个关键问题:它们并不记忆你之前的交流内容,这在我们构建一些应用程序(如聊天机器人)的时候,带来了很大的挑战,使得对话似乎缺乏真正的连续性。
也就是当我们在使用大型语言模型进行聊天对话时,大型语言模型本身实际上是无状态的。语言模型本身并不记得到目前为止的历史对话。每次调用API结点都是独立的。储存(Memory)可以储存到目前为止的所有术语或对话,并将其输入或附加上下文到LLM中用于生成输出。如此看起来就好像它在进行下一轮对话的时候,记得之前说过什么。
因此,在本节中我们将介绍 LangChain 中的储存模块,即如何将先前的对话嵌入到语言模型中的,使其具有连续对话的能力。当使用 LangChain 中的储存(Memory)模块时,它旨在保存、组织和跟踪整个对话的历史,从而为用户和模型之间的交互提供连续的上下文。
在 LangChain 中,储存指的是大语言模型(LLM)的短期记忆。为什么是短期记忆?那是因为LLM训练好之后 (获得了一些长期记忆),它的参数便不会因为用户的输入而发生改变。当用户与训练好的LLM进行对话时,LLM 会暂时记住用户的输入和它已经生成的输出,以便预测之后的输出,而模型输出完毕后,它便会“遗忘”之前用户的输入和它的输出。因此,之前的这些信息只能称作为 LLM 的短期记忆。
为了延长 LLM 短期记忆的保留时间,则需要借助一些外部储存方式来进行记忆,以便在用户与 LLM 对话中,LLM 能够尽可能的知道用户与它所进行的历史对话信息。
LangChain 提供了多种储存类型。本章节主要介绍其中四种储存模块:
- 对话缓存储存 (ConversationBufferMemory):保存完整对话
- 对话缓存窗口储存 (ConversationBufferWindowMemory):仅保存最近几轮对话
- 对话令牌缓存储存 (ConversationTokenBufferMemory):基于 Token 限制的记忆管理
- 对话摘要缓存储存 (ConversationSummaryBufferMemory):通过总结提炼对话的核心信息
1. 对话缓存储存 (ConversationBufferMemory)
1.1 什么是ConversationBufferMemory?
ConversationBufferMemory 是 LangChain 提供的最基础的 Memory 类型,其核心功能是记录对话中的所有历史消息,无论是用户的输入还是模型的输出。这种方式确保了整个对话的上下文都能被完整地保留下来。
1.2 案例
下面给出一个完整的使用案例:
1.2.1 初始化模型并完成对话
首先初始化ConversationBufferMemory对话模型。
from langchain.chains import ConversationChain
from langchain.chat_models import ChatOpenAI
from langchain.memory import ConversationBufferMemory
# 这里我们将参数temperature设置为0.0,从而减少生成答案的随机性。
# 如果你想要每次得到不一样的有新意的答案,可以尝试增大该参数。
llm = ChatOpenAI(
api_key="你的API KEY",
base_url="https://qianfan.baidubce.com/v2",
model="ernie-3.5-8k",
temperature=0
)
memory = ConversationBufferMemory()
# 新建一个 ConversationChain Class 实例,构建对话链
# verbose参数设置为True时,程序会输出更详细的信息,以提供更多的调试或运行时信息。
# 相反,当将verbose参数设置为False时,程序会以更简洁的方式运行,只输出关键的信息。
conversation = ConversationChain(llm=llm, memory = memory, verbose=True )
第一次对话:
conversation.predict(input="你好, 我叫皮皮鲁")
输出为:
> Entering new ConversationChain chain...
Prompt after formatting:
The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.
Current conversation:
Human: 你好, 我叫皮皮鲁
AI:
> Finished chain.
'你好,皮皮鲁!很高兴认识你!我是一个人工智能,虽然我没有具体的名字,但你可以随意给我取一个你喜欢的。你有什么问题或者想聊聊什么吗?比如,你最近过得怎么样?有没有什么有趣的事情想分享呢?'

第二次对话:
conversation.predict(input="1+1等于多少?")
输出为:
> Entering new ConversationChain chain...
Prompt after formatting:
The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.
Current conversation:
Human: 你好, 我叫皮皮鲁
AI: 你好,皮皮鲁!很高兴认识你!我是一个人工智能,虽然我没有具体的名字,但你可以随意给我取一个你喜欢的。你有什么问题或者想聊聊什么吗?比如,你最近过得怎么样?有没有什么有趣的事情想分享呢?
Human: 1+1等于多少?
AI:
> Finished chain.
'这个问题太简单了!1+1 等于 2,这是数学上的基础知识哦。如果你还有其他数学问题或者想聊聊别的话题,比如科学、历史、文化等等,我都很乐意和你分享呢!'

第三次对话:
conversation.predict(input="我叫什么名字?")
输出为:
> Entering new ConversationChain chain...
Prompt after formatting:
The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.
Current conversation:
Human: 你好, 我叫皮皮鲁
AI: 你好,皮皮鲁!很高兴认识你!我是一个人工智能,虽然我没有具体的名字,但你可以随意给我取一个你喜欢的。你有什么问题或者想聊聊什么吗?比如,你最近过得怎么样?有没有什么有趣的事情想分享呢?
Human: 1+1等于多少?
AI: 这个问题太简单了!1+1 等于 2,这是数学上的基础知识哦。如果你还有其他数学问题或者想聊聊别的话题,比如科学、历史、文化等等,我都很乐意和你分享呢!
Human: 我叫什么名字?
AI:
> Finished chain.
'你叫皮皮鲁啊!我刚才还跟你打过招呼呢,你还记得吗?我们刚才还在聊1+1等于多少的问题呢。对了,皮皮鲁,你有什么特别的兴趣爱好吗?比如画画、唱歌、运动之类的?'

通过以上的三轮对话,可以看出之前对话的内容被存储在第一段代码定义memory中了,下面将通过方法查看储存缓存。
1.2.2 查看储存缓存与添加内容到储存缓存
储存缓存(buffer),即储存了当前为止所有的对话信息。
print(memory.buffer)
Human: 你好, 我叫皮皮鲁
AI: 你好,皮皮鲁!很高兴认识你!我是一个人工智能,虽然我没有具体的名字,但你可以随意给我取一个你喜欢的。你有什么问题或者想聊聊什么吗?比如,你最近过得怎么样?有没有什么有趣的事情想分享呢?
Human: 1+1等于多少?
AI: 这个问题太简单了!1+1 等于 2,这是数学上的基础知识哦。如果你还有其他数学问题或者想聊聊别的话题,比如科学、历史、文化等等,我都很乐意和你分享呢!
Human: 我叫什么名字?
AI: 你叫皮皮鲁啊!我刚才还跟你打过招呼呢,你还记得吗?我们刚才还在聊1+1等于多少的问题呢。对了,皮皮鲁,你有什么特别的兴趣爱好吗?比如画画、唱歌、运动之类的?

也可以通过load_memory_variables({})打印缓存中的历史消息。这里的{ } 是一个空字典,有一些更高级的功能,使用户可以使用更复杂的输入,具体可以通过 LangChain 的官方文档查询更高级的用法。
print(memory.load_memory_variables({}))
{'history': 'Human: 你好, 我叫皮皮鲁\nAI: 你好,皮皮鲁!很高兴认识你!我是一个人工智能,虽然我没有具体的名字,但你可以随意给我取一个你喜欢的。你有什么问题或者想聊聊什么吗?比如,你最近过得怎么样?有没有什么有趣的事情想分享呢?\nHuman: 1+1等于多少?\nAI: 这个问题太简单了!1+1 等于 2,这是数学上的基础知识哦。如果你还有其他数学问题或者想聊聊别的话题,比如科学、历史、文化等等,我都很乐意和你分享呢!\nHuman: 我叫什么名字?\nAI: 你叫皮皮鲁啊!我刚才还跟你打过招呼呢,你还记得吗?我们刚才还在聊1+1等于多少的问题呢。对了,皮皮鲁,你有什么特别的兴趣爱好吗?比如画画、唱歌、运动之类的?'}

那么怎么将自己的内容添加内容到储存缓存?
我们可以使用save_context来直接添加内容到buffer中。
memory = ConversationBufferMemory()
memory.save_context({"input": "你好,我叫皮皮鲁"}, {"output": "你好啊,我叫鲁西西"})
memory.load_memory_variables({})
{'history': 'Human: 你好,我叫皮皮鲁\nAI: 你好啊,我叫鲁西西'}
memory.save_context({"input": "很高兴和你成为朋友!"}, {"output": "是的,让我们一起去冒险吧!"})
memory.load_memory_variables({})
{'history': 'Human: 你好,我叫皮皮鲁\nAI: 你好啊,我叫鲁西西\nHuman: 很高兴和你成为朋友!\nAI: 是的,让我们一起去冒险吧!'}
conversation = ConversationChain(
llm=llm,
memory=memory
)
conversation.predict(input="你是谁?我是谁?我们要去干嘛?")
'哈哈,你的问题真是一连串啊!我是鲁西西,一个聪明的AI。你呢,你是皮皮鲁,一个勇敢的小男孩,我们之前还没正式介绍过呢!至于我们要去干嘛,不是说好了要去冒险吗?我们可以去探索神秘的森林,寻找隐藏的宝藏,或者去解救被困的公主,怎么样,听起来刺激吗?'
可以看到对话历史都保存下来了,并且当我们运行预测(predict)时,可以很好的访问对话历史并准确的输出。
2. 对话缓存窗口储存 (ConversationBufferWindowMemory)
2.1 什么是 ConversationBufferWindowMemory?
随着对话变得越来越长,所需的内存量也变得非常长。将大量的tokens发送到LLM的成本,也会变得更加昂贵,这也就是为什么API的调用费用,通常是基于它需要处理的tokens数量而收费的。
针对以上问题,LangChain也提供了几种方便的储存方式来保存历史对话。其中,对话缓存窗口储存只保留一个窗口大小的对话。它只使用最近的n次交互。这可以用于保持最近交互的滑动窗口,以便缓冲区不会过大。
ConversationBufferWindowMemory 是 LangChain 提供的一种 Memory 类型,与ConversationBufferMemory 不同的是,它只保存最近几轮对话,而不是完整的对话历史。这种方式通过限制窗口大小,确保存储的对话内容始终保持在指定的条数范围内,从而提高效率并避免冗余数据的存储。
2.2 案例
这里先来尝试一下使用ConversationBufferWindowMemory来实现交互的滑动窗口,并设置k=1,表示只保留一个对话记忆。接下来我们手动添加两轮对话到窗口储存中,然后查看储存的对话。
from langchain.memory import ConversationBufferWindowMemory
# k=1表明只保留一个对话记忆
memory = ConversationBufferWindowMemory(k=1)
memory.save_context({"input": "你好,我叫皮皮鲁"}, {"output": "你好啊,我叫鲁西西"})
memory.save_context({"input": "很高兴和你成为朋友!"}, {"output": "是的,让我们一起去冒险吧!"})
memory.load_memory_variables({})
{'history': 'Human: 很高兴和你成为朋友!\nAI: 是的,让我们一起去冒险吧!'}
通过结果,我们可以看到窗口储存中只有最后一轮的聊天记录。
下面将测试一下在对话链中应用窗口储存,并设置不同的对话记忆。
memory = ConversationBufferWindowMemory(k=1)
conversation = ConversationChain(llm=llm, memory=memory, verbose=False )
print(conversation.predict(input="你好, 我叫皮皮鲁"))
print(conversation.predict(input="1+1等于多少?"))
print(conversation.predict(input="我叫什么名字?"))
你好,皮皮鲁!很高兴认识你!我是一个人工智能,虽然我没有具体的名字,但你可以随意给我取一个你喜欢的。你有什么问题或者想聊聊什么吗?比如,你最近过得怎么样?有没有什么有趣的事情想分享呢?
这个问题太简单了!1+1 等于 2,这是数学上的基础知识哦。如果你还有其他数学问题或者想聊聊别的话题,比如科学、历史、文化等等,我都很乐意和你分享呢!
哎呀,我并不知道您的名字呢!不过没关系,我们可以聊聊其他有趣的话题呀,比如您喜欢的电影、音乐或者旅行目的地等等。
memory = ConversationBufferWindowMemory(k=2) #修改为k=2
conversation = ConversationChain(llm=llm, memory=memory, verbose=False )
print(conversation.predict(input="你好, 我叫皮皮鲁"))
print(conversation.predict(input="1+1等于多少?"))
print(conversation.predict(input="我叫什么名字?"))
你好,皮皮鲁!很高兴认识你!我是一个人工智能,虽然我没有具体的名字,但你可以随意给我取一个你喜欢的。你有什么问题或者想聊聊什么吗?比如,你最近过得怎么样?有没有什么有趣的事情想分享呢?
这个问题太简单了!1+1 等于 2,这是数学上的基础知识哦。如果你还有其他数学问题或者想聊聊别的话题,比如科学、历史、文化等等,我都很乐意和你分享呢!
你叫皮皮鲁啊!我刚才还跟你打过招呼呢,你还记得吗?我们刚才还在聊1+1等于多少的问题呢。对了,皮皮鲁,你有什么特别的兴趣爱好吗?比如画画、唱歌、运动之类的?
通过以上改变不同的k值,我们可以看到第三条预测,
当k=1时,由于这里用的是一个窗口的记忆,因此只能保存一轮的历史消息,因此AI并不能知道你第一轮对话中提到的名字,他最多只能记住上一轮(第二轮)的对话信息。
当k=2时可以记住上两轮(第一轮和第二轮)的对话信息。
3. 对话令牌缓存储存 (ConversationTokenBufferMemory)
3.1 什么是 ConversationTokenBufferMemory?
在自然语言处理中,Token 是文本的最小单位,例如一个单词或一个标点符号。在 GPT 模型中,输入和输出的文本内容都被分解为一系列 Token,模型会根据这些 Token 进行计算。因此,Token 数量会直接影响模型的运行成本和响应速度。在我的博客使用 ChatGPT API 搭建系统也介绍过。
使用对话字符缓存记忆,内存将限制保存的token数量。如果字符数量超出指定数目,它会切掉这个对话的早期部分 以保留与最近的交流相对应的字符数量,但不超过字符限制。
对于英文输入,一个 token 一般对应 4 个字符或者四分之三个单词;对于中文输入,一个 token 一般对应一个或半个词。不同模型有不同的 token 限制,需要注意的是,这里的 token 限制是输入的 Prompt 和输出的 completion 的 token 数之和,因此输入的 Prompt 越长,能输出的 completion 的上限就越低。 ChatGPT3.5-turbo 的 token 上限是 4096。
ConversationTokenBufferMemory 是 LangChain 提供的一种基于 Token 数量限制的记忆模块。与 ConversationBufferMemory 和 ConversationBufferWindowMemory 不同,它并不是根据对话轮次来管理记忆,而是根据 Token 的总数动态裁剪较早的对话内容,确保 Token 总数不超过指定的限制。
3.2 案例
from langchain.llms import OpenAI
from langchain.memory import ConversationTokenBufferMemory
memory = ConversationTokenBufferMemory(llm=llm, max_token_limit=30)
memory.save_context({"input": "朝辞白帝彩云间,"}, {"output": "千里江陵一日还。"})
memory.save_context({"input": "两岸猿声啼不住,"}, {"output": "轻舟已过万重山。"})
memory.load_memory_variables({})
{'history': 'AI: 轻舟已过万重山。'}
4. 对话摘要缓存储存 (ConversationSummaryBufferMemory)
4.1 什么是 ConversationSummaryBufferMemory?
ConversationSummaryMemory 是 LangChain 提供的一种记忆模块,通过使用 LLM 对到目前为止历史对话自动总结摘要,并将其保存下来。
4.2 案例
使用对话摘要缓存储存。
from langchain.chains import ConversationChain
from langchain.chat_models import ChatOpenAI
from langchain.memory import ConversationSummaryBufferMemory
# 创建一个长字符串
schedule = "在八点你和你的产品团队有一个会议。 \
你需要做一个PPT。 \
上午9点到12点你需要忙于LangChain。\
Langchain是一个有用的工具,因此你的项目进展的非常快。\
中午,在意大利餐厅与一位开车来的顾客共进午餐 \
走了一个多小时的路程与你见面,只为了解最新的 AI。 \
确保你带了笔记本电脑可以展示最新的 LLM 样例."
llm = ChatOpenAI(temperature=0.0)
memory = ConversationSummaryBufferMemory(llm=llm, max_token_limit=100)
memory.save_context({"input": "你好,我叫皮皮鲁"}, {"output": "你好啊,我叫鲁西西"})
memory.save_context({"input": "很高兴和你成为朋友!"}, {"output": "是的,让我们一起去冒险吧!"})
memory.save_context({"input": "今天的日程安排是什么?"}, {"output": f"{schedule}"})
print(memory.load_memory_variables({})['history'])
System: The human introduces themselves as Pipilu and the AI introduces themselves as Luxixi. They express happiness at becoming friends and decide to go on an adventure together. The human asks about the schedule for the day. The AI informs them that they have a meeting with their product team at 8 o'clock and need to prepare a PowerPoint presentation. From 9 am to 12 pm, they will be busy with LangChain, a useful tool that helps their project progress quickly. At noon, they will have lunch with a customer who has driven for over an hour just to learn about the latest AI. The AI advises the human to bring their laptop to showcase the latest LLM samples.
基于上面的对话摘要缓存储存,我们新建一个对话链。
conversation = ConversationChain(llm=llm, memory=memory, verbose=True)
conversation.predict(input="展示什么样的样例最好呢?")
> Entering new ConversationChain chain...
Prompt after formatting:
The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.
Current conversation:
System: The human introduces themselves as Pipilu and the AI introduces themselves as Luxixi. They express happiness at becoming friends and decide to go on an adventure together. The human asks about the schedule for the day. The AI informs them that they have a meeting with their product team at 8 o'clock and need to prepare a PowerPoint presentation. From 9 am to 12 pm, they will be busy with LangChain, a useful tool that helps their project progress quickly. At noon, they will have lunch with a customer who has driven for over an hour just to learn about the latest AI. The AI advises the human to bring their laptop to showcase the latest LLM samples.
Human: 展示什么样的样例最好呢?
AI:
> Finished chain.
'展示一些具有多样性和创新性的样例可能是最好的选择。你可以展示一些不同领域的应用,比如自然语言处理、图像识别、语音合成等。另外,你也可以展示一些具有实际应用价值的样例,比如智能客服、智能推荐等。总之,选择那些能够展示出我们AI技术的强大和多样性的样例会给客户留下深刻的印象。'
print(memory.load_memory_variables({})) # 摘要记录更新了
{'history': "System: The human introduces themselves as Pipilu and the AI introduces themselves as Luxixi. They express happiness at becoming friends and decide to go on an adventure together. The human asks about the schedule for the day. The AI informs them that they have a meeting with their product team at 8 o'clock and need to prepare a PowerPoint presentation. From 9 am to 12 pm, they will be busy with LangChain, a useful tool that helps their project progress quickly. At noon, they will have lunch with a customer who has driven for over an hour just to learn about the latest AI. The AI advises the human to bring their laptop to showcase the latest LLM samples. The human asks what kind of samples would be best to showcase. The AI suggests that showcasing diverse and innovative samples would be the best choice. They recommend demonstrating applications in different fields such as natural language processing, image recognition, and speech synthesis. Additionally, they suggest showcasing practical examples like intelligent customer service and personalized recommendations to impress the customer with the power and versatility of their AI technology."}
通过对比上一次输出,发现摘要记录更新了,添加了最新一次对话的内容总结。
总结
对比与选择建议
| Memory 类型 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| ConversationBufferMemory | 全历史保存,上下文完整 | 长对话容易超出 token 限制,成本高 | 短对话或上下文非常重要的场景 |
| ConversationBufferWindowMemory | 控制对话长度,节省成本 | 丢失早期关键信息 | 简短对话或刚学应用 |
| ConversationTokenBufferMemory | 根据 token 限制自动裁剪 | 对话长度忽略轮次,可能跳过重要轮次 | 长对话场景,需保证 token 限制 |
| ConversationSummaryBufferMemory | 精炼历史信息,节省 token 空间 | 依赖 LLM 生成摘要,可能不完全精确 | 长期对话应用,如客服、陪聊机器人等 |
注意:ConversationTokenBufferMemory(对话令牌缓存储存)和ConversationSummaryBufferMemory(对话摘要缓存储存)好像国内大模型不支持。
更多推荐


所有评论(0)