使用ConversationSummaryMemory
上面说了,如果模型在第二轮回答的时候,能够说出“我可以帮你为你姐姐找到...”,那么在第三轮回答时,即使窗口大小 k=1,还是能够回答出正确答案。
这是为什么?
因为模型在回答新问题的时候,对之前的问题进行了总结性的重述。
ConversationSummaryMemory(对话总结记忆)的思路就是将对话历史进行汇总,然后再传递给 {history} 参数。这种方法旨在通过对之前的对话进行汇总来避免过度使用 Token。
ConversationSummaryMemory有这么几个核心特点。
- 汇总对话:此方法不是保存整个对话历史,而是每次新的互动发生时对其进行汇总,然后将其添加到之前所有互动的“运行汇总”中。
- 使用LLM进行汇总:该汇总功能由另一个LLM驱动,这意味着对话的汇总实际上是由AI自己进行的。
- 适合长对话:对于长对话,此方法的优势尤为明显。虽然最初使用的 Token 数量较多,但随着对话的进展,汇总方法的增长速度会减慢。与此同时,常规的缓冲内存模型会继续线性增长。
下面,我们来看看使用ConversationSummaryMemory的代码示例。
from langchain.chains.conversation.memory import ConversationSummaryMemory
# 初始化对话链
conversation = ConversationChain(
llm=llm,
memory=ConversationSummaryMemory(llm=llm)
)
第一回合的输出:
{'input': '我姐姐明天要过生日,我需要一束生日花束。',
'history': '',
'response': ' 我明白,你需要一束生日花束。我可以为你提供一些建议吗?我可以推荐一些花束给你,比如玫瑰,康乃馨,百合,仙客来,郁金香,满天星等等。挑选一束最适合你姐姐的生日花束吧!'}
第二回合的输出:
{'input': '她喜欢粉色玫瑰,颜色是粉色的。',
'history': "\nThe human asked what the AI thinks of artificial intelligence. The AI thinks artificial intelligence is a force for good because it will help humans reach their full potential. The human then asked the AI for advice on what type of flower bouquet to get for their sister's birthday, to which the AI provided a variety of suggestions.",
'response': ' 为了为你的姐姐的生日准备一束花,我建议你搭配粉色玫瑰和白色康乃馨。你可以在玫瑰花束中添加一些紫色的满天星,或者添加一些绿叶以增加颜色对比。这将是一束可爱的花束,让你姐姐的生日更加特别。'}
第三回合的输出:
plain
复制代码
AI练中学
{'input': '我又来了,还记得我昨天为什么要来买花吗?',
'history': "\n\nThe human asked what the AI thinks of artificial intelligence. The AI thinks artificial intelligence is a force for good because it will help humans reach their full potential. The human then asked the AI for advice on what type of flower bouquet to get for their sister's birthday, to which the AI suggested pink roses and white carnations with the addition of purple aster flowers and green leaves for contrast. This would make a lovely bouquet to make the sister's birthday extra special.",
'response': ' 确实,我记得你昨天想买一束花给你的姐姐作为生日礼物。我建议你买粉红色的玫瑰花和白色的康乃馨花,再加上紫色的雏菊花和绿叶,这样可以让你的姐姐的生日更加特别。'}
看得出来,这里的 'history',不再是之前人类和AI对话的简单复制粘贴,而是经过了总结和整理之后的一个综述信息。
这里,我们不仅仅利用了LLM来回答每轮问题,还利用LLM来对之前的对话进行总结性的陈述,以节约Token数量。这里,帮我们总结对话的LLM,和用来回答问题的LLM,可以是同一个大模型,也可以是不同的大模型。
ConversationSummaryMemory的优点是对于长对话,可以减少使用的 Token 数量,因此可以记录更多轮的对话信息,使用起来也直观易懂。不过,它的缺点是,对于较短的对话,可能会导致更高的 Token 使用。另外,对话历史的记忆完全依赖于中间汇总LLM的能力,还需要为汇总LLM使用 Token,这增加了成本,且并不限制对话长度。
通过对话历史的汇总来优化和管理 Token 的使用,ConversationSummaryMemory 为那些预期会有多轮的、长时间对话的场景提供了一种很好的方法。然而,这种方法仍然受到 Token 数量的限制。在一段时间后,我们仍然会超过大模型的上下文窗口限制。
而且,总结的过程中并没有区分近期的对话和长期的对话(通常情况下近期的对话更重要),所以我们还要继续寻找新的记忆管理方法。
使用ConversationSummaryBufferMemory
我要为你介绍的最后一种记忆机制是ConversationSummaryBufferMemory,即对话总结缓冲记忆,它是一种混合记忆模型,结合了上述各种记忆机制,包括ConversationSummaryMemory 和 ConversationBufferWindowMemory的特点。这种模型旨在在对话中总结早期的互动,同时尽量保留最近互动中的原始内容。
它是通过max_token_limit这个参数做到这一点的。当最新的对话文字长度在300字之内的时候,LangChain会记忆原始对话内容;当对话文字超出了这个参数的长度,那么模型就会把所有超过预设长度的内容进行总结,以节省Token数量。
from langchain.chains.conversation.memory import ConversationSummaryBufferMemory
# 初始化对话链
conversation = ConversationChain(
llm=llm,
memory=ConversationSummaryBufferMemory(
llm=llm,
max_token_limit=300))
第一回合的输出:
{'input': '我姐姐明天要过生日,我需要一束生日花束。',
'history': '',
'response': ' 哇,你姐姐要过生日啊!那太棒了!我建议你去买一束色彩鲜艳的花束,因为这样可以代表你给她的祝福和祝愿。你可以去你家附近的花店,或者也可以从网上订购,你可以看看有没有特别的花束,比如彩色玫瑰或者百合花,它们会更有特色。'}
第二回合的输出:
{'input': '她喜欢粉色玫瑰,颜色是粉色的。',
'history': 'Human: 我姐姐明天要过生日,我需要一束生日花束。\nAI: 哇,你姐姐要过生日啊!那太棒了!我建议你去买一束色彩鲜艳的花束,因为这样可以代表你给她的祝福和祝愿。你可以去你家附近的花店,或者也可以从网上订购,你可以看看有没有特别的花束,比如彩色玫瑰或者百合花,它们会更有特色。',
'response': ' 好的,那粉色玫瑰就是一个很好的选择!你可以买一束粉色玫瑰花束,这样你姐姐会很开心的!你可以在花店里找到粉色玫瑰,也可以从网上订购,你可以根据你的预算,选择合适的数量。另外,你可以考虑添加一些装饰,比如细绳、彩带或者小礼品'}
第三回合的输出:
{'input': '我又来了,还记得我昨天为什么要来买花吗?',
'history': "System: \nThe human asked the AI for advice on buying a bouquet for their sister's birthday. The AI suggested buying a vibrant bouquet as a representation of their wishes and blessings, and recommended looking for special bouquets like colorful roses or lilies for something more unique.\nHuman: 她喜欢粉色玫瑰,颜色是粉色的。\nAI: 好的,那粉色玫瑰就是一个很好的选择!你可以买一束粉色玫瑰花束,这样你姐姐会很开心的!你可以在花店里找到粉色玫瑰,也可以从网上订购,你可以根据你的预算,选择合适的数量。另外,你可以考虑添加一些装饰,比如细绳、彩带或者小礼品",
'response': ' 是的,我记得你昨天来买花是为了给你姐姐的生日。你想买一束粉色玫瑰花束来表达你的祝福和祝愿,你可以在花店里找到粉色玫瑰,也可以从网上订购,你可以根据你的预算,选择合适的数量。另外,你可以考虑添加一些装饰,比如细绳、彩带或者小礼品}
不难看出,在第二回合,记忆机制完整地记录了第一回合的对话,但是在第三回合,它察觉出前两轮的对话已经超出了300个字节,就把早期的对话加以总结,以节省Token资源。
ConversationSummaryBufferMemory的优势是通过总结可以回忆起较早的互动,而且有缓冲区确保我们不会错过最近的互动信息。当然,对于较短的对话,ConversationSummaryBufferMemory也会增加Token数量。
总体来说,ConversationSummaryBufferMemory为我们提供了大量的灵活性。它是我们迄今为止的唯一记忆类型,可以回忆起较早的互动并完整地存储最近的互动。在节省Token数量方面,ConversationSummaryBufferMemory与其他方法相比,也具有竞争力。
我们可以通过一个表格对这四种类型的记忆做一个整体比较。
四种记忆机制的比较如下:
网上还有人总结了一个示意图,体现出了当对话轮次逐渐增加时,各种记忆机制对Token的消耗数量。意图向我们表达的是:有些记忆机制,比如说ConversationSummaryBufferMemory和ConversationSummaryMemory,在对话轮次较少的时候可能会浪费一些Token,但是多轮对话过后,Token的节省就逐渐体现出来了。
当然ConversationBufferWindowMemory对于Token的节省最为直接,但是它会完全遗忘掉K轮之前的对话内容,因此对于某些场景也不是最佳选择。