CLAUDE.md 上下文工程:加载顺序、200 行上限、以及为什么 @import 不省上下文
三句话决定了 CLAUDE.md 该怎么写:
- 它是上下文,不是配置。内容作为系统提示之后的一条用户消息送达,模型会读、通常会遵守,但没有强制保证。要「一定发生」的事情写 hook。
- 每个会话都全量加载,不管多长。所以每一行都是持续的 token 成本,目标是每个文件 200 行以内。
@import不省上下文。导入的文件在启动时一样被展开加载。它帮的是组织,不是预算。
四层加载位置
Section titled “四层加载位置”按加载顺序,从范围最广到最具体:
| 作用域 | 位置 | 共享给谁 |
|---|---|---|
| managed 策略 | 系统目录下的 CLAUDE.md(各平台路径不同) | 组织全体 |
| 用户 | ~/.claude/CLAUDE.md | 你自己,所有项目 |
| 项目 | ./CLAUDE.md 或 ./.claude/CLAUDE.md | 团队,通过版本控制 |
| 本地 | ./CLAUDE.local.md | 你自己,当前项目(记得加 gitignore) |
managed 策略位置:
- macOS:
/Library/Application Support/ClaudeCode/CLAUDE.md - Linux 和 WSL:
/etc/claude-code/CLAUDE.md - Windows:
C:\Program Files\ClaudeCode\CLAUDE.md
这一层无法被个人设置排除,claudeMdExcludes 也排不掉它。
拼接顺序:为什么「越近的读得越晚」重要
Section titled “拼接顺序:为什么「越近的读得越晚」重要”Claude Code 从当前工作目录往上走目录树,逐级检查 CLAUDE.md 和 CLAUDE.local.md。
在 foo/bar/ 里启动会加载 foo/bar/CLAUDE.md、foo/CLAUDE.md 以及它们旁边的 CLAUDE.local.md。所有找到的文件是拼接进上下文,不是互相覆盖。
顺序是从文件系统根往工作目录方向。所以 foo/CLAUDE.md 出现在 foo/bar/CLAUDE.md 之前——离你启动位置越近的指令,被读得越晚。
每个目录内部,CLAUDE.local.md 追加在 CLAUDE.md 之后。所以你的个人笔记是该层级最后被读到的东西。
工作目录下面的子目录里的 CLAUDE.md 不在启动时加载。它们在 Claude 读取那些子目录里的文件时才被带进来。
monorepo 里被其他团队的 CLAUDE.md 干扰时,用 claudeMdExcludes:
{ "claudeMdExcludes": [ "**/monorepo/CLAUDE.md", "/home/user/monorepo/other-team/.claude/rules/**" ]}模式按绝对路径用 glob 语法匹配,各层配置的数组会合并。
一个省 token 的小技巧
Section titled “一个省 token 的小技巧”块级 HTML 注释在内容注入模型上下文之前会被剥掉:
<!-- 维护者注意:这一节是 2026 Q1 重构后加的,下次架构调整时复查 -->
## 构建命令...给人类维护者的备注写在 HTML 注释里,不消耗上下文 token。代码块内部的注释会被保留。
用 Read 工具直接打开 CLAUDE.md 文件时注释仍然可见——剥离只发生在自动注入的路径上。
@import 的真实作用
Section titled “@import 的真实作用”See @README for project overview and @package.json for available npm commands.
# Additional Instructions- git workflow @docs/git-instructions.md关键事实:导入的文件在启动时和引用它的 CLAUDE.md 一起被展开加载进上下文。
所以拆成 @import 对上下文预算毫无帮助。它的价值只在文件组织:让一个大文件变成几个小文件,方便维护。
规则细节:
- 相对路径和绝对路径都可以。相对路径相对包含 import 的那个文件解析,不是相对工作目录。
- 导入的文件可以递归导入,最大深度 4 跳。
- 解析会跳过 Markdown 代码跨度和围栏代码块。想在 CLAUDE.md 里提到一个路径而不导入它,用反引号包起来:
`@README`是字面文本,反引号外的@README会导入文件。
外部导入的批准对话框
Section titled “外部导入的批准对话框”项目层内存文件里的 import,如果路径解析到你工作目录之外,算作外部导入。
Claude Code 第一次遇到项目里的外部导入时会弹一个对话框列出这些文件。拒绝之后导入保持禁用,对话框不再出现。
这个对话框防的是别人提交到共享项目里的文件。用户作用域内存文件里的 import(~/.claude/CLAUDE.md、~/.claude/rules/)是你自己写的,不弹对话框。
跨 worktree 共享个人指令
Section titled “跨 worktree 共享个人指令”CLAUDE.local.md 被 gitignore 之后只存在于你创建它的那个 worktree 里。要在多个 worktree 之间共享个人指令,从家目录导入:
# Individual Preferences- @~/.claude/my-project-instructions.mdAGENTS.md 怎么办
Section titled “AGENTS.md 怎么办”Claude Code 读 CLAUDE.md,不读 AGENTS.md。
仓库已经在用 AGENTS.md 给别的编码 agent 用的话,建一个 CLAUDE.md 导入它,两边读同一份内容不重复:
@AGENTS.md
## Claude Code
Use plan mode for changes under `src/billing/`.不需要加 Claude 专属内容的话,符号链接也行:
ln -s AGENTS.md CLAUDE.mdWindows 上创建符号链接需要管理员权限或开发者模式,所以用 @AGENTS.md 导入更省事。
/init 会读 Cursor 规则(.cursor/rules/ 或 .cursorrules)和 Copilot 规则(.github/copilot-instructions.md),把相关部分并进生成的 CLAUDE.md。设了 CLAUDE_CODE_NEW_INIT=1 之后它还会读 AGENTS.md、.devin/rules/、.windsurf/rules/、.clinerules。
真正能省上下文的:.claude/rules/ 的条件加载
Section titled “真正能省上下文的:.claude/rules/ 的条件加载”.claude/rules/ 目录下的 markdown 文件,每个文件一个主题:
your-project/├── .claude/│ ├── CLAUDE.md│ └── rules/│ ├── code-style.md│ ├── testing.md│ └── security.md没有 paths frontmatter 的规则在启动时加载,优先级与 .claude/CLAUDE.md 相同。
有 paths 的规则只在 Claude 处理匹配文件时才加载——这是它和 @import 的本质区别:
---paths: - "src/api/**/*.ts"---
# API Development Rules
- All API endpoints must include input validation- Use the standard error response format触发时机是 Claude 读取匹配的文件,不是每次工具调用。
支持花括号展开:
paths: - "src/**/*.{ts,tsx}" - "lib/**/*.ts"每个花括号组会成倍增加展开后的模式数量:src/*.{ts,tsx} 展开成两个,{a,b}/{c,d}/*.{ts,tsx} 展开成八个。一条规则的整个 paths 列表共享 1000 个展开模式、4 MiB 的预算。超预算的模式会被原样使用,字面花括号匹配不到任何文件。
.claude/rules/ 支持符号链接,可以维护一份共享规则链进多个项目:
ln -s ~/shared-claude-rules .claude/rules/sharedln -s ~/company-standards/security.md .claude/rules/security.md循环符号链接会被检测并妥善处理。
用户级规则放 ~/.claude/rules/,在这台机器的每个项目都生效。用户级规则先于项目规则加载,所以项目规则优先级更高。
怎么写才管用
Section titled “怎么写才管用”三个维度:
大小:目标每个 CLAUDE.md 200 行以内。更长的文件消耗更多上下文,并且降低遵守度。
结构:用 markdown 标题和列表分组。Claude 扫描结构的方式和人读一样,组织好的章节比密集段落更容易跟随。
具体性:写具体到可验证的指令。
- 「用 2 空格缩进」而不是「正确格式化代码」
- 「提交前跑
npm test」而不是「测试你的改动」 - 「API handler 放
src/api/handlers/」而不是「保持文件组织」
什么时候该往里加内容——这个判断标准比写法更重要:
- Claude 第二次犯同一个错
- code review 抓到一个 Claude 本该知道的项目约定
- 你把上个会话打过的同一句纠正又打了一遍
- 一个新同事需要同样的上下文才能开工
什么时候不该加:如果一条内容是多步流程,或者只对代码库的某一部分有意义,它该去 skill 或路径限定的 rule,不该占用每个会话的上下文。
| 内容类型 | 放哪 |
|---|---|
| 每个会话都需要的事实(构建命令、约定、项目布局) | CLAUDE.md |
| 只对某些文件有意义的规则 | .claude/rules/ 配 paths |
| 多步流程(PR review、数据库迁移) | skill |
| 必须在特定时机发生的动作 | hook |
| 需要在系统提示层面的指令 | --append-system-prompt |
最后一项要每次调用都传,所以更适合脚本和自动化,不适合交互式使用。
压缩之后什么还在
Section titled “压缩之后什么还在”这是一个高频困惑:/compact 之后某条指令好像失效了。
规则是:
- 项目根的 CLAUDE.md 活过压缩。压缩后 Claude 从磁盘重读并重新注入。
- 子目录里的嵌套 CLAUDE.md 不会自动重新注入。它们在 Claude 下一次读取那个子目录里的文件时重新加载。
所以压缩后消失的指令,要么只在对话里给过(没写进文件),要么在一个还没重新加载的嵌套 CLAUDE.md 里。
对话里给的临时指令想让它持久,写进 CLAUDE.md。
排查「Claude 没听 CLAUDE.md」
Section titled “排查「Claude 没听 CLAUDE.md」”按顺序:
- 跑
/context,看Memory files下的列表确认文件真的加载了。不在列表里,Claude 就看不到。用/memory打开编辑。 - 确认这个 CLAUDE.md 在会话会加载的位置上。
- 让指令更具体。
- 找跨文件的冲突指令。两个文件对同一行为给了不同指导,Claude 可能任意选一个。
调试 path-scoped rules 和子目录里延迟加载的文件,用 InstructionsLoaded hook 记录到底加载了哪些指令文件、什么时候、为什么。这比反复猜快得多。
/doctor 会对已提交的 CLAUDE.md 提出精简建议:它删掉 Claude 能从代码库自行推断的内容(目录布局、依赖列表、架构概览),保留陷阱、理由、以及与工具默认行为不同的约定。这个精简检查需要 v2.1.206 或更高版本。
这个取舍逻辑值得记住:能推断的不写,不能推断的才写。目录结构 Claude 一个 ls 就知道了,写进 CLAUDE.md 是纯浪费;「这个模块看起来该用 A 方案但因为历史原因必须用 B」是它推不出来的,必须写。
自动记忆:Claude 自己写的那部分
Section titled “自动记忆:Claude 自己写的那部分”除了你写的 CLAUDE.md,还有一套 Claude 自己维护的记忆。
| CLAUDE.md | 自动记忆 | |
|---|---|---|
| 谁写 | 你 | Claude |
| 内容 | 指令和规则 | 学到的东西和模式 |
| 作用域 | 项目 / 用户 / 组织 | 每仓库,worktree 之间共享 |
| 加载 | 每个会话全量 | 每个会话前 200 行或 25KB |
存储位置是 ~/.claude/projects/<project>/memory/,<project> 从 git 仓库派生,所以同一个仓库的所有 worktree 和子目录共享一个目录。
目录结构:
~/.claude/projects/<project>/memory/├── MEMORY.md # 简明索引,每个会话加载├── debugging.md # 详细笔记└── api-conventions.mdMEMORY.md 只有前 200 行或前 25KB(先到者为准)在每个会话开始时加载,超出部分不加载。所以它被设计成一个索引,详细笔记放在单独的主题文件里,Claude 需要时用文件工具按需读。
Claude 写完 MEMORY.md 后 Claude Code 会量一次:接近上限时提醒 Claude 精简,超过上限时写入仍然成功但返回一个错误告诉 Claude 重写索引——因为超出部分在下次加载时会被丢掉。
量的是会加载的内容:YAML frontmatter 和块级 HTML 注释在加载前被剥掉,不计入上限。
这个上限只对 MEMORY.md 生效。CLAUDE.md 无论多长都全量加载,只是越短遵守度越好。
自动记忆是机器本地的,不跨机器同步。主对话的自动记忆不会加载进 subagent(fork 是例外,它继承父对话)。
关掉:
{ "autoMemoryEnabled": false }或环境变量 CLAUDE_CODE_DISABLE_AUTO_MEMORY=1。用 /memory 可以浏览 Claude 存了什么——都是纯 markdown,随时可以编辑或删除。
- 跑
/context看 CLAUDE.md 在上下文里占多少。如果占比让你意外,用/doctor的精简建议砍一轮。 - 把 CLAUDE.md 里最长的那个「流程」章节搬成 skill,再跑
/context对比。这个对比能让「200 行」这个建议从数字变成体感。 - 给一个只对某个目录有意义的规则加
pathsfrontmatter,然后分别在匹配和不匹配的文件上工作,用InstructionsLoadedhook 确认它真的只在该加载时加载。
还没确认的点
Section titled “还没确认的点”claudeMdExcludes对嵌套 rules 目录的匹配细节。文档给了示例,但「排除一个目录」和「排除目录下的具体文件」在行为上是否有区别没有核实。- 自动记忆的写入判定。文档说 Claude 根据「这条信息在未来对话里是否有用」决定要不要存,具体的判定过程不可观测。
/doctor精简建议的具体判定规则。文档描述了它保留什么删什么,但没有给出可预测的规则。- 压缩后重新注入的具体时机。「项目根 CLAUDE.md 活过压缩」确认了,但重新注入是紧接压缩之后还是下一轮请求时,未见说明。