一.概念引入
ChatGPT之所以能够记得你之前说过的话,正是因为它使用了记忆(Memory)机制,记录了之前的对话上下文,并且把这个上下文作为提示的一部分,在最新的调用中传递给了模型。在聊天机器人的构建中,记忆机制非常重要。
二.使用ConversationChain
from langchain import OpenAI
from langchain.chains import ConversationChain
# 初始化大语言模型
llm = OpenAI(
temperature=0.5,
model_name="gpt-3.5-turbo-instruct"
)
# 初始化对话链
conv_chain = ConversationChain(llm=llm)
# 打印对话的模板
print(conv_chain.prompt.template)
这里我们使用ConversationChain,这个Chain最主要的特点是,它提供了包含AI 前缀和人类前缀的对话摘要格式,这个对话格式和记忆机制结合得非常紧密。
history是存储会话记忆的地方,也就是人类和人工智能之间对话历史的信息。input是新输入的地方,你可以把它看成是和ChatGPT对话时文本框中的输入。这两个参数会通过提示模板传递给 LLM,我们希望返回的输出只是对话的延续。
那么当有了history参数,以及Human和AI这两个前缀,我们就能够把历史对话信息存储在提示模板中,并作为新的提示内容在新一轮的对话过程中传递给模型。—— 这就是记忆机制的原理。
三.使用ConversationBufferMemory
这里我们在构造对话链中添加了记忆这个模块并调用ConversationBufferMemory()方法。
# 导入所需的库
from langchain_openai import ChatOpenAI
from langchain.chains import ConversationChain
from langchain.chains.conversation.memory import ConversationBufferMemory
# 初始化大语言模型
llm = ChatOpenAI(
model=os.environ.get("LLM_MODELEND"),
temperature=0.5,
)
# 初始化对话链
conversation = ConversationChain(llm=llm, memory=ConversationBufferMemory())
# 第一天的对话
# 回合1
conversation("我姐姐明天要过生日,我需要一束生日花束。")
print("第一次对话后的记忆:", conversation.memory.buffer)
1.我们来看看输出结果:
2.接着我们继续对话。下一轮对话中,这些记忆会作为一部分传入提示。
# 回合2
conversation("她喜欢粉色玫瑰,颜色是粉色的。")
print("第二次对话后的记忆:", conversation.memory.buffer)
3.下面,我们继续对话,同时打印出此时提示模板的信息。
# 回合3 (第二天的对话)
conversation("我又来了,还记得我昨天为什么要来买花吗?")
print("/n第三次对话后时提示:/n", conversation.prompt.template)
print("/n第三次对话后的记忆:/n", conversation.memory.buffer)
这些聊天历史信息,都被传入了
ConversationChain的提示模板中的history参数,构建出了包含聊天记录的新的提示输入。
有了记忆机制,LLM能够了解之前的对话内容,这样简单直接地存储所有内容为LLM提供了最大量的信息,但是新输入中也包含了更多的Token(所有的聊天历史记录),这意味着响应时间变慢和更高的成本。而且,当达到LLM的令牌数(上下文窗口)限制时,太长的对话无法被记住。
下面我们来看看针对Token太多、聊天历史记录过长的一些解决方案。
四.使用ConversationBufferWindowMemory
ConversationBufferWindowMemory 是缓冲窗口记忆,它的思路就是只保存最新最近的几次人类和AI的互动。因此,它在之前的“缓冲记忆”基础上增加了一个窗口值 k。这意味着我们只保留一定数量的过去互动,然后“忘记”之前的互动。
from langchain.chains.conversation.memory import ConversationBufferWindowMemory
# 初始化对话链
conversation = ConversationChain(llm=llm, memory=ConversationBufferWindowMemory(k=1))
这里和上面不同的是记忆调用ConversationBufferWindowMemory()方法,令k=1意味着窗口只会记住与AI之间的最新的互动,即只保留上一次的人类回应和AI的回应。
尽管这种方法不适合记住遥远的互动,但它非常擅长限制使用的Token数量。如果只需要记住最近的互动,缓冲窗口记忆是一个很好的选择。但是,如果需要混合远期和近期的互动信息,则还有其他选择。
五.使用ConversationSummaryMemory
ConversationSummaryMemory(对话总结记忆)的思路就是将对话历史进行汇总,然后再传递给history参数。这种方法旨在通过对之前的对话进行汇总来避免过度使用 Token。
它有这么几个核心特点:
- 汇总对话:此方法不是保存整个对话历史,而是每次新的互动发生时对其进行汇总,然后将其添加到之前所有互动的“运行汇总”中。
- 使用LLM进行汇总:该汇总功能由另一个LLM驱动,这意味着对话的汇总实际上是由AI自己进行的。
- 适合长对话:对于长对话,此方法的优势尤为明显。虽然最初使用的 Token 数量较多,但随着对话的进展,汇总方法的增长速度会减慢。与此同时,常规的缓冲内存模型会继续线性增长。
from langchain.chains.conversation.memory import ConversationSummaryBufferMemory
# 初始化对话链
conversation = ConversationChain(
llm=llm, memory=ConversationSummaryMemory(llm=llm)
)
记忆调用ConversationSummaryMemory()方法,并且输入了模型,这个参数用来总结对话。这个帮我们总结对话的LLM,和用来回答问题的LLM,可以是同一个大模型,也可以是不同的大模型。
我们看到这里的
'history',不再是之前人类和AI对话的简单复制粘贴,而是经过了总结和整理之后的一个综述信息。
它的优点是对于长对话,可以减少使用的 Token 数量,因此可以记录更多轮的对话信息,使用起来也直观易懂。
不过,它的缺点是,对于较短的对话,可能会导致更高的 Token 使用。另外,对话历史的记忆完全依赖于中间汇总LLM的能力,还需要为汇总LLM使用 Token,这增加了成本,且并不限制对话长度。
六.使用ConversationSummaryBufferMemory
最后一种记忆机制是ConversationSummaryBufferMemory,即对话总结缓冲记忆,它是一种混合记忆模型,结合了上述各种记忆机制,包括ConversationSummaryMemory 和 ConversationBufferWindowMemory的特点。这种模型旨在在对话中总结早期的互动,同时尽量保留最近互动中的原始内容。
from langchain.chains.conversation.memory import ConversationSummaryBufferMemory
# 初始化对话链
conversation = ConversationChain(
llm=llm, memory=ConversationSummaryBufferMemory(llm=llm, max_token_limit=300)
)
记忆调用ConversationSummaryBufferMemory()方法,并且输入了模型和最大token限制。
通过max_token_limit这个参数,当最新的对话文字长度在300字之内的时候,LangChain会记忆原始对话内容;当对话文字超出了这个参数的长度,那么模型就会把所有超过预设长度的内容进行总结,以节省Token数量。
七.总结
1.四种记忆机制的比较如下:
2.当对话轮次逐渐增加时,各种记忆机制对Token的消耗数量:
以上就是学习记忆的全部内容了。