本文根据 Anthropic 的《Steering Claude Code: when to use CLAUDE.md, skills, hooks, and subagents》重述而成。
指令应该放在哪里 #
Claude Code 可以被定制,但“把要求写进某个 Markdown 文件”并不是唯一答案。当前有七种主要方式可以影响它的行为:CLAUDE.md、Rules、Skills、Subagents、Hooks、Output styles,以及启动时追加的 system prompt。
它们真正的区别,不只是文件名不同,而是四个问题的答案不同:
- 什么时候加载到上下文?
- 长对话发生压缩(compaction)后,它还在不在?
- 会持续占用多少上下文空间?
- 这条要求究竟是给模型的建议,还是系统层面的确定性约束?
可以先用下面这张表建立全局认识:
| 机制 | 加载时机 | 长对话压缩后的行为 | 上下文成本 | 更适合放什么 |
|---|---|---|---|---|
根目录 CLAUDE.md | 会话开始时加载,并持续存在 | 会话压缩后重新读取 | 高:每一行都可能影响每次任务 | 构建命令、目录结构、团队约定 |
子目录 CLAUDE.md | Claude 读取该目录下的文件时按需加载 | 目录再次被触及时才重新出现 | 低:只在相关目录工作时消耗 | 某个模块的局部约定 |
| Rules | 用户级规则在会话开始加载;路径规则在匹配文件被触及时加载 | 会重新注入 | 中 | 跨文件的约束、特定路径的规范 |
| Skills | 会话开始只加载名称和描述;调用后才加载正文 | 已调用的 Skill 按共享预算重新注入 | 低:正文按需进入 | 可复用的操作流程和检查清单 |
| Subagents | 会话开始加载名称、描述和工具列表;被调用时才加载正文 | 主会话只收到最终摘要 | 低:拥有独立上下文 | 并行搜索、日志分析、依赖审计 |
| Hooks | 在生命周期事件发生时触发 | 不受压缩影响 | 低:配置在主上下文之外执行 | 确定性自动化、拦截和通知 |
| Output styles | 会话开始注入 system prompt | 不会被压缩 | 高 | 改变 Claude 的整体角色和表达方式 |
| 追加 system prompt | 本次启动时传入 | 不会被压缩,只作用于这次调用 | 中 | 临时的语气、格式或领域要求 |
这张表背后的原则很简单:事实放在长期可见的位置,流程放进可调用的 Skill,必须发生的动作交给 Hook,需要隔离的工作派给 Subagent。
CLAUDE.md:项目的总说明书 #
根目录的 CLAUDE.md 会在会话开始时进入上下文,并在整个会话中持续生效。构建命令、代码库布局、monorepo 的边界、编码规范和团队约定,都适合写在这里。
它更像一本项目地图,而不是一份厚重的操作手册。比如:
# 项目约定
- 使用 pnpm 安装依赖。
- 前端开发服务器运行 `pnpm dev`。
- 修改 API handler 后运行 `pnpm test`。
- 不要直接编辑生成目录 `dist/`。根目录文件的便利也正是它的代价:无论当前任务是修复 API、调整文档,还是改一个图标,每一行都有可能被加载。文件越长,越容易出现上下文浪费和重要规则被稀释的问题。
一个实用的经验是:让根目录 CLAUDE.md 保持在两百行以内,明确维护者,并像审查代码一样审查它的变更。项目共享的内容写在仓库里,个人偏好则放到用户级配置中,不要把“我个人总是喜欢这样做”变成全团队都必须承担的上下文成本。
在 monorepo 中,可以给不同团队的目录配置自己的 CLAUDE.md。它们不会在会话开始时全部加载,而是在 Claude 真正读取对应目录中的文件时进入上下文。这种按目录延迟加载,能让局部知识只在需要时出现。
Rules:把约束绑定到路径 #
Rules 通常放在 .claude/rules/ 中,用来表达更具体的约束或规范。它们和 CLAUDE.md 很像,但更适合做路径范围的匹配。
例如,只有 API 代码才需要遵守输入校验规则,就可以写成:
---
paths:
- "src/api/**"
- "**/*.handler.ts"
---
所有 API handler 在处理业务逻辑前,必须使用 Zod 校验输入。路径规则的价值在于,它不会让一次纯文档任务背上 API 规范的上下文成本。只要规则涉及一个横跨多个目录、但并非所有任务都需要的文件模式,优先考虑 paths,而不是继续往根目录 CLAUDE.md 里堆内容。
不过,Rules 仍然主要是给模型的指令。它适合表达“应该遵守什么”,却不等于“无论如何都绝不会发生”。如果某个动作必须被可靠地阻止,就不应只依赖文字规则。
Skills:把经验写成可复用流程 #
Skill 存放在 .claude/skills/ 中,通常包含一个 SKILL.md,也可以带上脚本、参考资料和其他资源。
Skill 的加载方式体现了渐进式披露:会话开始时 Claude 只知道 Skill 的名称和描述;只有任务匹配并真正调用它时,完整正文才会进入上下文。因此,Skill 很适合承载部署流程、发布检查、代码审查、报告生成等“知道步骤还不够,还要按顺序执行”的工作。
一个 Skill 的入口可以这样写:
---
name: release-check
description: Check a release candidate before deployment. Use for release review, deployment readiness, or pre-release verification.
---正文中写核心流程,长篇 API 说明和指标定义放到 references/,确定性的检查则放进 scripts/。这样,Skill 既不会变成一份大而全的知识库,也不会把“请认真检查”这种模糊要求当成质量保证。
一个简单的判断方法是:
- 如果内容是“项目一直是什么样”,写进
CLAUDE.md。 - 如果内容是“每次遇到这类任务,按这些步骤完成”,写成 Skill。
- 如果内容是“这条命令执行前必须满足某个条件”,考虑 Hook 或权限控制。
Skill 的优势是可复用、可组合,而且过程发生在主会话中。用户可以看到每一步,也可以在执行过程中调整方向;这也是它与 Subagent 的重要区别。
Subagents:把旁支工作隔离出去 #
Subagent 是存放在 .claude/agents/ 中的独立助手定义。它们也会在会话开始时暴露名称、描述和工具列表,但详细指令只有在主 Agent 通过 Agent 工具调用它时才加载。
它最重要的特性不是“多一个助手”,而是独立上下文。Subagent 在自己的上下文窗口中完成工作,主会话最终只收到它的摘要和元数据,中间搜索结果、日志和尝试过程不会全部挤进主上下文。
这让 Subagent 很适合处理:
- 深度搜索一个大型代码库;
- 分析一批构建或生产日志;
- 独立做一次依赖或安全审计;
- 把多个互不依赖的目录并行检查一遍。
选择 Skill 还是 Subagent,可以看你是否希望过程留在眼前:
| 需求 | 更适合 |
|---|---|
| 希望主会话展示并引导每个步骤 | Skill |
| 希望旁支任务独立完成,只返回结论 | Subagent |
| 需要并行处理多个互不依赖的任务 | Subagent |
| 需要把一套流程做成团队可重复使用的操作手册 | Skill |
Subagent 不是无限扩张上下文的办法。它仍然会消耗模型调用和 token,只是把中间过程隔离开了。对于需要人类在每一步做决定的探索性任务,也不要把它当成无人值守的替代品。
Hooks:把“应该做”变成“必定执行” #
Hooks 是在 Claude Code 生命周期事件发生时触发的用户定义动作,可以是命令、HTTP endpoint、MCP 工具调用,也可以是 prompt 或 agent 类型的处理器。它们通常注册在 settings.json、托管策略配置,或 Skill/Subagent 的 frontmatter 中。
Hooks 与前面几种机制最大的区别是:它们由运行时在事件发生时触发,而不是等待模型记住一条指令再自行决定是否执行。
因此,下面这些需求适合交给 Hook:
- 文件修改后自动运行 lint;
- 会话结束后向 Slack 发送通知;
- 某类危险命令执行前进行检查;
- 在上下文压缩前备份聊天记录。
例如,PreToolUse hook 可以检查即将执行的工具调用;如果发现不允许的操作,以退出码 2 拒绝这次调用,并把原因反馈给 Claude。这样,“不要删除生产数据”就不再只是一句可能被遗忘的提醒,而变成了一个真正的执行边界。
这里需要区分两种可靠性:命令、HTTP 和 MCP 类型的 Hook 可以确定性地执行;prompt 和 agent 类型的 Hook 仍然需要模型判断输出内容。后两者比普通提示更结构化,但不能把它们误认为完全等价于程序规则。
Hook 的另一个特点是上下文成本很低。大多数执行结果不会自动进入主上下文,只有阻止调用时的错误信息等必要输出才会返回。因此,如果 Hook 在 PreCompact 时把历史保存到某个文件,Claude 并不会自动知道这个文件的路径;需要显式把信息返回,或者在其他规则中告诉它去哪里找。
Output styles 与追加 system prompt:改变整体语气的两把工具 #
Output style 存放在 .claude/output-styles/ 中,会在会话开始时注入 system prompt,并且不会被压缩。由于它处在系统提示层,约束力度较高,也会持续占据上下文空间。
它适合做比较大的角色调整,比如把编码助手变成教学助手,或者让输出长期采用某种报告风格。但要留意:自定义 output style 默认可能替换 Claude Code 原有的编码指令,包括改动范围、安全注意事项和测试习惯。除非明确保留编码指令,否则你可能得到一个“会写代码、却忘了自己应该怎样安全地改代码”的通用助手。
如果只想临时增加要求,启动时追加 system prompt 往往更合适。它是附加的,不会替换默认角色,而且只对本次调用生效:
claude --append-system-prompt "所有回答先给出结论,再给出最小必要的验证步骤。"它的缺点也很明确:每次启动都要传入,且指令越多,模型越容易在多个要求之间分散注意力。临时格式要求、特定领域背景和一次性的编码规范适合放在这里;需要跨会话稳定存在的内容,则应该回到项目文件或可复用机制中。
常见的错位 #
“每次都要自动运行”写进 CLAUDE.md #
如果目标是每次编辑后都运行格式化工具,那么让 Claude“记得去运行”不如配置一个 Hook。模型选择执行和运行时自动执行,是两种不同的可靠性。
“绝对不能做”只写成一句提示 #
面对删除、支付、安全和生产环境等高风险动作,文字指令不应是唯一防线。应该结合 Hook、权限配置,必要时使用组织级托管设置。托管设置由管理员部署,用户本地配置不能覆盖,才适合承载组织范围的确定性护栏。
把三十行操作流程塞进 CLAUDE.md #
CLAUDE.md 应该告诉 Claude 项目是什么样,Skill 才应该告诉它如何完成一套流程。把发布手册、事故响应和安全审查清单全部常驻,会让每一次无关任务都付出上下文代价。
没有路径范围的 API 规则 #
只对 src/api/** 生效的约定,不应在所有会话里无条件加载。给 Rule 加上路径范围,既减少干扰,也让规则的适用边界更加可读。
把个人偏好写进项目配置 #
“我喜欢语义化提交信息”是个人偏好;“团队所有提交都必须遵守某种格式”才是项目规范。前者应该放在用户级配置,后者才进入共享仓库。
选型口诀 #
当你不知道某条指令该放在哪里时,可以按下面的顺序判断:
- 它是项目的稳定事实吗?是的话,放根目录
CLAUDE.md。 - 它只对某个目录或文件类型有效吗?是的话,使用路径范围的 Rule 或子目录
CLAUDE.md。 - 它是一套需要复用的操作步骤吗?是的话,写成 Skill。
- 它是旁支任务,且中间结果不值得污染主会话吗?是的话,派给 Subagent。
- 它必须在某个事件发生时自动执行,或必须阻止某种行为吗?是的话,使用 Hook 和权限控制。
- 它只想改变这次会话的角色、语气或格式吗?使用追加 system prompt;如果是长期的整体角色变化,再考虑 Output style。
这些机制不是彼此竞争的七个“高级提示词入口”,而是一套分层系统:上下文负责提供事实,规则负责表达约束,Skill 负责组织流程,Subagent 负责隔离工作,Hook 负责确定性执行,Output style 和 system prompt 负责调整全局行为。
少写常驻提示,多做正确分层 #
Claude Code 的可定制性越强,越容易把所有经验都塞进同一个 CLAUDE.md。但真正成熟的配置,通常不是“写得更多”,而是“放得更对”。
可以把这套分工压缩成一句话:长期事实放在 CLAUDE.md,局部约束放在 Rules,可复用流程放在 Skills,隔离任务交给 Subagents,确定性动作交给 Hooks,临时全局要求使用 system prompt。
当这些机制各司其职,Claude Code 获得的就不只是一份更长的提示,而是一套更节省上下文、更容易维护、也更可靠的工作环境。
原文:Michael Segner,Anthropic,2026 年 6 月 18 日,Steering Claude Code: when to use CLAUDE.md, skills, hooks, and subagents。