1 随口一说,它居然记住了
你三天前随口提过一句「这个项目别用 npm,用 Bun」。今天你新开一个会话说「运行这个项目」 ,它就在过程中用Bun 的命令,而不用npm命令了——你没重复说过,但它自己记着了。
怎么做到的?我们一步一步来看。
2 什么时候记?
当你说「这个项目别用 npm,用 Bun」的时候,agent 是马上就记下来了吗?不全是。
三种情况,对应三个时机,拼在一条时间线上:
▍时机一:你说话的时候
Claude Code 每次调用 Claude,都会带一份系统提示词,里面写着什么值得记、怎么记。没有哪段代码专门检查你说的话,判断这件事就是模型自己顺带干的,每一轮都在干。
判断的依据不是感觉,是提示词里列好的四类「值得记」:
-
你是谁:角色、技术底子、在忙什么
-
你怎么要求它:纠正、认可都算
-
项目里正在发生、代码里看不出来的事
-
东西在哪:存在哪个外部系统里
示例: 「这个项目别用 npm,用 Bun」- 属于用户要求、记住
「你好」- 哪一类都不属于,不记
▍时机二:每轮对话结束的时候
模型说完了、没有工具要跑了,主对话分出一个分身,把这轮对话补读一遍:
- 哪怕这轮你只说了一句「你好」,也照读,空手回
- 主模型这轮已经写过记忆,就跳过
**为什么说话时已经记过,说完了还要再读一遍?**主模型的判断是顺带的,注意力在你的任务上,值得记的话容易被稀释遗漏。分身专捡这些漏。
**分身哪来的?**同一个模型复制的一份:系统提示词、工具、对话历史原样照搬,只在末尾追加一条「把这轮值得记的写进记忆目录」。
连模型都不能换:缓存按提示词、工具、模型、对话前缀整段计算,差一样就全部作废重算。照搬一份,主对话已付过钱的那段就不用再付,边际成本趋近于零。
几个限制让它跑得快、不出格:
- 只准往记忆目录写,命令只给只读的
- 最多转 5 轮:注释说规矩的提取两到四轮就完,硬上限防它跑去验证个没完
- 只看最近几条消息,不准回头调查核实:不 grep、不翻代码确认某个写法在不在、不跑 git
- 动手前先递清单:现有记忆的目录预先塞给它,省一轮「先看看有什么」
▍时机三:满一天、攒够五个会话的时候
距上次整理满一天、期间又攒了五个会话,后台就自动跑一次整理任务。源码里给它起的名字叫「做梦」,网友习惯叫梦境机制,趁睡觉整理白天的事。
为什么会话结束已经补读过,还要再来一次?
两件事不重样。补读是补漏,一轮管一轮,只看得见眼前几条消息;整理是收拾,面对的是几天的记忆:零散的并进主题文件,重复的、过时的清掉。这些跨会话才看得出来的活,补读做不了。
3 记的是什么?会话内容全存吗
不全存。「值得记」的界线画得很死:代码里已经有的,一律不记。
收进四类,type 四选一:
| 类型 | 记什么 | 例子 |
|---|---|---|
| user | 你是谁:角色、技术底子、在忙什么 | 你说「我是数据科学家,在看咱们的日志方案」→ 记下:数据科学家,关注日志/可观测性 |
| feedback | 你怎么要求它:纠正,以及认可 | 你说「测试里别 mock 数据库——上季度 mock 测试全过,上线迁移挂了」→ 记下:集成测试必须打真实数据库,原因是 mock 和线上不一致掩盖过 bug |
| project | 项目里正在发生、代码里看不出来的事 | 你说「周四之后非关键合并全冻结——移动端要切发布分支」→ 记下:2026-03-05 起合并冻结,为移动端发布 |
| reference | 东西在外部系统的哪里 | 你说「pipeline 的 bug 都跟踪在 Linear 项目 INGEST 里」→ 记下:pipeline bug 在 Linear 项目 "INGEST" |
四类的共同点:都从当前代码里看不出来的;看得出来的,它自己会读,不用记。
还有一张排除清单,五条:
- 代码模式、架构、文件路径:读当前代码就有
- git 历史、谁改了什么:git log 是权威
- 修 bug 的方案:修复在代码里,背景在提交信息里
- CLAUDE.md 里已经写了的:不记两遍
- 进行中的临时状态:本会话自己的事
清单后压着一句狠的:**以上排除项,连你明说「记住这个」也拦。**你说「把本周的 PR 清单记下来」,它会反问:哪里代码里看不出来,如果代码里能看出来,也拦。
两条容易漏的规矩:
- 认可也要记:你说「不对,别这么干」,话说得重,它记得住;你说「对,就这样」,话说得轻,容易被漏。漏了,它记住的就全是不许干的事,你认可过的好做法下次想不起来
- 相对日期要换算:你说「周四」,存进去的得是具体日期;照原样存,过一晚就说不清指哪天
4 记在哪、怎么写进去的
没有数据库,没有向量库,没有后台服务——就是你自己机器上的一个普通文件夹:~/.claude/projects/ 底下一个项目一个文件夹,里面的 memory/ 就是记忆目录。一个项目一份,互不相通;同一个仓库的几个并行工作目录,算同一个项目,共用这份。
一个项目的记忆,就由两部分组成:记忆本体,加一页索引。本体是一条一条的笔记文件,一条记忆一个,记忆的内容全在文件里;索引是一页目录,一行指一条,只指路、不装内容。
打开这个文件夹,看到的就是这两样(虚构):
memory/
├── MEMORY.md ← 索引,整个文件夹就这一个
├── deploy-migration.md ← 一条记忆
├── user-role.md ← 一条记忆
└── testing-real-db.md ← 一条记忆
再看单个文件。一条记忆,deploy-migration.md:
---
name: deploy-migration
description: 部署前必跑数据库迁移;上季度漏跑过一次,线上事故
type: feedback
---
(正文:这条要求的来龙去脉)
索引里指它的一行,在 MEMORY.md:
- [部署前必跑迁移](deploy-migration.md) — 上季度漏跑出过一次线上事故
开头三行登记,中间那行 description 最要紧:一条记忆将来能不能被找到,就看这行摘要写得对不对题。
**怎么写进去?**没有「保存记忆」这个专门动作,模型拿平时写代码的写文件工具直接写,固定两步:先写笔记文件,再往索引里加一行。索引也全靠模型:平时加行,隔天整理任务重排整页。
索引:提示词原文「它是一个索引,不是一条记忆」,每行约 150 个字符封顶。
目录不用模型建:会话一开,代码就顺手建好,提示词还叮嘱一句「目录已经在了,直接写」。
**一直追加,还是也会更新、删除?**都会。四条规则:
- 先查再写 - 有现成的一条能更新,就不另开新的,防重复
- 发现哪条错了、过时了,当场改掉或删掉
- 你说「忘掉这个」,它找出那条来删
- 按主题归堆,不按日期记流水账
**越记越多,会爆吗?**爆不了,三道看管:
- 写得少
- 重复的不写、过时的当场删
- 隔天整理任务再清一遍
真写多了也有硬上限:索引带进对话时最多 200 行、2.5 万字节,超了直接砍,还留一句警告,叫模型把细节搬回笔记文件。
5 什么时候读?
记了但读不到,这种失败最隐蔽:不报错、不留痕。你只看到它「不记得」,分不清是没记住,还是没读到。
读记忆的方式,一共三种:
- 开场带上索引:开场那一刻,整页索引就发进对话,之后每一轮都在。这条谈不上触发。
- 每轮自动读取:你一发消息,代码就单独调一次 Claude,用一个小模型把这轮用得上的记忆带进对话,赶在主模型答完之前。
- 自己读文件:不是每轮都读。模型看这轮的任务跟哪条记忆有关,有关才去读;只有一条硬规矩:你明确提到记忆、提到让它记过的东西,必须去读。
这套记忆系统有两个读取记忆的版本:
| 第一代(现在默认) | 第二代(藏在开关背后,没开) | |
|---|---|---|
| 读的方式 | 开场带上索引+自己读文件 | 每轮自动读取+自己读文件 |
| 什么时候读 | 索引一直在;笔记的正文,模型看相关才去读 | 每条消息都自动读一遍,用得上的正文直接带进对话 |
| 索引 | 要维护:写一条记忆得两步 | 取消:写记忆只剩写文件一步 |
| 主模型知道有哪些记忆吗 | 一直知道,索引就在上下文里 | 只知道读取的那几篇 |
| 代价 | 索引每一轮都占着窗口,用不用得上都在 | 每轮多一次模型调用的成本 |
6 怎么读?
第一代一句话就说完:开场整页索引发进对话,之后模型看相关才去读文件。
第二代每轮自动读一遍,拆开是固定的七步。你问「部署前要跑 migration 吗」,主模型还在组织第一句话,另一边同时开跑:
- 扫目录:最多 200 篇,每篇只读开头 30 行,查找的是本地磁盘,不调模型、不花钱
- 拼清单:一篇一行,报文件名、类型、日期、描述行
- 模型来选择内容:清单+你的这句话发送给模型,模型最多返回 5 个文件名(只返回名字、不返回理由);这步不带主对话的内容,模型固定用默认的 Sonnet,跟你主对话用哪个无关
- 核对文件名:返回的名字必须在清单里真有,模型会编出不存在的文件,编造的扔掉
- 读全文:选中的每篇最多拿 4KB,代码在开头加一行「这条记忆已 47 天」
- 送进对话:主模型一轮要被调用好多次,每调一次工具、拿一次结果,就接着干。记忆包成一条系统提醒,夹在其中一次工具结果后面送回去,下一次调用就看见了。
- 限额:带进对话的记忆累计满 60KB(一个会话算),自动读取整个停。算的是塞进来的量,不是记忆目录本身的大小
七步里没有一步在做字面匹配,因为字面匹配有两处硬伤。
- 第一处源码注释里点过:提问里有个 spawn,描述里也有个 spawn,对上了,但说的不是一回事。
- 第二处更要紧:条件没法写成词,「正在用的工具,说明书别塞、坑要塞」这句话,没有一个能拿来匹配的字面词。
所以这活交给模型,规则很明确:
- 拿不准的不选
- 没有值得选的,空手回来也行
- 你正在用的工具的说明书不选,但它的坑和已知问题要选。正在用,正是它们要紧的时候
那读错了怎么办?
读错分两种。
-
返回了错误文件名:模型返回了不存在的文件名,跟磁盘目录一对就露馅,扔掉;请求失败的,这轮当没读。
-
读的记忆已经过时:内容是真的,但记的东西过时了。还准不准,代码判断不了。把错的当对的用,危险就在这。防它不在读,在用:每篇贴一行「这条记忆已 47 天」,提示词要求核实过再讲给你。
-
读漏了也有救:代码发现不了漏——你说「按之前定的规矩来」,这句话里没有一个字写着「记忆」,规则无从下手;模型读得懂这是在指一段没出现的历史,顺着指向找不到,就知道缺了。一察觉,就照提示词里一条现成指令自己动手:先翻记忆目录,还没有,才翻会话记录——历史会话的原始存档,又大又慢,所以放最后。
7 为什么不用向量库?
向量库解决什么? 向量库把每条记忆算成一个向量:一串数,意思越近,数挨得越近。检索时把你的问题也算成向量,选距离最近的几条。
两个硬好处:
- 不怕量大:几万条记忆,查找成本几乎不涨
- 不怕措辞:你问「部署前要跑迁移」,能对上「数据库变更先执行迁移脚本」这条记忆——记的时候不用猜你将来会怎么问
专门做 agent 记忆的开源项目 Mem0、MemGPT用的都是向量库。
那 Claude Code 为什么不用向量库?
它自己的三个理由:
- 模型本来就会读写文件,不用再配一套存储和检索系统
- 明文你能看能改,出问题随手修
- 量小,用不着:最多读取200篇
前两个理由是工程量。第三个理由说白了:不是检索做得强,是压根没让记忆多到需要复杂的检索。写入有五条排除,连你点名要记的都拦;先查再写,隔天再清一遍。写入把记忆量控住了,读取才敢只看两百篇。
可这个记忆的少也是它的天花板:真大型项目记忆到几万条,读取还是只看两百篇——老记忆被挡在外面,模型再也看不见,这套做法自己就顶不住了。
向量库与文件检索对比
- 向量库给不了的:理解。「正在用的工具,说明书别塞、坑要塞」这句话,算不成向量距离,只有模型读得懂。例如都在说 spawn,距离很近,但讲的不是一回事。
- Claude Code 给不了的:快和便宜。向量查找毫秒级、几乎免费;Claude Code 每轮跑一次小模型来读记忆,又花钱又费时间,几十万用户的产品不得不算这笔账。所以量大了的成熟做法常常混着来:向量先捞一批,模型再选一遍。
一句话:不是向量库没用,是两边要解决的问题不一样。记忆多到几万条、跨用户共用、每轮等不起一次模型读取——问题换成这样,文件夹这套方案就行不通了。Claude Code 的选择是把问题改小:少记,记到用不着向量检索。
8 新记的,什么时候生效
分四种情况:
- 刚写的笔记:文件已在磁盘上,模型当场就能读;下一轮自动读取就可能带上它
- 索引新行(第二代里这步没了):要等下个会话。开场发出去之后,这个会话里不再重读
- 已经塞进过对话的记忆,不再塞第二遍——内容还在对话里,再塞一份白占地方;等压缩把历史改写完、旧的那份不在了,才能再塞进去
- 额度用完的会话:自动读取整个停,新笔记等下个会话
9 越记越多,怎么办
每一道门都有一个阈值限制。七步里已经碰过几个,这里补齐:
| 门 | 数 | 为什么 |
|---|---|---|
| 一篇记忆带进对话时 | 封顶 4KB | 工具结果进对话有大小检查,超大会被截断; |
| 一轮读取 | 最多 5 篇 | 同一轮塞进来的总量上限 |
| 一个会话累计 | 60KB | 注释记着线上实测约 2.6 万 token,约等于三轮读满的量 |
| 索引 | 200 行、2.5 万字节 | 开场那一页的天花板,超了截断加警告 |
除了数量,重复是另一种「多」:同一篇记忆被塞进去三次,等于浪费了三倍的窗口。防了三层:
- 读取时,跳过这一轮已经出现过的
- 全部带完,再滤一遍
- 模型自己已经读过、写过的文件,不再带
真涨上去,还有两招。
**定期整理。**满一天、攒够五个会话自动跑的那个整理任务,内部四个阶段:
- 通读:读一遍现有记忆
- 搜集:带着具体疑问去近期会话记录里搜,不通读——比如一条记忆提到「昨天构建失败」却没记报错,就拿报错关键词搜出当时的原文。提示词叮嘱:别把记录从头读一遍,只带着已有的疑问去搜
- 合并:把新东西并进现有主题文件,别另开近似的一条
- 清理:删掉多余的,重建索引
**你随时能动手。**记忆全是明文,一条命令打开编辑器随便改。
10 记的和眼前的对不上,信哪个
记错了比没记住更糟:它告诉你「某文件第 80 行」,有条有理带着行号,听着就像查证过的,可那文件你上周就删了。
怎么办?四个办法,同一个思路:让模型在用之前先起疑。
**保质期标签。**带进对话的每篇记忆,前面贴一行代码给出的记忆时效:「这条记忆已 47 天」。三条规矩:
- 不写日期,写天数:模型算不好两个日期差多少天,一长串日期数字也唤不起「有点久了」的警觉,「47 天」一眼就懂
- 一天以内不贴:刚记的就贴警告,纯属白提醒
- 模型自己去读文件也一样:读回来的内容里,照样带着这句提醒
**对不上时,信眼前看到的,不是信记忆里的。**提示词明说:把你此刻观察到的当准,顺手把过时的那条更新或删掉,不是明知它旧了还照用。
**推荐前先核实。**记忆里写了某个函数、某个文件,只说明写这条的时候它存在,后可能改了名、删了、根本没合进来。提示词配了一张清单:
- 写了文件路径:先确认文件还在不在
- 写了函数或参数名:先搜一遍代码,确认还在
- 只是问问过去的事,照记忆答就行;你要照着这条记忆动手,先核实
清单后面压着一句格言:「记忆里说 X 存在」,不等于「X 现在存在」。
这张清单在提示词里怎么放,是实测调出来的:单独占一节、标题就叫「推荐前先核实」,效果最佳。
11 回头看:记的松,读的严
把全文拼成一张图:
回头看,规律就一条,正是标题那句话:记的松,读的严。
**记的松:**什么时候记、记什么、怎么整理,规矩全是提示词里的话——是叮嘱,不是强制,模型听不听,代码管不着。
**读的严:**每一道门都是代码写死的数:最多扫 200 篇、选 5 篇、一篇 4KB、一个会话 60KB——超了就拦,没有商量。
12 三条经验,用在你自己的系统上
- **先问一句:这里出错了,后果严不严重?**轻的,忘了就忘了,下次还能补,规则写进提示词就够;重的,当着用户的面出错、每一轮都在花钱——把门写死在代码里。
- **给旧内容标明放了多久。**写「这条已 47 天」,别写一长串日期数字。
- **记忆几百条以内,先别上向量库。**让模型读着描述选,拿不准的不选;质量上的规则写进提示词,做成醒目的标题、别埋进清单条目,再配一组场景测试去调,实测数字比直觉可靠。
今晚就能试:跟它说一句「记住:这个项目部署前要先跑迁移」。然后去 ~/.claude/projects/ 下这个项目的 memory/ 目录看一眼。新添的笔记和索引行就在那儿,全是明文。明天新开一个会话再部署,看它记不记得。
Part 3 讲到这儿:窗口怎么装满、满了怎么压、你写的怎么每轮都带上、它记的怎么存、怎么读。剩最后一件事,token 怎么计、钱怎么省。下一篇收这个尾。
连载 《拆解 Claude Code》· 关注不迷路