Insights·2026-08-11

CLAUDE.md、Rules、Hooks、Skills、Agents — Claude Code 的五种配置文件

把 Claude Code 调成自己的用法,归根结底就是放好五类文件。CLAUDE.md 是每次会话最先读取的项目说明,rules 是说明变长后按条件拆分的地方,hooks 不是叮嘱而是用代码强制,skills 把重复的流程做成斜杠命令,agents 则把旁支工作放到另一个窗口做,从而保住主对话。本文依次讲清这五者各自建在哪里、格式如何,以及手上的一句话该归到哪一个。

CLAUDE.md·rules·hooks·skills·agents 다섯 가지가 어떤 순서로 넘어가는지와, 지시와 강제의 차이를 담은 요약 도식

五者各自负责什么

刚装好 Claude Code 时只看得到一个对话框。这样也能干活,但用几天就会发现自己在反复输入同样的话:这个项目这样启动、那个目录别动、提交前先跑测试。

消除这种重复的机制有五个。名字看着相似,性格却完全不同。区分它们的有两条轴:什么时候被读取,以及是叮嘱还是强制。

先看全景图。下面这张表就是全文的摘要。

名称创建位置何时读取性质
CLAUDE.md项目根目录或 ~/.claude/每次会话开始时叮嘱
Rules.claude/rules/*.md始终,或仅在触及匹配文件时叮嘱
Hooks.claude/settings.json指定事件每次发生时强制
Skills.claude/skills/<名称>/SKILL.md仅在调用时流程
Agents.claude/agents/<名称>.md仅在移交工作时分工
五者齐备的项目
my-project/
├── CLAUDE.md                  # 每次会话最先读取的说明
└── .claude/
    ├── settings.json          # 写钩子的地方
    ├── rules/
    │   ├── testing.md
    │   └── api.md             # 可以用 paths 加条件
    ├── skills/
    │   └── deploy/SKILL.md    # 用 /deploy 调用
    └── agents/
        └── code-reviewer.md   # 评审在另一个窗口进行

CLAUDE.md — 会话开始时最先读取的文件

顾名思义就是一个文件。在项目目录里建一个名为 CLAUDE.md 的文件,Claude Code 每次启动都会先读它再开始对话。

为什么需要它。语言模型记不住过去的对话,看上去像记得,是因为每次都把保存下来的内容和提示一起发过去,而那份保存的内容就是这个文件。不想今天再讲一遍昨天的构建命令,就写在这里。

写什么。写那些否则你还得再解释一遍的东西。判断信号有几个:同样的错误第二次出现、同样的纠正上次也打过、新同事同样会问到的内容。具体来说是项目做什么、用什么技术栈、用什么命令运行测试部署,以及绝对不能做的事。

文件可以放在四处,从大范围到小范围全部拼接后读取:面向组织下发的托管策略文件、作用于账号的 ~/.claude/CLAUDE.md、与团队共享的项目 CLAUDE.md,以及只给自己用的 CLAUDE.local.md。最后一个要加进 .gitignore,别提交。

从空白文件写起有困难,就在终端敲 /init。Claude 会扫一遍代码库,生成含有构建命令、测试方法和它发现的约定的草稿。已有文件时不覆盖,只提改进建议。

初学者在这里最常犯的错是篇幅。有人写成自我介绍,而官方文档建议每个文件保持在 200 行以内。这个文件每次会话都整份进入上下文,越长越费 token,也越不容易被遵守。措辞也要具体到可以验证:写「缩进用两个空格」比写「把代码格式化好」管用。

还有一个必须知道的事实。CLAUDE.md 是上下文而不是配置。它以用户消息的形式送达而非系统提示的一部分,所以 Claude 会读会参考,但不保证照做。必须拦住的事情属于后面的钩子。

想确认文件是否真的读进来了,在会话里敲 /context 看内存文件清单。想打开修改就用 /memory。

如果你已经为别的工具维护 AGENTS.md,Claude Code 不读那个文件。建一个 CLAUDE.md,第一行写 @AGENTS.md 引入即可两边共用一份。

范围位置适用场景
托管策略macOS 为 /Library/Application Support/ClaudeCode/CLAUDE.md组织级标准
个人账号~/.claude/CLAUDE.md适用于所有项目的个人偏好
项目./CLAUDE.md 或 ./.claude/CLAUDE.md与团队共享的规则
个人·项目./CLAUDE.local.md只给自己用的值,加入 .gitignore
CLAUDE.md — 简短示例
# my-shop

## 项目
用 Next.js 15 和 Supabase 做的在线下单页面。

## 命令
- 开发服务器: `npm run dev`
- 测试: `npm test`
- 部署: `vercel deploy --prod`

## 规则
- 金额计算只在 `src/lib/price.ts` 一处进行。
- 提交前必须运行 `npm test`。
- 绝不打开也不提交 `.env` 文件。

Rules — CLAUDE.md 变长后拆到这里

该图对比 .claude/rules/ 中始终加载的规则文件与带 paths、仅在打开匹配文件时才加载的规则文件,并列出常用 glob 模式。

「保持在 200 行以内」马上带来一个问题:多出来的往哪放。答案是 .claude/rules/。

做法很简单。在项目里建 .claude/rules/ 目录,按主题放 markdown 文件。取名让人一眼看出主题,比如 testing.md、api-design.md、security.md。放进子目录也会被递归找到。

只做到这一步,与把 CLAUDE.md 拆成几个文件没有区别。真正的差异在下一个功能。

在文件顶部写上 paths 就变成条件加载。像下面的例子那样写好 glob 模式,只有当 Claude 真的打开匹配的文件时,这条规则才进入上下文。也就是说,改前端时那三十行 API 规则不占位置。

没有 paths 的规则文件在会话开始时无条件全部读取,优先级与 .claude/CLAUDE.md 相同。

模式就是常见的 glob 语法。**/*.ts 是所有 TypeScript 文件,src/**/* 是 src 下全部,*.md 是根目录的 markdown,src/components/*.tsx 只指那个目录里的 React 组件。也可以用花括号合并扩展名,如 src/**/*.{ts,tsx}。

还有依附于「人」而非项目的规则。放在 ~/.claude/rules/ 里就作用于本机所有项目。读取顺序是个人规则在前、项目规则在后,所以项目规则位置更强。

记住一个区别就够了。规则是自动进入上下文的,要么始终,要么在触及对应文件时。而接下来的技能只在调用时进入。必须始终成立的句子是规则,只在特定任务用到的流程是技能。

.claude/rules/api.md
---
paths:
  - "src/api/**/*.ts"
---

# API 编写规则

- 所有端点都要校验输入。
- 错误响应只用一种约定格式。
- 新端点要写 OpenAPI 注释。

Hooks — 不靠叮嘱,用代码拦住

前面两者都是叮嘱。Claude 会读会参考,但也可能不照做。有些事必须成立,就需要性质不同的装置,那就是钩子。

钩子是在指定事件发生时自动执行的 shell 命令。不是 Claude 判断后执行,而是条件符合就一定执行。所以只有它才是强制。

写在 .claude/settings.json 里。结构分三层:挂在哪个事件上、在这个事件里只针对什么、以及执行什么命令。

事件名有很多,一开始知道五个就够。SessionStart 是会话开始时,UserPromptSubmit 是我发出提示时,PreToolUse 是工具执行前,PostToolUse 是执行后,Stop 是 Claude 结束回答时。

matcher 用来在事件内缩小对象。工具类事件按工具名过滤:写 Bash 只对 Bash 工具生效,写 Edit|Write 两个都生效,留空或写 * 则全部生效。

被执行的命令从标准输入收到 JSON,里面写明是哪个事件、哪个工具、传了什么参数。下面的例子就是读这些值来拦截强制推送。

关键在退出码,它就是钩子的判定。以 0 结束表示放行,以 2 结束则该工具调用被取消,写到 stderr 的句子会作为理由交给 Claude。其他值记为错误但工作照常进行。

实际用来做什么。改完文件自动跑格式化、禁止修改某个目录、任务结束时发通知,都很常见。如果 CLAUDE.md 里写了「提交前跑测试」却总被跳过,那句话就该降到钩子里。

退出码含义会发生什么
0成功照常进行。标准输出一般进调试日志
2拦截该工具调用被取消,stderr 作为理由交给 Claude
其他非拦截错误工作照常进行,只记录错误
.claude/settings.json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/no-force-push.sh"
          }
        ]
      }
    ]
  }
}
.claude/hooks/no-force-push.sh
#!/bin/bash
# 钩子从标准输入接收 JSON,tool_input.command 里是即将执行的命令。
cmd=$(jq -r '.tool_input.command // ""')

if [[ "$cmd" == *"push --force"* || "$cmd" == *"push -f"* ]]; then
  # 退出码 2 表示拦截。写到 stderr 的内容会作为理由交给 Claude。
  echo "本仓库禁止强制推送,请使用 --force-with-lease。" >&2
  exit 2
fi

exit 0

Skills — 把重复的流程变成斜杠命令

如果你在对话框里一再粘贴同一组指示,或者 CLAUDE.md 的某一段已经从「事实」长成了「流程」,那就是该搬进技能的信号。

做法是一个目录加一个文件。在 .claude/skills/ 下按名字建目录,里面放 SKILL.md。目录名就是命令名,用 /名称 调用。

SKILL.md 分两部分:三横线之间的 frontmatter,以及下面的正文。frontmatter 的字段全是可选的,但 description 实际上是必需的,因为 Claude 靠它判断何时使用这个技能。要同时写清做什么和什么时候用。

正文写 Claude 要遵循的指示。技能真正的好处在这里:正文只在技能实际被使用时才读取,所以写长了平时也完全不占上下文。把长流程放进 CLAUDE.md 每次会话都要付费,放进技能则是零。

调用方式有两种。直接敲 /名称,或者提出符合 description 的请求让 Claude 自己加载。不想被自动调用、只想手动使用时,在 frontmatter 里把 disable-model-invocation 设为 true。

在正文里用感叹号加反引号包住命令,Claude 看到内容前那里就已经填好了执行结果。下面例子里的 git diff 就是这样,Claude 读技能时改动已经在里面了。

存放位置有三处。~/.claude/skills/ 作用于所有项目,项目的 .claude/skills/ 只作用于该项目,插件里的 skills/ 在插件启用处生效。另外,早先的 .claude/commands/ 已并入技能,原有文件继续可用。

改了文件不用重启会话,当场生效。

位置路径适用范围
个人~/.claude/skills/<名称>/SKILL.md我的所有项目
项目.claude/skills/<名称>/SKILL.md仅此项目
插件<插件>/skills/<名称>/SKILL.md插件启用之处
~/.claude/skills/summarize-changes/SKILL.md
---
description: 概括改动内容并指出风险点。当用户问改了什么或需要提交信息时使用。
---

## 当前改动

!`git diff HEAD`

## 指示

用两三行概括上面的改动,然后列出缺失的错误处理、硬编码的值、
需要一并修改的测试。如果没有改动就直说。

Agents — 把旁支工作放到另一个窗口

该图展示子代理在自己的上下文窗口中工作、只将结果摘要返回主对话的流程,以及代理文件前置字段的四个项目。

前面四个是告诉 Claude 做什么、怎么做,最后这个分的是「谁来做」。

子智能体是拥有独立上下文窗口的助手型 Claude。把工作交给它,它在自己的窗口里独立进行,只回传结果摘要。翻代码库产生的大量搜索结果和文件内容不会堆进我的对话。想到上下文窗口越满答案越差,就知道这为什么是一件工具。

做法是一个 markdown 文件。放到 .claude/agents/ 下,frontmatter 写四项:name 是由小写字母和连字符组成的唯一名称,description 是何时把工作交给它,tools 是可用工具清单,model 是用哪个模型跑。

name 和 description 是必填,其余可省。省略 tools 就继承子智能体可用的全部工具。想设为只读,只写 Read, Grep, Glob 即可让它无法改文件。把 model 设为 haiku 会用更便宜的模型从而省钱,省略则继承主对话的模型。

位置有两处。~/.claude/agents/ 作用于所有项目,项目的 .claude/agents/ 只在该仓库生效。后者提交上去,团队就能共用共改。

不自己做也有现成的。Explore 是翻代码库的只读智能体,Plan 在计划模式下负责调研,General-purpose 负责既要探索又要修改的复合任务。Claude 靠各智能体的 description 决定是否移交,所以自己写时要把说明写成「何时使用」的句子。

新建了文件却看不到,就重启会话。这只在会话开始时 agents 目录本身还不存在的情况下发生。

~/.claude/agents/code-improver.md
---
name: code-improver
description: 扫描文件并就可读性、性能与最佳实践提出改进建议。写完或改完代码后使用。
tools: Read, Grep, Glob
model: sonnet
---

你是代码改进专家。对发现的每个问题,先说明问题所在,
再展示当前代码,然后给出改进后的版本。

五者之中该选哪个

名字和格式都清楚了,真正卡住的还是这一步:手上这句话该放进哪里。

三个问题就能分开。第一,这是必须始终知道的事实,还是某项任务的流程?事实进 CLAUDE.md,流程进技能。第二,是不是只在触及特定文件时才需要?那就是带 paths 的规则。第三,这是可以违反的建议,还是必须拦住的事?后者是钩子。

还有第四个。这项工作做完后,还会再看它的日志吗?不会的话就交给智能体,让自己的对话保持干净。

常见的错误也是对称的。一种是把流程塞进 CLAUDE.md,文件胀到 300 行,结果什么都不被遵守;另一种是把必须拦住的事写成 CLAUDE.md 里的一句话,然后为它不被遵守而着急。前者搬到技能,后者降到钩子,就都解开了。

手上的东西该去的地方
构建命令、目录结构、始终要守的约定CLAUDE.md
只在特定目录或扩展名下成立的规则Rules(指定 paths)
必须无例外拦住或必须执行的事Hooks
由多个步骤组成的重复作业Skills
只要结果、不看过程的调研与评审Agents

现在就动手

不必一次做齐五个。它们有顺序,前一个装不下时才轮到下一个。

先在正在做的项目里敲 /init。Claude 扫过代码库后会生成 CLAUDE.md 草稿。打开草稿确认命令是否正确,再手动补上 Claude 无从知晓的几条规则。第一天做到这里就够。

之后是被动式的。文件接近 200 行时按主题拆到 .claude/rules/,只在特定目录成立的就加 paths。同一组指示第三次粘贴时,搬到 .claude/skills/。写下来却总不被遵守的句子,那就是该降到钩子的句子。调研把对话刷成日志时,就做一个智能体。

确认用 /context。这一屏会显示当前会话实际加载了哪些文件、上下文占了百分之多少。写了却不在清单里,问题不在内容而在位置。