【Claude Code 系列课程】02 | Claude Code 记忆系统与 CLAUDE.md

0 阅读15分钟

第一讲里,我们从高处看了一遍 Claude Code 的整体技术栈。现在开始进入第一块真正会影响日常协作质量的基础能力,也就是记忆系统。

这一讲我想先请你思考一个很具体的问题。

为什么很多人刚开始用 Claude Code 时,会觉得它很聪明,可一旦项目做了几天,就开始觉得它有点不稳定?

表面上看,问题像是出在模型身上。这个会话里答得很好,下个会话里又像换了个人。刚刚才说过项目用 TypeScript strict,转头又给你写出一段松散的 JavaScript。上午刚强调过接口统一走 schema 校验,下午新开一个会话,它又把这一套忘得差不多了。

其实,很多时候问题不在模型突然变笨,而在于你们之间缺了一层长期协作所必需的共同背景。

人和人一起工作,时间久了,会慢慢形成默契。你知道这个团队不喜欢什么写法,知道某些目录为什么不能乱动,知道这类需求大概率该先补测试还是先改 schema。可 Claude Code 默认并不天然拥有这些背景。你不显式提供,它就只能每次从当前对话里临时猜。

这就是为什么记忆系统会成为 Claude Code 工程化使用的起点。

记忆系统解决的,到底是什么问题

很多人第一次接触 CLAUDE.md,会把它理解成一个提示词文件。这个理解不能算错,但明显不够。

更准确一点说,记忆系统解决的是协作中的重复校准成本。

什么意思呢?

当你和 Claude Code 一起做一个真实项目时,有一批信息会被反复用到。比如项目的技术栈,目录分层,错误处理方式,测试命令,代码风格,甚至你个人的一些固定偏好。它们本身不复杂,但如果每次开新会话都要重新说一遍,协作成本就会越来越高。

所以,记忆系统本质上是在做一件事:把那些反复出现、相对稳定、会影响决策的背景信息,从即时对话里抽离出来,变成长期生效的上下文。

有了这层长期上下文,Claude Code 才更像一个进入过项目、知道基本规则的同事。没有这层东西,它就更像一个每次都临时加入、每次都要重新 brief 的外部顾问。

这两种协作方式,体验差别会非常大。

记忆从来都不只是一个 CLAUDE.md

如果你把 Claude Code 的记忆理解成项目根目录里那一个 CLAUDE.md,其实只看到了其中一部分。

按照当前官方文档,Claude Code 的长期记忆更像一套分层结构。它既包括你手工写下来的规则文件,也包括系统自己在项目里沉淀出来的 Auto Memory。前者负责定义,后者负责积累。前者告诉 Claude 应该遵守什么,后者让 Claude 在持续协作中慢慢记住一些模式和经验。

所以这一讲,我们不要把视角只盯在一个 Markdown 文件上,而是要把它看成一整套上下文注入机制。

先把这套结构想清楚,后面你才知道什么内容该放哪里,什么内容该长期保留,什么内容根本不值得写。

Claude Code 的几层记忆,分别该放什么

我们先从最常见的几层作用域开始。

第一层:企业级记忆

这一层针对的是组织范围内的统一规则。比如安全要求、合规限制、敏感数据处理方式、禁止使用的依赖,或者某些必须执行的审计规范。

官方文档里当前给出的默认位置是:

  • macOS:/Library/Application Support/ClaudeCode/CLAUDE.md
  • Linux:/etc/claude-code/CLAUDE.md
  • Windows:C:\Program Files\ClaudeCode\CLAUDE.md

大多数个人开发者碰不到这一层,但在企业环境里,它的意义很大。因为它定义的是组织底线,不是普通项目偏好。项目可以不同,技术栈可以不同,企业级约束通常不能变。

如果你在个人项目里使用 Claude Code,这层了解即可,不是重点。

第二层:用户级记忆

用户级记忆对应你的长期个人偏好,通常放在 ~/.claude/CLAUDE.md

它适合记录那些跨项目都大概率成立的习惯。比如你希望默认用中文沟通,注释尽量写英文,常用包管理器是 pnpm,做文档时偏好直接一点的表达,不喜欢太长的铺垫,等等。

它的价值在于让 Claude Code 在不同项目之间,也能保留你的基本工作方式。你不用每开一个新仓库都重新交代一遍。

一个很简单的例子:

# 我的长期偏好

## 沟通
- 默认使用中文
- 注释尽量写英文
- 回答先给结论,再展开

## 编码习惯
- 优先使用 TypeScript
- 优先 async/await
- 默认使用 camelCase

## 常用工具
- 包管理器优先 pnpm
- 编辑器是 VS Code
- 终端使用 zsh

当然,用户级规则并不意味着永远优先。项目规则比它更近,也更具体。你个人偏好 2 空格缩进,但当前仓库明确要求 4 空格,那就应该以项目约定为准。

第三层:项目级记忆

项目级记忆,才是大多数人最需要认真经营的一层。

官方现在支持把项目记忆放在项目根目录的 CLAUDE.md,也支持放在 .claude/CLAUDE.md。无论选哪种形式,核心都一样,它承载的是团队共享、值得跟着仓库一起演进的项目知识。

这类内容通常包括:

  • 技术栈和关键依赖
  • 目录结构和模块职责
  • 团队编码规范
  • 常用命令
  • 重要设计决策
  • 新增功能时默认遵循的工作流

判断标准其实不复杂。只要某件事在这个项目里会被反复提起,而且它会实打实影响 Claude 的行为,就值得写进项目级记忆。

例如一个后端服务项目,可以这样写:

# 项目:订单服务

## 技术栈
- Node.js 20
- TypeScript
- Fastify
- Prisma
- PostgreSQL
- Redis
- Zod

## 目录结构
src/
├── routes/
├── controllers/
├── services/
├── repositories/
├── schemas/
└── types/

## 项目约定
- API 输入统一经过 schema 校验
- 禁止使用 any
- 数据访问集中在 repositories
- 业务错误统一使用自定义错误类型

## 常用命令
- pnpm dev
- pnpm test
- pnpm prisma migrate dev

这里最重要的,是少写正确但空泛的话,多写真正能改变输出结果的项目事实。

比如 写高质量代码、遵循最佳实践、提高可维护性,这类表述人看着都知道没错,但对模型来说过于抽象,很难变成稳定行为。相反,像 所有数据库访问都走 Prisma、controller 不直接写 SQL、API 输入必须先过 schema 这类约束,才是真正有决策意义的内容。

第四层:本地级记忆

项目越来越大以后,你会发现还有一类信息,其实很重要,但又不适合提交到 Git。这些内容更像是你自己的工作底稿,比如本地环境信息、临时调试技巧、当前正在推进的事项,甚至某些只适合自己保留的测试备注。

这就是 CLAUDE.local.md 的位置。

它通常放在项目根目录,只对当前本地工作空间生效,而且一般应该加入 .gitignore

例如:

# 本地工作笔记

## 我的环境
- API: http://localhost:3000
- Redis: localhost:6379
- 调试日志:LOG_LEVEL=debug

## 当前任务
- 正在重构支付模块
- 本周内要补齐集成测试

## 额外备注
- 参考 PR #234 的讨论

这一层很像你写在工位旁边的便利贴。它未必是团队共识,但对你当前这段时间的工作非常有帮助。尤其当你和 Claude Code 长时间协作、经历过多轮压缩之后,这些本地记忆会明显提高接续效率。

第五层:Rules,按条件加载的规则

前面几层更像固定作用域。rules 则是另一种思路,它解决的是规则不该全量常驻的问题。

当项目开始变复杂时,你很快会遇到一个现实问题。所有规范都塞进主 CLAUDE.md,文件会越来越胖,启动时每次都带着一整坨上下文,真正关键的信息反而被淹没了。

这个时候,正确做法通常是先拆分,再分层管理。

把只在特定场景才需要的规则放进 .claude/rules/。官方文档也支持用户目录 ~/.claude/rules/ 下的规则文件。然后用 paths 把作用范围限定住,让它只在相关文件生效。

比如测试规范:

---
paths:
  - "src/**/*.test.ts"
  - "tests/**/*.ts"
---

# 测试约定

- 单元测试命名为 `*.test.ts`
- 集成测试命名为 `*.integration.test.ts`
- 优先使用 Arrange-Act-Assert 结构

这样做的好处非常直接。测试规则只会在你处理测试文件时进入上下文,平时写业务代码不会被无关细节占满。

对大型项目来说,rules 很像把团队规范做成按需加载的模块。前端一套,测试一套,接口设计一套,安全一套。主记忆保持稳定和精简,细则按场景触发。

null (1).png

还有一层很多人容易忽略:Auto Memory

如果前面的内容都属于你主动配置的长期记忆,那么 Auto Memory 则是 Claude Code 自动沉淀出来的项目记忆。

根据当前官方文档,Auto Memory 默认开启。它会在 ~/.claude/projects/<project>/memory/ 目录下为项目维护记忆文件,入口通常是 MEMORY.md,旁边还可以继续拆分出其他主题文件。

这一层的意义是补充 CLAUDE.md,不是替代它。

你可以把它理解成项目合作过程中形成的经验缓存。某个目录历史负担很重,某类错误通常要先查哪个中间件,或者这个仓库里新增接口时习惯先补 schema 再写 route。这些内容未必一开始就会被你写进项目级记忆,但随着协作推进,Claude Code 可以把它们沉淀下来。

不过这里有个判断一定要有。真正稳定、重要、团队需要长期共享的规则,最好还是回写到 CLAUDE.md 或 rules 里。Auto Memory 更适合承接经验,不适合作为团队治理的主入口。

写好记忆文件,关键不在于写得多

讲到这里,问题就变成了另一个更实际的话题。

到底什么样的内容,才配写进 CLAUDE.md

我给你一个特别简单的标准。

如果这条信息写进去之后,能够稳定改变 Claude Code 在项目中的行为,那它大概率值得保留。反过来,如果你不写,它多半也会做得八九不离十,那这条内容的价值通常不高。

所以,一份真正有用的记忆文件,最应该回答的其实是三个问题。

第一,为什么这样做。

如果只是告诉它结论,很多时候只能形成机械遵守。把决策背后的背景补上,它在遇到相似问题时才更容易做出一致判断。

例如:

## 为什么使用 Zod
- TypeScript 只覆盖编译期类型检查
- API 输入还需要运行时验证
- Zod 可以统一类型和校验逻辑

第二,边界在哪里。

也就是哪些事情能做,哪些事情不要做,应该在哪一层做。

例如:

## 数据库访问规则
- 所有查询统一走 Prisma
- 复杂查询封装在 repositories
- controller 和 service 中不要直接写 SQL

第三,默认工作流是什么。

当团队内部已经形成稳定步骤时,直接写出来最省事。

## 新增 API 的默认流程
1. 在 schemas 中定义输入输出
2. 在 routes 中注册路由
3. 在 controllers 中处理请求
4. 在 services 中补业务逻辑
5. 在 tests 中补齐测试

这三件事加起来,Claude 的行为会稳定很多。因为它接收到的已经从散乱偏好,变成了一套能指导判断的默认决策模型。

Less is More,在这里是工程原则

很多人看到这里会有一个冲动,既然记忆这么重要,那我是不是应该把所有东西都写进去?

答案通常是否定的。

原因也很现实。CLAUDE.md 会参与长期上下文构建,体积越大,成本越高。真正有价值的做法,是把它写成默认决策入口,而不是项目百科全书。

更细的内容,应该拆出去。

当前官方文档支持在 CLAUDE.md 里通过 @路径 引入其他 Markdown 文件,甚至可以引入 AGENTS.md。这意味着你完全可以把数据库设计、接口规范、部署文档、测试清单拆到独立文档里,让主记忆保持轻量。

比如:

# 项目核心规范

## 默认规则
- 使用 TypeScript strict
- 输入统一做 schema 校验
- 组件按功能目录组织

## 补充资料
- API 规范:@docs/api.md
- 数据库设计:@docs/database.md
- 发布流程:@docs/deployment.md

这个思路很重要。主文件负责给方向,外部文档负责给细节。真正需要的时候再展开,而不是一上来把所有资料都塞进启动上下文。

一个更稳的起步方式

如果你现在正准备给一个新项目建立记忆,我建议你按下面的顺序来。

先用 /init 生成初始项目记忆。按照当前官方文档,它会基于代码库帮你起草 CLAUDE.md,如果你启用了更新的交互式初始化体验,它还会顺带协助配置 commands、hooks 或 agents 的初始结构。

然后你做的第一件事,最好不是继续往里加内容,先删掉低价值信息更重要。

把项目里最稳定、最常用、最能改变行为的部分留下。技术栈、目录结构、固定命令、默认工作流、重要约束。先把骨架搭起来。

接着,如果你有个人环境或阶段性任务,再补 CLAUDE.local.md。如果项目规范很复杂,再把测试、前端、接口、安全等规则拆到 rules 里。

这么做的核心好处是,记忆系统从第一天开始就是分层的,而不是等文件膨胀之后再回头返工。

当记忆越写越多,怎么判断该瘦身了

一个常见的危险信号是,CLAUDE.md 明显越来越长,但 Claude Code 的表现并没有更稳定。

你以为自己是在补充知识,实际可能是在稀释重点。

这时候可以用一个很简单的三步法来处理。

先问自己,哪些内容是每次协作都必须知道的。如果不是每次都需要,那它就不一定应该留在主记忆里。

再看这些内容能不能拆分。详细 API 文档、数据库字段说明、部署流程、排障笔记,大多数都更适合独立文档,或者改成规则文件按条件触发。

最后,检查有没有空话。很多写给人的口号,写给模型时几乎没有约束力。与其写 要注意代码质量,不如直接写 新增接口必须补测试。后者虽然更窄,但执行起来更稳定。

记忆文件真正要追求的,是更高的命中率,不是体量感。

/memory 的价值,在于帮你检查现实而不是想象

很多时候,我们以为某条规则已经在发挥作用,其实只是自己以为它会生效。

所以我很建议你养成一个习惯。写完记忆之后,直接用 /memory 看看当前会话到底加载了什么。

按官方文档,/memory 会展示当前加载到的项目记忆、用户记忆、本地记忆、rules,以及 Auto Memory 的状态。这个命令最有用的地方,就是帮你把 觉得已经生效 变成 真正确认已生效。

比如你可以拿它排查这些问题:

  • 为什么我刚写的规则这次没有体现
  • 某个子目录里的局部记忆有没有被加载
  • 当前项目的 Auto Memory 现在是什么状态

很多协作问题,最后查下来往往不是模型理解错了,问题出在记忆根本没有按你以为的方式组织进来。

还有一个边界要分清楚:记忆不是权限控制

这一点在实际使用里非常重要。

CLAUDE.md 会强烈影响 Claude 的行为,但它本质上仍然属于协作层和提示层。它很适合表达长期规则、偏好和工作约定,却不等于底层权限系统。

真正涉及执行边界、审批要求、工具访问控制时,应该放在对应的设置体系里。当前官方文档也明确把 settings.json、managed settings 和 CLAUDE.md 分开处理。

你可以把这件事理解成两类约束。

一类是 你应该怎么做,这适合写进记忆。

另一类是 你最多能做到哪一步,这属于系统配置和权限控制。

把这两类东西分开,你的协作策略才会更稳。