重新审视 LLM 对话中的记忆问题,核心不在模型能否记忆,而在如何构造上下文和情境。对优化 prompt 工程和 API 调用策略有实际启发。
我想我一开始问错了问题。
有一段时间,问题很简单:记忆能让 AI 智能体更聪明吗?
这听起来像是对的问题。但这也是个陷阱,因为它假设记忆应该被评判为一种通用的智能增强器。你添加一个记忆层,智能体能记住更多东西,输出在某种程度上应该变得更好。更完整。更准确。更像人。或者用任何词来掩饰"我希望这昂贵的东西能工作"。
运行了更多实验后,我认为这个框架是错的。
记忆并不能在任何一般意义上让模型更聪明。大多数时候,它不能。模型的权重中已经压缩了大量的通用过程知识和领域知识。如果你的记忆层回忆的是模型已经知道的信息,你就不是在增加智能,而只是在添加一条说同样事情的第二条路径,但延迟更高。
这是令人不舒适的部分。很多 AI 智能体记忆系统并不是因为回忆失败了而失败。它们失败是因为回忆的信息没有边际价值。
它们给了模型一些它本来就打算做的东西。
这个实验不是胜利圈
我在 OrKa Brain 上进行了后续基准测试:250 个任务、5 个轨道、有脑 vs 无脑。目的是看程序性记忆在规模和更多任务类型中是否会显示出更强的结果。
绝对评分标准几乎是平的。有脑得分 8.39。无脑得分 8.27。这是在 10 分制上的 +0.12 差异。在技术上是正的,但这不是你用来宣布持久记忆已开启 AI 智能体智能新时代的那种结果,除非你与证据的关系基本上是装饰性的。
配对结果一开始看起来稍好一些。有脑在 53.8% 的比较中获胜。但随后评委显示了 74.4% 的位置偏差,这意味着原始配对分数不是你可以直接读出的。如果评委四分之三的时间选择第一个答案,那么测量工具并不是真正穿着实验白大褂。它是在翻一枚有偏的硬币,然后之后自信地写出解释。
一旦我控制了位置,大部分假定的有脑优势就消失了。跨域转移没有存活。反模式避免没有存活。多技能组合崩溃成了硬币翻转。路由被混淆了,需要重新运行。
只有一条轨道存活:长期同域序列。在该轨道中,即使被放在不利位置,有脑也赢了 74% 的时间。
这是值得保留的部分。
不是因为它证明记忆在一般情况下有效。它没有。它证明了一些更狭隘但更有用的东西:当输出依赖于同一演变情况中的先前状态时,记忆似乎会有帮助。
这是一个完全不同的主张。
天花板就是信息
乍一看,你可能会说有脑失败了。我认为这太容易了。
更好的理解是基准测试暴露了错误的记忆类别。OrKa Brain 回忆的大部分是程序性知识:如何分解任务、如何权衡推理、如何避免通用错误、如何构建解决方案、如何从一个域转移模式到另一个。
问题是有能力的 LLM 已经知道很多这样的东西。它们看到了无尽的架构评审、调试会话、迁移计划、支持流程、项目回顾、Stack Overflow 争论、事故报告、代码审查、框架文档和公司文件的例子,这些文档不知怎么地用五页来说"我们忘记了缓存"。
所以当记忆层回忆一个通用过程时,模型不会突然收到缺失的信息。它接收的是已经存在于权重中的东西的提醒。
这就是为什么天花板效应很重要。这不仅仅是一个实现失败。这是一个类别警告。如果记忆存储通用能力,模型通常可以绕过它,因为模型已经有通用能力。
这就是为什么"AI 智能体记忆"在演示中看起来令人印象深刻,然后在基准测试中变得奇怪地无力。在演示中,记住的上下文感觉有用,因为我们可以看到系统引用过去。在基准测试中,如果记住的东西不改变答案,效果就消失在风格、冗长或评委偏好中。
有用的问题不是"系统记住了什么吗?"
有用的问题是"记住的东西是否包含模型无法知道或安全推断的信息?"
记忆是为了偶然信息
该理论的更敏锐版本是这样的:
当答案依赖于模型权重中不存在的偶然信息时,记忆会有帮助。
不是纵向知识。不是领域知识。不是作为通用法术的"更多上下文"。偶然信息。
我所说的偶然,是指其真理取决于特定用户、系统、公司、客户、代码库、先前决定、本地过程或时刻的信息。这是在一般情况下不成立的信息。它在这里是成立的。
模型可以知道软件迁移通常如何失败。它无法知道在这个代码库中,上次迁移失败是因为账单工作线程默默地依赖了一个已弃用的 Redis 密钥。
模型可以知道简洁写作是什么。它无法知道特定用户喜欢直接的技术答案,没有填充物、没有虚假热情、也没有喷在段落上的公司香水。
模型可以知道客户支持分诊如何工作。它无法知道这个特定的客户总是用错误的产品名称报告账单 bug。
模型可以知道部署管道通常如何工作。它无法知道这个团队避免周五发布,因为一条回滚路径仍然取决于一个没人想承认存在的手动脚本。
其他一切都有可能成为通用知识的第二份副本,附加到已经拥有第一份副本的模型。
代码库 AI 使这种区别显而易见
以软件工程为例,因为错误在那里变得非常清楚。
一个天真的方法说:"我们需要记忆,以便模型知道如何编码。"这听起来很合理,直到你看看模型已经知道什么。有能力的模型对编程语言、设计模式、API 约定、测试策略、重构技术、基础设施模式以及每个人都引用但一半行业忽视的通常最佳实践有广泛的接触。
如果你使用记忆来教它通用软件知识,你可能会遇到与程序性记忆相同的天花板。模型已经知道函数应该很小、测试应该覆盖边界情况、迁移应该是可逆的,分布式系统喜欢毁掉你的下午。将这些想法存储为记忆不会增加太多。它主要给了模型一种更慢的方式来说它原本打算说的话。
问题不是模型不知道软件工程。问题是它不知道这个软件系统。
这意味着通用工程知识不应该被视为有价值的记忆层。它已经在模型中了。有价值的层是代码库的本地形状:命名约定、架构疤痕、禁止的依赖项、部署约束、隐藏的耦合、脆弱的测试、内部抽象、遗留决定,以及为什么一个丑陋的函数不应该被"清理"的原因,除非你喜欢事故报告。
存储库检索的工作不是提醒模型什么是干净代码。工作是向它展示这个代码库实际上是什么。
模型可以知道身份验证通常如何工作。它无法知道在这个系统中,管理员角色跨两个服务重复,因为迁移在 2022 年只完成了一半。
模型可以知道 Redis 密钥应该有明确的所有权。它无法知道计费仍然依赖于已弃用的缓存密钥,因为一个工作线程从未被移到新的事件管道。
模型可以知道如何编写数据库迁移。它无法知道这个团队避免破坏性迁移,除非回滚计划由仍然记得旧架构存在原因的人审查。
这就是记忆证明其价值的地方。
风格指南告诉模型代码应该如何看起来。存储库历史告诉它为什么代码看起来错误但仍然有效。事故记忆告诉它尸体埋在哪里。团队约定告诉它什么更改将被接受或拒绝,在 CI 甚至有机会抱怨之前。
这些不是同一层,将它们视为一个通用 RAG 堆是你如何获得检索很多但理解很少的系统的方式。
在软件产品中,这种区别很重要。文档基础。存储库状态塑造。事故和决定历史约束。
如果把这三项工作混在一起,助手可能仍然会表现得像一名资深工程师。它甚至可能显得比以前更资深——这通常意味着它学会了自信地说出“权衡”二字。问题在于,它是否理解眼前这个具体的系统。
正是在这里,记忆不再是装饰,而开始真正发挥作用。
这并不只是代码库的问题。对话记忆以一种规模更小、也更为人熟悉的形式,呈现出了相同的结构。
当系统记住某位用户希望回答简洁时,它之所以能提供帮助,并不是因为模型不理解“简洁”这个概念。模型知道如何做到简洁。有用的信息不是“简洁是什么意思?”,而是“对这位用户而言,简洁意味着什么?”
这是一个与实体绑定的事实。它把一种通用能力关联到某个具体的人。
这就是为什么个性化即使在思想层面并不深奥,也依然可以很有用。它的价值不在于从头教会模型一种新的写作风格,而在于根据一个已知身份选择正确的行为。
公司内部也会发生同样的事情。模型知道如何审查代码,却不知道这个代码库禁用某个特定库,是因为它在三年前引发过一次生产事故。模型知道如何处理退款,却不知道年度合作伙伴合同有一套不同的退款流程。模型知道如何总结客户投诉,却不知道这位客户总是把生产事故描述成“小问题”,因为他们礼貌到了自我妨碍的程度。
这种局部的怪异之处并不是噪声。它就是工作本身。
真实系统并不只由通用规则构成。它们由例外、伤痕、习惯、约定、过去的错误、策略捷径、未记录的依赖关系,以及那些人人都知道——直到唯一知道的人离开公司——的事情构成。
这些才是记忆应该存储的材料。
不是通用能力,而是运营沉积物。
我之前的框架更接近技能复用。智能体学习一项技能,将其存储起来,稍后再召回,并应用于一项新任务。这是一个简洁的模型。它也非常适合画成漂亮的架构图,而这种情况向来值得我们警惕。
数据推动我转向一种不同的架构。
记忆系统不应该从“哪项技能与这个任务相似?”这个问题开始。有时这样做确实有效,但它作为核心检索原则还是太弱了。相似性并不等于必要性。一段记忆可能在语义上非常接近,但如果它不能改变答案,就仍然毫无用处。
更好的检索问题是:“关于当前情境,有哪些信息是模型无法推断,或推断起来不安全、代价高昂的?”
这个问题会改变一切。
你进行检索,是因为任务依赖于某个已知实体。你进行检索,是因为答案会随产品、客户、代码库、环境、部署目标或团队约定而变化。你进行检索,是因为用户有稳定的偏好。你进行检索,是因为代码库有自己的局部规则。你进行检索,是因为客户有一段历史。你进行检索,是因为系统以前曾以一种与当前情况看起来相关的方式失败过。你进行检索,是因为模型即将在需要具体状态的地方,给出基于通用能力的答案。
这与语义相似性是不同的触发条件。
触发条件是信息不足。模型可以生成一个答案,但提示词和通用知识不足以充分确定这个答案。它需要局部状态。
这也意味着,记忆不应该只是一个大桶。一个严肃的系统可能需要多个相互独立、职责各异的存储区。
Grounding 用于提供权威依据。它为模型提供准确的来源、最新文档、代码库文件、API 契约、Schema、配置和可验证的参考资料。
运营知识用于描述局部形态。它告诉模型这个产品、代码库、客户、工作流、团队或部署环境是如何运作的。
情景记忆用于记录历史。它告诉模型,这位用户、这个客户、这项任务、这次会话、这项服务或这个系统过去发生过什么。
推理则将这三者组合起来。
当人们把这一切都归入“记忆”时,系统就会变得更难评估。他们也会更容易自欺欺人——只要涉及智能体,这显然就是默认的 MLOps 工作流。
过去的问题是:记忆能否提高输出质量。这个问题太宽泛了。
如果模型凭借通用能力就能给出不错的答案,记忆就很难展现出显著效果。评审模型可能更喜欢其中一个答案,但最终衡量到的可能只是风格、啰唆程度、格式、位置偏差,或者评审模型早餐吃了什么。
下一代基准测试应该让记忆成为必需品。
一项任务应当要求使用一个只存在于记忆或 Grounding 中的事实。没有这个事实,模型就应该失败、给出含糊保守的回答,或者生成一个通用答案。有了这个事实,模型才应该正确作答。
不是答得更漂亮,而是答得正确。
对于对话助手,答案可能取决于已存储的用户偏好。对于代码助手,答案可能取决于代码库特有的规则、过去的事故、被禁用的依赖、迁移约束,或者一项从未存在于模型权重中的团队约定。对于客服自动化,答案可能取决于客户特有的例外、产品方案中的特殊规定,或以往的升级处理模式。对于路由,答案可能取决于同一系统内部之前失败过的一条路径。
评分标准不应该是“哪个答案感觉更完整?”这只会让评审偏差走进房间、坐到桌边,然后开始凭感觉打分。
评分标准应该是具体的。系统是否使用了正确的代码库规则?是否遵守了部署约束?是否记住了用户偏好?是否避开了已知的失败路径?是否引用了最新的内部文档?是否保留了之前的产品决策?是否区分了通用知识和临时状态?
记忆应该在这些地方体现出真正的差距。不是 +0.12 的润色优势,而是正确性上的差距。
如果记忆连在这里都无法胜出,那么这套记忆系统大概没有发挥多大作用。如果它确实能够胜出,那么它的价值就不再神秘,而是可以衡量的。
我不认为“记忆让智能体变得更聪明”是正确的说法。这太含糊了,而含糊的说法正是 AI 炒作繁衍生息的温床。
更准确的说法没那么令人兴奋,却更有用:当任务依赖于模型权重中不存在、且无法根据通用知识安全推断的信息时,记忆就能提供帮助。
这解释了为什么通用流程的回忆几乎没有产生影响。它解释了为什么长篇幅的同领域回忆是唯一保留下来的信号。它解释了为什么对话偏好很重要。它解释了为什么代码助手需要代码库状态和决策历史,而不仅仅是更多文档。它解释了为什么企业助手只有在了解公司内部那些局部的怪异之处后,才真正变得有用。
模型已经掌握了平均值。
记忆是为偏差而存在的。
而大多数真实工作都是偏差。
我认为,这才是我们一直忽略的部分。我们不断构建记忆系统,仿佛模型是空的,需要往里面填充东西。但模型并不是空的。它充满了平均值,充满了通用模式,充满了看似合理的流程,充满了那种通常正确的答案——直到它遇见一家真实的组织、一位真实的客户、一个真实的代码库,或一个偏好不符合中位数的真实人类。
记忆并不是为了与预训练竞争。
记忆的作用,是在局部情境有此要求时纠正平均值。
我会这样划定边界。
不要存储模型已经知道的东西。不要检索模型能够安全推断的东西。不要把通用建议称为记忆。不要混淆领域知识与临时状态。
应该存储的是那些会改变答案的内容,因为它们具有特定性、时效性、局部性、个人属性、历史属性、流程属性,或与代码库、客户、产品、系统绑定。
使用 Grounding 提供权威依据。使用记忆处理情境差异。使用推理进行组合。
把这三者混在一起,你构建出来的就只是一个非常昂贵、旁边还贴着剪贴簿的自动补全工具。
这项基准测试并没有证明记忆毫无用处。它证明的是,记忆必须证明自己值得存在。如果被记住的东西不能改变答案,那么从任何有实际意义的运营角度来看,它都不是记忆,只是带有时间戳的噪声。
模型不需要记忆。
对于后续操作,你可以考虑屏蔽此人和/或举报滥用行为。