Agent记忆退化不是因为忘记重要信息,而是记住太多无关信息导致检索质量下降;解决方案是实现有策略的遗忘机制,而非单纯扩大上下文。
每隔几周,就会有一个新的框架发布更大的上下文窗口,然后有人宣称 Agent 记忆问题已经解决了。其实并没有。我见过三个不同的生产环境 Agent 以完全相同的方式降级——不是因为它们忘记了重要的事情,而是因为它们记得太多,以至于无法判断什么才是重要的。解决方案不是更大的存储空间,而是淘汰策略。
这里有一个热门观点:保留不是 Agent 记忆的难题。遗忘才是。大多数团队在 Agent 中构建"记忆",实际上只是在构建一个只能追加的日志,外加一个向量索引用于搜索。那不是记忆,是一本没人编辑的日记。而一本永远不编辑的日记,最终会把一条相关的记录埋在一万条无关记录下面——此时你的检索步骤做的是考古工作,而不是推理。
症状是具体且可识别的:如果你让一个 Agent 运行超过几百个会话,检索质量会下降,但失败看起来像是提示词问题,而不是记忆问题。Agent 自相矛盾。它重复询问已经有答案的问题。它抛出一个在三次迭代前是正确的、但已经被取代的事实,并以完全自信的方式陈述它,因为没有任何东西标记它已过时。你去调整检索提示词、添加重排序、提高 top_k。这些都修复不了真正的缺陷——记忆存储对事实过期没有任何概念。
这个朴素的设计很有吸引力,因为它不需要预先做任何决策:每次工具调用、每条用户消息、每一步中间推理都被嵌入并存储。决策被推迟到检索时,届时相似性搜索应该能搞定。这在演示中效果很好,因为演示没有运行足够长的时间来积累矛盾。
生产环境中的 Agent 会积累矛盾。考虑一个在几个月内管理用户项目偏好的 Agent。第一轮会话:"我偏好 TypeScript。"第四十轮会话:"实际上,把这个仓库改成 Python。"现在这两条陈述在任何关于语言偏好的查询中都语义接近——余弦相似度不知道哪个是当前的。更糟的是,如果第一条陈述在更多会话中被强化(因为它为真的时间更长),在朴素的 top-k 检索中它通常会排名高于修正后的陈述,因为频率和时近性不是同一个信号,而大多数记忆层只追踪其中一个。
这不是检索调优问题。这是数据建模问题。你无法通过重排序来摆脱存储两个相互矛盾且毫无关联的事实这个问题。
把 Agent 记忆当作缓存来对待,而不是日志。具体来说,这意味着三个大多数"记忆"实现完全跳过的机制:
显式 Supersession。当一个新事实与存储的事实在同一实体/属性对上矛盾时,不要只是添加新事实——将旧事实标记为已 superseded 并在它们之间保持一个指针。这很便宜:在写入时做一个简单的实体-属性提取("user.language_preference: Python, supersedes fact_id 4471"),把你的存储从一个 embedding 袋变成一个更接近带历史的事实表。然后检索默认返回当前值,只有在显式询问时才暴露历史。
显著度衰减,而不仅仅是时近性衰减。时近性加权检索(越新 = 分数越高)是一个开始,但混淆了"最近说的"和"当前相关的"。一个六个月内偶尔说过的事实("我对贝类过敏")应该比一个每周重复但现在已经无关的事实存活得更久("正在做 Q2 报告"——Q2 已经结束了)。显著度应该是事实被检索和下游使用的频率的函数,而不是它被写入的频率。在 N 个会话中没有被检索过的事实是归档的候选——移出热索引,不是删除,这样如果需要你仍然可以恢复它们,但它们停止污染 top-k。
写入时矛盾检查,而不是读取时清理。诱惑是把这一切都推迟到检索阶段——过度获取,让 LLM 在上下文中了结矛盾。这在你矛盾数量超过上下文预算之前都有效,此时你正在支付 token 成本,让模型做它不太适合的数据清洗工作。在写入时捕获矛盾——当你已经有新事实并可以对同一实体/属性键进行定向查找时——比在读取时从一堆块中重新推导它便宜几个数量级。
反论点值得认真听取,因为它不是错误的,只是片面的。有了百万 token 的上下文窗口和廉价的提示词缓存,你确实可以将大量原始历史倾倒到上下文中,让模型直接在其上做注意力——无需检索步骤、无需 embedding、无需构建或维护淘汰逻辑。对于短寿命的 Agent(单会话、单任务、有界范围),这是正确的选择。为一个只运行一次对话的 Agent 构建记忆淘汰管道,是在解决一个你根本不存在的问题。
而且对于保留密集型设计有一个更微妙的观点:有时"无关"的老事实正是你处理意外边缘情况所需要的。激进的淘汰有风险丢弃那些在 99% 情况下看起来是噪音但在 1% 情况下是承重结构的上下文。如果你归档而不是删除,这个风险会被缓解——但归档本身有系统复杂性的成本。
这在上下文窗口不再成为答案的规模上就会崩溃:长期运行的 Agent 在数周或数月的交互中积累状态,在这种规模下,每次调用重新发送完整历史的 token 成本成为主要开销,而矛盾——而不是体积——是实际的失败模式。在这个规模下,更大的上下文窗口不能修复一个排名高于其修正版本的过时事实;它只是给过时事实找了更多的伙伴。
如果你构建的 Agent 旨在运行单个会话,不要构建记忆系统——你不需要一个,而且大的上下文窗口确实是更简单、更正确的答案。但如果你的 Agent 旨在随时间积累关于用户、代码库或项目的知识,要把记忆当作一个带有写入路径、supersession 模型和衰减函数的系统来对待——而不是一张你只能追加的表。目前正在交付可靠的长期运行 Agent 的团队,不是那些拥有最大向量索引的团队。他们是那些记忆层知道事实何时变坏,并在任何人注意到它出错之前悄悄地停止呈现它的团队。