人类程序员在工作中依赖两种知识:一种是长期积累的经验和规范,比如"团队代码风格要求用 4 空格缩进""提交信息要用英文""测试覆盖率不能低于 80%";另一种是当前任务的工作记录,比如"刚才修改了哪个文件""上次调试发现的 bug 根因是什么""接下来要完成的步骤"。
AI Agent 同样需要这两种知识。但它面临一个人类不会遇到的问题:每次对话开始时,它的记忆是空白的。大语言模型没有跨 session 的持久记忆,上一次对话中积累的所有信息在 session 结束后就消失了。如果没有外部记忆系统的支持,Agent 每次都像一个刚入职的新员工,需要从零开始了解项目规范和背景。
业界的不同 Agent 产品对记忆问题的处理差异很大。GitHub Copilot 长期以来依赖当前文件和最近打开的 tab 作为上下文,没有持久记忆机制,直到最近才引入了类似 instructions 文件的能力。Cursor 提供了 .cursorrules 文件来承载项目规范,但只有一层,没有层级覆盖机制。Aider 通过 .aider.conf.yml 文件做配置,但这更像是工具配置而非知识记忆。Devin 有内置的 knowledge base 功能,但其结构相对不透明。
Claude Code 的记忆系统在层次性、可控性和自动化程度上都做到了业界最细致的设计。
Claude Code 的记忆分为两大类,各自承担不同的职责:
指令记忆,CLAUDE.md 体系
告诉 Agent "你应该怎么做"。这类记忆跨 session 持久化,由用户手动编写和维护。它的内容包括代码风格规范、项目架构说明、团队约定、个人偏好等。类似于给新员工的入职指南和规范手册。
会话记忆,MEMORY.md + Session Memory
记录 Agent "之前做了什么"。这类记忆在 session 内自动积累,部分可以跨 session 持久化。它的内容包括已完成的操作、发现的问题、做出的决策、待办事项等。类似于工作日志和会议纪要。
两类记忆的分离是一个重要的设计决策。指令记忆是 声明式的,描述"应该是什么样";会话记忆是 过程式的,记录"发生了什么"。混在一起会导致信息管理混乱:今天的调试记录不应该和永久的代码风格规范放在同一个地方。
CLAUDE.md 是 Claude Code 指令记忆的核心载体。当前 2.1.x 版本加载四种指令作用域,从范围最广到最具体依次是:
优先级从低到高:
1. 全局管理级 /etc/claude-code/CLAUDE.md
→ 由运维或管理员配置,所有用户共享
→ 典型内容:公司级安全策略、合规要求
2. 用户级 ~/.claude/CLAUDE.md
→ 个人全局偏好,跨项目生效
→ 典型内容:偏好的编程语言、通用的编码习惯
3. 项目级 CLAUDE.md / .claude/CLAUDE.md / .claude/rules/*.md
→ 项目约定,checkin 到 git,团队共享
→ 典型内容:项目架构说明、API 设计规范、测试策略
4. 本地级 CLAUDE.local.md
→ 个人项目配置,加入 gitignore,不共享
→ 典型内容:本地环境特殊配置、个人开发偏好
高优先级的内容会跟随低优先级的冲突配置,不冲突的部分全部保留。自动记忆是独立机制:当前默认开启,将每个仓库的笔记保存到 ~/.claude/projects/<project>/memory/,每次 session 加载 MEMORY.md 的前 200 行或 25 KB。可以通过 /memory、autoMemoryEnabled 或 CLAUDE_CODE_DISABLE_AUTO_MEMORY 管理。
这个作用域设计的灵感来源很明显:它和软件工程中的配置管理层级是同构的。想一下 CSS 的层叠规则、Kubernetes 的 ConfigMap 覆盖链、Git 的配置层级。它们都遵循同一个模式:更具体的配置跟随更通用的配置。Claude Code 把这个久经验证的模式应用到了 Agent 的指令记忆管理中。
这种层级设计解决了一个现实中很常见的需求:同一家公司的不同项目需要不同的 Agent 行为,但又有一些公司级的通用规范。全局管理级放通用规范,项目级放项目特定的规范,本地级放个人偏好,互不干扰。
当项目规范很复杂时,把所有内容塞进一个 CLAUDE.md 文件会导致文件臃肿难维护。@include 指令解决了这个问题,它允许 CLAUDE.md 引用外部文件:
# 项目规范
@./docs/coding-standards.md
@./docs/api-conventions.md
# 团队约定
@~/.claude/team-rules.md路径语法:
@path相对于当前文件的路径@./relative显式相对路径@~/home用户主目录@/absolute绝对路径
这种设计让团队可以将不同领域的规范拆分到独立的文件中,由各自领域的负责人维护。前端规范、后端规范、数据库规范、CI/CD 规范可以各自独立演进,最终通过 @include 汇聚到 CLAUDE.md 中。
引入文件包含机制就引入了安全风险。Claude Code 在这方面做了周密的防护:
- 代码块免疫:只在叶子文本节点中解析 @ 符号,markdown 代码块内的 @ 不会触发文件包含。这防止了代码示例中的 @ 被误解析
- 循环引用检测:通过 Set 数据结构追踪已处理的文件路径。如果 A 包含 B,B 又包含 A,检测机制会在第二次遇到 A 时停止,避免无限递归
- 嵌套引用深度:当前公开文档将嵌套
@引用限制为四层。检测引用时会跳过代码片段和 fenced code block。
MEMORY.md 和 CLAUDE.md 有一个根本区别:CLAUDE.md 是人写给 Agent 看的,MEMORY.md 是 Agent 自己写给自己看的。
自动记忆会保存工作中值得记住的信息,比如构建命令、调试经验、项目约定和用户偏好。它把索引和主题文件保存在仓库专属的 memory 目录,下次 session 开始时加载索引。
MEMORY.md 有严格的容量限制:
- 最大 200 行
- 最大 25,000 字节
- 超出时先按行截断,再按字节截断,在最后一个换行处断开
为什么要限制?因为 MEMORY.md 的内容会被注入到每次对话的 system prompt 中。如果不加限制,随着使用时间增长,MEMORY.md 会越来越大,吃掉越来越多的上下文窗口空间,最终影响 Agent 的正常工作。200 行和 25,000 字节是一个经过权衡的上限:足够存储几十条有价值的记忆,但不会过度侵占上下文空间。
系统记录一个 contentDiffersFromDisk 标志,用于 cache 去重。如果 MEMORY.md 在内存中被截断了,但磁盘上的原文没有变化,系统不会重复写入磁盘。这避免了截断操作产生不必要的磁盘 I/O,也防止了 file watcher 被误触发。
Claude Code 源码中还包含几个与记忆相关的功能模块,展示了这个系统的演进方向:
- memoryAge.ts:追踪每条记忆条目的年龄。旧的记忆条目可能已经过时,追踪年龄为未来实现"记忆淘汰"策略奠定基础
- memoryScan.ts:对记忆内容做扫描和模式匹配,用于识别重复、冲突或过时的记忆条目
- EXTRACT_MEMORIES 和 TEAMMEM feature flag:这些名称属于 3 月源码快照。自动记忆提取已经成为当前版本的正式用户功能,通过自动记忆设置控制,不再由这些快照 flag 控制。
- MEMORY_SHAPE_TELEMETRY feature flag:记忆形态遥测,收集用户记忆使用的统计数据来指导未来优化
Session Memory 是会话级别的短期记忆,和压缩系统深度集成。
在第 05 篇中我们介绍了三层压缩体系。Session Memory 正是第二层压缩的核心机制:当上下文即将溢出时,系统先尝试将对话中的关键信息沉淀到 session memory 文件,然后从消息历史中删除已被记录的旧消息。
Session Memory 和 MEMORY.md 的区别在于 生命周期:
- MEMORY.md 是跨 session 的长期记忆,内容需要在未来的 session 中仍然有价值
- Session Memory 是 session 内的短期记忆,只在当前会话中有效,session 结束后可以被清理
这种分离确保了长期记忆不会被短期的上下文压缩需求污染。一次调试中发现的临时变量值不应该和"项目使用 4 空格缩进"这样的长期规范放在一起。
所有类型的记忆最终都汇聚到 system prompt 中,构成 Agent 在每次对话中的"初始知识":
system prompt =
内置指令
+ 全局管理级 CLAUDE.md
+ 用户级 CLAUDE.md
+ 项目级 CLAUDE.md + rules/*.md
+ 本地级 CLAUDE.local.md
+ MEMORY.md 内容
+ 当日 session memory
+ Skill 列表
+ 权限规则
注入时附带一段关键的强调指令:这些 instructions OVERRIDE default behavior。
为什么需要这段强调?因为大语言模型有自己的默认行为模式,这些模式来源于预训练和 RLHF 阶段的大量数据。如果用户通过 CLAUDE.md 配置了与默认行为冲突的指令,模型可能会选择忽略用户指令而遵循自己的默认行为。显式的 OVERRIDE 声明是一种 prompt 层面的优先级标记,告诉模型"当指令冲突时,以这些配置为准"。
这和操作系统中的 优先级倒置 问题有相似之处。在实时操作系统中,低优先级任务持有的资源可能阻塞高优先级任务的执行。解决方案是通过优先级继承或优先级天花板来确保高优先级任务的指令得到执行。Claude Code 的 OVERRIDE 声明本质上是在做同样的事情:确保用户配置的优先级高于模型内建的默认行为。
| 项目 | 指令记忆 | 会话记忆 | 层级系统 | 自动化程度 |
|---|---|---|---|---|
| Claude Code | CLAUDE.md 四种作用域 + @include | 自动记忆 + Session Memory | 四层覆盖 | 中高,自动记忆持久化项目笔记 |
| Cursor | .cursorrules 单文件 | 无持久化 | 单层 | 低 |
| GitHub Copilot | .github/copilot-instructions.md | 无 | 单层 | 低 |
| Aider | .aider.conf.yml 配置 | 无 | 单层 | 低 |
| Devin | 内置 knowledge base | 有限的 session 记忆 | 不透明 | 中 |
Claude Code 在记忆系统的设计上明显领先于同类产品。四种指令作用域覆盖了从管理策略到本地项目偏好的范围,@include 支持模块化组织,自动记忆和 Session Memory 将持久化项目笔记与短期压缩上下文分开。这些设计决策背后是一个清晰的认识:Agent 的能力上限取决于它能获得多少正确的上下文信息。记忆系统是上下文信息的源头,它的质量直接决定了 Agent 的表现。
下一篇:08-工具与Skill系统


