learn-claude-code-s08-s09-学习笔记

从零手写 Agent:Learn Claude Code s08--s09 学习笔记与薄弱点复盘(记忆管理篇)

学习对象:learnclaudecode 教程「记忆管理」模块(s08 上下文压缩 / s09 长期记忆)

学习方式:跑通 → 改坏 → 回看 三步法(延续 s01-s04 笔记)

配套可运行 demo:s08_compaction.py、s09_memory.py


目录


零、全局心智模型:记忆三层金字塔

s05-s09 五课其实都在解决同一个矛盾:上下文有限,任务无限。记忆问题分三层,每层一个载体:

层 载体 活多久 对应课 淘汰条件
工作记忆 messages(当轮上下文) 一次 API 调用 s01 调用结束即忘
会话记忆 压缩摘要 + todos 计划表 一次会话 s05 / s08 会话结束销毁
长期记忆 磁盘文件 跨会话,理论上永久 s09 几乎不淘汰(但要定期整理)

两条贯穿本模块的铁律:

  1. 模型无状态 :LLM 没有记忆模块,每次调用进去一串文字、出来一串文字,结束即忘。它的一切"知道"都来自这次调用塞给它的文字。所谓记忆,全是程序在搬运文字。
  2. 压缩和记忆是同一笔交易的两面:用「信息变少」换「上下文变轻」。s06 子代理隔离如此,s08 压缩如此,s09 的"只记结论不记流水账"也如此。

一、s08 上下文压缩:账本太厚,先归档再记

1.1 要解决的问题

messages 每轮 +2(assistant 的 tool_use + user 的 tool_result),无限增长。s04 里那个 context_watch 钩子打印的 1 → 3 → 5 → 7 就是无人管理的加法------迟早撞上模型的上下文窗口上限。

1.2 核心代码

循环开头的"水位检查"(s08 对主循环的全部改动):

python 复制代码
for turn in range(1, max_turns + 1):
    if len(messages) > THRESHOLD:        # 水位超了(demo 里 THRESHOLD=9)
        messages = compact(messages)     # 压缩
    ...

压缩函数的三段式保留:

python 复制代码
new_messages = (
    [messages[0]],            # ① 原始任务 ------ 永远第一位,碰都不碰
    [摘要消息],               # ② 历史摘要(一段话概括干过什么)
    messages[cut:],           # ③ 最近 KEEP_RECENT 条原文
)

成对删除(真实开发中最高频的压缩 bug 防线):

python 复制代码
cut = n - KEEP_RECENT
while cut > 1 and messages[cut].get("role") == "assistant":
    cut -= 1        # 切点落在 tool_use 上就往前挪,保住 tool_use/tool_result 成对

1.3 知识点

# 知识点 要点
1 水位检查只能放循环里 控制"循环自身节奏"的东西(Nag、压缩、重试)只能改主循环;注册表救不了这类需求(s02/s04 的开闭原则管的是"被调用者")
2 三段式保留 原始任务 + 摘要 + 最近原文。丢了原始任务 = 目标漂移,后面全白干
3 tool_use / tool_result 必须成对删 拆散一半,API 报 tool_use without corresponding tool_result。测试通过 ≠ 逻辑正确,可能只是没踩中
4 压缩是笔交易 细节永久丢失换上下文变轻。摘要质量决定一切
5 摘要要 LLM 写,不是规则拼 程序判断不了什么信息重要、什么能丢;模型能读懂语义做取舍------又是"用模型智能补程序能力"

1.4 摘要四要素(设计简答题的答案)

要素 为什么必须有
① 目标复述 不写目标 → 模型漂移(Q2 排障场景)
② 已完成进度 不写进度 → 模型重复劳动
③ 当前状态 / 下一步 压缩发生在任务中途,模型醒来第一件事是"我到哪了"
④ 关键发现与约束 "用户要用中文""X 文件是坏的"------最易漏,只活在摘要里

自检方法:压缩后问模型三个问题------"你要干嘛?干到哪了?有什么坑要注意?"三个都答得上,摘要才合格。

进阶认识 :s05 的 todos 本质是"结构化的进度摘要"------它放在循环外,压缩碰不到,所以比摘要更可靠。真实产品的做法:能结构化的就结构化,剩下才压成摘要。

1.5 动手实验

  1. THRESHOLD = 9 改成 5 → 观察压缩频率变化;太频繁 = 丢更多细节 + 更多总结开销
  2. 删掉 while 循环 + KEEP_RECENT = 1 → 尝试踩中"拆散配对"的事故
  3. 把 fake_summarize 改成只写"已完成若干步"不写具体内容 → 问模型"第 3 步跑了什么",答不上 = 亲眼看丢失

二、s09 长期记忆:会话会死,记忆不死

2.1 要解决的问题

会话一结束,messages 整个消失;压缩还会丢细节。下次会话,Agent 是个失忆的人。

2.2 核心代码

全部核心 = 两个文件操作 + 一个注入点:

python 复制代码
def memory_save(text):                      # 追加写(append,不覆盖)
    with open(MEMORY_FILE, "a", encoding="utf-8") as f:
        f.write(text.strip() + "\n")

def memory_load():                          # 启动时读全文
    with open(MEMORY_FILE, "r", encoding="utf-8") as f:
        return f.read().strip()

新会话的注入点:

python 复制代码
memory_text = memory_load()                 # ① 程序读磁盘
query = f"[长期记忆-启动时自动注入]\n{memory_text}\n\n你好"   # ② 缝进第一条消息
messages = [{"role": "user", "content": query}]               # ③ 发给模型

2.3 知识点

# 知识点 要点
1 "记得"的本质是注入 会话 B 模型什么都没"记得"------它只是在新会话第一条消息里看到了一段文字 。记忆在磁盘,搬运靠程序,模型只负责读到。铁证:demo 里判断"有没有记忆"用的是 "用户约定" in str(messages[0]) ------ 字符串查找,不是记忆检索
2 无状态是模型的本性 LLM 没有记忆模块、没有存储区。它的一切"知道"(记忆、Python 语法、用户名字)机制完全相同:上下文里的文字
3 该记什么:约定/偏好、踩坑结论、项目约定 不该记:流水账、原始对话全文、临时报错。判断标准:这条信息下次会话还用得上吗? 记忆文件本身也占上下文,垃圾记忆 = 每个新会话背废料
4 当场写,不等结束 三个致命场景:①压缩随时触发,晚写细节先丢,模型自己都忘了谁去写?②会话可能崩/被杀,"结束"可能永远不来;③长会话结束时的"回忆"本身已被压缩污染,不可靠。笔记是当场记的,没人散会后再凭记忆补全场
5 记忆要可更新 用户改主意时旧约定必须能被替换(更新/覆盖/带时间戳定期整理)。永远 append = 矛盾记忆越攒越多。完整生命周期 = 当场写 + 定期整理

2.5 动手实验

  1. 会话 B 启动改成不注入 → 看回答变成"没找到任何记忆"------记忆的价值 = 有/没有的行为差
  2. 连跑两次脚本 → 第二次读到的是叠加记忆(append 不覆盖)→ 体会"记忆失效"问题

三、我的薄弱点清单(错题本)

# 薄弱点 我的错误答案 正确理解 状态
1 分不清"原件"和"复印件" 把"摘要"当成最不能压掉的信息 摘要是压缩的产物(复印件),丢了可重生成;messages[0] 原始任务是原件,丢了永久漂移。备份原则:先分清原件和复印件 ✅ 已理解
2 给模型安"记忆模块" "会话 B 记得是因为模型有专属记忆模块" LLM 无状态,没有记忆模块。"记得" = 程序把磁盘文件缝进上下文,模型只是读到文字。模型 = 每次都失忆的天才 ✅ 已理解
3 记忆写入时机 "会话结束时统一整理再写" 当场写。压缩随机触发 + 会话可能崩 + 结束时的回忆已被污染。当场写 + 定期整理 才是完整生命周期 ✅ 已理解
4 混淆"保留哪些消息"和"摘要里写什么" 设计摘要内容时答成了三段式结构 结构问题(原始任务+摘要+最近原文)≠ 内容问题(摘要四要素:目标/进度/状态/约束) ✅ 已理解

历次复发提醒 :本模块之前,"程序/模型会自己想办法"的误区已复发 5 次(s01 权限换方案、s02 未知工具查询、s03 重试降级、s05 收尾改状态、s06 整理重派)。s08/s09 测验中该误区未再出现于程序侧,但转移到了模型侧("模型有记忆模块")。统一判据不变:

先找代码。程序做的,代码里一定有那几行;找不到,就是模型"读到文字后的生成行为"。而模型的"能力",也必须能指出来自上下文里的哪段文字。


四、s08 vs s09 横向对比速查表

维度 s08 压缩 s09 记忆
解决什么 会话内上下文装不下 跨会话全部失忆
作用范围 一次会话内 跨会话
机制 水位检查 + 三段式替换 追加写文件 + 启动注入
信息代价 细节永久丢失(换空间) 只留结论(放弃过程)
谁写摘要/记忆 LLM(程序判断不了重要性) LLM(同左)
什么时候触发 循环开头水位检查 当场写(不能等)
与 s05 的关系 摘要是"临时进度" todos 是结构化摘要,压缩碰不到
真实对应 WorkBuddy/Claude Code 的会话压缩 MEMORY.md / 每日学习日志

一次长任务里两者的配合 :干活 → 发现重要约定 → 当场 memory_save (s09)→ 上下文满了 → compact 压缩(s08,细节丢但约定已在磁盘)→ 会话结束 messages 销毁 → 新会话 memory_load 注入 → 无缝续命。


五、自测题(附答案)

1. messages 每轮 +2,第 20 轮时多少条?压缩的触发条件写在哪里?
答案 1 + 19×2 = 39 条。触发条件在 agent_loop 的循环开头:`if len(messages) > THRESHOLD`。水位检查只能放循环里------控制循环节奏的东西改不了注册表。

2. 压缩时为什么必须保留 messages0?为什么摘要反而可以丢?
答案 messages0 是原始任务(原件),丢了 = 目标漂移,且无法重生成。摘要是压缩的产物(复印件),旧消息还在时随时可重写。先分清原件和复印件。

3. API 报 tool_use without corresponding tool_result,最可能的原因?
答案 压缩切点落在 assistant(tool_use) 上,把它的 tool_result 切走了。修复:切点回退 while 循环,保证成对删除。

4. 摘要里必须写清哪四样东西?
答案 ① 目标复述(防漂移)② 已完成进度(防重复劳动)③ 当前状态/下一步 ④ 关键发现与约束(最易漏)。自检:压缩后模型能答"干嘛/到哪了/什么坑"三问。

5. 为什么摘要用 LLM 写而不是程序规则拼?
答案 程序判断不了哪些信息重要、哪些能丢;模型能读懂语义做取舍。又是"用模型智能补程序能力"。

6. 会话 B 全新 messages,它"记得"约定的机制是什么?
答案 程序启动时 memory_load() 读磁盘文件,缝进 messages0 发给模型。模型无状态、无记忆模块,它只是"读到了一段文字"。验证:demo 里用 `"用户约定" in str(messages0)` 字符串查找判断。

7. 哪些信息该进长期记忆?哪些不该?判断标准一句话?
答案 该记:用户约定/偏好、踩坑结论、项目约定。不该记:流水账、原始对话全文、临时报错。标准:这条信息下次会话还用得上吗?(记忆文件本身占上下文)

8. 为什么重要约定必须当场写进记忆,不能等会话结束?
答案 ① 压缩随时触发,晚写细节先丢,到结束时模型自己都不记得了,谁去写?② 会话可能崩溃/被杀,"结束"可能永远不来。③ 长会话结束时的回忆已被压缩污染。当场写 + 定期整理才是完整生命周期。

9. 用户改主意了(简短→详细),旧记忆还在文件里,怎么办?
答案 memory_save 要支持更新/覆盖旧条目(或带时间戳定期整理)。永远 append = 矛盾记忆越攒越多,每个新会话都读到自相矛盾的指令。

10. s05 的 todos 和 s08 的摘要是什么关系?哪个更可靠?为什么?
答案 todos 本质是"结构化的进度摘要"。它更可靠:放在循环外(模块级变量),压缩碰不到、会话内不丢;摘要则是 LLM 总结的产物,可能漏。真实产品原则:能结构化的就结构化,剩下才压摘要。


本篇收束句 :会话是会死的,但账本可以不死。模型是每次都失忆的天才,程序是那个勤快记笔记的秘书------天才负责判断,秘书负责让天才"以为"自己没失忆。

相关推荐
星恒随风1 小时前
Linux网络编程基础(二):彻底理解局域网通信、MAC与IP地址、数据封装、端口号、网络字节序及Socket接口
linux·网络·笔记·学习·tcp/ip·1024程序员节
嘻嘻嘻3212 小时前
ICLR2025|MIM‑Refiner:利用预训练掩码图像建模中间表征提升对比学习
学习
星恒随风2 小时前
Linux线程同步与互斥:彻底理解读者写者问题、读写锁原理、读者优先与写者优先(附pthread完整实验)
linux·服务器·笔记·1024程序员节
Young Doro3 小时前
从 KSG 到 InfoAtlas:互信息估计完整学习册
学习
Be for thing3 小时前
PyCharm安装与PyTorch项目配置|Jupyter Notebook与PyCharm使用对比
pytorch·学习·1024程序员节
ljt27249606613 小时前
Vue笔记(十五)--自定义引用
笔记·1024程序员节
xieyan08113 小时前
笔记不只是仓库:让 AI 帮旧想法重新参与思考
人工智能·笔记
java_nnnn4 小时前
Linux 学习第二篇:从零认识 Linux 文件系统、目录结构与基本命令
linux·运维·学习·1024程序员节
江屿风4 小时前
【Linux-System】【System V共享内存:从原理到实战的完整解析】流食般投喂
linux·运维·服务器·笔记·系统架构·1024程序员节