Agent-Memory介绍

309 阅读10分钟

一:短期记忆:大模型上下文

就是大模型的上下文

二:中期记忆:执行任务记忆

  1. 对上一阶段的对话进行阶段性摘要。根据任务精心设计prompt,目标明确。摘要序列存储。

  2. 即将Agent过程中的Action单独存储,维护一个”Action栈“,对历史执行任务进行管理。

比如,对话助手,每20轮就需要

三: 长期记忆:借助外部存储

letta(原MemGPT):

Main Context:

MemGPT可以看做一个Agent,每次输入的Main Context主要包含三部分:

  • System Instructions:静态的系统prompt,不可修改,用来定义Agent的基本设定、工作流、所有tools的定义描述等。如:

The following is information about myself. My task is to completely immerse myself in this role (I should never say that I am an AI, and should reply as if I am playing this role).If the user asks me a question, I should reply with a best guess using the information in core memory and conversation search.

  • Working Context:是一个固定长度的、可以修改的、非结构化文本描述,只能通过Agent的function calls来修改。用来存储Agent所接受的关键信息、个人偏好以及其他重要信息。在默认模式下,这个模块包含两个block,一个是human(保存用户基本信息),一个是persona(保存agent自身角色设定)。随着对话的进行,agent根据上下文信息,通过function calls自动地、持续地修改和补充block中的内容。
  • FIFO Queue: 存储队列,存储历史对话记录、agent系统输出信息(比如memory用量提醒等)、function call的输入和输出。由于LLM的context窗口长度限制,agent会定期把一部分message记录从Context中删除,保存到Recall Storage中,这时agent会将删除的message记录以及结合当前的Summary,生成一个Recursive Memory,构建新的记忆,因此,在Context中,这部分内容的调整最频繁。

下图是letta框架实际方案中Context的组成:

  • Queue Manager(QM):

队列管理模块管理在Recall Storage和FIFO Queue中的message。

当新的用户message进来,QM把新的message先加入到FIFO队列中,与prompt拼接到一起送给大模型,得到输出。QM将输入和输出同时写入到Recall Storage数据库中。在后面的对话中,如果agent通过function call从RS中检索出来特定的message,QM会把他们加入到队列的后面,并拼接到LLM的输入prompt中。

QM还负责实现队列移除策略,用来控制对话溢出,当prompt的token长度超过70%,QM回想队列中插入一句“system提醒”,触发agent使用functions来总结FIFO队里中的重要信息,然后存储到数据库(Archival storage)中。

当prompt的token长度超过了100%,QM将释放空间(比如50%),用当前的Summary和释放的message重新生成一个新的Summary。当队列空间被被释放,弹出的message就不会出现在输入contaxt中,只会储存在Recall Storage中了,只能被agent通过funcitons来访问,参考上面的流程。

  • Function executor

MemGPT精心设计了在context和外部存储单元之间进行信息移动的策略,这些策略都是通过function call来实现。记忆的编辑和检索是完全自我驱动的:MemGPT根据当前的语境和从记忆库中搜索来自动更新记忆,比如,当对话太长的时候,会选择移除一些信息。实现方式就是在system prompt中直接说明(instructions)来实现,这些instructions主要包好两个部分:1.详细描述说明记忆模块的层次结构以及他们的具体功能,2.funciton列表和描述,agent用来使用他们修改意义内容。

了解context的限制是MemGPT正确运行的关键因素。

参考:

  1. zhuanlan.zhihu.com/p/173158756…
  2. research.memgpt.ai/
  3. medium.com
  4. www.letta.com/
  5. What is Model Context Protocol (MCP)? | Letta

Agent Workflow Memory

本文提出的Agent workflow Memory(AWM)框架,是通过Agent来持续地归纳出工作流(workflow),再整合使用工作流(workflow)来提升系统表现。工作流(workflow)的定义是为了解决一个特定问题的子步骤拆解,可以有效地帮助agent解决后续越来越复杂的任务,如下图中,显示了AWM在WebArena 地图导航数据集上导出的workflows:

AWM可以通过agent轨迹持续地归纳出工作流(workflow),也就是抽取可以重复使用的路径,然后把这些workflow整合到agent的记忆模块来指导解决下面的任务。

  AWM的实现方式:

  • 一个workflow包含两个部分:1.关于此workflow的文本描述d,2.完成此workflow的一系列行动(p1,p2,...)(p_1,p_2,...),如下图所示。

    • Workflow描述:需要使用一个特定的格式,可以让agent正确的获取信息,如何从更高维度描述这一系列行动的目标是至关重要的。本质上就是对workflow功能的描述,具体实现就是启发性的经验提取或者直接使用LLM从总结出来的。
    • Workflow轨迹:也就是完成此workflow的一系列行动轨迹(p1,p2,...)(p_1,p_2,...),每一个行动p包含三个部分,分别是[env des],[reason]和[action]。具体的,如下图中Step3所示:1.[env des]是对当前环境状态的文字描述,比如:“Order {id} is shown”。2.[reason]是基于当前的观察,说明Agent详细的推理过程,决定接下来需要执行什么action,比如:“Order {id} is found, I will now terminate the task.” 。3.[action]即具体可以代码执行的函数,比如:stop() 函数,即终止workflow。

LM-based Workflow Induction:

AWM的核心就是workflow的归纳模块,主要作用就是agent根据过去的所有经验ε={ei}i=1m \varepsilon = \{e_i\}^m_{i=1},归纳出一系列的workflows。每一个经验e=(q,Pe)e=(q,P^e)包含两部分,1.定义某个任务q的文本描述,2.行动轨迹PeP^e,即解决任务q的一系列行动(观察和action)。

不同于任务描述,我们故意的让LLM尽可能抽取出来最细粒度的workflows,比如“search for a product on Amazon”,这个子任务会重复出现在各个任务中。同时,我们会用槽位标识来代替具体的值,使得workflow更加通用性,比如:用“{product-name}”代替“dry cat food”来表示。

LLM输出这些workflows的通过prompt来约定格式,比如通过json格式或者用双空格分隔。在具体使用的时候分开存储在Agent的记忆模块中。在workflows W被归纳出来之后,直接加入到agent当前的记忆M中,生成强化的记忆MwM_w。当处理指示q时,agent推理出一系列action通过function call方式执行,过程可以表示成:L(q,Mw,o)=L(q,M+W,o)aL(q,M_w,o)=L(q,M+W,o)\to a

AWM有下面两种工作模式,“离线模式”和“在线模式”。

  1. “离线模式”分成两个独立的阶段,下方左图:

    1. induce workflows:首选使用人工标注的训练数据或者模型合成的数据来,即把所有训练数据拼接成一个prompt,然后输入到LLM中抽取出一系列workflows,这个可以看做“训练”阶段。
    2. 把“训练”阶段得到的所有workflows作为agent的记忆,来解决每个待测试指令(instructions)。
  2. “在线模式”,下方右图:

    1.     很多时候获得训练数据是很困难的,因此AWM还支持“无监督”模式,只需要测试query即可。AWM在“推理”的时候循环执行“推导”、“整合”和 “使用workflow”几个环节,并持续更新记忆的内容。
    2.     具体的,解决第t个测试query qtq^t的时候,首先agent通过LLM推理得到行动轨迹(p1t,p2t,...)(p^t_1,p^t_2,...),他们共同组成经验et=(qt,pt)e^t = (q^t, {p^t})。然后,我们采用基于LM的评估模型每个ete^t是不是能成功解决 qtq^t,如果判断是可以,就把这个经验转换成特定的workflow wt{w^t},然后更新到agent的记忆中 Mt+wtMt+1M^t + {w^t} → M^{t+1},然后继续解决后续的query。

评估指标:agent对每个测试query推理action轨迹的平均成功率。

AWM推导出的一些workflow例子:

WebArena数据集:

参考:

  1. webarena.dev/
  2. github.com/zorazrw/age…
  3. arxiv.org/abs/2409.07…
  4. medium.com/@techsachin…

A-MEM(Agentic Memory)

这个memory的设计主要包含4个模块:便条构建、构建链接、记忆进化和记忆检索

1.构建便条Note

当Agent每次与环境交互时候,使用一个特定结构的便条mim_i来提取记忆内容,所有时刻的记忆汇总就构成了整个系统的记忆链。mim_i的具体结构是 mi={ci,tj,Ki,Gi,Xi,ei,Li}m_i = \{c_i,t_j,K_i,G_i,X_i,e_i,L_i\},其中,

  1. cic_i表示原始的交互内容(也就是输入query)

  2. tit_i表示交互的时间戳(格式为“YYYYMMDDHHMM”)

  3. KiK_i表示LLM生成的关键词,是使用大模型从当前语境用提炼出来的突出的关键词,通常是名词、动词或者通用概念。

  4. GiG_i表示当前记忆Note的标签,是LLM生成的,用来对记忆Note分组。

  5. XiX_i表示对当前语境的文本描述,用来提供更加丰富的语义理解,是LLM生成。

  6. LiL_i维护一组关联的记忆Note,他们具有相同语义。

  7. eie_i是当前记忆Note的向量表示,用BERT来编码,用于快速检索和构建关联关系。

  • 构建KiK_iGiG_iXiX_i

其中,KiK_iGiG_iXiX_i是通过精心设计的prompt Ps1P_{s1}模板使用LLM生成: Ki,Gi,Xi=LLM(citiPs1)K_i,G_i,X_i = LLM(c_i || t_i || P_{s1}),具体的prompt如下:

prompt = """Generate a structured analysis of the following content by:

1. Identifying the most salient keywords (focus on nouns, verbs, and key concepts)

2. Extracting core themes and contextual elements

3. Creating relevant categorical tags

Format the response as a JSON object:

{

"keywords": [

// several specific, distinct keywords that capture key concepts and terminology

// Order from most to least important

// Don't include keywords that are the name of the speaker or time

// At least three keywords, but don't be too redundant.

],

"context":

// one sentence summarizing:

// - Main topic/domain

// - Key arguments/points

// - Intended audience/purpose

,

"tags": [

// several broad categories/themes for classification

// Include domain, format, and type tags

// At least three tags, but don't be too redundant.

]

}

Content for analysis:

""" + content
  • 构建eie_i

直接使用BERT类型编码器对记忆Note进行编码,直接将Note 中的cic_iKiK_iGiG_iXiX_i拼接起来:

ei=fenc[concat(ci,Ki,Gi,Xi)]e_i = f_{enc}[concat(c_i,K_i,G_i,X_i)]

2.构建链接(Link Generation)

本文的方案可以自动构建Note之间的关联关系,当新构建了一个记忆Note,可以自动找到现有Note哪些是有关联的,构建起边的关系。具体的,

第一步,当引入新的记忆便条mim_i,使用上面的方法得到向量表示,然后与所有记忆Note 的向量计算cos相似度,sn,j=en.njenejs_{n,j} = \frac{e_n.n_j}{|e_n||e_j|},再计算得到topk最相似的候选记忆Note MnearnM^n_{near}

第二步,对于所有的候选记忆Note,我们再用采用精心设计的prompt通过大模型来分析两个Note之间是否有关联关系。

最终得到了mim_i的所有关联Note,用数组来存储起来:li={mi,...,mk}l_i = \{m_i,...,m_k\}

这种方法,首先借助文本相似度筛选出来候选Note,不用穷举计算所有Note,更加高效。第二步借助LLM的能力,能够更精细化的分析两个Note之间是否有关系,弥补了只使用文本相似度的不足。

因为大模型能够分析出更多关系,比如隐晦的模式、因果关系和概念上的关联等,这些关系通过文本相似度都无法实现。

具体的prompt:

3.记忆进化(Memory Evolution)

经过上面的步骤,我们构建了所有记忆Note的“边”,A-MEM基于他们的文本描述和检索到的关联Note来决定是否更新每个Note的内容。即对于每一个记忆Notemjm_j,以及他们的邻近节点列表MnearnM^n_{near},通过精心设计的prompt用大模型来决定是不是更新mjm_j的上下文、关键词和标签,得到新的记忆Note mjm^*_j,替换掉原本的Note mjm_j

这个过程是在后台持续进行的,并有可能产生新的连接。随着系统持续运行,整个记忆Note 的网络持续更新,能够产生更高维度的关联关系,并能带来更丰富的知识。

具体的promtp:

4.检索关联记忆(Retrieve Relative Memory)

检索过程就是简单的计算k近邻,我们采用相同的文本编码器,对用户的query进行编码,然后与所有以及Note 的向量表示计算文本相似度,取topk。然后将检索到的记忆Note 的内容拼接到当前的promot中,回答用户问题。具体prompt为:

参考:

  1. github.com/agiresearch…