Memory 记忆设计讨论:Agent Memory 到底应该是什么?

从一个 Agent 开发者的视角重新拆解 Agent Memory

这是一个关于 Agent Memory 的系列文章。

第一篇不急着讨论某个框架,也不急着选择数据库。先把一个更基础的问题说清楚:Agent Memory 到底应该是什么?

后续三篇会继续讨论:

  1. 为什么 Agent Memory 不能只靠向量数据库
  2. Agent Memory 的数据模型:Event、Evidence、Candidate 与 Memory Fact
  3. Agent Memory 的安全边界:权限、删除、版本与回执

一、先说结论

我以前也容易把 Agent Memory 理解成两件事:保存聊天记录,或者接入一个向量数据库。

但真正把 Agent 的任务连续性拆开之后,会发现这两种理解都不完整。

Memory 要解决的不是"过去说过什么",而是:

Agent 在下一次工作时,能不能找回正确的信息,知道这些信息是否仍然有效,并且能够继续完成任务。

比如,用户对助手说:

继续处理我上次的旅行计划。

这句话看起来很简单,但 Agent 至少需要知道:上次计划去哪,出行日期有没有变化,预算是多少,同行人是谁,已经看过哪些方案,哪些偏好是长期的,哪些只是这一次的临时要求,以及上次任务究竟做到哪一步。

如果系统只保存了聊天记录,它也许能找到一大段对话,却不一定能回答这些问题。

所以我现在更愿意这样定义 Agent Memory:

Agent Memory 是一套把历史事件、事实、偏好、任务状态和证据来源组织成可信上下文的能力,让 Agent 在正确范围内记住正确的信息,并在需要时找回来。

二、聊天记录不等于 Memory

聊天记录当然有价值,它是最重要的原始材料之一。但原始材料和可继续使用的记忆不是一回事。

还是用旅行计划举例。

用户先说:"这次预算尽量控制在 8000 元以内。"

过了一会儿又说:"如果是直飞,预算可以放宽一点。"

第二天用户补充:"这次带孩子,航班不要太晚。"

这几句话的性质并不一样:

  • 8000 元是这次行程的预算约束
  • 直飞优先可能是本次选择,也可能逐渐成为偏好
  • 带孩子时不要太晚,是一个有条件的偏好
  • 它们都来自对话,但适用范围和有效时间不同

如果系统把所有句子放在一个"聊天记录"里,下一次只能重新让模型阅读和猜测。如果系统把它们整理成带有范围、时间和来源的结构化信息,Agent 才能更可靠地继续工作。

因此,Memory 不是把聊天记录复制到另一个表里,而是对历史信息进行整理、筛选、标注和治理。

三、Memory 也不只是摘要

摘要比原文短,但短不代表可靠。

一个摘要可能写成:

用户喜欢直飞,预算 8000 元,同行有孩子。

这句话看起来很方便,但它丢失了几个关键问题:

  • 这是用户长期偏好,还是这次旅行的临时要求?
  • 预算是硬性上限,还是大致目标?
  • "同行有孩子"适用于哪一次行程?
  • 这条信息来自哪次对话?
  • 后来用户有没有修改过?

没有来源、范围和版本的摘要,可能比原始对话更危险,因为它看起来更像一个确定事实。

一个可信的 Memory 不只保存"内容",还要保存内容的上下文:谁说的、什么时候说的、适用于什么事情、是否已经被更新、是否需要用户确认。

四、Memory 和任务状态也不是一回事

用户说"继续上次的旅行计划",除了需要找回事实和偏好,还需要恢复任务进度。

例如:

  • 目的地已经确定
  • 航班已经筛选
  • 酒店还没有比较
  • 付款还没有确认

这些信息描述的是任务当前做到哪一步。它们应该由任务运行状态来管理,而不是藏在一条语义记忆里。

Memory 可以告诉 Agent 用户通常偏好可取消的酒店;任务状态则要明确告诉 Agent 当前这个任务是否已经选定酒店、是否等待用户确认。

这两个概念混在一起,就容易出现两类错误:把一次任务的临时状态误认为长期偏好,或者把长期偏好误当成当前任务已经完成的步骤。

我的判断是:

Memory 提供历史依据和连续性,Task Runtime 负责当前任务怎么继续,权限和回执负责动作是否有资格执行、是否真的成功。

五、一个可信 Memory 要经过哪些步骤

如果把 Memory 看成一条流水线,大致需要经历以下过程。

1. 接收事件

系统先接收发生过的事情,比如用户说了一句话、修改了预算、查看了某个方案,或者外部系统返回了结果。

这一步的重点不是"尽量多收集",而是明确数据来源和用户授权。系统不能默认读取所有设备、文件和应用。

2. 提取候选

系统从原始事件中提取可能值得记住的内容。

比如从多次选择中发现"用户通常优先直飞",或者从当前对话中发现"这次不要安排晚于晚上九点的航班"。

这里的结果只能叫 Candidate,也就是候选记忆。模型认为某件事可能成立,不代表它已经是系统可以长期相信的事实。

3. 治理候选

系统需要判断候选的来源、作用范围、有效时间、敏感程度,以及它是否和已有内容冲突。

如果用户以前说过"预算不超过 8000 元",现在又说"这次预算可以到 10000 元",系统不能简单地让两条记忆同时生效。

4. 分类保存

原始事件、证据、长期事实、偏好和任务状态应该分开保存。它们的生命周期和读取方式不同。

5. 按条件召回

当用户再次提出请求时,系统先确认用户、任务和范围,再读取当前有效的任务状态和事实,最后用关键词、向量和时间等方式补充相关内容。

6. 更新和纠正

记忆不是写入之后永远不变。用户可以修改、暂停或删除它,新的证据也可能替代旧版本。

这也是为什么 Memory 不应该被设计成一个只进不出的黑盒。

六、工程上至少需要哪些对象

如果要把上面的概念真正实现出来,至少需要区分几类数据。

Event:发生记录

Event 记录系统发生了什么,例如用户修改了预算,或者任务完成了酒店筛选。

Evidence:证据来源

Evidence 记录这条信息来自哪里,可以是某次对话、某个文档、一个表单或外部系统的返回结果。

Candidate:候选记忆

Candidate 是模型或规则提取出的可能记忆。它还没有通过治理,不能直接当成正式事实。

Memory Fact:规范记忆

Memory Fact 是经过判断后,系统允许后续使用的事实或偏好。它需要带来源、作用域、版本和有效时间。

Task State:任务状态

Task State 记录目标、已经完成的内容、缺失项、下一步和阻塞点。

最容易犯的错误,就是把 Candidate 直接写成 Memory Fact。模型说"用户可能喜欢早班机",不等于系统就应该永久相信这件事。

七、从"继续旅行计划"看完整闭环

用户再次说:"继续处理我上次的旅行计划。"

系统应该按这样的顺序工作:

  1. 找到可能对应的旅行任务;如果有多个相似任务,先澄清。
  2. 确认当前用户、任务范围和信息权限。
  3. 找回目的地、日期、预算、同行人和相关证据。
  4. 区分长期偏好、本次要求和临时例外。
  5. 恢复任务状态,明确已经完成和仍然缺失的步骤。
  6. 生成下一步建议,但涉及付款或预订时重新请求确认。
  7. 执行动作后查询外部系统的真实结果。
  8. 只有确认成功,才把结果写入已完成状态或新的事实。

这条链路说明,Memory 并不是一个孤立组件。它需要和任务运行、权限控制、动作执行以及结果回执配合起来。

八、应该分几步建设

我倾向于把建设过程分成三步。

第一步:Remember,先记得住

先让系统能够跨会话找回事实、证据和任务状态,重点验证来源、作用域、跨任务隔离和用户纠错。

第二步:Reconcile,再记得准

再处理冲突、过期、版本、长期偏好、临时例外、删除和多来源合并。

第三步:Continue,最后接着做

最后让任务能够跨时间、跨设备和跨应用继续,但关键动作仍然需要当前授权,并以外部系统的真实回执作为完成依据。

这三步不适合颠倒。没有稳定的来源和任务状态,个性化只会放大错误;没有准确的事实和版本,跨设备执行就不可靠。

结尾

Agent Memory 不是让 Agent 记住一切。

它真正要做的是:

在正确范围内记住正确的信息,在需要时把它找回来,并且允许用户理解、纠正和控制它。

后面的三篇文章会继续把这个判断拆开:为什么向量数据库不等于 Memory,这些数据对象如何设计,以及权限、删除、版本和回执应该放在哪些控制点上。