Agent 记忆:可写、可找、可纠正的外部状态
把会话上下文、工作状态、情景记录和长期事实分开,理解写入、整合、检索、遗忘、冲突与隐私治理。
- 从任务中提取候选事实与事件。
- 按效用、置信、敏感度决定是否写入。
- 去重、关联、设置作用域与保留期。
- 按任务、权限、时间检索并重排。
- 带来源注入上下文并完成任务。
- 依据结果、用户纠正和过期规则更新或删除。
1为什么 Agent 需要外部记忆直觉
Agent 每一次对话或每一步推理都依赖上下文,而上下文窗口无论拉得多长,都无法成为"全部历史永远附上"的替代品。原因在于四条彼此独立的限制。
第一是预算。把每一次交互的原始记录都塞进上下文,很快会耗尽可用的 token 预算,剩下的空间不足以容纳当前任务真正需要的指令与工具结果。窗口长度提升了上限,但没有改变"无限历史"与"有限预算"之间的根本矛盾。
第二是过期。历史里的大量内容随时间失效:用户改了口味、项目换了依赖版本、决定已经被撤销。原文照搬旧记录会把已经作废的信息当作仍然有效的依据。
第三是冲突。同一事实在不同时间点可能以互相矛盾的版本出现,例如"默认语言是中文"之后又出现"以后都用英文回复"。全部附上意味着把矛盾同时递给模型,而不提供任何裁决依据。
第四是拖慢调用。无关历史占据上下文后,模型要处理更多内容,延迟上升,注意力被稀释,真正关键的信息反而更难被可靠利用。
此外还有跨会话的持久化问题。单次对话结束时上下文随之消失,但用户偏好、已完成的工作、仍在进行中的项目状态需要在下次会话中继续可用,否则每次都要重新交代背景。
外部记忆正是针对这些问题的答案:让系统只在需要时取回相关状态,而不是把全部历史始终挂在上下文里。它缓解容量限制,同时支撑长期个性化和任务连续性。但这份能力不是免费的——一旦引入检索,就可能取回错误的记录,系统也随之承担起数据治理的责任,包括什么该写、什么能读、何时该改、何时该删。
因此,可以把外部记忆定义为:Agent 可持久写入、按需检索并可纠正的状态系统。它的输入是那些超出当前窗口、但未来仍可能有用的事件、事实和偏好;它的输出是当前任务被允许使用的记忆证据。系统通过写入、检索和更新维持跨会话的连续性。需要反复强调的是,检索"命中"只表示找到了一条记录,绝不等于这条记录是最新的、真实的,也不等于当前调用者有权使用它。命中与正确是两件事,这个区分贯穿记忆系统的全部设计。
2“记忆”包含哪些层分类
"记忆"并不是一个可以一视同仁的单一仓库,而是由若干寿命、来源和用途都不同的层组成。把它们区分开,是为了回答一个具体问题:当前工具结果和用户长期偏好,是否应该住在同一个地方、用同一种方式处理?答案是否定的——它们的有效期和可靠程度完全不同。
按保存内容,记忆可以分成五层。
上下文内历史保存的是当前轮可见的消息与工具结果。它的作用域最短,只服务于眼前这一次调用,对话一结束就随上下文一起消散。它几乎不需要检索,因为内容就在窗口里,但它也最不稳定。
工作状态保存计划、待办、约束和已验证的事实。它比上下文内历史长,跨越多轮或多步任务,是执行过程中需要持续参照和更新的中间产物。
情景记忆保存带时间戳和来源的具体事件。它记录"什么时候、在哪里、谁做了什么",为后续判断提供可追溯的原始素材。情景记忆的可靠之处在于它是记录而非推断,但它本身不回答"现在是否仍然成立"。
语义记忆是从多次事件整合出的稳定事实或偏好。例如,一条两次都写明"默认用中文回复"的记录,才被提升为"该用户偏好中文"。语义记忆是最高一层的沉淀,但它必须建立在多条证据之上,单条事件不足以支撑。
程序性规则保存可复用的流程、策略与操作规范,例如"部署前必须先跑测试"。它描述的是怎么做,而不是发生过什么或用户喜欢什么。
这个分层的输入是一条候选信息,连同它的用途和有效期;输出则是把这条信息归入上下文历史、工作状态、情景、语义或程序性记忆中的某一类。分类的直接收益是帮助选择写入策略和淘汰策略:当前工具结果服务眼前任务,具体事件保留时间来源,只有多次相互印证的事件才能整合为稳定偏好,而规则则需要明确的作用域和触发条件。
但分类不是自动化的结论。现实中的信息往往跨越层级,一条记录可能既是情景事件又包含偏好的线索。不能仅凭它被贴上了某个层的名称,就自动把它升级为长期规则或永久事实。层级之间的提升必须有证据和治理依据,这正是后续写入与整合环节要解决的问题。
| 记忆层 | 保存内容 |
|---|---|
| 上下文内历史 | 当前轮可见消息与工具结果 |
| 工作状态 | 计划、待办、约束、已验证事实 |
| 情景记忆 | 带时间来源的具体事件 |
| 语义记忆 | 从多次事件整合出的稳定事实或偏好 |
| 程序性规则 | 可复用流程、策略与操作规范 |
3写入比存储更难原理
如果存储足够便宜,把每一句话都原样保存下来,记忆反而会变差。原因不在于容量,而在于无选择写入会系统性地污染记忆:重复的记录让同一事实以多个副本存在,噪声淹没真正重要的信息,敏感信息在不该留存的地方被持久化,而互相矛盾的记录则让系统在检索时无法判断哪条才代表当前状态。
因此写入环节的核心不是"存下",而是"判断该不该存、以什么形式存"。写入门控接收候选内容,连同它的来源、置信度、未来效用、敏感度、作用域和保留期,输出三种可能:拒绝、待确认,或一条带版本的记录。
判断依据首先是未来效用——这条信息在之后的任务里还会被用到吗?一次性的临时指令不值得占用长期记忆。其次是置信度与来源:来自用户明确表述的信息可信度高,来自模型推断或二手转述的信息可信度低。再次是敏感度,涉及隐私或凭据的内容要么不写,要么脱敏并严格限定读取权限。最后是作用域和保留期,决定这条记录对谁可见、在什么条件下生效、多久之后应当失效。
一个典型的陷阱是把"用户这次要求简洁"直接提升为永久偏好。单次要求可能只是针对眼前这一条回复的风格,把它写成长期偏好就是错误泛化。重要事实应当经过用户确认,或者由多次独立证据相互支持后再提升;这正是从情景记忆升级为语义记忆所需的门槛。
写入门控解决的是噪声、冲突和隐私这三类问题:通过拒绝低效用内容降低噪声,通过版本化处理矛盾,通过敏感度与作用域约束保护隐私。需要明确它的边界——写入成功只表示这条内容符合保存策略,并不证明内容本身为真,也不保证它未来一定有用。写入与验证是两件不同的事,前者管"该不该留",后者才管"留得对不对"。
4检索是带时间与权限的 RAG检索
检索是记忆系统与 RAG(检索增强生成)的交汇点,但它比普通 RAG 多出两个普通相似度搜索不会考虑的因素:时间和权限。如果只按向量相似度找记忆,就会漏掉"这条记录现在还成立吗"和"这条记录当前调用者能看吗"这两个关键问题。
记忆检索的输入包括当前任务、主体权限、时间,以及候选记忆和五类评分信号;输出是经过过滤和重排的、带来源的记录。
排序可以用一个加权分数表达。对候选记忆 m,其排序分数为:
score(m) = α × Rel(m) + β × Rec(m) + γ × Imp(m) + δ × Src(m) − Penalty(m)
其中 Rel 是相关性,衡量记录与当前任务的匹配程度;Rec 是时效性,衡量记录的新旧与当前时刻的接近程度;Imp 是重要度,衡量这条信息对决策的权重;Src 是来源置信,衡量信息出处的可靠程度。α、β、γ、δ 是对应的权重,决定这四项信号各自对排序的贡献;Penalty 是冲突惩罚,当多条记录互相矛盾时对相关记录施加的扣分。
这个公式的关键在于它产生的是排序分数,而不是真值概率。分数高只表示"更值得被取回供模型参考",并不表示这条记录就是真的或应该被无条件采纳。四项权重需要依任务校准:有的任务偏重相关性,有的任务必须优先时效。
真正决定隐私安全的是权限的位置。权限必须在评分之前做硬过滤,绝不能把它做成一个"负分惩罚项"混进上面的公式里。如果权限只是扣分,一条"很相关但属于另一个用户"的记忆仍可能因为相关性极高而总分靠前,最终被模型读到,造成隐私泄漏。正确的顺序是:先按用户和项目作用域做硬性过滤,只保留当前主体有权访问的记录,再对剩下的记录做评分和重排,最后才交给模型。
真实系统通常不会只依赖单一的向量检索,而是组合语义检索、关键词匹配、时间过滤、实体关系和重排等多种手段,以弥补纯向量相似度在精确匹配和时效表达上的不足。无论组合多么复杂,那条红线不变:作用域过滤必须先于一切评分,权限永远不能用软惩罚来代替硬过滤。
5整合、更新与遗忘管理
当新事实与旧事实冲突时,系统面临三种选择:覆盖旧的、让两者并存、还是询问用户。答案取决于证据的强度,而正确做法是保留来源和时间,把"当前值"与"历史事件"分开存放,而不是物理覆盖掉旧记录。
整合管理接收新旧记录、时间、来源和用户纠正作为输入,输出当前视图、历史轨迹、冲突状态或删除结果。它的核心原则是版本失效而非物理覆盖:当高置信的新证据出现时,可以让旧记录失效、不再作为当前值参与检索,但绝不抹去它曾经存在过的痕迹。这条审计轨迹的意义在于,将来如果发现新证据本身有误,系统还能回溯到被失效的版本,而不是面对一片空白。
摘要属于有损压缩,这是它和原始记录的本质区别。把多条事件压缩成一句"用户偏好 X"时,必然丢失细节。因此摘要必须保留关键约束,并链接回原始引用,让任何被压缩掉的细节在需要时仍可追溯。一个没有证据链接的摘要,只是模型对记忆的二次叙述,其可靠性并不高于一次幻觉。
这就引出一条明确的边界:模型反思不是事实验证。模型写出的"总结记忆"仍然可能幻觉,即使它读起来通顺合理。因此系统必须保留证据和置信度,让整合的结果始终可以回溯到来源,而不是让模型的一段自我总结自动升级为事实。
记忆的生命周期也不应只增不减。系统需要支持过期、删除、用户纠正和按法规保留。过期适用于那些设定了保留期的记录;删除响应用户的明确请求;用户纠正是最高优先级的更新来源;而按法规保留则意味着某些记录即使被请求删除,也必须按合规要求留存。
最终的"当前值"只是在明确规则下最适用的那条记录,而不是"绝对正确的事实"。当冲突无法通过规则裁决时,正确的做法是让它们并存并标记冲突状态,或者直接询问用户,而不是由系统擅自挑选一个版本。模型总结永远不能充当事实验证,它只能提出候选,不能充当裁决者。
6记忆也是攻击面安全
记忆一旦可写,就同时成为攻击面。最危险的路径是一条恶意网页内容通过记忆污染未来很多次任务:Agent 在浏览网页或调用工具时,把页面里的提示注入或工具返回的错误内容写进了长期记忆,于是这次单点污染被持久化,之后每一次相关检索都可能把恶意指令带回上下文,影响一连串后续任务。
记忆安全接收外部候选、来源、敏感度和目标作用域作为输入,输出允许写入的数据记录或拒绝决定。它的第一道防线是来源分级:外部内容默认只是数据,不是规则。数据可以被检索和引用,但绝不自动获得"指示系统如何行动"的地位。只有经过明确授权的内容才可能成为规则,且这一提升不能由外部内容自己触发。
第二道防线是写入前的内容过滤和作用域限制。候选内容在进入长期记忆之前要经过检查,识别其中的指令性语言和可疑模式;同时把写入限制在最小作用域内,让任何被污染的内容都只能影响最小的范围。
第三道防线是读取时标明出处。当一条记忆被取回并交给模型时,它必须带着来源标签,让模型知道这是"来自某个网页的数据",而不是"系统自身的策略"。出处标注使模型有机会对可疑内容保持警惕,而不是把外部文本当作可信指令直接执行。
还有一条不可触碰的红线:权限、系统策略和秘密不得由普通记忆覆盖。无论一条外部内容看起来多么权威、多么匹配当前任务,它都不能改写系统级别的规则或凭据。这类内容要么根本不进入可被普通记忆影响的层,要么在检索时被强制隔离。
过滤机制的有效性有明确边界:过滤只对已经测试过的攻击模式有效。跨用户泄漏和间接注入这类更隐蔽的攻击,无法仅靠内容过滤兜底,必须通过专门的评测持续检验。写入过滤是必要的防线,但不是充分的安全保证。
7完整记忆链综合
把前面的各环节串起来,一条记忆要安全地产生价值,需要经过一条完整的链式流程,每一步都有明确的输入、输出和治理判断。
第一步是提取:从任务执行过程中识别候选事实与事件。模型在对话或工具调用中产生的原始内容里,只有一部分值得进入记忆,提取这一步就是把"可能有用"的素材从"转瞬即逝"的噪声里区分出来。
第二步是写入决策:按效用、置信和敏感度决定是否写入。这一步回答"值不值得存、够不够可信、会不会泄密"。低效用、低置信或高敏感的内容在这里被拒绝或转为待确认。
第三步是整合与关联:对通过写入决策的内容做去重、关联已有记录、设置作用域与保留期。去重避免同一事实产生多个副本,关联把新记录接到相关实体和既有证据上,作用域与保留期则提前规定了它将来对谁可见、何时失效。
第四步是检索与重排:按任务、权限和时间取回相关记录并排序。作用域过滤在评分之前硬性执行,然后才综合相关性、时效性、重要度和来源置信对候选排序,最终只把最值得参考的带来源记录交给模型。
第五步是注入与使用:把带来源的记忆注入上下文,支撑当前任务的完成。此时模型看到的是"来自某时、某处、由谁提供的记录",而不是一段失去出处的裸文本,这让模型能够恰当地权衡它。
第六步是更新与遗忘:依据任务结果、用户纠正和过期规则,对记录做更新或删除。任务过程中发现旧记录已不适用,用户可以显式纠正,保留期到期触发失效,这些都会回到整合环节,让记忆在闭环中持续收敛。
这六个环节构成一个可重复的循环,而贯穿始终的是同一组约束:写入有门控、检索有权限、冲突有版本、外部内容始终是数据而非规则。记忆不是一次性的存取动作,而是这条链上每个环节都带着治理判断的生命周期管理。
8完整示例:一次偏好变化怎样进入、命中并被纠正案例推演
用一个具体案例把整条记忆链走一遍。用户先说"报告默认中文",后来又说"这个项目必须英文",系统该记住哪一句?答案是两句话都记住,但在不同的作用域上,最终由更具体的约束在具体场景里胜出。
写入阶段,第一句"报告默认中文"形成一个用户级候选偏好,来源是消息 m17,置信度 0.8。第二句"这个项目必须英文"形成项目 P 级的显式约束,来源是消息 m42。两者的关键差别在于作用域:m17 作用于整个用户,m42 只作用于项目 P,且 m42 是显式约束、优先级更高。它们并不互相覆盖,因为作用域不同,一条是全局默认,一条是局部覆盖。
检索阶段,当要生成项目 P 的报告时,系统先按用户与项目权限做硬过滤,再以项目作用域、时效和显式程度对候选重排。m42 因为作用域更贴近当前任务、且是显式约束而胜出;而 m17 作为全局默认仍然有效,只是被更具体的规则压过。如果换一个不属于项目 P 的报告,m42 不在作用域内,m17 的中文偏好仍然适用。
使用阶段,注入上下文时输出的是"项目 P:英文(m42)"这样带作用域和来源的表述,而不是含糊的"用户偏好语言"。带来源的表述让模型清楚地知道这条结论只适用于项目 P,且它的依据是哪条消息,从而不会把局部约束误用为全局偏好。
纠正阶段,若用户随后又说"P 也恢复中文",系统写入新事件 m58,并把 m42 标为失效,而不是删除历史。m42 仍然保留在审计轨迹里,只是不再作为当前值参与检索。此时三条记录的状态是:m17 默认中文(用户级,有效,被更具体规则压过);m42 项目 P 必须英文(项目 P 级,有效→被 m58 替代,替代前使用);m58 项目 P 恢复中文(项目 P 级,当前,使用并带来源)。
遗忘阶段,当用户要求删除语言偏好时,系统删除当前记录、派生摘要和检索索引,并留下不含原值的合规删除凭据。原值本身被移除,但删除这一动作留下可审计的记录,以满足合规要求。
这个案例的核心结论是:m42 胜出只表示它对项目 P 更具体、更适合当前任务,绝不表示用户的全局中文偏好被删除或否定了。作用域决定了哪条记录在哪个场景里生效,而版本与来源让每一次选择都可以被追溯和纠正。
| 候选 | 作用域 | 状态 | 本任务是否使用 |
|---|---|---|---|
| 默认中文 m17 | 用户 | 有效 | 被更具体规则压过 |
| P必须英文 m42 | 项目P | 有效→被m58替代 | 替代前使用 |
| P恢复中文 m58 | 项目P | 当前 | 使用并带来源 |
9原创图:记忆是一条有治理闸门的生命周期可视化
如果只做"向量化之后写进数据库",记忆系统其实只完成了最容易的一小步。原因在于,写入存储本身不带来任何治理能力:它不会自动拥有权限、版本和删除能力,也不会自动区分"当前值"与"历史事件"。把这一步当作记忆的全部,正是很多系统出问题的起点。
这张原创图描述的是记忆的完整生命周期:候选事件首先经过写入闸门,只有通过效用、置信和敏感度判断的内容才能进入带版本和来源的记忆库;随后经过权限、时间和任务三重约束的检索,取回的内容被用于行动;行动产生的反馈再反过来触发纠正或删除,形成闭环。整条路径概括为图 1 所示的四要素:写入控制、版本化存储、读取隔离和可逆更新,这四者共同构成可信记忆的必要条件。
生命周期图的输入是候选事件和任务请求,输出是经过写入闸门、版本化存储、权限检索与反馈纠正后的记忆。图中每一个箭头都是一次可记录的状态变化,这使它可以充当故障定位工具:当记忆出错时,可以沿着箭头判断错误发生在写、存、找、用还是改的哪一个环节,而不是笼统地把问题归为"模型记错了"。
需要注意这张图的定位:它描述的是治理流程,而不是某种具体的数据库实现。向量数据库、图数据库或普通关系型存储都可以承载这套流程,但流程本身——尤其是权限过滤、版本失效和删除——必须被显式地实现出来。仅仅完成向量化写库,不会自动获得图中所画的任何一道闸门。
10冲突不是“选相似度最高”,而是做时态与作用域推理一致性
"住在上海"和"下周在北京"为什么不能互相覆盖?因为它们根本不是同一类事实:前者是稳定的当前状态,后者是一个未来计划。把一个计划当作现状、或用现状覆盖计划,都是错误的。冲突检测不是"选相似度最高的那条",而是先做时态与作用域的推理。
第一步是区分事实类型。稳定属性、当前状态、计划、一次事件和推断,各有不同的有效期和可信度。稳定属性(如"用户住在上海")长期成立;当前状态随时可能变化;计划指向未来,尚未发生;一次事件是过去的记录;推断则是模型从证据中得出的、置信度较低的候选。类型不同,冲突的裁决方式就不同。
第二步是划定冲突的范围。只有同一实体、同一属性、且时间作用域重叠的两条记录,才构成直接冲突。不同作用域的记录应当并存,读取时按作用域选择,而不是把它们强行合并成一条全局偏好。计划与已发生的事件分属不同事实类型,应当保留各自的类型,而不是把计划当成现状。
第三步是按规则裁决当前视图。来源权威、用户确认、事件时间和写入时间共同决定哪条记录作为当前值:用户明确确认的高于推断,权威来源高于转述,新事件高于旧事件。当同属性的新值出现时,旧值失效但历史保留,绝不物理覆盖审计记录。
第四步处理无法裁决的情况。当来源同级且互相矛盾时,正确的做法是把冲突标记出来并澄清,而不是用向量分数裁决真伪。语言模型凭流畅度把两条矛盾记录合并成一段通顺的文字,恰恰是最危险的路径,因为流畅度不携带任何真值信息。
冲突推理的输入是实体、属性、事实类型、作用域、有效时间和来源,输出是并存、失效、当前选择或澄清请求四种结果之一。它的核心约束是:当前视图不是永恒真相,而只是在明确规则下当前最适用的记录;计划与现状、不同项目的偏好,都不能被相似度分数强行覆盖。真正的冲突裁决依赖时间与作用域的结构化推理,而不是向量距离的大小。
| 情况 | 处理 | 不能做什么 |
|---|---|---|
| 同属性新值 | 旧值失效,保留历史 | 物理覆盖审计记录 |
| 不同作用域 | 并存,读取时按作用域选择 | 提升成全局偏好 |
| 计划 vs 已发生 | 保留不同事实类型 | 把计划当现状 |
| 来源同级且矛盾 | 标冲突并澄清 | 用向量分数裁决真伪 |
11评测要覆盖写、找、用、改、忘五个阶段实验设计
检索 Recall@10 很高,为什么 Agent 仍可能被旧记忆带偏?因为召回只是"找得到"的度量,它不回答"取回的是不是当前应使用的值"。一条已经失效的旧记忆可能被高召回地取回来,然后被模型当作现状采用。评测因此不能只看检索,而要覆盖记忆生命周期的五个阶段:写、找、用、改、忘。
评测应当离线构造跨轮任务:在长对话中埋入稳定偏好、临时约束、相互冲突的更新、敏感信息和恶意外部文本,然后分别测试这五件事是否都正确——正确写入(该存的存、不该存的拒)、在适当任务取回(相关记忆出现在需要的场景,无关记忆不出现)、引用最新有效值(冲突更新后用的是当前值而非旧值)、执行纠正和删除(用户纠正与删除请求真正生效)。每阶段都记录错误归因,把生成器的失败与检索的失败分开,避免把一个"检索到了但模型没用对"的问题误算成检索失败。
但单看各阶段的正确率还不够,记忆的价值要用净贡献衡量。记忆相对无记忆基线的净贡献 V 定义为:
V = Success(memory) − Success(noMemory) − λ × Cost(privacyStale)
其中 Success(memory) 是使用记忆时的任务成功率,Success(noMemory) 是不使用记忆时的任务成功率,二者之差是记忆带来的任务增益。Cost(privacyStale) 是隐私越界和陈旧引用带来的成本,λ 是把这部分风险成本换算到与成功率可比较尺度的权重。净贡献为正,才说明记忆在扣除代价后仍然值得。
评测报告需要同时给出端到端任务成功、错误记忆使用率、跨用户泄漏率、纠正生效延迟、删除残留率,以及 token 消耗与 P95 延迟,并把这些结果与"仅最近历史""滚动摘要""检索记忆"几种基线做消融对比。安全测试还必须包含持久化提示注入:即使一段恶意网页内容在语义上与当前任务高度相关,它也绝不能升级为程序性规则,这一点要在评测中显式验证。
五阶段评测的输入是含稳定、临时、冲突、敏感和恶意记录的跨轮任务,输出是写入、检索、使用、纠正、遗忘各阶段及端到端的指标。它的两个边界需要牢记:净贡献依赖具体指标尺度和系数 λ,不能跨项目直接比较;高召回也不能用来抵消跨用户泄漏——一个召回满分但会泄漏他人记忆的系统,其净贡献依然是负的。
- MemGPT:分层记忆与虚拟上下文管理。
- Generative Agents:记忆流、检索与反思机制。
- MemoryBank:长期交互中的记忆存储与更新。