AfterTrace 的核心规则:memory 提议,live evidence 裁决。Agent 可记住历史修复,但任何变更必须经实时验证才能执行。
上个月,我的事故恢复 Agent 查看了一条失败的搜索查询,想起它之前修过同样的症状,于是提出了相同的修复方案。那个方案是错的。
Bug 是搜索网关返回了错误语料版本的搜索结果。第一次是别名指向了旧的集合。这次别名已经正确了,真正的问题在别处。
AfterTrace 是一个命令行工具,帮助从事故中恢复。它遵循一个循环:检测、诊断、提议、审批、修复、验证、记录。
我围绕这个规则构建的:记忆负责提议,现场证据负责裁定。Agent 可以记住,但它永远不会仅凭记忆行动。
完整项目可以在这里看到:https://github.com/Ramakrishna1967/AfterTrace
大多数关于 Agent 记忆的讨论都聚焦在正面:Agent 记住了,所以它变得更好。但一个让 Agent 更快速做对事的记忆,同样也让它在情况变化时更快速地做错事。
所以我给 Hindsight 的 recall 功能分配了一个小任务:决定先检查什么。一个被召回的修复方案绝不会自动应用。在任何变更之前,Agent 都会重新检查该修复方案所依赖的现场状态。
我用三个场景测试了这个设计,每个场景都在全新进程中运行,这样唯一能传递过来的就是存储在记忆中的东西。
一个新的语料库 B 加载正确了,但别名仍然指向旧的 A。一次应该返回 B 的查询返回了 A。
没有记忆的情况下,Agent 检查 B 没问题,看到别名指向 A,提出切换方案,然后等待审批。变更后它重新查询 B,确认可以工作,然后把这次真实的事故保存到 Hindsight。我只保存真正发生过的事,绝不编造摘要。
不同的语料库,同样的 Bug 类型。全新的进程通过 Hindsight API 调用 recall,获取场景一的经验,然后优先检查别名。这是持久化记忆的回报:它直接定位到了最可能的原因。它仍然在写任何东西之前检查了现场状态,查询通过了。
同样的症状,但这次别名已经指向 B 了。真正的原因是过期的缓存提供了旧结果。
Recall 返回了之前的别名修复方案。Agent 重新检查现场的别名,发现该修复方案已不再适用,打印出:
REJECTED recalled alias fix
然后它寻找另一个原因,发现了过期缓存,只清理了缓存。别名保持不动。
没有这个检查,场景三会以 Agent 重写一个健康的别名而真正故障依然存在告终。有了这个检查,记忆决定去看什么,现场证据决定改什么。
用 recall 来排序优先级,而不是授权。按过去事故来排列嫌疑对象帮助很大。但盲目依据它们行动就是风险。
重新检查一个被召回的修复方案所依赖的条件。一个记住的修复方案假设世界没有变化。测试它。
只保存真实的事故。编造的回忆会毒害未来的 recall。
先测试失败场景。记忆出错的那个场景能检验你的设计是否安全。
全新进程能展示记忆真正做了什么。在场景之间重启让我清楚地区分了哪些来自 Hindsight,哪些只是存在 RAM 里。
目前这些检查覆盖了别名和缓存故障。其他故障类型需要各自的检查。我也希望能保存被拒绝的 recall,这样拒绝本身也能成为 Agent 学习的内容。
如果你在构建一个有持久记忆的 Agent,Hindsight 值得一试。对我来说最大的一课是:精确地决定记忆被允许决定什么。