在与语言模型交互时,一个关键问题:记忆缺失使得对话缺乏真正的连续性。
因此,接下来介绍 LangChain 中的储存模块,即如何将先前的对话嵌入到语言模型中的,使其具有连续对话的能力。
当使用 LangChain 中的储存(Memory)模块时,它旨在保存、组织和跟踪整个对话的历史,从而为用户和模型之间的交互提供连续的上下文。
LangChain 提供了多种储存类型。缓冲区储存 允许保留最近的聊天消息,摘要储存 则提供了对整个对话的摘要。实体储存 则允许在多轮对话中保留有关特定实体的信息。
这些记忆组件都是模块化的,可与其他组件组合使用,从而增强机器人的对话管理能力。
储存模块可以通过简单的 API 调用来访问和更新,允许开发人员更轻松地实现对话历史记录的管理和维护。
此次课程主要介绍其中四种储存模块,其他模块可查看文档学习。
- 对话缓存储存 (ConversationBufferMemory)
- 对话缓存窗口储存 (ConversationBufferWindowMemory)
- 对话令牌缓存储存 (ConversationTokenBufferMemory)
- 对话摘要缓存储存 (ConversationSummaryBufferMemory)
短期记忆在LangChain中的大语言模型(LLM)里的重要知识点包括:
- 长期记忆与短期记忆的区别:LLM通过训练获得长期记忆,而短期记忆是用户与模型互动时产生的。
- 参数不变性:LLM的长期参数在训练完成后不会因用户输入而改变。
- 对话中的信息处理:LLM在对话过程中会暂时记住用户的输入和自己的输出,以便进行下一步的预测和生成响应。
- 信息的临时性:一旦LLM完成输出,它会"遗忘"之前的用户输入和自己的输出,这些信息仅作为短期记忆存在。
- 短期记忆的作用:短期记忆帮助LLM在特定会话中维持上下文的连贯性和相关性。
为了延长 LLM 短期记忆的保留时间,则需要借助一些外部储存方式来进行记忆,以便在用户与 LLM 对话中,LLM 能够尽可能的知道用户与它所进行的历史对话信息。
一、对话缓存储存
1.1 初始化对话模型
让我们先来初始化对话模型。
from langchain.chains import ConversationChain
from langchain.chat_models import ChatOpenAI
from langchain.memory import ConversationBufferMemory
# 这里我们将参数temperature设置为0.0,从而减少生成答案的随机性。
# 如果你想要每次得到不一样的有新意的答案,可以尝试增大该参数。
llm = ChatOpenAI(temperature=0.0)
memory = ConversationBufferMemory()
# 新建一个 ConversationChain Class 实例
# verbose参数设置为True时,程序会输出更详细的信息,以提供更多的调试或运行时信息。
# 相反,当将verbose参数设置为False时,程序会以更简洁的方式运行,只输出关键的信息。
conversation = ConversationChain(llm=llm, memory = memory, verbose=True )
1.2 第一轮对话
当我们运行预测(predict)时,生成了一些提示,如下所见,他说"以下是人类和 AI 之间友好的对话,AI 健谈"等等,这实际上是 LangChain 生成的提示,以使系统进行希望和友好的对话,并且必须保存对话,并提示了当前已完成的模型链。
conversation.predict(input="你好, 我叫皮皮鲁")
> Entering new 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.
'你好,皮皮鲁!很高兴认识你。我是一个AI助手,可以回答你的问题和提供帮助。有什么我可以帮你的吗?'
1.3 第二轮对话
当我们进行第二轮对话时,它会保留上面的提示
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: 你好,皮皮鲁!很高兴认识你。我是一个AI助手,可以回答你的问题和提供帮助。有什么我可以帮你的吗?
Human: 1+1等于多少?
AI:
> Finished chain.
'1+1等于2。'
1.4 第三轮对话
为了验证他是否记忆了前面的对话内容,我们让他回答前面已经说过的内容(我的名字),可以看到他确实输出了正确的名字,因此这个对话链随着往下进行会越来越长。
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: 你好,皮皮鲁!很高兴认识你。我是一个AI助手,可以回答你的问题和提供帮助。有什么我可以帮你的吗?
Human: 1+1等于多少?
AI: 1+1等于2。
Human: 我叫什么名字?
AI:
> Finished chain.
'你叫皮皮鲁。'
1.5 查看储存缓存
储存缓存(buffer),即储存了当前为止所有的对话信息
print(memory.buffer)
Human: 你好, 我叫皮皮鲁
AI: 你好,皮皮鲁!很高兴认识你。我是一个AI助手,可以回答你的问题和提供帮助。有什么我可以帮你的吗?
Human: 1+1等于多少?
AI: 1+1等于2。
Human: 我叫什么名字?
AI: 你叫皮皮鲁。
也可以通过load_memory_variables({})打印缓存中的历史消息。这里的{}是一个空字典,有一些更高级的功能,使用户可以使用更复杂的输入,具体可以通过 LangChain 的官方文档查询更高级的用法。
print(memory.load_memory_variables({})) {'history': 'Human: 你好, 我叫皮皮鲁\nAI: 你好,皮皮鲁!很高兴认识你。我是一个AI助手,可以回答你的问题和提供帮助。有什么我可以帮你的吗?\nHuman: 1+1等于多少?\nAI: 1+1等于2。\nHuman: 我叫什么名字?\nAI: 你叫皮皮鲁。'}
1.6 直接添加内容到储存缓存
我们可以使用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: 是的,让我们一起去冒险吧!'}
可以看到对话历史都保存下来了!
当我们在使用大型语言模型进行聊天对话时,大型语言模型本身实际上是无状态的。语言模型本身并不记得到目前为止的历史对话。每次调用API结点都是独立的。储存(Memory)可以储存到目前为止的所有术语或对话,并将其输入或附加上下文到LLM中用于生成输出。如此看起来就好像它在进行下一轮对话的时候,记得之前说过什么。
二、对话缓存窗口储存
随着对话变得越来越长,所需的内存量也变得非常长。将大量的tokens发送到LLM的成本,也会变得更加昂贵,这也就是为什么API的调用费用,通常是基于它需要处理的tokens数量而收费的。
针对以上问题,LangChain也提供了几种方便的储存方式来保存历史对话。其中,对话缓存窗口储存只保留一个窗口大小的对话。它只使用最近的n次交互。这可以用于保持最近交互的滑动窗口,以便缓冲区不会过大。
2.1 添加两轮对话到窗口储存
我们先来尝试一下使用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: 是的,让我们一起去冒险吧!'}
通过结果,我们可以看到窗口储存中只有最后一轮的聊天记录。
2.2 在对话链中应用窗口储存
接下来,让我们来看看如何在ConversationChain中运用ConversationBufferWindowMemory吧!
llm = ChatOpenAI(temperature=0.0)
memory = ConversationBufferWindowMemory(k=1)
conversation = ConversationChain(llm=llm, memory=memory, verbose=False )
print("第一轮对话:")
print(conversation.predict(input="你好, 我叫皮皮鲁"))
print("第二轮对话:")
print(conversation.predict(input="1+1等于多少?"))
print("第三轮对话:")
print(conversation.predict(input="我叫什么名字?"))
第一轮对话:
你好,皮皮鲁!很高兴认识你。我是一个AI助手,可以回答你的问题和提供帮助。有什么我可以帮你的吗?
第二轮对话:
1+1等于2。
第三轮对话:
很抱歉,我无法知道您的名字。
注意此处!由于这里用的是一个窗口的记忆,因此只能保存一轮的历史消息,因此AI并不能知道你第一轮对话中提到的名字,他最多只能记住上一轮(第二轮)的对话信息
三、对话字符缓存储存
使用对话字符缓存记忆,内存将限制保存的token数量。如果字符数量超出指定数目,它会切掉这个对话的早期部分 以保留与最近的交流相对应的字符数量,但不超过字符限制。
添加对话到Token缓存储存,限制token数量,进行测试
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: 轻舟已过万重山。'}
ChatGPT 使用一种基于字节对编码(Byte Pair Encoding,BPE)的方法来进行 tokenization (将输入文本拆分为token)。BPE 是一种常见的 tokenization 技术,它将输入文本分割成较小的子词单元。 OpenAI 在其官方 GitHub 上公开了一个最新的开源 Python 库 tiktoken(https://github.com/openai/tiktoken),这个库主要是用来计算%EF%BC%8C%E8%BF%99%E4%B8%AA%E5%BA%93%E4%B8%BB%E8%A6%81%E6%98%AF%E7%94%A8%E6%9D%A5%E8%AE%A1%E7%AE%97) tokens 数量的。相比较 HuggingFace 的 tokenizer ,其速度提升了好几倍。 具体 token 计算方式,特别是汉字和英文单词的 token 区别,具体可参考知乎文章(https://www.zhihu.com/question/594159910)。%E3%80%82)
四、对话摘要缓存储存
对话摘要缓存储存,使用 LLM 对到目前为止历史对话自动总结摘要,并将其保存下来。
4.1 使用对话摘要缓存储存
我们创建了一个长字符串,其中包含某人的日程安排。
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.
4.2 基于对话摘要缓存储存的对话链
基于上面的对话摘要缓存储存,我们新建一个对话链。
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."}
通过对比上一次输出,发现摘要记录更新了,添加了最新一次对话的内容总结。