这里有一个越来越常见的场景。

你发现你的AI代理第三次犯同样的错误。你告诉它保存修正。它在记忆文件中写下了规则。你看着它发生。文件在磁盘上更新了。代理确认:“保存成功!我会记住这个。”

下一个会话。同样的错误。

你不是疯了。代理确实写下了文件。文件确实存在于磁盘上。但它完全会忽略它。这是LLM代理系统中最常见的失败模式之一,而且几乎从未是因为代理“没有保存”。这是因为保存和学习是完全不同的两个操作。

记忆的幻象

人们假设:如果代理记下了它,它就学会了它。

实际上:代理只是记录了信息。这些信息是否会影响未来的决策取决于代理是否在正确的时间、以正确的格式、在提示的正确位置检索到它。大多数时候,它不会。

想象一下,你把“永远不要触碰生产数据库”写在笔记本上。如果你从不打开笔记本,你仍然会触碰生产数据库。笔记本不是问题。问题是检索。

代理的记忆文件只是笔记本。

七种笔记不起作用的原因

在多个代理框架(赫尔墨斯、克劳德代码、开放双手等)中深入研究后,同样的模式不断出现。有七种不同的失败模式,大多数代理至少会遇到三种。

1. 截图冻结

大多数代理框架在会话开始时将记忆文件加载为冻结的快照。这是为了性能优化——保持LLM前缀缓存,这样模型在每次回合中不需要重新处理相同的系统提示。

但问题是:当代理在会话中写入修正时,文件在磁盘上更新了,但活跃的系统提示保持冻结状态。代理在整个会话中实际上看不到其更新的记忆。它会继续犯同样的错误,直到你开始新的会话。

这是最常见的原因。这也是最容易测试的:错误在新的会话后停止了吗?如果是,这就是答案。

2. 错误的文件

代理记忆系统通常有多个层级。在赫尔墨斯中,例如:

  • SOUL.md — 永久的身份规则,只有人类可以编辑
  • USER.md — 用户偏好,代理可以更新
  • MEMORY.md — 工作笔记,受自动总结的影响

陷阱:如果你的修正写入了MEMORY.md,它会随着时间的推移被总结和合并。细微之处会被压缩成模糊的内容。或者当文件达到其字符限制(默认约为2,200个字符)时被完全移除。

同时,MEMORY.md是通过全文搜索召回的。如果你未来的查询与保存笔记中的关键词不匹配,它将不会被调用。教训就存放在磁盘上,看不见。

必须永不违反的硬规则应该放在SOUL.md或AGENTS.md中。将它们放在MEMORY.md中就像把禁止令贴在便签上一样。

3. 技能是可选的

在大多数代理系统中,技能或学习的程序不会自动执行。在会话开始时,代理会获得一个简短的技能名称和简短描述的列表。它必须自愿调用加载函数来读取完整的内容。

较弱的模型经常忽略“必须加载匹配的技能”指令。较强的模型有时也会跳过它。没有自动触发机制会根据上下文强制加载技能。

即使技能存在,如果其触发描述与错误重新出现的方式不完全匹配——不同的措辞、不同的工具调用序列、不同的任务框架——它也不会触发。

4. 字符限制

记忆文件很小。MEMORY.md:约2,200个字符。USER.md:约1,375个字符。当它们填满时,代理会合并旧条目以腾出空间。你的具体修正可能会被概括成通用的内容,或者为了“更重要的”条目而完全被放弃。

你可以增加这些限制(hermes config set memory.memory_char_limit 5000),但根本问题仍然存在:由LLM管理的有限文本文件最终会压缩掉重要的细节。

5. 中途丢失

即使记忆被注入到提示中,LLM也有一个众所周知的注意力问题。斯坦福大学的研究(arXiv 2307.03172)显示,语言模型对长上下文中的中间信息关注不足。它们记住的是开头和结尾。中间部分被忽略了。

如果你的代理加载了400行技能,而关键规则在第200行,模型确实有可能跳过它。提示的顺序比人们意识到的更重要:

差: 系统提示 → 用户请求 → 文件 → 历史记录 → 记忆
好: 系统提示 → 关键规则 → 任务 → 文件

规则应该排在首位。大多数框架将它们放在最后。

6. 压缩失败与推理模型

如果你使用推理模型(DeepSeek-R1,Qwen推理变体)作为主要或辅助模型,还存在一个额外的陷阱。代理框架会压缩长对话以保持在上下文限制内。推理模型需要在API调用时传递先前的推理内容——但压缩工作流程不支持这一点。

在多次压缩尝试失败后,某些框架会触发自动重置,这会抹去整个会话上下文。代理会忘记一切,包括持久化的规则。在对话中途。没有任何警告。

7. 子代理从零开始

当代理为委派任务生成子代理时,子代理以完全新鲜的上下文启动。没有继承的记忆。没有技能。没有对话历史。如果你没有显式地将规则打包到任务描述中,子代理会重复父代理刚刚学到的相同错误。

这是那些认为自己构建了复杂的多代理系统的人会遇到的问题。如果上下文不转移,这种复杂性就是一种幻象。

记忆不等于智能

核心问题是概念性的。人们将记忆等同于学习。这并不正确。

一个在笔记本上写下“始终验证输入”的人并没有学会验证输入。他们记录了一个意图。学习发生在他们遇到需要验证输入的情况时,回忆起规则,并实际应用它。回忆步骤是大多数代理的失败之处。

LLM是概率机器。如果提示几乎与之前一个模型选择了错误行动的提示相同,它将再次选择同样的行动——除非上下文中的某些内容显著改变了概率分布。记忆文件可能埋在提示的中间,可能被总结成模糊的内容,不会显著改变分布。

模型并不“知道”它犯了错误。它没有“我之前做过这个并且是错误的”这样的概念。它只是在上下文窗口中有令牌和下一个令牌的概率分布。如果来自记忆文件的令牌相对于任务令牌不够显着,任务模式会获胜。

真正有效的方法

按影响程度排序的修复方法:

1. 保存后重启会话

这解决了大多数情况。记忆文件是快照。更改只有在下次会话加载时才会生效。如果你告诉代理保存一条规则并在同一个会话中继续聊天,这条规则从未生效。

2. 将硬规则放在AGENTS.md或SOUL.md中

AGENTS.md在每次会话开始时都会被读取,自动读取。它不受总结或字符限制的影响(在合理范围内)。将你的“永不执行X”约束规则放在这里,而不是在技能或MEMORY.md中。

以命令式、具体的方式写下来:

  • 差: “小心数据库查询”
  • 好: “NEVER在没有明确用户确认的情况下运行DELETE或DROP查询。始终将写操作包裹在事务中。”

3. 通过名称强制执行技能

不要依赖代理来确定哪个技能适用。明确告诉它:“使用deploy-staging技能。”这完全绕过了自愿加载步骤。

4. 将记忆写成条件规则,而不是日记条目

差: “我在解析JSON时忘记了括号。”
好: “如果解析JSON输出,则始终在发送到API之前将其包裹在[]中。”

更好: 存储失败的操作与修复一起:

失败:调用api/v1/users → 陈旧数据
修复:始终调用api/v2/users → 当前数据

LLM从负面示例中学习得比正面建议更好。

5. 添加验证门

不要让代理在没有证明成功的情况下宣告成功。在完成任务后强制其运行检查:

任务完成后,运行:grep -r "bad_pattern"./ || echo "PASS"
如果失败,请重新应用修正。

这将一个模糊的规则变成了可执行的检查点。建议是可选的。验证命令是不可选的。

6. 对子代理:将规则打包到上下文中

在委派任务时,将关键规则直接包含在任务描述中:

关键:永远不要使用api/v1。总是使用api/v2。
最后一次尝试使用v1并得到了陈旧数据。
文件:/path/to/file.py

关于上下文字段的懒惰是子代理任务失败的主要原因。

金钥匙

验证和检索比存储更重要。

最大的改进不来自于添加更多的记忆。而是让代理在计划之前检索正确的记忆,并在完成之前证明错误实际上已经被修复。

失败循环:任务 → 计划 → 执行 → 失败 → 保存到记忆 → 从未检索 → 再次失败。

成功循环:任务 → 检索相关规则 → 围绕它们计划 → 执行 → 验证 → 完成。

大多数代理架构都执行了第一个循环,却不知道代理为什么会重复自己。问题不是更多的记忆。而是记忆与决策之间的更好连接。

最后一句话

AI代理不会从文件中学习。它们从上下文中学习。文件只有在其内容最终在提示的正确位置、正确时间以模型可以行动的方式出现时才重要。大多数时候,它们不会。

如果你的代理不断重复同样的错误,不要向记忆文件添加另一条笔记。检查现有的笔记是否实际上被读取了。检查它们是否在正确的文件中。检查会话是否过时了。检查格式是否可行动。

笔记本不是问题。问题是检索。

参考资料: