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

2026-04-13 12:581阅读0评论SEO问题
  • 内容介绍
  • 文章标签
  • 相关推荐
问题描述:

新一期来啦:

深入浅出 Claude Code(二):从源码解构 MEMORY.md,重塑你的上下文注入链路 开发调优
上一篇我们重写了 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 网友解答:


--【壹】--:

先赞后看


--【贰】--:

学习一下,追更


--【叁】--:

厉害,学习了


--【肆】--:

前排学习一下


--【伍】--:

佬太用心了


--【陆】--:

前排学习


--【柒】--:

感谢大佬。


--【捌】--:

感谢分享


--【玖】--:

好贴,赞了


--【拾】--:

学习了,感谢分享


--【拾壹】--:

好文,多发


--【拾贰】--:

感谢分享,


--【拾叁】--:

前排学习


--【拾肆】--:

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


--【拾伍】--:

已加入阅读收藏


--【拾陆】--:

太强了佬


--【拾柒】--:

学AI,上L站!


--【拾捌】--:

学习,感谢分享


--【拾玖】--:

感谢分享

问题描述:

新一期来啦:

深入浅出 Claude Code(二):从源码解构 MEMORY.md,重塑你的上下文注入链路 开发调优
上一篇我们重写了 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 网友解答:


--【壹】--:

先赞后看


--【贰】--:

学习一下,追更


--【叁】--:

厉害,学习了


--【肆】--:

前排学习一下


--【伍】--:

佬太用心了


--【陆】--:

前排学习


--【柒】--:

感谢大佬。


--【捌】--:

感谢分享


--【玖】--:

好贴,赞了


--【拾】--:

学习了,感谢分享


--【拾壹】--:

好文,多发


--【拾贰】--:

感谢分享,


--【拾叁】--:

前排学习


--【拾肆】--:

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


--【拾伍】--:

已加入阅读收藏


--【拾陆】--:

太强了佬


--【拾柒】--:

学AI,上L站!


--【拾捌】--:

学习,感谢分享


--【拾玖】--:

感谢分享