Agent Engineering 课程阅读
首页/课程十一 · Agent 执行骨架/跨会话记忆文件:Agent 的操作记忆

在 GitHub 查看原文

本页目录

Lesson 03 — 跨会话记忆文件:Agent 的操作记忆

本课目标:让会话结束不再等于失忆——「用户要求报告 ≤500 字」这类操作性事实落成记忆文件:单事实单文件 + 索引常驻正文按需 + 写入纪律 gate + 可反悔。跨会话生效有测试为证;一条记忆的常驻成本只有一行。


1. 三种记忆分家:谁都不取代谁

v4 已有两种记忆,但「用户上次说报告要短」在哪都放不下:

任务经验(memory.py,frontier) 任务进度(task_ledger.py) 操作记忆(本课)
存什么 研究结论与教训 TODO 树、增量简报 用户偏好、长期约束、项目状态
例子 「LangGraph 1.2 已稳定」 「子题 3 已完成」 「报告必须中文 ≤500 字」
形态 向量库(量大,语义召回) sqlite(结构化语义) 文件+索引(量小、价值密度高)
谁能改 agent agent agent 和人都能直接改

信息形态选存储,不是谁先进谁淘汰:任务经验量大适合向量检索;操作记忆一共几十条,文件+索引让它可读、可编辑、可审计、可 git。

活参考:Claude Code 对它的用户就是这套机制——MEMORY.md 索引每次会话装载、单事实单文件、正文按需读。你学这门课的过程中,很可能正在被这套机制服务着。

2. 形态:单事实单文件 + 索引常驻、正文按需

memory_files/
  MEMORY.md            # 索引:每条记忆一行(常驻窗口的全部成本)
  report-style.md      # ---
                       # name: report-style
                       # description: 报告语言与长度偏好(用户纠正)
                       # type: preference
                       # triggers: 报告,写作,格式
                       # ---
                       # 报告必须用中文撰写;正文控制在 500 字以内。

索引是文件的视图:每次写/删后从文件重建(按名排序,双跑逐字节一致)——单一事实源是文件,索引坏了删掉重建即可。这是渐进披露在本课程的第一次出现:把「可能有用」和「此刻在场」分开计价。实测(demo 会话 2):5 条记忆,索引 48 token 常驻,正文合计 1,526 token 住磁盘,本次任务只命中 1 条——另外 4 条只付了一行索引的钱。

3. 写入纪律:比怎么记更难的是何时记

「这句话值不值得记」是模型的判断(真实系统里 LLM 提名 MemoryCandidate);gate 是代码的纪律(判断交给模型、纪律交给代码,第三次出现):

候选 gate 判定 理由
「报告必须中文 ≤500 字」(durable) ✅ 收 用户纠正,跨会话有效
「这次先查 3 个来源」(session) 🚫 拒 只对本轮有意义——记了就是噪音
「本项目用 LangGraph」(already_recorded) 🚫 拒 仓库/代码已记录,记忆不做代码的镜像
空名/空正文/未知类型 🚫 拒 结构完整性

另外两条:同名 = 更新而非新增(用户改主意是常态,「500 字→800 字」不该留下两条打架的记忆);记错了 = delete(记忆可反悔,索引同步消行)。

4. 召回纪律:记忆是背景,不是指令

  • trigger 命中才读正文:每条记忆带 triggers 触发词,query(summary+feedback)命中任一才注入——未命中零注入,「按需」不是「常注入」(测试锁死)。
  • 防污染提示内置:注入块固定携带「可能过时,与当前对话冲突时以当前对话为准」——旧记忆可能过时(用户上季度要 500 字,这次明说要长文),盲从旧记忆比没有记忆更糟。
  • 归桶:记忆块进账本的 history 桶(agent 过往学到的产物),与 researcher 的 memory_instr 同桶。

5. 流派对比:跨会话记住用户的四条路线

流派 思路 取舍
无记忆 每会话从零 v4 现状:用户每次重复自己,长途任务跨会话即失忆
全历史回放 把旧会话全塞回窗口 窗口炸 + 大海捞针(lost in the middle 拿捏这种做法)
向量检索记忆 旧对话切块进向量库 适合海量任务经验(frontier 路线);操作记忆几十条用它=杀鸡用牛刀,且人没法直接审计改错
文件记忆+索引 少而精、索引常驻正文按需 本课选择:可读可改可 git;代价是写入纪律要维护(记什么/不记什么)

业界锚点:Claude Code(CLAUDE.md/MEMORY.md 双层:指令文件人写、记忆文件 agent 写——恰好是 L08 skill 与本课记忆的同构预告);MemGPT(记忆分层的学术先驱:主上下文/外部存储 + 自编辑记忆);ChatGPT memory(产品化的偏好记忆,同样走「少而精+可管理」而非全量回放)。

6. 跑起来

cd harness-lessons/03_memory_files
python code.py    # 三会话生命周期:纠正→gate→落盘 / 跨会话召回+账本量证 / 改主意→更新→删除

关键输出:会话 2 的 writer prompt 含「500 字」✅(跨会话生效);关开关重跑不含 ✅(默认零差异);无关任务(财务图表)零注入 ✅;改主意后文件数仍 1、索引仍一行 ✅。

7. 落地清单

文件 改动
src/research_assistant/memory_files.py 新增:MemoryCandidate + gate_memory(写入纪律)+ write/delete(upsert/可反悔/索引重建)+ match/recall_block(trigger 召回+防污染提示)
src/research_assistant/nodes.py writer 在开关下按需召回注入(skill 段之后;账本入 history 桶;prompt 关态逐字节不变)
src/research_assistant/config.py enable_memory_files(默认 off)、memory_files_dir
tests/test_memory_files.py 新增 15 测试(gate 四规则/upsert/删除/索引确定性/trigger 召回/writer 三态集成/经济性)

「索引常驻 system」的完整形态属于长程单窗模式(v5/L09)——v4 map-reduce 没有常驻 system 段,诚实起见不做注入戏,RA 的落点是 writer 按需召回。

验收

cd portfolio-projects/research-assistant
python -m pytest tests/test_memory_files.py -q   # 15 passed
python -m pytest -q                               # 387 passed(372 + 15)
cd ../../harness-lessons/03_memory_files && python code.py

8. 本课在两条主线上的位置

窗口经济:操作记忆的常驻成本被压到「每条一行」——第一次把「可能有用」和「此刻在场」分开计价(48 token 索引 vs 1,526 token 正文)。外置化:跨会话事实是第一块真正落盘的内容——会话断了文件还在;L06 把工作集也搬出去。

🎯 面试话术

「我的 Agent 有三种记忆各管一段:任务经验走向量召回、任务进度进账本、操作记忆进文件。文件记忆的关键是写入纪律——用户纠正记、临时参数拒收、代码里已有的不重复记、记错了能删、改主意是更新不是新增;这些规则是代码 gate 强制的,不靠模型自觉。召回侧索引常驻正文按需:5 条记忆常驻成本 48 token,触发词命中才读正文,注入块自带『过时以当前对话为准』防记忆污染。这套机制和 Claude Code 对用户做的是同一件事——我手写过一遍,知道每条纪律是防哪种事故的。」