文章指出大多数AI应用失败不是因为模型遗忘,而是记住了错误的东西。作者区分了对话历史、最终结论、证据和经验教训四种不同的记忆类型,并分析了各自的适用场景。

大多数生产级 AI 系统之所以失败,不是因为模型忘记了什么。
而是因为应用程序记住了错误的东西。
假设你正在构建一个 AI 编程助手。上周,它在审查一个 Pull Request 时发现了一个分页 bug,该 bug 是由非确定性排序引起的。今天,另一个 Pull Request 修改了相同的分页逻辑。
助手应该记住之前的那次事件吗?
乍一看,答案似乎显而易见。
"当然。存下对话就行了。"
但这引出了一个更加有趣的问题。
应用程序应该记住整个讨论过程、最终结论、导致该结论的证据,还是只记住在 bug 修复之后仍然成立的教训?
这些是截然不同的信息类型,而将它们混为一谈,正是许多 AI 应用程序开始出现问题的根源。
这是关于 LLM Memory 讨论中最大的误解之一。Memory 往往被简单地当作存储在数据库或向量索引中的对话历史。如果上一条消息看起来与当前请求相似,就检索出来并附加到提示词中。
这种方法在原型阶段效果很好。
在生产系统中却很少奏效。
真实的应用程序会随时间积累经验。有些信息随着新证据的确认而变得更加可靠。有些知识已经过时。有些观察被纠正了。还有一些被完全替换了。而应用程序观察到的大量信息根本不应该影响未来的决策。
换句话说,存储信息很容易。
决定什么值得被记住才是困难的部分。
一旦你开始以这种方式思考 Memory,就会意识到它不再是存储问题。
而是一个知识管理问题。
一个生产级 Memory 系统必须回答这样的问题:
这是一个观察还是一个已建立的事实?
它是在强化现有记忆还是在替换它?
它应该影响当前决策吗?
注意,这些问题都不涉及嵌入向量、向量数据库或上下文窗口。
它们关乎的是决定过去的经验如何影响未来的决策。
这正是我们将在本文中构建的思维模型。
我们不会关注记忆存储在哪里,而是探索什么值得成为记忆、知识如何随时间演变,以及如何构建一个能改善未来决策的生产级 Memory 系统。
因为生产级 Memory 系统不是由信息存储在哪里来定义的。
而是由经验如何被转化、演进、检索,以及最终被遗忘来定义的。
常见错误:将聊天历史当作记忆
当人们首次为 LLM 应用程序添加 Memory 时,实现方式往往简单得令人惊讶。
存储对话。当新请求到来时,检索之前的消息并附加到提示词中。
对于简单的聊天机器人,这种方法是完全合理的。如果用户问"我五分钟前问了什么?",对话本身就是答案。
问题开始于应用程序被期望从经验中学习而不是仅仅回忆它的时候。
让我们回到 AI 编程助手的例子。
在 Pull Request 审查期间,它发现了一个由非确定性排序引起的分页 bug。讨论跨越了数十条消息,因为开发者调查了不同的假设、测试了多种修复方案,最终得出了正确的结论。
一周后,另一个 Pull Request 修改了相同的分页逻辑。
应用程序需要再次获取整个对话吗?
这些消息中的大部分记录的是调查过程,而非教训。它们包含了不正确的假设、被放弃的想法,以及不再有价值的中间推理。
真正重要的是在调查结束后仍然成立的知识点。
游标分页必须使用确定性排序,当主排序字段不唯一时,包括一个次要键。
这一知识点比回放每一条导致它的消息要有价值得多。
这是许多 Memory 实现忽略的根本区别。
聊天历史记录了发生了什么。记忆保留了应该影响未来决策的内容。
这两个目标有重叠,但并不相同。
一旦你认识到这个差异,另一个认识就会自然跟上。生产级 Memory 系统不应该问"哪些之前的消息与这个请求相似?"
相反,它应该问"过去的经验中,哪些知识仍然相关且足够可信,能够影响这个决策?"
这种思维方式的转变改变了整个系统的设计方式。你不再无限积累对话,而是开始将 Memory 视为随时间演变的精心策划的知识。
本文的其余部分将探讨这种演进是什么样的,以及为什么在构建可靠 AI 应用程序时它很重要。
Memory、Context 和执行状态
LLM Memory 让人感到困惑的原因之一是,三个不同的概念经常被当作同义词使用:Memory、Context 和执行状态。
它们都以某种形式在请求之间携带信息,但解决的是完全不同的问题。
Memory 是因为可能改善未来决策而被保留的信息。
AI 编程助手可能记住某个仓库总是使用游标分页,或者某个团队更喜欢简洁的 Pull Request 评论。这些知识点在单次交互之外仍然有用。
Context 是为模型当前决策组装的信息。
它可能包括用户最新的请求、相关文档、工具输出、系统指令,以及少量检索到的 Memory。一旦请求完成,那个 Context 就消失了。新的请求可能会收到不同的 Context,即使它使用了相同的底层 Memory。
执行状态 跟踪正在进行的任务的进度。
假设一个 AI 智能体正在审查 Pull Request,在等待安全扫描完成时暂停。它需要记住已经分析了哪些文件、调用了哪个工具,以及执行应该从哪里恢复。这些信息保持工作流正确运行,但它不是应该影响未来 Pull Request 审查的知识。
思考这些概念的一个有用方式是,为每个概念提出不同的问题:
Memory 回答:"我们学到了什么?"
Context 回答:"模型现在需要什么?"
执行状态回答:"我们在工作流的哪个位置?"
这种区分很重要,因为它们有不同的生命周期。
工作流的执行状态在任务完成时消失。Context 每次调用模型时都会重建。然而,Memory 可以存活数周、数月甚至数年,随着应用程序获得更多经验而不断演变。
这也揭示了它们之间的一种重要关系。
Memory 不直接进入提示词。
相反,它首先经过一个检索过程来决定什么是相关的。只有被选中的 Memory 才会成为当前请求 Context 的一部分。
换句话说,一个应用程序可能存储了数千条 Memory,但只有少数几条应该影响任何单个决策。
Memory 随时间持久存在。
Context 为当前正在做出的决策而存在。
执行状态只存在到当前工作完成。
什么实际上算作 Memory?
生产级 AI 应用程序在其生命周期中观察到大量信息。每次对话、工具调用、文档检索、工作流执行和模型响应都会产生新数据。
其中大部分永远不应该成为 Memory。
考虑一个审查 Pull Request 的 AI 编程助手。
在审查期间,它读取代码库、分析静态分析结果、调用外部工具、评估多个假设,并在得出正确建议之前产生多个中间结论。
所有这些信息在审查期间都存在。
其中很少有值得在审查结束后保留的。
一个常见错误是假设如果某个东西曾经有用,就应该永远存储。实际上,生产级 Memory 应该有高度选择性。否则,应用程序会慢慢积累噪音而不是知识。
思考 Memory 的一个有用方式是:
Memory 是因有潜力改善未来决策而被有意保留的信息。
这个定义立即排除了大量信息。
临时工具结果不是 Memory。
中间推理步骤不是 Memory。
被丢弃的假设不是 Memory。
对话记录不自动成为 Memory。
这些信息可能有助于解决当前问题,但一旦那个问题完成,它们通常本身没有什么价值。
值得保留的是在原始交互之外仍然有用的知识。
回到分页的例子,调查本身并不是记忆。
持久的教训是:
游标分页应使用确定性排序,包括当主排序字段不能保证唯一时使用辅助键。
这一条教训是可复用的。它无需重放整个调查过程,就能改善未来的代码审查。
这种区别很微妙,但它从根本上改变了记忆系统的设计方式。
我们不再问"能存储什么信息?",而是开始问一个难得多的:
"什么信息值得记住?"
本文后续所有内容都建立在这个理念之上。在一个应用能够检索、修订、强化或遗忘某条记忆之前,它必须首先判断这条信息是否值得成为记忆。
从观察到持久记忆
LLM 记忆最大的误解之一,是每次交互都应该成为记忆。
实际上,生产系统不应该将信息直接从对话提升为长期记忆。这样做会迅速使系统充满重复的事实、临时观察、错误结论和过时知识。
记忆是应该被争取的,而不是自动产生的。

每条信息都始于一条观察。
观察是应用所见、所闻或所产生的东西。它可能来自用户对话、工具结果、代码审查、检索到的文档,甚至来自另一个 AI 模型。在这个阶段,应用不会假设这些信息是有用的,甚至不会假设它是正确的。
例如,在 Pull Request 审查期间,助手可能会观察到:
"分页查询返回重复记录,因为排序不稳定。"
这一陈述是有价值的,但它仍然只是一条观察。它没有被验证,没有被复用,当然也还没有在应用的记忆中赢得永久位置。
下一步是判断这条观察是否值得保留。
也许同样的问题在不同代码仓库中反复出现。也许有其他工程师确认了根本原因。也许自动化测试验证了这个结论。在这种情况下,应用有足够的证据将其作为候选记忆。
候选记忆是看起来有用、但在影响未来决策之前仍需要验证的信息。
只有在有足够证据存在时,应用才应该将其提升为持久记忆。
对于我们的编码助手来说,持久记忆不再是原始的 bug 报告。它变成了可复用的教训:
游标分页应始终使用确定性排序,包括当主排序字段不能保证唯一时使用辅助键。
应用记住的不是对话。
它记住的是从对话中提取的知识。
这种区别就是生产记忆系统与对话存档的区别。应用不是无限积累消息,而是持续过滤经验,只保留已证明具有长期价值的信息。
将记忆视为生命周期而非存储操作,也为我们准备好了下一个挑战。一旦记忆存在,它不会保持静态。新的证据可能会强化它、完善它、替换它,甚至完全否定它。
这就是记忆设计比简单存储信息难得多之处。
记忆身份比存储更难
一旦应用决定某事值得成为记忆,另一个挑战立即出现。
应用是学到了新东西,还是学到了它已经知道的东西?
乍一听,这像是一个重复检测问题。
实际上,它要复杂得多。
想象我们的 AI 编码助手已经有这条记忆:
大型数据集应使用游标分页。
几周后,另一次代码审查发现了一个微妙的问题。游标分页仍然产生了重复记录,因为排序不是确定性的。
现在应用收到了一条新信息:
当主排序字段不能保证唯一时,游标分页应包含一个确定性的辅助键。
它应该创建一条全新的记忆吗?
它应该覆盖现有的记忆吗?
还是应该将新信息视为对它已知内容的完善?
这就是记忆身份问题。
在存储任何内容之前,应用需要理解传入的知识与现有记忆的关系。广义上,一条新观察可以分为以下几类:
一条全新的记忆。 现有记忆的重复。 支持现有记忆的额外证据。 增加更多细节的完善。 修复早期理解的修正。 需要进一步验证的矛盾。 成为新权威版本的替换。
这些区分很重要,因为并非所有更新都应该以相同方式处理。
重复不应该增加记忆存储的体积。
额外证据应该在不改变底层知识的情况下增强置信度。
完善应该使记忆更加精确。
修正应该在更新当前理解的同时保留历史。
而替换应该允许未来决策依赖较新知识,而不是假装较旧版本从未存在。
考虑一条记忆如何随时间演变。
使用偏移分页。 大型数据集使用游标分页。 游标分页应使用确定性排序,包括在必要时使用辅助键。
这不是三条无关的记忆。
它们是同一想法的三个演进阶段。
生产记忆系统应该识别这种关系。否则,它会逐渐充满碎片化知识,迫使检索在本质上同一记忆的竞争版本之间做出选择。
这自然引入了另一个重要概念:替代。
当一条记忆被替代时,较旧版本不一定是错误的。它反映的是在特定时间点相信的内容。较新版本只是成为当前证据下最好的可用理解。
以这种方式思考会彻底改变记忆的角色。
它不再像一本无限收集事实的笔记本,而是开始类似于一个不断完善对世界理解的知识库。
与传统数据库不同,记忆不是静态的。
每条新体验都有潜力改变应用所知的内容。有时这种改变增加置信度。有时它添加缺失的细节。有时它完全替换了较早的理解。
生产记忆系统应该支持所有这些可能性,而不是将每次更新都视为新记录。
最简单的演进形式是强化。
假设 AI 编码助手在不同的代码仓库中反复遇到同样的问题。每个独立事件都确认游标分页需要确定性排序。
底层记忆没有改变。
随着证据积累,应用越来越确定这条知识应该影响未来决策。
然而,并非每个更新都会强化记忆。
有时新信息揭示现有记忆是不完整的。
也许另一项调查发现,仅有确定性排序是不够的。游标还必须编码数据库使用的完整排序状态,以保证一致的分页。
在这种情况下,记忆不会被丢弃。
它被修订为更加准确和完整。
最重要的演进发生在新证据从根本上改变应用理解的时候。
想象团队最终在每个服务中用键集分页替换了游标分页。
之前的记忆在被创建时并非不正确。它反映了当时最好的工程决策。
然而今天,它不应该再指导未来决策。
这就是替代。
替代不会抹去历史。相反,它建立一个新的权威版本,同时为历史参考保留较旧版本。
这种区别很重要,因为工程知识持续演进。最佳实践会改变。框架引入新能力。曾经合适的架构决策日后可能成为技术债务。
从不更新的记忆系统最终会成为负担而非资产。
综合来看,强化、修订和替代描述了应用如何随时间学习。
它对经过验证的知识增加置信度。 它完善不完整的知识。 当更好的证据出现时,它替换过时的知识。
记忆不再是存储事实的集合。
它成为应用程序当前认为为真的内容的持续演化表示。

为什么新鲜度不仅仅是缓存失效
当工程师听到新鲜度这个词时,他们通常会想到缓存。
Redis 更新了吗?
缓存失效了吗?
我们正在从数据库读取最新的值吗?
这些都是重要的问题,但这只是故事的一部分。
生产环境的记忆系统不仅关心存储的副本是否是最新的。它还需要判断知识本身是否仍然有效。
考虑这条记忆:
"部署冻结在周五结束。"
假设应用程序在规范存储改变时正确地更新了每个缓存。Redis 包含最新的修订版本,每个服务都已同步,任何地方都没有过期缓存。
然而,在周六,这条记忆仍然是错误的。
技术上没有任何失败。
知识只是过期了。
这说明了一个传统缓存失效无法解决的重要区别。
记忆新鲜度有多个维度。
这是最新存储的修订版本吗?
这是大多数系统已经理解的新鲜度类型。每个组件应该对当前哪个版本的记忆是当前版本达成一致。
这条记忆在今天仍然正确吗?
有些知识自然会随着时间推移而失效,即使没有人修改它。部署计划、临时解决方案、维护窗口和功能开关都有有限的使用期限。
这条记忆最近一次被验证是什么时候?
在过去一个月内被反复确认的建议,比两年未验证的建议值得更高的置信度。
即使一条记忆是准确的,它应该影响当前的决策吗?
一个前端应用程序的编码指南在审查数据库迁移时不太可能有帮助。记忆仍然是正确的,但它不相关。
以这种方式思考新鲜度会改变我们评估记忆质量的方式。
我们不再问"这条记忆是最新的吗?",而是开始问"这是现在应该使用的正确记忆吗?"
这是一个更难的问题。
这也解释了为什么生产环境的记忆系统不能仅仅依赖存储基础设施。一个完美同步的缓存仍然可能返回过时、不相关或不再可信的知识。
缓存可能是新鲜的,而其中的记忆可能是过时的。
认识到这种区别至关重要,因为检索不仅仅是找到相似的记忆。它是关于选择既相关又仍然值得影响决策的记忆。
这正是我们接下来要探索的内容。
检索是一个决策,而不是搜索
一旦应用程序积累了一系列记忆,下一个挑战就是决定哪些记忆应该影响当前的决策。
一种常见的方法很简单。
将当前请求转换为嵌入,执行相似性搜索,检索最接近的记忆,并将它们附加到提示中。
语义搜索是一个有价值的工具。
它只是不是整个检索策略。
考虑一个 AI 编码助手在审查修改分页逻辑的拉取请求。
假设它检索到这两条记忆:
记忆 A:游标分页应该使用确定性排序,必要时包括辅助键。
记忆 B:团队偏好简洁的拉取请求评论。
两条记忆都是正确的。
两条可能都与当前请求在语义上相关。
但只有一条有助于判断分页实现是否正确。
一个生产环境的记忆系统不应该问:
"哪些记忆最相似?"
相反,它应该问:
"哪些记忆值得影响这个决策?"
这种区别改变了检索的设计方式。
语义相似性成为众多信号之一,而不是唯一的排名标准。
在执行语义搜索之前,应用程序通常已经知道关于请求的重要事实。
它可能知道正在被审查的仓库、被修改的组件、编程语言、环境,甚至模型即将做出的决策类型。
这些结构信号可以在语义检索扩大召回率之前显著减少搜索空间。
在识别出潜在记忆之后,其他因素决定哪些记忆应该实际被使用。
一个生产环境的检索策略应该考虑以下问题:
这条记忆是否属于同一个实体或组件?
这是最新活跃版本吗?
支持证据的强度如何?
它在以前类似情况下有用吗?
它是否添加了新信息,还是只是重复了另一条检索到的记忆?
注意,相似性甚至不是最重要的问题。
目标不是检索最接近的记忆。
目标是检索最有用的记忆。
这是一个重要的区别,因为 AI 应用程序决策的质量较少取决于它记住了多少,而更多取决于它是否在正确的时间记住了正确的东西。
因此,检索不仅仅是一个搜索问题。
它是一个排序问题。
最终,它是一个决策问题。
记忆干扰:当记住更多反而使决策更糟时
很容易认为更多的记忆总是导致更好的决策。
在实践中,情况往往相反。
随着记忆系统的增长,挑战从记住信息转变为防止错误的信息影响模型。
这被称为记忆干扰。
记忆