1. 为什么 Claude Code 会“不听” CLAUDE.md
你大概率遇到过这种场景:项目根目录的 CLAUDE.md 里明明写了“包管理器统一用 pnpm”,结果 Claude Code 在某个子包里还是给你生成了npm install;或者你写了“所有测试必须带 Redis”,它却直接跑了一个纯单元测试。前一天刚强调过的规则,第二天就像没看见一样。
先给结论:这通常不是模型变笨,也不是 CLAUDE.md 完全没生效。更准确的说法是,CLAUDE.md 在 Claude Code 里属于上下文指令,而不是不可违背的强制配置。它会在会话开始时被读取,用来承载项目规则、编码标准、架构背景和常见工作流,但它本质上是一条注入到上下文里的说明,不是操作系统级别的门禁。
这里有个关键细节很多人会忽略:CLAUDE.md 的内容并不是 system prompt 的一部分,而是作为 system prompt 之后的一条 user message 传给模型。模型会读、会尽量遵循,但无法保证 100% 严格合规,尤其在规则模糊、规则冲突、或者当前任务上下文信号很强的时候。
打个比方,CLAUDE.md 很像团队给新人写的 onboarding 文档。文档可以告诉新人“提交前跑测试”“接口错误格式要统一”,但文档不是 Git hook,也不是 CI。新人认真读了通常会照做,可一旦文档写得含糊、前后矛盾,或者手头任务压力大,他仍然可能漏掉一条。真正能硬性拦住错误提交的,是 Git hook、CI pipeline、lint rule 这些自动化机制。
所以排查这类问题时,别只问“为什么 Claude 不听话”。更有效的问题是四个:Claude 是否真的看到了那份文件?规则是否足够清晰?是否有别的文件给了相反指令?这条要求到底应该是软规则还是硬约束?
这篇就围绕这四个问题,从InstructionsLoaded事件和 hooks 入手,给你一套可复制的排查路径,包括settings.json骨架、hooks 配置片段,以及验证 CLAUDE.md 是否被加载的具体动作。
2. 前置准备:确认加载路径与 TaoToken 接入
在动手排查之前,先把两件事准备好:一是确认 Claude Code 的加载路径规则,二是把模型接入配置好,避免把“接入问题”误判成“CLAUDE.md 问题”。
Claude Code 有自己的文件查找规则。它会从当前工作目录开始向上遍历目录树,查找沿途的CLAUDE.md和CLAUDE.local.md,并把发现的文件拼接进上下文,而不是互相覆盖。对于工作目录下方子目录里的 CLAUDE.md,它们不会在启动时立刻加载,而是在 Claude 读取对应子目录文件时按需加载。
这对大型仓库特别重要。你在apps/web目录启动 Claude Code,它会看到apps/web/CLAUDE.md,也会看到apps/CLAUDE.md,还可能看到仓库根目录的CLAUDE.md。但如果某条规则放在packages/payment/CLAUDE.md,而当前会话从未读取过packages/payment下的文件,那这份子目录规则可能还没进入上下文。此时要求 Claude 遵循支付模块的专属规则,就像在会上要求同事遵循一份还没发到群里的文档。
接入层面,如果你用的是兼容 Anthropic 协议的网关,需要把 base URL 和 API Key 配好。TaoToken 的 API 地址是https://taotoken.net/api,控制台和密钥管理入口如下:
- 控制台:https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=
- API Keys:https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=
- 接入文档:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=
环境变量可以这样设置,让 Claude Code 走这个端点:
export ANTHROPIC_BASE_URL="https://taotoken.net/api" export ANTHROPIC_API_KEY="sk-你的密钥"注意:
ANTHROPIC_BASE_URL不要带末尾斜杠,也不要拼/v1,具体以接入文档为准。配错端点会直接导致请求失败,这种报错和 CLAUDE.md 无关,别混在一起排查。
3. 可复制配置:settings.json 骨架与 hooks 片段
排查 CLAUDE.md 是否生效,最直接的工具是/memory命令,它会列出当前会话已加载的 CLAUDE.md、CLAUDE.local.md 和 rules 文件。如果目标文件不在列表里,就别急着优化 prompt,先解决加载路径问题。
但/memory是手动查看,复杂仓库里你更需要一份自动记录。这就是InstructionsLoadedhook 的用武之地。它在 CLAUDE.md 或.claude/rules/*.md文件加载进上下文时触发,既会在 session start 触发,也会在会话期间文件被懒加载时触发。
下面是一份可复制的settings.json骨架,放在项目.claude/settings.json里:
{ "hooks": { "InstructionsLoaded": [ { "hooks": [ { "type": "command", "command": "bash .claude/hooks/log-instructions.sh" } ] } ], "PostToolUse": [ { "matcher": "Edit|Write", "hooks": [ { "type": "command", "command": "bash .claude/hooks/guard-generated.sh" } ] } ] } }对应的日志脚本.claude/hooks/log-instructions.sh,把每次加载事件追加到文件,方便事后审计:
#!/usr/bin/env bash set -euo pipefail LOG_DIR=".claude/logs" mkdir -p "$LOG_DIR" INPUT="$(cat)" TS="$(date '+%Y-%m-%d %H:%M:%S')" echo "[$TS] $INPUT" >> "$LOG_DIR/instructions-loaded.log" exit 0再配一个拦截生成目录被误改的脚本.claude/hooks/guard-generated.sh,把“不要改 src/generated”从口头提醒变成关卡:
#!/usr/bin/env bash set -euo pipefail INPUT="$(cat)" FILE_PATH="$(echo "$INPUT" | python3 -c 'import sys,json; d=json.load(sys.stdin); print(d.get("tool_input",{}).get("file_path",""))' 2>/dev/null || true)" if [[ "$FILE_PATH" == *"src/generated"* ]]; then echo "BLOCK: src/generated 为自动生成目录,禁止手工编辑" >&2 exit 2 fi exit 0提示:hook 脚本记得加执行权限
chmod +x .claude/hooks/*.sh。退出码2在 Claude Code 里表示阻断该工具调用,退出码0表示放行。
4. 验证请求:确认 CLAUDE.md 到底有没有被加载
配置写好后,先做一次最小验证,确认日志真的在记录。
第一步,启动 Claude Code,随便让它读一个文件,触发会话初始化:
claude第二步,在会话里输入/memory,看目标 CLAUDE.md 是否出现在列表里。如果没出现,直接跳到第 5 节的排查清单。
第三步,退出会话,查看日志文件:
cat .claude/logs/instructions-loaded.log正常输出应该能看到类似这样的记录,包含加载的文件路径和触发时机:
[2025-01-15 10:22:31] {"event":"InstructionsLoaded","file":"/repo/CLAUDE.md","trigger":"session_start"} [2025-01-15 10:23:05] {"event":"InstructionsLoaded","file":"/repo/apps/api/CLAUDE.md","trigger":"lazy_load"}如果日志里只有根目录的 CLAUDE.md,没有子目录的,说明子目录规则还没被懒加载。这时候你可以主动让 Claude 读取相关目录文件,比如:
请先读取 apps/api/CLAUDE.md 和 apps/api/package.json,再开始修改接口。再跑一次日志,就能看到lazy_load记录出现了。这一步能帮你区分两类问题:一类是规则根本没加载(路径放错、启动目录不对),另一类是规则加载太晚(Claude 前几轮已经做完设计决策,后面才读到子目录规则)。
对于后者,解决办法是把必须全局生效的规则上移到项目根目录,或者养成任务开始时先让 Claude 读相关目录文件的习惯。
5. 本篇常见错排查
排查时按下面这个顺序走,基本能覆盖 90% 的“CLAUDE.md 不生效”场景。
第一,文件位置放错。项目级 CLAUDE.md 可以放在./CLAUDE.md或./.claude/CLAUDE.md,个人偏好放项目根目录的CLAUDE.local.md(建议加进.gitignore)。有人把规则写进./.claude/CLAUDE.md,有人放./CLAUDE.md,路径不统一时,Claude 只能凭当前上下文猜。
第二,启动目录不对。在apps/web启动,就不会自动加载packages/payment的规则。用/memory和InstructionsLoaded日志交叉确认。
第三,规则写得太抽象。“保持代码优雅”“遵循最佳实践”这类话人类读没问题,模型读起来没有可操作的判断边界。官方给的典型对照是:Use 2-space indentation比format code nicely有效得多。把“注意错误处理”改成“所有 controller 捕获业务异常时统一返回 ApiErrorResponse,日志必须包含 requestId,禁止打印 access token”,遵循稳定性会明显提升。
第四,规则冲突。CLAUDE.md 的加载是拼接而非覆盖。根目录写“用 npm”,子项目写“用 pnpm”,个人 local 文件写“用 yarn”,Claude 看到的是三套指令。这就像 Spring Boot 里 application.yml、环境变量、启动参数叠在一起,冲突时不一定报错,但行为会让人困惑。稳妥做法是根目录写全局原则,子目录写局部例外,并显式标注优先级。
第五,该硬执行的动作只写了软提醒。每次编辑后格式化、commit 前跑测试、危险命令拦截,这些必须发生在固定时机的动作,应该交给 hooks,而不是靠一句 CLAUDE.md 提醒。PostToolUse绑定Edit|Write后自动格式化,PreToolUse拦截危险命令,才是确定性机制。
第六,需要 system prompt 级别的要求没用对 flag。如果某条要求确实要提升到 system prompt 层级,Claude Code 提供了--append-system-prompt和--append-system-prompt-file。但要注意这些 flag 只作用于当前 invocation,更适合脚本化、CI 场景,不适合普通交互式会话。追加型 flag 会保留默认的工具指导和安全说明,替换型--system-prompt会丢掉这些默认内容,要非常谨慎。
第七,CLAUDE.md 写太长。官方建议每个 CLAUDE.md 控制在 200 行以内,更大的文件会消耗更多上下文,也可能降低指令遵循度。一页纸的规则更容易被稳定遵循,几十页的规范反而会被压缩成模糊印象。
6. 把软约束和硬机制分层,问题就从玄学变工程
排查到最后你会发现,Claude Code 的配置不是单点魔法,而是一套层级系统。CLAUDE.md 负责提供项目上下文,rules 负责模块化和路径作用域,hooks 负责固定生命周期动作,CLI system prompt flags 负责单次调用里的高优先级补充,settings 负责权限和环境。
当 Claude 没有遵循 CLAUDE.md,按这个路径走:先运行/memory确认加载情况,再确认作用域和路径,然后检查表达是否具体、是否存在冲突,接着判断是否需要迁移到 hook,最后才考虑是否需要 system prompt 层级。大型仓库、路径规则、懒加载子目录,都建议打开InstructionsLoaded审计,没有日志的规则系统排查起来只能靠猜。
如果你在验证模型行为、对比不同配置下的遵循效果,可以直接用模型对话入口快速试:
- 模型对话:https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=
如果你是要长期跑编码任务、Agent 工作流,建议用 Coding Plan,把接入和额度管理固定下来:
- Coding Plan:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=
接入和密钥相关的配置,统一在 API Keys 和接入文档里查:
- API Keys:https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=
- 接入文档:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=
我试过把团队规范一股脑塞进 CLAUDE.md,结果就是又长又冲突,遵循率反而下降。后来拆成三层——根目录写全局原则、子目录写局部例外、固定动作交给 hooks——Claude Code 的行为才从“偶尔聪明”变成稳定可用。地图清楚,护栏可靠,日志可查,剩下的就是正常写代码了。