记忆API返回语义相似度最高的结果,但丢弃了记录间的时间因果关系,导致Agent基于过时信息做决策。
记忆存储可能知道真相,但接口层可能把它丢弃了。
这篇文章源于 Edward Izgorodin 在 Edward Izgorodin 的帖子《Agent Memory: Everything It Remembers Has the Same Authority, and That Is the Bug》下的一场讨论。以下一些尖锐的观点有对应的署名,我已尽可能标注。
想象一个 AI 智能体向它的记忆系统提了一个直接的问题:
记忆 API 返回:
[
{"content": "生产数据库是 PostgreSQL。", "score": 0.94},
{"content": "生产数据库是 MongoDB。", "score": 0.91}
]
检索成功了。它找到了两条高度相关的记忆,给出了评分、做了排序并返回了结果。智能体选择了 PostgreSQL。
然而,生产环境早在四个月前就已经迁移到 MongoDB 了。
检索过程没有任何失败。PostgreSQL 这条记录在语义上确实可能与查询更相似。但语义相关性从来就不是智能体需要回答的问题。存储知道更多:PostgreSQL 从 2025 年 1 月到 2026 年 4 月一直是主导,2026 年 4 月在新的架构决策下被 MongoDB 取代。在存储和智能体之间的某个环节,这种关联关系消失了。

API 返回了记录,却丢弃了记录之间的关系。这是一种截然不同的记忆失败,也是本文要讨论的核心问题。
在深入讨论之前,有必要诚实地说清楚什么才是真正的新问题,因为这个问题的一部分在智能体出现之前就已经被解决了。
将事实"何时为真"与系统"何时得知"区分开来,是双时态建模(bitemporal modeling),在 SQL:2011 中已标准化为应用时间(application-time)和系统版本(system-versioned)表。Edward 在讨论中提到了这一点,他说得对——数据库世界处理"彼时为真、此刻为真"已经超过十年了。一个构建良好的存储可以用关闭事实的有效期窗口来替代直接覆盖,历史记录依然可解释。
所以真正有趣的问题不在存储。如果你的存储仍然是更新时删除,先解决这个问题,相关文献已经准备好了。本文要讨论的问题从更上一层开始:即使存储保留了所有信息,检索接口通常也只是把一个扁平的有序列表交给智能体,结构在传递过程中被丢弃了。存储解决了问题,但 API 在输出时又把问题还原了。
这句话出自 Edward 在讨论中的发言,它可能是一句打破整个抽象层的话。一旦你静下心来理解它,剩下的内容就顺理成章了。
大多数 AI 记忆接口都沿用了一种熟悉的检索形态:给系统一个查询,返回一组按相关性排序的结果。可能附加了元数据、时间戳、文档 ID、来源、置信度等。但核心抽象没有变:记忆是一个条目集合,检索返回的是最匹配的条目。
这在"找东西"这个场景下效果很好。但智能系统越来越需要记忆去做更难的事:呈现系统当前知道什么、曾经知道什么、知识从何而来、是否仍然有效、以及明显矛盾的记录之间如何关联。排序列表是这种世界的糟糕表示,因为记录之间的关系本身就是知识的一部分,而列表没有地方容纳它们。
以两条记录为例:客户退款需要经理审批,以及客户退款金额在 100 美元以下不需要审批。第二条可能是对第一条的纠错,因为第一条录入了错误;也可能是对第一条的取代,因为政策变了;也可能两条在不同的司法管辖区都成立,而第一条对当前交易不再适用。这些不是同一种操作的变体,它们对历史做出了截然不同的声明。

在存储层,这三种情况看起来都像是一次更新。但在审计层和检索层,它们是根本不同的事件。
CRUD 训练我们只用一个动词——UPDATE,但持久的记忆至少需要三个,而第三个经常被遗漏。
取代(Supersession)说的是世界变了。政策 A 曾为真,政策 B 当前为真,A 没有错,它只是关闭了。纠错(Correction)说的是我们的记录是错的,包括在智能体可能依赖过它的那段时间里,所以 A 从未为真。否定(Invalidation)才是值得停下来仔细想的,因为它根本不是一个真值声明,而是一个权威声明。一条记录可能完全为真,但不再适用。
这个区别是承重结构。一个将这三者都压缩为单一值变更的存储,依然能干净地回答"现在什么为真",但会在任何人问"为什么智能体在 3 月 17 日批准了那笔交易"时悄然失效。答案可能取决于一条已关闭、或已纠错、或被剥夺了权威性的记录,而那个存储已经不知道是哪条了。
这里才是让所有正在构建此类系统的人在写下一个功能之前都应该检查一下自己系统的地方。
Giulio D'Erme 看了原讨论后,去数了数自己的语料库:他记忆存储中 152 条备忘录里,声明了有效期窗口或取代边的记录是 0 条;他的文档中 59 份文档里也是 0 份。引擎可以读取这些字段,但写入记忆的东西从来没写过这些。正如他所说的:可用不等于使用,这对 Schema 和对工具一样适用。
这就是藏在丰富 Schema 背后悄悄发生的失败模式。你可以交付读取路径、编写字段文档、看着一个活跃的 API 服务着一个已死的功能,因为写入记忆的东西——一个提示词、一个模板、或者另一个智能体——从未学过这些字段。一个没有被填充的取代列不是保留,而是空列。
Tae Kim 从生产交易数据中描述了同样的形态:同一家公司在收购前后作为不同节点出现,存储默默选了一个。两边都加上时间范围并返回两个版本有帮助。但后来咬人的是,智能体在两者之间的选择仍然悄无声息地消失了——这正是下一个要讨论的问题。
PostgreSQL 的例子暴露了排序检索背后的假设。相似度得分大致回答的是"这条记录与查询有多相关",而不是"哪条记录当前有效"。这两者有关联,但不是同一回事。PostgreSQL 得了 0.94 可能只是因为它包含了查询中的精确措辞,而 MongoDB 得 0.91 是因为迁移决定的表述不同。检索完成了它的工作,但智能体仍然得到了错误的答案,因为 0.94 > 0.91 悄悄变成了冲突解决,而语义相似性从来就不能确立任何关于权威性的东西。
这就是为什么我开始把记忆看作基础设施(Infrastructure)而不是数据库功能。一旦记忆参与了重大决策,检索质量只是子系统的一个属性。来源、权威性、生命周期、时间有效性以及纠错语义同样重要。最接近的记忆不一定就是掌舵的记忆。
记忆系统往往把冲突记录当作检索质量问题来处理:删除旧的那条、提高新那条的排名、用元数据过滤掉其中一条。有时候这是对的。但有时候矛盾恰恰是记忆知道的最重要的事。
以采购部门的一条记录(供应商 X 已批准用于受监管工作负载)和安全部门的一条记录(供应商 X 被禁止)为例。两条可能都在各自的有效期内。可能不存在任何取代关系。正确的做法不是默默决定哪条赢,而是报告这两条记录存在冲突、各自来自哪里、各自由哪个权威机构发布、以及需要人工介入解决。

如果存储知道冲突存在,但 API 返回的是两条普通的有序命中,在最需要这个信息的时刻,分歧恰好消失了。
这就是为什么修复比添加一列元数据要困难得多。如果记忆包含关系,响应类型就必须能够携带关系。一个更丰富的接口在概念上可能会返回如下内容:
{
"records": [
{"id": "A", "content": "生产环境使用 PostgreSQL。"},
{"id": "B", "content": "生产环境使用 MongoDB。"}
],
"relationships": [
{
"type": "supersession",
"from": "A",
"to": "B",
"effective_at": "2026-04-15T00:00:00Z"
}
]
}
具体的 schema 不是重点,我也不是在提议将这个 JSON 作为标准。概念上的变化在于:响应不再是一组记忆列表,而是一个知识状态的表示,它可以携带矛盾、替代、修正、失效、出处、权威作为一等公民内容。一旦这些关系影响到智能体行为,它们就不能被困在存储层。
有两个实际问题伴随而来,在线程中浮现后,随着我和 Edward 进一步深入探讨,变得更棘手了。
第一个是预算问题,结果它比资源分配要深得多。排序列表虽然简陋,但成本很低,而 top_k 是一种决定丢弃什么的简洁方式。一旦响应携带事实、关系、权威、出处和先前的决策,问题就停止于排序,而变成在有限上下文预算内分配不同类型知识的问题。排名较低的一条权威边可能比下一条高度相关的事实更重要,而丢弃一条替代关系可能会改变幸存记录的含义。
诱人的修复方案是在排序之后选择边,作为对 top_k 返回结果的后置过滤。Edward 的反驳是重塑我想法的部分:要判断一条替代边是否值得携带,你必须已经持有着它所替代的那条记录。因此边的填充(edge hydration)不能是一个后置过滤器。它必须影响哪些候选项首先被考虑,这意味着分配发生在排序之前而不是之后。这是对检索堆栈比向响应添加字段更深层次的改变,正是在这一点上,"改进存储层"不再是解决方案。
第二个是寻址问题,它需要比"给冲突一个身份"更精确。我的第一反应是用这对记录来键控分歧,A 与 B 冲突。Edward 的改进更好:配对是不稳定的,因为第三条记录一到达,"A 与 B 冲突"就不再是同一个对象,而昨天的决定现在指向一个不再以那种形状存在的冲突。改为用记录所争论的主题来键控,即问题本身而非配对,这样无论多少条记录随着时间推移堆积在其下,决定都保持可寻址。
智能体上次做了什么?
第二个问题指向一种关系,一旦智能体反复碰到相同的知识,这种关系就变得重要。假设昨天的智能体遇到记录 A 和 B 冲突,确定 B 因为安全对受监管工作负载拥有权威而应该遵循,并依据 B 采取了行动。今天另一个智能体碰到了同样的冲突。如果系统只存储了 A 和 B,今天的智能体就得从头开始解决它。如果昨天的决定只存在于别处的审计日志中,它存在但在可能阻止重复的时刻无法获取。
这就是推理账本(Reasoning Ledger)变得具有操作意义的地方,也是我想保持一条线而不是模糊它的地方。我仍然认为持久记忆和决策记录值得不同的保管。知识可以被替代;决策记录不能,因为它必须不断说明在当时相信什么,即使在信念被撤回之后。这种分离属于存储层。
但它不应该在检索层面继续存在。Mike Czerwinski 在帖子中直截了当地提出了风险:如果智能体在冲突记录之间的选择没有记录在案,沉默的解决只是把问题从存储重新定位到推理步骤,在更难找到的地址上出现同样的 bug,因为现在存储看起来是诚实的。Tae Kim 开始把这些选择写回为事件,仅仅是因为客户询问一个奇怪的输出,而没有任何东西可以指向。通常是审计压力而不是架构偏好使这个领域变得真实。
所以我主张的形状不是将记忆 + 账本呈现为两样东西。它是记录系统背后的一组统一接口,可以一起返回事实、关系、权威和相关先前的决定。独立的保管,一个接口,这是我找到的表达它的最简洁方式。

不同的子系统可能有非常不同的存储需求、保留策略和安全边界。错误在于假设这些实现边界必须决定智能体在检索时被允许知道什么。存储边界不一定是检索边界。
API 在做断言
每个接口都决定着什么在抽象中存活下来。一个只返回内容和相似度分数的记忆 API,在隐含地告诉智能体:记录是独立项,排序是它们之间唯一有意义的关系。当记忆意味着获取段落来塞进提示时,这是一个合理的断言。当记忆携带策略、组织决策、历史状态、权威和自主智能体的证据时,它就变成了一个危险的断言。
以下是在一个丰富的记忆系统被压缩成排序列表时可能会消失的东西:
从 API 的角度来看,没有问题。从智能体的角度来看,几乎所有重要的东西都消失了。
我们已经投入了大量努力改进检索:更好的嵌入、混合搜索、重排器、元数据过滤器、图检索、更大的上下文窗口。所有这些都帮助系统找到相关信息。找到正确的记录和理解它们彼此之间的含义是两个不同的问题,而智能体系统正在将记忆推向第二个问题。如果存储保留了那种结构但接口丢弃了它,改进存储将无济于事。API 已经成为有损边界。
一个知道 A 被 B 替代但交给智能体 [A: 0.94, B: 0.91] 的记忆 API,不仅仅是丢弃了一些元数据。
它改变了记忆的含义。
产生这篇文章的讨论已经将问题推进到了我最初提出它的地方。"排序列表没有地方放置边"是一个正确的初步切入点,这是关于响应形状的陈述。更尖锐的版本,我正在追逐的版本,是某些边需要持久的身份,而且必须有东西来决定哪些边值得填充,在排序之前而不是之后。这不再是对响应形状的断言。这是对检索本身形状的断言。这需要更长的对话,而且,我怀疑,是下一个话题。
感谢 Edward Izgorodin,他的帖子开启了这个话题,"没有地方放置边"的框架将其锚定;也感谢 Giulio D'Erme、Tae Kim 和 Mike Czerwinski,他们在上文引用的讨论中的贡献。方向不同,撞上了同一堵墙。