一文搞懂 Claude Code 的记忆系统:教程版 vs 真实实现,附医疗场景思考(学习笔记)
如果你也想弄懂 Claude Code 这类 Coding Agent 到底是怎么工作的,这个仓库也许能帮你少走一些弯路。目前我的 github 的项目(OpenAI SDK 版),我学习的课程为 learn-claude-code 的 v2 版本课程(感谢原作者),大家一起学习,共同进步。如果对你有帮助,请为我点一个 Star。
项目地址:https://github.com/peijiping/learn-claude-code-langchain
为什么 Coding Agent 需要记忆?
LLM 天生是"金鱼记忆"——每次对话都是从头再来。上次你告诉它"用 bun 不要 npm",下次它又忘了。没有记忆的 Agent 永远是新手:每次都要重新了解你的偏好、重新踩同样的坑。
Claude Code(以下简称 CC)的做法是把记忆落盘到文件,跨会话、跨进程持久化。这是它和我们平时用 Chat 的最大区别之一——它不是"聊完就忘",而是会积累关于你、关于项目的知识。
一、CC 的记忆长什么样
CC 的记忆存储在用户目录下,基于你的项目 git 根路径生成一个目录:
~/.claude/projects/<sanitized-git-root>/memory/ ├── MEMORY.md # 索引文件(≤200行/25KB) ├── user_role.md # 用户角色记忆 ├── feedback_testing.md # 反馈记忆 ├── project_auth_rewrite.md # 项目记忆 ├── reference_linear.md # 参考记忆 ├── team/ # 团队共享记忆 └── logs/ # 日志模式
几个关键点:
- MEMORY.md 是固定的"索引文件",永远存在,作用是给模型一个"我有哪些记忆"的目录视图。
- 其他文件都是按需生成的,根据你的对话内容实时产生,不同项目、不同用户看到的完全不一样,没有固定模板。
- 记忆文件用 Markdown + YAML frontmatter 的结构:开头几行是元数据(name / description / type),下面是正文详情。
二、四类记忆:该记什么、什么时候记
CC 把记忆分成四类,每种类型都定义了"什么时候记":
| 类型 | 含义 | 写入时机 |
|---|---|---|
| user | 用户的角色、偏好、知识背景 | 学到用户信息时(如"我习惯用 tab 缩进") |
| feedback | 行为纠正 + 正向确认 | 用户纠正了你的做法,或肯定了某个非显而易见的做法时 |
| project | 项目目标、约束、deadline | 了解到不可从代码推导的信息时(如"上线日期是 X") |
| reference | 外部系统指针 | 知道某个外部资源的位置和用途时(如"bug 跟踪在 Linear") |
同时,CC 也明确规定了什么不该记:代码模式(从代码里能读到的东西)、Git 历史(用 git log 就能查到)、调试方案(修复就写在代码里)、临时任务状态。
三、记忆怎么读
CC 读取记忆有两条路:
- 索引常驻 System Prompt——
MEMORY.md的索引永远跟着系统提示走,模型每轮对话都能看到"我有哪些记忆"。这条路径 token 开销极小,是记忆的"目录"。 - 按需读取正文——模型发现某条记忆和当前任务相关时,通过
read_file工具读具体的记忆文件。记忆正文不塞进 System Prompt,避免上下文被撑爆。
这个"索引-内容分离"的设计是精髓:轻量索引常驻,完整内容按需加载,兼顾了效率和上下文控制。
四、记忆怎么写:这是核心
这里是我学习过程中最大的认知转变。
教程版:事后分析(不推荐)
教程 s09 的做法是事后分析:每个 turn 结束后,额外调一次 LLM,把最近一段对话喂给它,让它分析"刚才聊了什么值得记",返回 JSON 再批量写盘。
# 教程版伪代码:每轮结束都要"抽取记忆"
if response.stop_reason != "tool_use":
extract_memories(pre_compress) # ← 额外一次 LLM 调用
consolidate_memories() # ← 还要整合去重
这带来了三个问题:
- 贵——每轮对话额外多 1~2 次 LLM 调用,成本翻倍。
- 不准——靠模型事后"猜"哪些值得记,经常记不到点子上,或者把随口一句话当成偏好。
- 脏——事后批量抽取天然会产生重复、矛盾的条目,所以教程又得写一个
consolidate_memories(做梦/Dream)来清洗数据——抽脏了再洗,等于自己给自己找事。
CC 真实版:Tool 驱动(模型自己决定)
CC 的真实做法截然相反——把记忆的写入权交给模型,给模型两个工具,让它自己判断什么时候动手:
TOOLS = [
# 写入记忆
{"name": "write_memory",
"description": "当用户表达偏好、纠正做法、确认方案、透露项目事实时调用",
"input_schema": {...}}, # name / type / description / body
# 删除记忆
{"name": "forget_memory",
"description": "当用户推翻某条已有记忆时调用",
"input_schema": {...}},
]
然后在 System Prompt 里写清楚触发规则:
## Memory system 调用 write_memory 的场景: - 用户说出个人偏好("我喜欢 X"、"别用 Y") - 用户纠正你的做法("不对,应该这样") - 用户肯定了某个做法("对,就这样") - 用户透露项目事实或约束("我们用 PostgreSQL"、"截止日期是 X") - 用户明确说"记住这个" 调用 forget_memory 的场景: - 用户推翻了某条已保存的记忆
核心变化:记忆的触发权从代码转移到了模型。 代码不再替模型做"哪些值得记"的判断,而是给模型工具和规则,让它在对话过程中实时决策。用户说"记住这个"的下一秒,模型就直接调 write_memory 落盘了。
两种方式的对比
| 维度 | 教程版(事后分析) | CC 真实版(Tool 驱动) |
|---|---|---|
| 额外 LLM 调用 | 每轮 1~2 次 | 0 次(只有文件 IO) |
| 触发时机 | turn 结束后被动批处理 | 对话中模型即时主动决策 |
| 写入精度 | 靠"猜",易漏易错 | 模型参与对话,知道什么值得记 |
| 数据质量 | 易产生重复/矛盾 | 源头干净 |
| 是否需要做梦 | 需要(清洗脏数据) | 不需要 |
关于"做梦"(Auto Dream / consolidate)
教程版因为"事后分析"会抽出一堆重复矛盾的数据,所以发明了"做梦"来全量重写、去重、合并。但只要源头干净,清洗环节就是多余的——Tool 驱动模式下,模型不会重复抽取同一件事;用户推翻了旧记忆,模型直接调 forget_memory 删掉再写新的,根本不需要集中整合。
所以我的结论是:设计上可以砍掉 Dream。记忆系统只需要"按需更新",不需要"定期做梦"。
五、我把教程版改造成了 CC 真实版
基于上面这个认知,我把教程的 s09 代码做了一个简化重构(s09_code_cc.py),原则是只保留必要的,删掉一切为了"事后分析"而存在的代码:
- 删除了
select_relevant_memories/load_memories/extract_memories/consolidate_memories四个函数——它们都是"事后分析"模式的产物,每轮浪费 1~2 次 LLM 调用。 - 新增了
write_memory/forget_memory两个工具,模型自主调用,即时落盘。 - 重写了
build_system(),把触发规则写清楚。 - 简化了
agent_loop(),去掉所有记忆预处理/后处理,只保留压缩管道。
整个记忆系统最后只剩三句话的核心:
- MEMORY.md 索引在 System Prompt 里 —— 模型永远看得到有什么
write_memory/forget_memory两个工具 —— 模型自己决定何时写、何时删- System Prompt 里写清楚触发规则 —— 告诉模型什么时候该动手
干净利落,零额外 token 开销。
六、我的思考:垂直场景(医疗)下怎么设计记忆
这是我把记忆系统用到自己工作(医疗 AI)时的思考,供参考。
记忆需要基于用户的指令提炼
自动化后台生成的数据,不能直接当成用户的记忆。 记忆必须来自用户主动表达或操作指令的提炼总结。系统后台自动跑的日志、结果、对话,顶多算"数据",不算"记忆"——因为用户并没有明确表态"我喜欢这样"。
医生的记忆
- 初始化阶段:需要医生主动写入自己的偏好(比如"我习惯看结构化报告"、"用药方案倾向保守")。
- 运行阶段:大模型根据医生后续的操作指令去更新记忆(比如医生纠正过一次用药建议,这就是一条 feedback 记忆)。
- 关键限制:如果只是后端自动生成的结果和对话,不可以抽取成医生的偏好和记忆——那是系统的输出,不是医生的表态。
患者的记忆 —— 其实叫"患者摘要"更准确
理论上,患者不直接和模型交互,所以患者没有"记忆"。
准确地说,大模型对患者临床数据的提炼总结,应该叫患者摘要,而不是患者记忆。比如患者的性别、年龄、疾病史、过敏史、手术史——这些是"可以直接拿来给大模型参考使用的精简数据",属于摘要性质,而不是用户主动表达的偏好。
这个区分很重要:记忆属于"对话方"(用户/医生),摘要属于"被描述对象"(患者/数据)。设计系统时搞清这一层,才不会把数据当记忆用,也不会给没有交互的对象硬造记忆。
七、总结
CC 记忆系统的本质,可以浓缩成一句话:
用文件做存储,用工具做写入,用 System Prompt 做规则,让模型自己在对话中决策。
- 存储:
MEMORY.md索引 + 按需生成的详情文件 - 读取:索引常驻 System Prompt,正文按需
read_file - 写入:
write_memory/forget_memory两个工具,模型自主调用 - 不需要"做梦":源头干净,按需更新即可
这也是一个很好的工程原则:把判断交给最懂上下文的人(模型),把执行做成最小的工具(写入/删除),把规则写进它每轮都看得到的地方(System Prompt)。
如果你也想弄懂 Claude Code 这类 Coding Agent 到底是怎么工作的,欢迎到我的仓库一起交流。如果对你有帮助,请为我点一个 Star ⭐
https://github.com/peijiping/learn-claude-code-langchain
更多推荐




所有评论(0)