🐎 第2章 温故知新:记忆系统工程实践
📄 一句话概括

记忆不是一个文件,是五层叠起来的:企业策略、用户记忆、项目记忆、目录记忆、会话临时。 它们不是覆盖关系,是叠加关系——全都进上下文,冲突时越具体的层赢,但企业层的红线例外, 它下面几层推翻不了。

📝 本章和前面几章的关系

上一节说「把该记的搬到窗口外面」,这一节说搬出去之后怎么分层放。 分层的依据只有一个:这条规矩该在多大范围里生效。范围越大放得越靠上,范围越窄放得越靠下。

一、五层,从最远到最近

最上面是企业策略。 它由组织统一下发,管的是「哪些事绝对不能做」:不能访问哪些目录、依赖必须走哪个源、能不能连外网。 第二层是用户记忆,放在用户主目录下,跨项目生效。 管的是「我这个人习惯怎么干」:回答用中文、提交信息用什么语言、缩进几个空格。 第三层是项目记忆,就是仓库根目录的 CLAUDE.md。 管的是「这个仓库怎么干」:技术栈、命令、目录约定、禁区。

第四层是目录记忆,写在某个子目录里。 管的是「进到这个目录才需要知道的规矩」:比如 src/api 下的返回格式、tests 下的造数据方式。 第五层是会话临时,就是你在对话里随口交代的事:这次只改 README,别动代码。 五层的边界不是按文件位置划的,是按生效范围划的。这一点想通了,一条新规矩该放哪儿基本不用犹豫。

二、叠加,不是覆盖

很多人第一反应是「覆盖」:下面的层把上面的盖掉。实际是叠加——全部读进来,一起生效。 叠加意味着两件事。第一,条数是相加的,五层十三条就是十三条,不是最后那条。 第二,冲突时没有删除动作,只是「后说的算」。 演示里造了两处冲突:提交信息用英文还是中文,缩进四格还是两格。 用户层给英文和四格,项目层给中文和两格,最后生效的都是项目层的。

规则是就近原则:越具体的层赢。项目层比用户层具体,因为它描述的是这个仓库; 目录层又比项目层具体。 但要注意,「就近赢」是行为上的结果,不是文件被改写。 用户层那句「提交信息用英文」还在那儿,只是这次不生效。 这带来一个后果:白写的规矩会一直白写。你换一个项目,它可能又生效了——因为那个项目没说反话。

三、企业层为什么是例外

上一段说「越具体越赢」,但企业层在最上面、最不具体,它凭什么压倒下面几层? 因为企业层的规矩不是「建议」而是「不许」。 它的措辞通常是禁止式的:不能访问源码目录以外的文件、不能连外部 API、依赖必须走公司镜像源。 举个真实场景:给金融客户做的系统,合同里写明代码与数据不出内网。 这时候「能不能连外网」这件事,不能由项目自己决定。

所以在冲突处理上,企业层是硬约束:下层可以补充它,不能推翻它。 这一点跟「就近原则」不矛盾。 就近原则管的是同类的偏好冲突(英文还是中文、四格还是两格),企业层管的是红线。 偏好可以就近,红线不能。 一个副产物是:企业层的条数应该很少。它要是长到几十条,说明有人在拿它做项目管理,那层意思就变了。

四、叠加的代价:规则打架

演示里十三条规矩,两处冲突。 真实项目里这个比例会更高,因为 CLAUDE.md 常常是几个人陆续加的,没人统稿。 规则一多,模型要在一堆互相矛盾的句子里挑一个执行。它挑得对不对取决于哪条写得更响,而不是哪条更对。 所以抄别人的 CLAUDE.md 是件危险事:那份文件里可能有一半规矩在跟你的环境打架, 而你不知道哪一半赢了。

一个实用的自查办法:把五层里所有规矩按主题排一遍,同一个主题出现两次以上的,就是一处隐患。 要么删一条,要么把它们合并成一条。

五、目录层的特殊之处:按位置加载

前四层里,企业、用户、项目三层都是每次会话必读,目录层不是。 目录层是按位置加载的:你不动那个目录,它就不读。这一条差别带来了一个很实用的做法。 把「只跟某个子目录有关」的规矩写进那个目录,而不是全塞根目录。 演示里算过:两条目录规矩放根目录是 167 token,拆出去之后根目录剩 128。

少了 39,看着不多。但这个数要乘每一轮:一次会话三十轮就是 1170。 更重要的是它不占「注意力」。根目录那份文件越短,里面的每一条就越显眼。 这个思路可以一直往下推:能放目录的不放根目录,能放技能的不放记忆, 能派子智能体去做的不要在主窗口做。同一条原则,四种粒度。

六、动手 Demo:五层记忆叠起来,同一条规矩谁说了算

造五层:企业策略 2 条、用户记忆 3 条、项目记忆 5 条、目录记忆 2 条、会话临时 1 条,一共 13 条。 先看每层什么时候进上下文,再找冲突的两处看谁赢,最后算一算把目录规矩拆出去能省多少。

LAYERS = [
    (u"企业策略", u"每次会话,最先加载", 1, [...]),
    (u"用户记忆", u"每次会话,跨项目", 2, [...]),
    (u"项目记忆", u"每次会话,进这个仓库就加载", 3, [...]),
    (u"目录记忆", u"进到 src/api 才加载", 4, [...]),
    (u"会话临时", u"只在本次对话有效", 5, [...]),
]
# 叠加:全部读进来,冲突时越具体的层赢
# 不是覆盖——上面那层那句还在,只是这次不生效

实跑输出:

--------------------------------------------------------------------
五层记忆叠起来,同一条规矩谁说了算
--------------------------------------------------------------------


  ① 五层各管什么
  --------------------------------------------------------------------
| 层 | 条数 | 什么时候进上下文 |
|---|---:|---|
| 企业策略 | 2 | 每次会话,最先加载 |
| 用户记忆 | 3 | 每次会话,跨项目 |
| 项目记忆 | 5 | 每次会话,进这个仓库就加载 |
| 目录记忆 | 2 | 进到 src/api 才加载 |
| 会话临时 | 1 | 只在本次对话有效 |

  五层加起来 13 条。前四层是文件,最后一层是对话里临时说的。

  ② 叠起来之后,冲突的有几处
  --------------------------------------------------------------------
| 主题 | 用户层 | 项目层 | 生效 |
|---|---|---|---|
| 提交信息语言 | 用英文 | 用中文 | 用中文 |
| 缩进宽度 | 用四个空格 | 用两个空格 | 用两个空格 |

  ! 越具体的层赢。
  用户层说英文、项目层说中文,最后是中文生效——因为项目层的规则是【这个仓库】的规则,
  它比「我这个人一般怎么写」更贴当下。

  ③ 但叠加有个代价:条数会打架
  --------------------------------------------------------------------
  五层里有两层都提到缩进,实际生效的只有一条,另一条白写。
  规则越多,这种白写越多,而且模型要在一堆互相矛盾的句子之间挑。
  所以抄别人的 CLAUDE.md 是件危险事:那份文件里可能有一半规矩在跟你的环境打架,
  而你不知道哪一半赢了。

  ④ 目录那一层的特殊之处
  --------------------------------------------------------------------
  前四层每次会话都进上下文,只有目录记忆是按位置加载的:不进 src/api 就不读它。
  这带来一个很实用的做法:把「只跟某个子目录有关」的规矩写进那个目录的 CLAUDE.md,
  而不是全塞根目录。根目录的常驻账单能省一截。
| 做法 | 根目录条数 | 根目录 token |
|---|---:|---:|
| 目录规矩也放根目录 | 8 | 167 |
| 目录规矩放子目录 | 6 | 128 |

  少了 39 token。这个数不大,但它乘的是每一轮——一次会话三十轮,就是 1170。

①五层十三条。看第三列:前四层是文件,最后一层是对话里临时说的,它连文件都没有。 这一列还藏着一条规律:越靠上的层越稳定(几乎不改),越靠下的层越临时。 所以写规矩的时候,稳定的放上面,一次性的放下面。 ②两处冲突,赢的都是项目层。这就是就近原则。 注意用户层那句「提交信息用英文」并没有被删掉,它仍在文件里,只是这次不生效。 换个项目(那个项目没说反话),它又会生效。 ③叠加的代价在这一段:十三条里两条白写,比例不算高。 真实项目里这个比例常常到三成以上,因为文件是几个人陆续加的。 ④最后这张表是目录层的价值。两条规矩从根目录搬到 src/api 下,根目录从 167 降到 128。 39 token 听着不多,乘三十轮是 1170。而且它让根目录那份文件短了一截,剩下的每一条都更显眼。

30 秒实验

  1. 在会话临时那一层加一条「这次回答用英文」,重跑第 ② 步。它跟用户层的「回答用中文」谁赢?为什么?
  2. 把项目记忆拆成两个文件(一个管命令、一个管风格),重跑第 ③ 步。白写的条数会变吗?
  3. 把目录层那两条改成「按路径条件加载的规则文件」(也就是下一节的做法),根目录还能再省多少?

常见误区

  1. 以为下层会覆盖上层。实际是叠加,上层那句还在,只是不生效——换个项目它可能又生效。
  2. 把所有规矩都塞进项目记忆。企业级的要求放项目里,换个项目就丢了; 只跟某目录有关的放根目录,每轮都要白带。
  3. 不查冲突。同一个主题在两层写了两个相反的值,模型挑哪个取决于哪句写得更响,不取决于哪句更对。
  4. 把一次性交代写进文件。会话临时那一层就是为这个准备的,写进文件会污染下次会话。
  5. 以为层数越多越好。每加一层都多一处冲突可能,先问「这条真的需要在更大范围生效吗」。
自测三问(点开对答案) 1. 五层记忆之间是覆盖关系还是叠加关系? —— 叠加。五层全部读进上下文一起生效,冲突时越具体的层赢。上层那条并没有被删除,只是这次不生效。 2. 一条新规矩该放哪一层,判断依据是什么? —— 看它该在多大范围里生效。 跨组织放企业层,跨项目放用户层,这个仓库放项目层,只跟某个子目录有关放目录层, 一次性放会话临时。 3. 为什么目录层值得单独拿出来? —— 因为它是按位置加载的:不进那个目录就不读。 所以能把只跟子目录有关的规矩搬出去,让根目录的常驻内容短一截,每轮都省。

层级清楚了,下一节讲最常写坏的那一层:项目记忆里的 CLAUDE.md 到底该怎么写。

相关笔记