学习资料来源自:基于LangChain的大语言模型应用开发3——记忆_哔哩哔哩_bilibili
前言
LangChain是一个用于构建大语言模型应用的开源框架,在开发LLM复杂应用时,发现有一些公共可以抽象出来的内容,于是LangChain应运而生。
关于记忆
接下来就详细介绍下LangChain所包含的四种主要存储方式:
(1)ConversationBufferMemory
import os
from dotenv import load_dotenv, find_dotenv
_ = load_dotenv(find_dotenv())
from langchain.chat_models import ChatOpenAI
from langchain.chains import ConversationChain
from langcahin.memory import ConversationBufferMemory
llm = ChatOpenAI(temperature=0.0)
memory = ConversationBufferMemory()
conversation = ConversationChain(
llm = llm,
memory=memory,
verbose = False
)
conversation.predict(input="Hi, my name is Andrew")
conversation.predict(input="What is 1+1?")
conversation.predict(input="What is my name?")
如果想要了解LangChain更多的细节,可以将verbose变量改为true。看看实际是在做什么。
可以看到,LangChain内置的有提示词,会记录下所有的对话记录。
随着对话的延长,提示词长度会越来越长,如何控制呢?
如果这时候,我们想查看LangChain的记忆,有两种方式:
1. memory.buffer
2. memory.load_memory_variables({})
print(memory.buffer)
能够发现,它把所有的对话都存储起来了。也可以使用:
memory.load_memory_variables({})
可以在这个花括号里传值,修改选项,做高级定制。
除此之外,LangChain还提供了一个ConversationBufferMemory的方法临时存储对话记忆。要使用ConversationBufferMemory存储消息,可以往其中添加输入和输出。比如:
memory = ConversationBufferMemory()
memory.save_context({"input":"Hi"}, {"output":"What's up"})
print(memory.buffer)
如果想往存储中添加新的内容,按照此方法就好。这就是记忆存储的状态。
如果,你想向记忆存储中添加更多数据,可以:
memory.save_context(
{"input":"Not much, just hanging"},
{"output":"Cool"}
)
print(memory.load_memory_variables({}))
会发现,记忆中存储了更多的内容。
当使用大语言模型进行聊天对话时,大语言模型自身其实是无状态的。语言模型并不会记住和你对话时产生的历史消息。每次的请求交互、调用api都是独立的。聊天机器人看起来有记忆,实际上是因为借助了代码,提供历史消息作为和LLM对话的上下文。所以记忆存储可以明确地存储到目前为止的对话消息。在LLM生成输出时,可以基于之前所说过的会话内容,再生成新的回话,让你感觉它好像“记得”你说过的话。
Large Language Models are "stateless".
Each transaction is independent.
Chatbots appear to have memory by providing the full conversation as "context".
随着对话变得越来越长,记忆存储量也会变得非常非常大,向LLM发送大量的Token的成本也会逐渐增加,因为通常是根据需要处理的token数量收费。LangChain提供了集中便捷的记忆存储模式,来存储对话消息、累积对话内容。
LangChain provides several kinds of "memory" to store and accumulate the conversation.
(2)ConversationBufferWindowMemory 窗口记忆
保留窗口记忆,仅保留最后若干轮对话消息。如果我将k参数设置为1,表示我仅想记住最后一轮对话,也就是我发出的和聊天机器人的最后一句话。
from langchain.memory import ConversationBufferWindowMemory
memory = ConversationBufferWindowMemory(k=1)
memory.save_context({"input":"Hi"}, {"output":"What's up"})
memory.save_context({"input":"Not much, just hanging"}, {"output":"Cool"})
memory.load_memory_variables({})
在实际使用此功能时,将k设置为较大的数字。这样就可以防止记忆存储量随着对话而无限增长。
会发现,此时已经无法记住姓名。
(3)ConversationTokenBufferMemory
由于很多LLM定价是基于token的,所以token数量直接反映了LLM调用成本。
from langchain.memory import ConversationTokenBufferMemory
from langchain.llms import OpenAI
llm = ChatOpenAI(temperature=0.0)
memory = ConversationTokenBufferMemory(llm=llm, max_token_limit=50)
memory = ConversationBufferWindowMemory(k=1)
memory.save_context({"input":"Hi"}, {"output":"What's up"})
memory.save_context({"input":"Not much, just hanging"}, {"output":"Cool"})
memory.load_memory_variables({})
如果把token限制的值调的比较高,运行这段代码,可以发现几乎可以包含整段对话。如果降低,则会删除对话最早的部分信息,只保留最近的对话消息,保证内容长度不超过设置的token值。
关于第一个参数,为什么要制定llm,因为不同的大模型使用的token计算方法不一样,我们这里选用了ChatOpenAI LLM使用的计算token的方式。
(4)ConversationSummaryBufferMemory
与其将记忆的存储数量限制在最近若干对话数量上,或限制在token数上,不如让LLM为所有的历史消息生成摘要,在记忆中存储历史消息的摘要。
from langchain.memory import ConversationSummaryBufferMemory
schedule = "..."
最近的memory摘要里已经包含了最近的对话内容。对于ConversationSummaryBufferMemory试图将消息的显性记忆(蓝色选中部分)保持在我们指定的token上限100。
当然,这些记忆存储方式对于其他应用程序也很有用。如果是在检索场景下,你希望你的检索总数保持在限定范围内,非无限制增长,可以采用摘要式记忆。
这四种记忆方式总结如下:
# | 记忆方式 | 特性 |
1 | ConversationBufferMemory | 该内存允许存储消息,然后提取消息存到记忆变量中。 |
2 | ConversationBufferWindowMemory | 这个记忆保存了一段时间以来对话的交互对话列表。通过配置参数k,它只留存最后k个交互对话。 |
3 | ConversationTokenBufferMemory | 该内存在内存中保留最近交互对话,并使用设定的token长度而不是交互次数来刷新记忆留存内容。 |
4 | ConversationSummaryBufferMemory | 记忆中创建了一段时间以来对话的摘要,可设定记忆留存最高的token长度。 |
但其实,除了以上四种,还支持其余的记忆方式,最强大的就是向量数据存储(Vector data memory),如果熟悉词嵌入(Word Embeddings)和文本嵌入(Text Embeddings),向量数据库时间上就是存储这些嵌入的。可以使用向量数据库,检索最相关的文本块。Langchain还支持实体记忆存储(Entity memories),当想记住关于特定人或其他实体的详细信息时,是适用的。比如,如果谈论一个特定的朋友,可以让LangChain记住关于那个朋友的事实,将以明确的方式称为一个实体。
当使用LangChain实现应用程序时,也可以使用多种类型的记忆存储,比如使用某种对话记忆存储类型再加上实体记忆存储来回忆个人(Conversation memory + Entity memory)。可以记住对话的大致内容,并且可以明确记录对话中重要人物的重要事实。
除了以上的存储方式,开发者也经常将整个对话存储中传统数据库中,如键值存储(key-value store)或SQL数据库中,这样就可以回顾整个对话,并进一步改进系统。
希望这些能够帮助你更好的构建你的LLM应用。