深入浅出 Claude Code(一):从源码理解 CLAUDE.md,重写你的配置

Khalil Gao 2026-04-01 10:03 1

新一期来啦:


上一篇我们重写了 CLAUDE.md,但这只是表层。今天这篇,我们将顺着代码的调用栈,扒开 Claude Code 的记忆系统,看看你的指令在底层究竟被赋予了什么级别的权重。
你让 Claude Code "记住"的东西,下次对话它可能完全不记得。或者记得,但不照做。
其实不是功能坏了——是你不清楚这些 memory 在系统里是怎么流动的。存在哪、怎么加载、权重多高、什么格式才有效,搞清楚这条…


大多数人写 CLAUDE.md 的方式是:随便写几条规则,感觉效果不明显,然后放弃。


你不知道这些文字在 Claude 眼里长什么样、被放在哪里、和其他指令的优先级关系是什么。


今天我来带各位佬友们剖析一下 claudecode 的底层逻辑


全网统一小恐龙头像,在别的地方看到的家人们点点赞




1. CLAUDE.md 不在 System Prompt 里


这是最大的认知误区。


大多数人以为 CLAUDE.md 的内容被拼接到 system prompt 中。不是的


源码中,CLAUDE.md 通过 prependUserContext() 函数注入(utils/api.ts:449-474):


export function prependUserContext(messages, context): Message[] {
return [
createUserMessage({
content: `<system-reminder>
As you answer the user's questions, you can use the following context:
# claudeMd
${claudeMdContent}

IMPORTANT: this context may or may not be relevant to your tasks.
You should not respond to this context unless it is highly relevant to your task.
</system-reminder>`,
isMeta: true,
}),
...messages,
]
}

你的 CLAUDE.md 被包装在 <system-reminder> 标签里,作为第一条 user message 插入到对话开头。不是 system prompt,是 user message。


这样会导致几个直接后果:




  1. CLAUDE.md 的优先级低于 system prompt。System prompt 是模型的"宪法",user message 是"法律"。当两者冲突时,system prompt 通常胜出。




  2. 模型被告知这些内容"可能不相关"——注意那句 IMPORTANT: this context may or may not be relevant to your tasks。这是一个显式的降权信号。




  3. 但是——CLAUDE.md 的开头有一句强指令(utils/claudemd.ts:89):




Codebase and user instructions are shown below. Be sure to adhere to these
instructions. IMPORTANT: These instructions OVERRIDE any default behavior
and you MUST follow them exactly as written.

“OVERRIDE any default behavior” + “MUST follow them exactly” —— 这是在用强语气对抗降权信号。效果取决于指令的具体性:越具体的指令越容易被遵守,越模糊的越容易被忽略。


所以别在 CLAUDE.md 里写"尽量简洁"这种模糊指令。写"回答不超过 3 句话"或"不要添加注释到未修改的代码"这种可验证的具体指令。




2. 从 CLAUDE 向上遍历到根目录


CLAUDE.md 不只是项目根目录的一个文件。Claude Code 从当前目录开始,逐级向上遍历到文件系统根目录,在每一级检查以下文件(utils/claudemd.ts:790-1075):


每一级目录检查:
├── CLAUDE.md → Project 类型(可版本控制)
├── .claude/CLAUDE.md → Project 类型(可版本控制)
├── .claude/rules/*.md → Project 类型(可版本控制,递归扫描子目录)
└── CLAUDE.local.md → Local 类型(不应提交到版本控制)

加上两个全局位置:


~/.claude/CLAUDE.md        → User 类型(个人全局指令)
~/.claude/rules/*.md → User 类型(个人全局规则)
/etc/claude-code/CLAUDE.md → Managed 类型(企业管理员策略)
/etc/claude-code/.claude/rules/*.md → Managed 类型

加载顺序决定优先级。源码注释写得很清楚:


Files are loaded in reverse order of priority, i.e. the latest files
are highest priority with the model paying more attention to them.

“最后加载的优先级最高”——因为模型对靠近输入末尾的内容关注度更高(recency bias)。


完整的加载顺序(从先到后,优先级从低到高):


1. /etc/claude-code/CLAUDE.md             (Managed - 企业策略)
2. /etc/claude-code/.claude/rules/*.md (Managed)
3. ~/.claude/CLAUDE.md (User - 个人全局)
4. ~/.claude/rules/*.md (User)
5. /repo-root/CLAUDE.md (Project - 仓库根)
6. /repo-root/.claude/CLAUDE.md (Project)
7. /repo-root/.claude/rules/*.md (Project)
8. /repo-root/src/CLAUDE.md (Project - 更近的目录)
9. /repo-root/src/.claude/rules/*.md (Project)
10. /repo-root/src/feature/CLAUDE.md (Project - 当前目录)
11. /repo-root/src/feature/CLAUDE.local.md (Local - 最高优先级)

规则放在仓库根目录的 .claude/rules/ 下。

个人偏好(语言、风格)放在 ~/.claude/CLAUDE.md

子目录特定规则(比如 frontend/CLAUDE.md 说"用 React 不用 Vue")放在对应目录

离当前工作目录越近的文件,优先级越高




3. @include 指令引入外部文件


CLAUDE.md 支持 @ 语法引入其他文件(utils/claudemd.ts:451-535):


# 我的项目规则

@./coding-standards.md
@~/global-rules.md
@/etc/team-rules/backend.md

解析逻辑:


@./path@path — 相对于当前 CLAUDE.md 文件的路径

@~/path — 相对于 home 目录

@/path — 绝对路径

支持转义空格:@./my\ file.md

支持 #fragment 后缀(会被忽略):@./rules.md#section-1


安全限制




  1. 扩展名白名单——只能引入文本文件。支持的扩展名包括 .md.txt.json.yaml.ts.py.go.rs.sql 等约 80 种(完整列表见 TEXT_FILE_EXTENSIONS)。二进制文件被静默忽略。




  2. 循环引用检测——processedPaths Set 追踪已处理的文件路径(规范化后比较)。最大递归深度 MAX_INCLUDE_DEPTH




  3. 代码块免疫——@ 指令只在 Markdown 的文本节点(leaf text nodes)中生效。代码块和行内代码中的 @ 不会被解析:




// 这些不会被解析为 include:
if (element.type === 'code' || element.type === 'codespan') {
continue
}



  1. 外部文件需审批——引入工作目录以外的文件需要 claudeMdExternalIncludesApproved 配置项启用。User 类型的 CLAUDE.md 默认可以引入外部文件,Project 类型需要显式审批。




  2. 不存在的文件静默忽略——不会报错,不会中断加载。




可以用 @include 把 CLAUDE.md 拆成模块:


# 项目根目录 CLAUDE.md

@.claude/rules/code-style.md
@.claude/rules/git-conventions.md
@.claude/rules/testing.md



4. Memory 系统


Memory 系统(memdir/)和 CLAUDE.md 是两套独立的机制,但它们在 system prompt 中紧挨着。


Memory 通过 loadMemoryPrompt() 加载(memdir/memdir.ts:419-507),注入到 system prompt 的动态部分——在 SYSTEM_PROMPT_DYNAMIC_BOUNDARY 之后。


MEMORY.md 有硬编码的截断限制(memdir/memdir.ts:35-38):


export const MAX_ENTRYPOINT_LINES = 200
export const MAX_ENTRYPOINT_BYTES = 25_000 // 25KB

截断逻辑(truncateEntrypointContent):



  1. 先按行截断——超过 200 行就砍到 200 行

  2. 再按字节截断——超过 25KB 就在最后一个换行符处切断

  3. 截断后追加警告:


> WARNING: MEMORY.md is 350 lines (limit: 200). Only part of it was loaded.
> Keep index entries to one line under ~200 chars; move detail into topic files.

所以 MEMORY.md 应该当索引用,不是写内容的地方。每条记忆写一行,控制在 200 字符以内,详细内容放单独的 topic 文件。


错误写法:


## 用户偏好
用户是一名高级 Go 开发者,有 10 年经验,主要在 Linux 环境下工作。
偏好简洁代码,不喜欢过度抽象。使用 Neovim 编辑器...(大量详细描述)

正确写法:


- [User Profile](user_profile.md) — 高级 Go 开发者,10年经验,偏好简洁



5. System Prompt 22 层概览


Claude Code 的 system prompt 由 22 个模块拼接(constants/prompts.ts:444-577),分三层缓存策略:


┌─── 静态层(全局可缓存,跨用户复用)────────────────────────────┐
│ 1. Attribution Header(指纹校验) │
│ 2. CLI 前缀标识 │
│ 3. 身份介绍:"You are Claude Code..." │
│ 4. 系统规则(prompt injection 警告等) │
│ 5. 编码任务指南 │
│ 6. 操作谨慎性指南 │
│ 7. 工具使用指南 │
│ 8. 语气风格 │
│ 9. 输出效率 │
├─── SYSTEM_PROMPT_DYNAMIC_BOUNDARY ───────────────────────┤
│ │
│ ── 动态层(会话级缓存,/clear 或 /compact 后重算)── │
│ 10. 会话指导 │
│ 11. ★ Memory 内容(loadMemoryPrompt) │
│ 12. 环境信息(平台、shell、模型名、Git 状态) │
│ 13. 语言偏好 │
│ 14. 输出样式 │
│ │
│ ── 易变层(每轮重算,标记为 DANGEROUS_uncached)── │
│ 15. MCP 服务器指令 │
│ 16. 暂存板指令 │
│ 17. 函数结果清除提醒 │
│ 18. 工具结果摘要 │
│ │
│ ── 条件注入 ── │
│ 19-22. token 预算 / Advisor 指令 / 系统上下文 │
└──────────────────────────────────────────────────────────┘

然后,在 system prompt 之外:

┌─── User Message 层 ─────────────────────────────────────┐
│ ★ CLAUDE.md 内容(包装在 <system-reminder> 中) │
│ 作为对话的第一条 user message │
├──────────────────────────────────────────────────────────┤
│ 用户的第一条实际消息 │
│ ...后续对话... │
└──────────────────────────────────────────────────────────┘

注意两个星号(★)标记的位置:


Memory 在 system prompt 的动态层(第 11 位)

CLAUDE.md 在 user message 层(对话的第一条消息)


它们的定位不同:

Memory 是 system prompt 的一部分,模型会将其视为"系统级指令"

CLAUDE.md 是 user message,模型将其视为"用户提供的上下文"


但 CLAUDE.md 有那句 “OVERRIDE any default behavior” 来提升权重。




6. 缓存经济学


三层结构直接影响 API 成本。


静态层(第 1-9 个模块)使用 cacheScope: 'global'——跨所有用户和组织复用。你不需要为这些 token 付费(cache hit)。


动态层(第 10-14 个模块)使用 cacheScope: 'org'——在你的组织/账户内复用。第一次会话需要创建缓存,后续会话 cache hit。


易变层(第 15-18 个模块)标记为 DANGEROUS_uncached——每轮重新计算。这些会破坏缓存。源码要求每个易变 section 必须注明破坏原因:


DANGEROUS_uncachedSystemPromptSection(
'mcp_instructions',
() => getMcpInstructionsSection(mcpClients),
'MCP servers connect/disconnect between turns', // 必须说明原因
)

CLAUDE.md 在 user message 中,不在 system prompt 中,所以它不影响 system prompt 的缓存。你修改 CLAUDE.md 不会导致 system prompt 缓存失效。但 CLAUDE.md 本身作为 user message 的一部分,会被 prompt caching 机制缓存——只要内容不变,后续轮次不需要重新处理这些 token。


几个点注意一下:



  1. 不要频繁修改 CLAUDE.md——每次修改都会使 user message 缓存失效

  2. MCP 服务器配置变化是免费的——它在易变层,本来就每轮重算

  3. /compact/clear 会清除所有动态层缓存——Memory、环境信息等都会重新加载




7. .claude/rules/ 的递归发现


.claude/rules/ 目录支持子目录递归utils/claudemd.ts:697-788)。你可以这样组织:


.claude/
├── CLAUDE.md
└── rules/
├── general.md
├── frontend/
│ ├── react.md
│ └── testing.md
└── backend/
├── api-design.md
└── database.md

所有 .md 文件都会被递归发现和加载。非 .md 文件被忽略。


rules 目录的处理有一个特殊逻辑:symlink 安全。代码会解析 symlink 的真实路径,并在 visitedDirsprocessedPaths 中同时记录原始路径和解析后的路径,防止 symlink 循环。




8. Git Worktree 去重


如果你在 Git worktree 中工作,CLAUDE.md 的发现有一个特殊处理(utils/claudemd.ts:860-875):


// When running from a git worktree nested inside its main repo,
// the upward walk passes through both the worktree root and the
// main repo root. Both contain checked-in files like CLAUDE.md,
// so the same content gets loaded twice.

Claude Code 会检测你是否在一个嵌套 worktree 中(worktree 目录在主仓库目录内部)。如果是,它会跳过主仓库中的 Project 类型文件——因为 worktree 已经有自己的 checkout。


CLAUDE.local.md 不受影响——它是 gitignore 的,只存在于主仓库,不会被 worktree 复制。




9. 一些未曾使用过的功能


功能 1:--bare 模式跳过 CLAUDE.md


const shouldDisableClaudeMd =
isEnvTruthy(process.env.CLAUDE_CODE_DISABLE_CLAUDE_MDS) ||
(isBareMode() && getAdditionalDirectoriesForClaudeMd().length === 0)

如果你用 claude --bare 或设置了 CLAUDE_CODE_DISABLE_CLAUDE_MDS=1,所有 CLAUDE.md 都不会加载。--bare 的语义是"跳过我没明确要求的东西"。


功能 2:settings.json 可以关闭特定来源


if (isSettingSourceEnabled('projectSettings')) {
// 才会加载 Project 类型的 CLAUDE.md
}
if (isSettingSourceEnabled('localSettings')) {
// 才会加载 Local 类型的 CLAUDE.local.md
}

企业管理员可以通过 policy settings 禁用 Project 或 Local 类型的 CLAUDE.md。如果你的指令不生效,检查 settings.jsonsettingSources 配置。


功能 3:GrowthBook feature flag 可以跳过 Project 和 Local


const skipProjectLevel = getFeatureValue_CACHED_MAY_BE_STALE(
'tengu_paper_halyard', false,
)
if (skipProjectLevel && (file.type === 'Project' || file.type === 'Local'))
continue

Anthropic 内部有一个 feature flag tengu_paper_halyard,开启后会跳过所有 Project 和 Local 类型的 CLAUDE.md。这个 flag 目前默认关闭,但说明 Anthropic 保留了从服务端禁用项目级指令的能力。




10. 我推荐的配置方式


重构 CLAUDE.md


# 项目根目录 CLAUDE.md

@.claude/rules/code-style.md
@.claude/rules/git-workflow.md
@.claude/rules/testing.md

每个规则文件一个主题,一个文件不超过 50 行。


写具体的、可验证的指令


不要写:


代码要简洁

要写:


- 函数不超过 30 行
- 不要添加注释到你没有修改的代码行
- commit message 用中文,不超过 50 字

多利用优先级层级


全局偏好放 ~/.claude/CLAUDE.md


- 用中文回答
- 不要在回答末尾总结你做了什么

项目规则放 项目根目录/.claude/rules/


# .claude/rules/backend.md
- 使用 Go 1.22+ 的新特性
- 错误处理用 errors.Join 而不是 fmt.Errorf
- 测试用 testify

子目录规则放对应目录的 CLAUDE.md


# frontend/CLAUDE.md
- 组件用函数组件,不用 class 组件
- 状态管理用 zustand
最新回复 (19)
  • KWang 04-01 10:07
    1

    学AI,上L站!

  • Znwhyo 04-01 10:07
    2

    感谢分享^-^

  • 树洞小兔 04-01 10:10
    3

    先赞后看

  • 炫彩小鱼干 04-01 10:11
    4

    相当详细的分析!前排学习

  • shawn89 04-01 10:12
    5

    前排学习

  • 昔日织 04-01 10:16
    6

    学习了,感谢分享^-^

  • sharkly 04-01 10:20
    7

    好贴,赞了

  • 老路 04-01 10:21
    8

    感谢分享

  • 蓦言﹉ 04-01 10:25
    9

    厉害,学习了 ^-^

  • jawi 04-01 10:28
    10

    佬太用心了

  • YQF 04-01 10:30
    11

    感谢分享,

  • 不吃猪肉 04-01 10:31
    12

    前排学习一下

  • 大帅哥 04-01 10:32
    13

    感谢大佬。

  • 北川 04-01 10:35
    14

    学习一下,追更

  • Sokoly 04-01 10:35
    15

    前排学习

  • SIX_MAN 04-01 10:37
    16

    太强了佬

  • waken 04-01 10:38
    17

    好文,多发

  • 希望的葱 04-01 10:42
    18

    已加入阅读收藏 ^-^

  • 遗忘的风 04-01 10:43
    19

    学习,感谢分享

* 帖子来源Linux.do
返回