Agent 做长任务时,可能会在换一轮会话后,重新尝试已经排除的方案。刚验证过的结果,上下文压缩后变得含糊;失败原因明明记在日志里,下次却没有用上。
遇到这种情况,只说“它又忘了”很难定位问题。信息没有保存、检索没有找到、找到了却没有用于决策,需要分开排查。
设计记忆模块,也得从这些具体问题入手:眼前推理需要什么,任务中间结果放在哪里,哪些经验值得长期保留,以及过期信息怎么处理。
先把两种分类分开
讨论 Agent 记忆时,“保存的是什么”和“放在哪里、保留多久”是两回事。
CoALA(Cognitive Architectures for Language Agents)借鉴认知架构,区分工作记忆与长期记忆,再把长期记忆分为情节记忆、语义记忆和程序性记忆:
- 工作记忆:当前决策需要的目标、观察、判断和中间结果。
- 情节记忆:某次任务中发生了什么、采取过哪些行动、结果如何。
- 语义记忆:相对稳定的事实、概念和领域知识。
- 程序性记忆:如何完成某类任务的方法、规则或技能。
情节、语义和程序性记忆描述长期信息的性质,工作记忆强调当前决策能用到的信息。工作记忆也不等于单次模型调用的上下文窗口:任务状态可以跨多轮保存,每次只把需要的部分放进上下文。
例如,一次排错过程属于情节记忆,其中经过验证、可以复用的做法,才可能整理成程序性记忆。具体怎么存、什么时候加载,还得另做安排。
信息应该放在哪里
可以先按使用方式分三层:当前上下文、任务工作区和长期记忆。
当前上下文放本轮目标、必要约束、近期观察和这次推理需要的证据。它离推理最近,也最需要控制体积。
任务工作区(scratchpad)保存中间结论、待验证假设、执行进度和临时材料。它跟着任务更新,需要时再读回来。任务结束后,其中的内容也不必全部留下。
长期记忆适合放稳定的项目知识、用户明确的偏好,以及经过验证的可复用经验。使用时按范围和情境检索,还要定期审查。
三层只是一个起点。层级太少,临时猜测容易和稳定知识混在一起;层级太多,每层都得维护写入、同步和淘汰规则,也有成本。
放置一条信息时,先看它属于谁、能用多久。用户、组织、项目、任务、会话和当前一轮,各有不同的范围;有效期也可能从几分钟到一个项目周期不等。
再看来源和证据。用户明确提出的要求、工具返回的结果、框架生成的记录、模型自己的推断,不能混成同一种事实。测试结果、代码位置和用户确认可以留下作为依据;还没验证的判断,就标成假设。
最后考虑上下文预算。某条信息是否必须常驻,还是只需保留摘要和原始材料的位置?从工作区转入长期记忆时,要筛掉一次性的过程材料,留下已经验证、可以复用、适用范围明确的部分。来源变了、经验过期了,也要能找到对应记录,更新、降权或删除。
RAG 与任务状态各管什么
长期记忆多了以后,很难每次全部装进上下文。RAG(检索增强生成)在生成前检索相关材料,让模型结合外部信息作答。原始论文将模型的参数化知识与外部非参数化记忆结合,用于知识密集型任务。
工程上要照顾两头:
- 写入侧:管理知识源、文档解析、切块、向量与关键词索引、版本,以及访问权限。
- 读取侧:根据任务和权限过滤候选材料,再进行召回、重排,并保留引用来源。
这不意味着所有问题都要先做一次语义检索。已知路径的文件直接打开,结构明确的数据通过查询获取,工具是否执行成功则检查返回结果和对应状态。
比如,检索出一份“部署成功”的历史记录,只能说明过去某次部署成功,不能证明当前版本已经上线。任务状态记录和运行结果要核对时间;涉及外部系统时,还要确认记录是否仍能反映当前状态,必要时实时查询。
知识和状态都要保留来源与时间,决策时才分得清:哪些是可参考的历史经验,哪些是这次执行的依据。
因此,召回多少段文字还不够。材料是否适用于当前任务、来源是否可信、版本是否有效,决定了它能不能用来支持下一步行动。
进度记录要让任务接得上
长任务的进度记录,要让下一次接手时知道做到哪了,以及为什么做到这里。
只记“修改了接口”“运行了测试”,接手时还是得重新调查:改动解决了什么,测试证明了什么,哪些方案已被排除,还有什么没验证。
一份交接记录可以按下面的结构写:
任务目标:需要达成的结果与验收条件
已完成:做过的改动或检查
验证证据:命令、结果、代码位置或工具记录
当前判断:已确认的事实,以及仍待验证的假设
决策依据:为什么采用当前方案,关键取舍是什么
已排除:尝试过的方案与排除理由
下一步:接下来要验证或执行的动作
约束:用户明确要求保留的行为或禁止的操作不必保存每一次思考,但行动、理由、证据和下一步要能对上。较长的日志和工具输出,留下关键结论与原始材料的位置即可,避免反复复制全文。
完成修改、验证通过、部署成功是不同阶段,要分开记。计划中的动作不能提前写成已完成,历史上的成功也不能代替这一次的验证。
把失败经验用到下一次
错误写进日志,之后却没有被读到,对下一次执行就帮不上忙。失败回流要做的,是把这次的证据和处理方法留下来,在同类任务的关键节点取回,例如调用某类工具之前。
一条失败记录应该包括什么
先检测失败、判断是否值得保留,再收集证据、确认原因和补救方法。记录经过审查后才能进入召回库,待验证的草稿要与可用经验区分。下面这些内容需要留下:
| 内容 | 要回答的问题 |
|---|---|
| 失败边界 | 这次失败是否值得保留?哪些现象属于同一个问题? |
| 失败分类 | 属于哪类失败,便于后续查找和匹配? |
| 证据包 | 当时的输入、错误信息、环境和实际观察是什么? |
| 根因与补救 | 哪些原因已被验证?什么方法有效,适用于什么条件? |
| 召回触发器 | 下次在什么任务、环境或操作前,应当取回这条经验? |
| 留存与审查 | 谁确认过这条经验?何时复查,什么情况下应淘汰? |
召回条件和适用边界要写具体。某个环境里有效的临时处理,换个环境未必适用;一次超时,也不足以推出“以后都要重试”。根因还没验证,就保留“尚未确认”的状态。
环境、依赖或工具行为变了,旧经验可能失效。要定期复查,更新记录或将它移出召回范围。
重复犯错时查这几个环节
同样的失败再次出现时,可以沿着记录的使用过程检查:
- 没有记录,先检查当时是否判断过这次失败值得保留,以及写入是否完成。
- 记录已经存在,却没有取回,检查检索与触发条件。
- 取回之后没有用,检查规划和决策过程。
- 用了仍然失败,检查经验本身、适用边界,以及补救动作是否正确执行。
查清卡在哪一步,才知道该改记录、改检索,还是重新验证处理方法。
怎样判断记忆有没有用
记录数量、索引规模和召回条数能说明系统在运行,却不能单独证明任务做得更好了。失败经验有没有用,可以看这几个指标:
| 指标 | 观察重点 |
|---|---|
| 重复失败率 | 在同类任务中,已经沉淀过经验的失败是否仍然反复出现 |
| 召回有效率 | 召回的经验中,有多少实际帮助调整了执行并取得预期效果 |
| 漏召回率 | 重复失败发生时,有适用经验却未被取回的情况有多少 |
| 误提醒率 | 取回的经验中,有多少与当前任务无关或不适用 |
统计时按相近的任务类型和条件比较,不要只数失败次数。同类任务变少了,失败次数也可能跟着下降。需要结合具体执行记录,才能判断经验是否起了作用。