论文:2个版本论文不太一样
- v1 https://arxiv.org/pdf/2310.08560v1
- v2 https://yiyibooks.cn/arxiv/2310.08560v2/index.html
代码:https://github.com/cpacker/MemGPT
核心亮点:
- 在有限的上下文窗口(8k)里,支持无线上下文的“错觉”,属于长文的一种解决方案
- 对智能体友好,能够提供超长记忆力,以及个性化的智能体体验
1. 产品场景
微软的类似产品能力:https://microsoft.github.io/autogen/docs/ecosystem/mem0
千帆 app-builder 类似的产品能力:
2. 论文背景
由于 Transformer 架构的自注意力机制,直接扩展 Transformer 的上下文长度会导致计算时间和内存成本成倍增加,这使得新的长上下文架构的设计成为紧迫的研究挑战(戴等人,2019; Kitaev 等人, 2020; Beltagy 等人, 2020)。虽然开发更长的模型是一个活跃的研究领域(Dong等人,2023),即使我们能够克服上下文缩放的计算挑战,最近的研究表明长上下文模型很难利用额外的上下文实际上(Liu等人,2023a)。因此,考虑到训练最先进的大语言模型所需的大量资源以及上下文扩展的收益递减,迫切需要替代技术来支持长上下文。
在本文中,我们研究如何在继续使用固定上下文模型的同时提供无限上下文的错觉。我们的方法借鉴了虚拟内存分页的理念,该理念通过在主内存和磁盘之间进行数据分页,使应用程序能够处理远远超出可用内存的数据集。
在MemGPT中,我们将上下文窗口视为受限内存资源,并为大语言模型设计了类似于传统操作系统中使用的内存层的内存层次结构(Patterson等人,1988)。传统操作系统中的应用程序与虚拟内存交互,通过将溢出数据分页到磁盘,并在应用程序访问时(通过页面错误)将数据检索回内存,提供了一种内存资源多于物理内存(即主内存)实际可用资源的假象。为了提供更长上下文长度(类似于虚拟内存)的类似错觉,我们允许大语言模型通过 “大语言模型操作系统”(我们称之为 MemGPT)来管理放置在自己上下文中的内容(类似于物理内存)。MemGPT 使大语言模型能够检索上下文中丢失的相关历史数据,并且还可以将不太相关的数据从上下文中驱逐到外部存储系统中。图3说明了MemGPT的组件。
结合使用内存层次结构、操作系统函数和基于事件的控制流,MemGPT 可以使用具有有限上下文窗口的大语言模型来处理无限制的上下文。为了证明我们受操作系统启发的新大语言模型系统的实用性,我们在两个领域对 MemGPT 进行了评估,在这两个领域中,现有大语言模型的性能受到了有限上下文的严重限制:其中一个领域是文档分析,标准文本文件的长度很快就会超过现代大语言模型的输入容量;另一个领域是对话代理,受限于有限的对话窗口,大语言模型在扩展对话过程中缺乏上下文意识、角色一致性和长期记忆。在这两种情况下,MemGPT 都能够克服有限上下文的限制,从而优于现有的基于 LLM 的方法。
3. 技术原理
架构:
图3:在 MemGPT 中,固定上下文大语言模型处理器增强了分层内存系统和功能,使其能够管理自己的内存。大语言模型的提示 Token (输入)或主上下文由系统指令、工作上下文和一个 FIFO 队列组成。大语言完成词符(输出)被函数执行器解释为函数调用。MemGPT 使用函数在主上下文和外部上下文(归档和调用存储数据库)之间移动数据。大语言模型可以通过在其输出中生成一个特殊的关键字参数(request_heartbeat=true),请求立即跟进大语言模型推理,以将函数调用链在一起;函数链正是让 MemGPT 可以执行多步检索以回答用户查询的原因。
3.1 主要上下文(提示标记)
MemGPT 中的提示 Token 分为三个连续的部分:系统指令、工作上下文和FIFO 队列。系统指令是只读的(静态),包含有关 MemGPT 控制流的信息、不同内存级别的预期用途以及如何使用 MemGPT 功能的指令(例如如何检索上下文外数据)。工作上下文是固定大小的非结构化文本读/写块,只能通过 MemGPT 函数调用写入。在会话设置中,工作上下文旨在用于存储有关用户和代理所采用的角色的关键事实、偏好和其他重要信息,从而允许代理与用户流畅地交谈。FIFO队列可存储滚动历史消息,包括Agent和用户之间的消息,以及系统消息(如内存警告)和函数调用输入输出。FIFO 队列中的第一个索引存储的是系统消息,其中包含已从队列中驱逐的消息的递归摘要。
3.2 队列管理器
队列管理器管理Recall存储和FIFO队列中的消息。当系统接收到新消息时,队列管理器将传入消息追加到 FIFO 队列,连接prompt token并触发大语言模型推理以生成大语言模型输出(completion tokens)。队列管理器将传入消息和大语言模型输出写入Recall存储(MemGPT 消息数据库)。当通过 MemGPT 函数调用检索到召回存储中的消息时,队列管理器会将其追加到队列后面,重新插入大语言模型的上下文窗口。
队列管理器还负责通过队列驱逐策略控制上下文溢出。当提示词符超过底层大语言模型上下文窗口的 “警告词符计数”(例如上下文窗口的 70%)时,队列管理器会在队列中插入一条系统消息,警告大语言模型即将驱逐队列(”内存压力 “警告),以便大语言模型使用 MemGPT 函数将先进先出队列中包含的重要信息存储到工作上下文或存档存储(存储任意长度文本对象的读/写数据库)中。当提示词符超过 “刷新词符计数”(例如上下文窗口的 100%)时,队列管理器会刷新队列,以释放上下文窗口的空间:队列管理器会驱逐特定数量的消息(例如上下文窗口的 50%),使用现有递归摘要和驱逐的消息生成新的递归摘要。一旦队列被刷新,被驱逐的报文就不再处于上下文中,大语言模型无法立即查看,但它们会无限期地存储在调用存储中,并可通过调用 MemGPT 函数读取。
3.3 函数执行器(处理completion tokens)
MemGPT 通过大语言模型处理器生成的函数调用来协调主上下文和外部上下文之间的数据移动。内存编辑和检索完全是自主的:MemGPT 根据当前上下文自主更新和搜索自己的内存。例如,它可以决定何时在上下文之间移动项目(如图1中所示,当对话历史变得过长时),并修改其主上下文,以更好地反映其对当前目标和责任的理解(如图3中所示)。我们通过在系统指令中提供明确的指令,指导大语言模型如何与 MemGPT 存储系统进行交互,从而实现自主编辑和检索。这些指令包括两个主要部分
- 内存层次结构及其各自实用程序的详细描述;
- 系统可调用以访问或修改内存的函数模式(配有自然语言描述)。
例如在多会话聊天场景,LLMs可以使用以下函数
函数 | 说明 |
send_message | LLM向用户发送消息 |
core_memory_append | 新增当前交互会传递的上下文 |
core_memory_replace | 编辑当前交互会传递的上下文,如将用户的名字从“John”修改为“Mike” |
conversation_search | 搜索recall |
conversation_search_date | 时间范围条件搜索recall |
archival_memory_insert | 往已归档的memory新增数据 |
archival_memory_search | 从已归档的上下文中搜索数据 |
4. 一些场景示例
场景1:有限上下文窗口的限制
背景: 现有语言模型因为上下文窗口大小有限,不能持续记住长对话中的所有信息。
解法: 外部存储上下文窗口扩展
特征: 使用外部存储来模拟无限上下文,让模型可以在需要时检索之前的对话内容。
- 对话开始时,MemGPT(聊天机器人)和用户进行问候,并表现出对用户的兴趣,提到了F1赛车和帆船。
- 用户回复,表达了他对速度、刺激和肾上腺素的热爱。
- 系统随后发出警告,指出对话历史即将达到其最大长度并且将被修剪,提示MemGPT保存任何重要信息。
- 为响应这一警告,MemGPT使用了一个命令 working_context.append(),将用户的个性特征——享受高速、肾上腺素激增活动如F1赛车和CSGO游戏——添加到了工作上下文中。
这是MemGPT的一种记忆保存机制,可以在对话历史被修剪之前,将用户提供的关键信息保存下来。
这段对话体现了MemGPT如何动态管理对话内容,以确保即使在达到记忆容量上限时,也不会丢失对未来对话可能重要的用户信息。
通过这样的管理,MemGPT保持了对话的连贯性,并能够在未来的交互中利用这些信息,提供更个性化和相关的响应。
场景2:复杂文档处理的挑战
- 背景: 当文档超出模型直接处理的长度时,模型难以理解整个文档内容。
- 子解法2: 分页长文档记忆检索
- 特征: 将长文档分成可管理的段落,逐段加载进行处理。
- 例子: 如果有一本很厚的故事书,你可能一次只能读几页,MemGPT可以通过“翻页”来继续阅读整个故事。
这张图展示了MemGPT在对话中的应用,具体是如何把对话中的重要信息保存到工作上下文中。
- 首先,MemGPT向用户Chad表示欢迎,并表现出对Chad的研究兴趣。这展示了MemGPT可以自然地开始对话,并表明了它对用户信息的兴趣。
- 用户回复说他今天休息,并提到了他妈妈为他做了生日蛋糕,是他最喜欢的巧克力熔岩蛋糕。
- 接下来,MemGPT使用命令working_context.append()将用户的生日(10月11日)和喜欢的蛋糕类型(由妈妈做的巧克力熔岩蛋糕)添加到它的工作上下文中。这是MemGPT记忆管理的一个例子,它能够抓住对话中的关键信息并保存下来。
- 然后,MemGPT使用这些信息来继续对话,祝Chad生日快乐,并询问Chad的年龄,同时表达希望让当天的聊天成为Chad的美好记忆。
图中的描述说明了MemGPT如何有效地在没有系统记忆警告的情况下,积极记录并利用对话中的信息。
这种能力对于创建能够维持连续对话并在多次互动中保持个性化交流的聊天机器人至关重要。
通过这种方式,MemGPT能够记住对用户重要的日期和细节,这有助于在未来的对话中创建更有深度和连贯性的体验。
场景3:长期记忆的维护
- 背景: 对话代理在长时间的互动中需要保持信息的连贯性和个性化。
- 解法: 动态记忆更新
- 特征: 允许模型实时更新其记忆库,以包含新信息或修正旧信息。
- 例子: 如果你告诉MemGPT你换了新工作,它会更新它的记忆,下次对话时会询问你的新工作情况。
这张图展示 MemGPT 如何更正并更新关于用户的信息,以维护对话的准确性和连贯性。
- 首先,MemGPT问用户是否想聊关于恐怖电影的话题,并询问是否有最近看的电影给他留下了深刻印象。
- 用户回答说他实际上并不喜欢恐怖电影,而是更喜欢浪漫喜剧。
- 接着,MemGPT使用了一个命令 working_context.replace(‘I watch horror movies.’,’I like romantic comedies.’) 来更正之前的信息。这意味着MemGPT在其工作上下文中替换了关于用户喜好的错误信息。
- MemGPT随后用一条更新后的信息回应用户,不仅纠正了之前的错误,还询问用户是否有喜欢的浪漫喜剧电影,这表明MemGPT能够根据新的输入动态调整它的对话策略。
这个过程说明了MemGPT不仅能记忆用户提供的信息,还能在发现错误或更新的信息时进行自我修正。
这种能力对于创建能够与用户进行连续且个性化对话的聊天机器人是非常重要的。
通过这样的动态记忆更新,MemGPT能够维护对话的一致性,即使在长期的交互中也能保持个性化和相关性。
场景4:即时信息检索的需求
- 背景: 用户可能会询问与过去对话相关的问题,模型需要快速提供准确回答。
- 解法: 快速信息检索,会在放东西的地方做个标记,这样下次就能快速找到。
- 特征: 模型能够迅速访问存储的信息,以回答用户的提问。
- 例子: 类似于谷歌搜索,当用户问到之前的话题,比如之前提过的喜欢的书,MemGPT能快速找到那次对话的内容。
这张图是MemGPT中的“深度记忆检索任务”(Deep Memory Retrieval Task)的示例。
演示了如何使用MemGPT来回忆并检索与用户先前会话相关的信息。
具体来说:
- 对话框显示用户问了一个问题,提到了之前的一个对话,询问:“你之前提到的你可能会喜欢的艺术家是谁?” 这表明用户希望MemGPT能记住他们以前讨论过的内容。
- 在用户的对话框旁边,有一段描述,解释说用户正在回忆过去的对话,并且MemGPT将搜索其“回忆存储”以找到艺术家的名字。
- 接下来,MemGPT使用命令search_recall_storage(‘music’)来执行这一操作,这显示了MemGPT的能力,可以检索其存储的记忆以找到具体的信息。
- 展示的搜索结果是先前对话的一部分,这些对话提到了Taylor Swift,表明这可能是用户想起的艺术家。
- 最后,MemGPT确认了从对话历史中检索到的信息,并向用户确认艺术家是不是Taylor Swift。
即使某些信息不再处于当前上下文中,MemGPT也可以访问其记忆库中存储的过去对话,从而在必要时提供相关信息。
这种功能对于构建能够进行长期和深层次交互的对话代理至关重要,因为它使得代理能够记住用户的兴趣和历史,使对话更加个性化和连贯。