---
title: "06 · 把重复的话写成文件"
description: "Skills 不是给 Claude 装新能力，而是把你反复说的流程写成文件。description 决定何时触发，正文决定怎么做。"
canonical: "https://xiangyugongzuoliu.com/tag/claude-code"
verifiedAt: "2026-05-08"
---

Aaron有一段时间每天都跟 Claude Code 说同样的话：处理 PDF 先提取文本，再处理表格；扫描件走 OCR；表格结果转 CSV；最后给一份可复核摘要。说了十几遍以后突然意识到——这不是提示词技巧，这是一个应该写进文件的工作流。——Aaron

> [!NOTE]
> **这一篇用 15 分钟换什么**：前 5 篇分别讲了位置、上下文、记忆、提示词和思考深度。现在进入第一个"复用层"：**Skills（技能）**。读完你会知道，什么该写进 `CLAUDE.md`，什么该写成 `SKILL.md`，什么时候让 Claude 自动触发，什么时候必须你手动输入 `/skill-name`。

## 1. 你每天重复的不是"知识"，是"流程"

先看一个具体场景。

你让 Claude 处理一个 PDF：

> [!NOTE]
> **👤 你**：帮我把这个 PDF 里的表格提出来。

Claude 能做吗？能。第 1 篇讲过，Claude Code 住在你电脑上。你电脑装了 `pdfplumber`、OCR 工具、Python 脚本，它就能读文件、跑命令、改脚本。

但第一次结果常常不稳。它可能先尝试普通文本提取，漏掉扫描页；也可能把表格当段落处理；还可能直接给你总结，没有留下 CSV。

你只好补一句：

```text
处理 PDF 时先判断是不是扫描件。普通 PDF 用 pdfplumber 提取文本和表格。
扫描件先 OCR。表格转 CSV，保留页码，最后输出摘要和异常页列表。
```

第二天又来一个 PDF。你再说一遍。

第三天又说一遍。

这时要停下来判断：你重复的不是"PDF 是什么"，也不是"pdfplumber 怎么安装"。Claude 大概率知道这些。你重复的是**你的处理流程**。

> [!IMPORTANT]
> **第一性原理**：Skill 解决的不是"Claude 不会什么"，而是"Claude 不知道你遇到某类任务时希望它按什么流程做"。

这句话很关键。很多人写 Skill 写偏，就是因为把 Skill 当成百科词条，开始解释 PDF、CSV、OCR 的定义。真正该写的是：

- 先判断什么
- 用哪个工具
- 遇到什么异常怎么分支
- 输出什么格式
- 做完怎么验收

这些才是你每天反复说的东西。

> [!IMPORTANT]
> **这一篇要回答的三个核心问题**
>
> 写一个 Skill 的代码很容易，难的是理解它**怎么被 Claude 看见 / 何时被加载 / 跟其它机制怎么分工**。后面 12 节就是按这三个问题展开：
>
> 1. **description 写什么 Claude 才会真用它？**——§3 看 description 怎么作为"触发索引"被模型在每轮决策前扫一遍；§5 看 frontmatter 控制面板的全部字段。
> 2. **一篇 SKILL.md 几千字 Claude 不会被淹？**——§6 看 supporting files 外置；§7 看渐进式加载怎么把"装很多"和"上下文不爆"同时做到。
> 3. **Skill 跟 CLAUDE.md / SubAgent / Plugin 边界在哪？**——§9 看跟 CLAUDE.md / Hook / MCP 的 4 维对照；§11 看跟 SubAgent 的衔接；12 篇 Plugin 看怎么被打包分发。
>
> 读完每一节自检：这一节回答了哪个问题、用什么机制回答的、为什么不是另一种实现。

## 2. 写进 `SKILL.md`

最小 Skill 只有一个文件：`SKILL.md`。

目录长这样：

```files
pdf-workflow/
  SKILL.md
```

文件可以这样写：

```md
---
description: 从 PDF 中抽取文本和表格。用户提到处理 PDF、抽表格、跑 OCR、把 PDF 数据转成 CSV 时使用。
---

## 工作流程

1. 先检查 PDF 本身，判断每页是文本型还是扫描图像。
2. 文本型页面用 pdfplumber 抽取文本和表格。
3. 扫描页面先跑 OCR 再抽取。
4. 提取出来的表格按页码存成 CSV。
5. 返回摘要：包含产出文件、跳过的页、置信度偏低的 OCR 段落。
```

注意它**没有**写"PDF 是 Portable Document Format"。也没有写"OCR 是光学字符识别"。这些不是你的流程，是百科。

它写的是：**遇到 PDF 任务时，Claude 应该怎么做**。

> [!TIP]
> **一句话理解**：`SKILL.md` 是"遇到这类任务时，请按这个流程处理"的说明书。它不是代码，也不是插件，更不是知识库百科。

这就是 Skills 和第 4 篇提示词的关系：提示词是**本轮临时说明**，Skill 是**反复出现的说明**。当你发现一段提示词第三次出现，就该考虑把它写成 Skill。

**新手最常见的写法误区**：把 Skill 当百科词条写。开篇先解释"PDF 是什么"、"OCR 是什么"，然后才进流程。Claude 已经知道这些定义，正文应该直接进流程。把"概念解释"塞进 SKILL.md 等于让 Claude 每次触发都把百科再"读"一遍——既占上下文又稀释流程主线。

## 3. `description` 才是触发器

只写流程还不够。Claude 怎么知道什么时候读这个文件？

关键在 frontmatter（文件开头 `---` 之间的元数据）。

官方文档说，每个 Skill 需要 `SKILL.md`，frontmatter 告诉 Claude 什么时候用，正文告诉 Claude 用的时候怎么做。`description` 帮 Claude 决定是否自动加载这个 Skill。完整说明见 [Extend Claude with skills](https://code.claude.com/docs/en/skills#create-your-first-skill)。

所以 `description` 不是简介文案，它是**触发索引**。

坏写法：

```yaml
---
description: 处理文件相关的任务。
---
```

这个描述太泛。PDF 是文件，图片是文件，代码也是文件。Claude 无法判断它什么时候该触发。

好写法：

```yaml
---
description: 从 PDF 中抽取文本和表格。用户提到处理 PDF、抽表格、跑 OCR、把 PDF 数据转成 CSV 时使用。
---
```

这里有四类触发词：PDF、抽表格、OCR、CSV。用户说"把这个扫描 PDF 里的发票表格转成 CSV"，Claude 就容易匹配上。

```mermaid
flowchart LR
    User["👤 用户请求<br/>把扫描 PDF 表格转 CSV"]
    Index["📇 Skill 索引<br/>description 常驻上下文"]
    Match{"匹配到<br/>PDF / OCR / CSV？"}
    Load["📖 加载 SKILL.md 正文"]
    Work["🛠️ 按流程执行"]

    User --> Index
    Index --> Match
    Match -->|是| Load
    Match -->|否| Work
    Load --> Work

    style Index fill:#dbeafe,stroke:#3b82f6
    style Match fill:#fef3c7,stroke:#f59e0b,stroke-width:2px
    style Load fill:#dcfce7,stroke:#22c55e
```

要写好 description，得先理解它**什么时候被读、被谁读**。

**注册时机**：会话启动时，Claude Code 把所有可用 Skill 的 `name + description + frontmatter`（不含正文）拼进 system prompt（system prompt = 启动时 Claude 看到的指令背景）注册，相当于给 Claude 摆了一份"工具菜单"。

**决策时机**：你每发一句话，Claude 在生成回答前会**扫一遍菜单**，决定要不要打开某个 Skill 的正文。这跟 [09 篇 MCP](/docs/claude-code/understanding/09-mcp) 里 tool 的发现机制是同一类——区别是 Skill 描述的是"工作流"，MCP tool 描述的是"动作"。

**Token 经济学**：每个 Skill metadata 大约 50-150 token。装 50 个 Skill 启动时常驻约 5-8 K token——只占 1M 上下文窗口的不到 1%，但占启动 system prompt 的相当一块。这就是为什么 description 写法直接决定 Skill 是不是真有用：

| 写法 | 后果 |
|------|------|
| 写得**太宽**（"处理文件相关的任务"）| 模型每轮都把它当候选，**误触发率高** —— 用户说"打开 README" 也可能错触发，主对话被 SKILL.md 正文淹 |
| 写得**太窄**（"处理 2026 Q4 发票 PDF"）| 模型在用户说"提取这个 PDF"时识别不到，该用没用 |
| 写得**精准 + 列触发词**（"从 PDF 抽取... 用户提到 PDF / OCR / CSV 时使用"）| 模型只在真相关时拉正文，不污染主对话 |

**为什么 Anthropic 选"模型决策"而不是"keyword 索引"**：keyword 匹配（grep）会被语义近义、跨语言、措辞变体打败——用户说"扫描发票转表格"包含"发票"、"扫描"、"表格"，但没"PDF"也没"CSV"，keyword 匹配就漏了。让模型自己读 description 决策能跟人类自然语言对齐，代价是占 system prompt token——这是 Anthropic 在"准确率 vs 上下文成本"之间选了准确率。

类比：description 像菜单上的菜名 + "什么时候点"。菜名太泛（"美食"）服务员（Claude）不知道你想吃什么；菜名太窄（"周三限定×××套餐"）你点了别的相似菜它也认不出。

这里有一个设计细节：Claude 会自动判断，但你也可以手动调用。

如果 Skill 叫 `pdf-workflow`，你可以直接输入：

```text
/pdf-workflow 处理 invoice.pdf
```

官方文档也明确说，custom commands 已经合并进 Skills：`.claude/commands/deploy.md` 和 `.claude/skills/deploy/SKILL.md` 都能创建 `/deploy`，现有 `.claude/commands/` 还可用，但 Skills 支持 supporting files、frontmatter 控制自动触发等更多能力。

> [!WARNING]
> **关键点**：不要把 Skill 理解成"只能自动触发"。它同时有两种入口：Claude 觉得相关时**自动加载**；你也可以用 `/skill-name` **手动调用**。

## 4. 什么时候自动，什么时候手动

这一步很容易混。

不是所有 Skill 都应该让 Claude 自动触发。

PDF **提取**这种流程，自动触发没问题。用户说 PDF、OCR、表格，Claude 自动用它，符合预期。

但 PDF **自动覆盖原文件 / 处理完直接发邮件给客户**这种有副作用的流程，不能让 Claude 自己看起来"时机合适"就执行。

这时用 `disable-model-invocation: true`：

```yaml
---
name: pdf-archive-and-email
description: 处理 PDF 后用结果覆盖原文件并发邮件给收件人。
disable-model-invocation: true
---
```

意思是：Claude 不会自动调用，只有你输入 `/pdf-archive-and-email` 才会触发。

另一个方向也存在：有些 Skill 只是背景知识，不适合人手动调用。比如：

```yaml
---
name: pdf-legacy-format-notes
description: 说明 2020 年以前归档 PDF 的特殊格式约定与解析陷阱。
user-invocable: false
---
```

它可以让 Claude 在相关任务中自动加载（用户处理老 PDF 时自动参考），但不出现在你日常要手动执行的命令入口里。

可以这样判断：

- PDF 提取、读取分析、生成摘要：**默认自动**即可，Claude 能安全自动触发。
- PDF 自动覆盖、自动发邮件、自动归档删源文件：**`disable-model-invocation: true`**，必须由你决定时机。
- PDF 历史格式说明、领域词典、旧系统约定：**`user-invocable: false`**，给 Claude 读，不给人当命令用。

> [!CAUTION]
> **关键原则**：只要一个 Skill 会产生**真实外部副作用**（覆盖文件 / 发消息 / 调用付费 API / 删数据），就不要让它自动触发。自动触发适合"指导 Claude 怎么思考和处理"，不适合"替你决定何时发布、提交或通知别人"。

**为什么 Anthropic 设计成"默认允许 + 高危 opt-out"而不是反过来**：

如果默认全部禁止自动触发，所有 Skill 都要 `/skill-name` 手动调——失去 Skill 最大价值"按需自动加载"。如果默认全部允许自动，副作用 Skill 危险。Anthropic 选了"默认允许 + 高危显式 opt-out"——**把"什么算危险"的判断权交给 Skill 作者**，因为：

- 平台不知道每个 Skill 实际做什么（覆盖文件？只读分析？）
- 静态扫描判断"是否有副作用" 太弱（脚本里调外部 API 难自动识别）
- 让作者声明意图，比让平台猜更准确

代价：**坏作者可以写"自动覆盖"还不加 opt-out**。这就是 [12 篇 Plugin](/docs/claude-code/understanding/12-plugins) 强调"插件是 high-trust 组件"的根因——Skill 系统的安全模型依赖作者诚信 + 安装者审查，不靠平台兜底。

**新手最常踩的坑**：把"PDF 自动归档" Skill 默认 auto，description 写得宽（"处理 PDF 文件"）。当用户说"这个 PDF 太长帮我看看" 时，Claude 误识别成归档场景，把原文件覆盖成提取摘要——**真实数据没了**。修复方式不是改提示词，是改 Skill 配置：副作用 Skill 必须 `disable-model-invocation: true`。

## 5. frontmatter 是控制面板

到这里，Skill 已经不是一段纯文字了。frontmatter 让你控制它的工作方式。

常用字段一张表看清：每个字段的**作用 + 什么时候用 + 不写会怎样**：

| 字段 | 作用 | 什么时候用 | 不写会怎样 / 默认 |
|------|------|------|------|
| `name` | 显示名 / 命令名 | 想固定 `/skill-name` 调用时 | 默认用目录名（如目录叫 `pdf-workflow` 就是 `/pdf-workflow`）|
| `description` | 触发索引 | **几乎每个 Skill 都该写** | 不写 = Claude 完全不知道何时用，等于装了等于没装 |
| `when_to_use` | 追加触发场景 | 触发条件复杂、单条 description 写不完时 | 不写 = 仅靠 description；写了 = 跟 description 叠加增强匹配 |
| `argument-hint` | `/` 自动补全显示参数提示 | 手动命令型 Skill | 不写 = 手动调用时用户不知道传什么参数（如 `/pdf-workflow` 不知道传不传路径） |
| `allowed-tools` | 预批准某些工具 | 确认安全、想减少重复确认时 | 不写 = 用户全局权限规则约束（[11 篇 Permissions](/docs/claude-code/understanding/11-permissions)）；列了 = 这些工具在 Skill 激活时无需逐次确认 |
| `model` | Skill 激活时临时切模型 | 某类任务固定需要 Opus / Haiku 时 | 不写 = 沿用主会话模型 |
| `effort` | Skill 激活时覆盖思考深度 | 审计类深想、格式类低 effort | 不写 = 沿用主会话 effort（详见 [05 篇](/docs/claude-code/understanding/05-thinking-depth)）|
| `context` | `fork` 时在子代理上下文运行 | 不想污染主对话时 | 不写 = 在主对话执行；写 `fork` = 启动 SubAgent（[07 篇](/docs/claude-code/understanding/07-subagents)）|
| `disable-model-invocation` | 禁止自动触发 | 副作用 Skill | 默认 false（允许自动）|
| `user-invocable` | 用户能否手动调用 | 后台知识 Skill | 默认 true（用户可 `/cmd`）|

第 5 篇刚讲过 effort。这里它开始进入工程配置。

比如 PDF **审查** Skill 需要深度推理：

```yaml
---
name: pdf-content-review
description: 审查 PDF 中的合同条款是否存在风险点。
effort: xhigh
allowed-tools: Read Grep Glob
---
```

这说明：这个 Skill 主要读文件、搜文本，不应该随便改文件；同时合同审查需要更深推理，所以 effort 提高。

再比如 PDF **格式润色** Skill 路径很直，不需要高 effort：

```yaml
---
name: pdf-extracted-text-polish
description: 润色已抽取的 PDF 文本格式（断行 / 表头 / 标点），不改事实。
effort: low
---
```

> [!IMPORTANT]
> **边界要讲清**：`allowed-tools` 是预批准工具，**不是唯一可用工具**。官方文档说明，它让列出的工具在 Skill 激活时无需逐次确认；其它工具仍受你的 [11 篇权限设置](/docs/claude-code/understanding/11-permissions) 约束。不要把它当成完整沙箱。

**新手最常漏写的字段是 `argument-hint`**：写了一个 `/pdf-workflow` Skill 但不写 hint，用户在终端打 `/pdf-workflow` 后不知道下一步该输什么——是空格加路径？还是带引号？还是直接回车？hint 一行就解决：`argument-hint: "[PDF 路径]"`。

## 6. 支持文件：别把正文写成仓库

很多人第一次写 Skill，会把所有东西都塞进 `SKILL.md`：

- 20 个示例
- 详细 API 文档
- 项目模板
- 完整检查清单
- 大段背景说明

这样很快失控。**3000 行 SKILL.md** 触发后会一次性注入主对话——主任务上下文直接被挤压，Claude 还没干活就已经"满桌子"，跟 [02 篇上下文](/docs/claude-code/understanding/02-context-window) 讲的"桌子要干净"原则反着干。

官方建议 `SKILL.md` 控制在 **500 行以内**。这不是协议硬限，是**经验阈值**：

- 一行 markdown 平均 8-12 token，500 行 ≈ 5-7 K token
- Skill 触发后正文一次性注入主对话，5-7 K token 是"显著占空间但不挤压主对话"的临界点
- 超过 1 万 token 的 Skill 正文会让 Claude 在每轮都得重读这一大段——上下文经济崩了
- 低于 200 行又意味着流程描述不够细，Claude 仍然要靠猜

所以"500 行" 的本质是：在 Claude 当前 1M 上下文 + 多 Skill 同时启用 + 每轮重读全文的约束下，给单个 Skill 留约 0.5-0.7% 的上下文配额。

大型参考资料应该放到同目录的 supporting files。比如：

```files
pdf-workflow/
  SKILL.md
  reference.md          # 表格抽取边界情况、OCR 置信度判定阈值
  templates/
    extraction-report.md  # 输出报告模板
  examples/
    invoice-summary.md    # 发票类示例
    contract-summary.md   # 合同类示例
  scripts/
    extract_tables.py     # 辅助脚本
```

`SKILL.md` 只写导航：

```md

## 附加资源

- 表格抽取边界情况详见同目录 `reference.md`。
- 最终报告格式见 `templates/extraction-report.md` 模板。
- 辅助脚本见 [scripts/extract_tables.py](scripts/extract_tables.py)，需要时直接运行。
```

这样 Claude 先读流程。只有当它真的需要处理表格边界、生成报告或执行脚本时，才去看对应文件。

**为什么这种"分层"特别划算**：你写 100 个 PDF 处理示例放 examples/，单次任务可能只用到 1-2 个发票类。如果全塞 SKILL.md，触发后 100 个示例全进上下文；放 supporting files 只在 Claude 真要参考时 Read 进来——上下文成本差几十倍。

## 7. 渐进式加载：装很多，但只读当前需要的

Skills 能规模化，靠的是渐进式加载。

它不是启动时把所有 Skill 正文都塞进上下文。它更像三层索引：

```mermaid
flowchart TB
    Level1["第 1 层：Skill metadata<br/>name / description 常驻"]
    Level2["第 2 层：SKILL.md 正文<br/>触发后加载"]
    Level3["第 3 层：supporting files<br/>需要时再读"]
    Work["当前任务上下文"]

    Level1 -->|匹配用户请求| Level2
    Level2 -->|正文提示需要| Level3
    Level2 --> Work
    Level3 --> Work

    style Level1 fill:#dbeafe,stroke:#3b82f6
    style Level2 fill:#dcfce7,stroke:#22c55e
    style Level3 fill:#fef3c7,stroke:#f59e0b
    style Work fill:#f3e8ff,stroke:#a855f7
```

这解决了一个实际矛盾：

- 你希望装很多 Skill，让 Claude 懂你的各种工作流
- 你又不希望上下文被无关 Skill 塞满

渐进式加载让两件事同时成立。

| 加载层级 | 加载什么 | 为什么这样设计 |
|------|------|------|
| 启动时 | `name` / `description` 等元数据 | Claude 需要知道有哪些 Skill 可用 |
| 触发时 | `SKILL.md` 正文 | 只把当前任务相关流程放进上下文 |
| 执行中 | `reference.md` / `examples/` / `scripts/` | 详细资料按需读取，不常驻 |

把"渐进式加载"摊到一次真实会话的时间线上，更直观：

```mermaid
flowchart TB
    T0["🚀 <b>T0 启动会话</b><br/>注册全部 Skill metadata<br/>≈ 50-150 token / 个 · <b>常驻整场</b>"]
    T1["💬 <b>T1 用户输入</b><br/>把这个 PDF 表格转 CSV<br/>Claude 扫菜单 → 命中 pdf-workflow"]
    T2["📖 <b>T2 触发加载正文</b><br/>SKILL.md ≤ 500 行 / 5-7K token<br/>进主对话 · <b>常驻直到 /compact 或 /clear</b>"]
    T3["📂 <b>T3 执行中按需读</b><br/>Read reference.md / 跑脚本<br/><b>单次读完不常驻</b>，除非被引入对话"]
    T4["✅ <b>T4 完成回报</b><br/>摘要 + 输出文件列表给你"]

    T0 --> T1 --> T2 --> T3 --> T4

    style T0 fill:#fef3c7,stroke:#f59e0b,stroke-width:2px
    style T1 fill:#dbeafe,stroke:#3b82f6
    style T2 fill:#dcfce7,stroke:#22c55e,stroke-width:2px
    style T3 fill:#f3e8ff,stroke:#a855f7
    style T4 fill:#fee2e2,stroke:#ef4444
```

读一遍这条时间线，几个反直觉的点会变直观：

- **metadata 永远在 system prompt**：装了 50 个 Skill，不管你今天用不用，启动时这 50 份 metadata 都在线——这是"自动触发"的代价。
- **正文进了主对话就**留**在主对话**：触发后 Skill body 不会"用完释放"。如果同一会话连续触发 5 个 Skill，主对话上下文会累积 5 份正文。这是为什么"不要在一个会话里调一堆 Skill"，跟 [02 篇](/docs/claude-code/understanding/02-context-window) 的桌子原则一致。
- **supporting files 是真的"按需"**：只有 Claude 显式 Read 才进上下文，这就是为什么 500 行的硬数据外置最划算——你写 100 个示例放 examples/，Claude 大多数任务只读 1-2 个。

> [!TIP]
> **一句话理解**：`description` 像书架目录，`SKILL.md` 像当前打开的书，`references/` 和 `scripts/` 像书里的附录。你不会把整座图书馆都摊在桌上。

这也解释了为什么 description 要写得准。metadata 常驻，但正文不常驻。Claude 是否能打开正确那本书，首先取决于目录条目写得清不清楚。

## 8. 放在哪里：个人、项目、插件

Skill 的位置决定作用范围。

官方列了几类位置，日常最常用的是个人级和项目级：

| 位置 | 路径 | 适合放什么 |
|------|------|------|
| 个人级 | `~/.claude/skills/<skill-name>/SKILL.md` | 你自己跨项目都用的 PDF 通用流程 |
| 项目级 | `.claude/skills/<skill-name>/SKILL.md` | 这个项目独有的 PDF 处理约定（如发票格式 / 业务字段映射），可提交 git |
| 插件级 | `<plugin>/skills/<skill-name>/SKILL.md` | 随插件分发的 PDF 工具集（详见 [12 篇](/docs/claude-code/understanding/12-plugins)）|
| 企业级 | managed settings | 公司统一下发的合规 PDF 审查 Skill |

如果同名 Skill 出现在多个层级，**企业级 > 个人级 > 项目级**。插件 Skill 会带插件命名空间（详见 [12 篇](/docs/claude-code/understanding/12-plugins)），避免和普通 Skill 冲突。

还有一个细节：Claude Code 会监听 Skill 目录变化。你在现有 `~/.claude/skills/` 或项目 `.claude/skills/` 里新增、编辑、删除 Skill，**当前会话内会自动生效**。只有顶层 skills 目录本身原来不存在、你刚创建时，可能需要重启 Claude Code 才能被监听。

把它和第 3 篇记忆系统对应起来：

- 个人偏好型 PDF 流程：放 `~/.claude/skills/pdf-workflow/`
- 团队共享 PDF 流程：放项目 `.claude/skills/pdf-invoice/`
- 单纯项目事实（用 pdfplumber 不用 PyMuPDF）：放 `CLAUDE.md`
- 复杂可复用 PDF 工作流：放 `SKILL.md`

## 9. Skills 和 `CLAUDE.md` / Hook / MCP 的边界

这一步最容易和 [03 篇](/docs/claude-code/understanding/03-memory) / [10 篇](/docs/claude-code/understanding/10-operation-control) / [09 篇](/docs/claude-code/understanding/09-mcp) 混。

四者都是"配置 Claude 行为"的方式，但职责不同。一张 4 维对照表看清：

| 维度 | `CLAUDE.md`（[03 篇](/docs/claude-code/understanding/03-memory)）| **`SKILL.md`（本篇）** | Hook（[10 篇](/docs/claude-code/understanding/10-operation-control)）| MCP（[09 篇](/docs/claude-code/understanding/09-mcp)）|
|------|------|------|------|------|
| **加载时机** | 每次会话启动**全量注入** | 触发后才注入正文 | 事件点自动运行 | 工具被调用时连接 |
| **上下文成本** | 全量常驻（建议 ≤200 行）| metadata 约 50-150 token / Skill；触发后正文进上下文 | Hook 自身不占上下文，输出可能进 | 工具 schema 常驻；输出可能进 |
| **触发条件** | 不需要触发，启动即生效 | description 模型语义匹配 | 事件类型 + matcher | 模型决定 / 用户 `@` 引用 |
| **复用粒度** | 项目 / 用户 / 系统 / 本地 4 层 | 项目 / 用户 / 插件 / 企业 4 类 | settings scope（同 CLAUDE.md）| local / project / user 3 类 |
| **典型用法** | 项目规则 / 团队约定 / 个人偏好 | 任务流程 / 领域工作流 | 自动化检查 / 副作用拦截 | 接外部系统 / 数据源 |
| **何时升级** | 重复 ≥3 次的话 → 写 CLAUDE.md | 重复 ≥3 次的流程 → 写 Skill | 漏一次会出事的规则 → 写 Hook | 频繁复制粘贴的外部系统数据 → 装 MCP |

读这张表的诀窍：**机制选错的代价**——CLAUDE.md 写流程会膨胀；Skill 写规则会"加载晚了不起作用"；Hook 写偏好会"动不动就拦你"；MCP 写本地操作是"用大炮打蚊子"。每个机制有它最贴的活，错配是大多数 Claude Code 配置失控的根因。

**3 个判断练习**——下面三句话，分别该写进哪里？读完答案再看你判断对了几个：

> **场景 A**：团队约定 PDF 处理一律用 `pdfplumber`，不要 PyMuPDF。
>
> **场景 B**：处理 PDF 的完整 7 步流程（判断扫描 → OCR → 抽表 → 转 CSV → 摘要）。
>
> **场景 C**：每次写 PDF 处理代码后自动跑 `pytest tests/pdf_test.py`。

<details>
<summary>📌 看答案</summary>

- **A → CLAUDE.md**：是项目级"用什么库"约定，每次会话都要知道，不需要触发。
- **B → SKILL.md**：是反复出现的工作流，只在 PDF 任务时才需要。
- **C → Hook（PostToolUse）**：是"动作完成后自动执行"的规则，不依赖 Claude 触发判断。

判断诀窍——问自己：
1. 这条信息**每次会话**都要进上下文吗？是 → CLAUDE.md。
2. 这条信息**只在某类任务**才需要？是 → SKILL.md。
3. 这条规则**漏一次就出事**？是 → Hook。
4. 这条信息**来自外部系统**（GitHub / 数据库 / SaaS）？是 → MCP。

</details>

## 10. 一个合格 Skill 长什么样

回到 PDF 例子。一个更完整的版本可以这样写：

```md
---
name: pdf-workflow
description: 从 PDF 中抽取文本和表格。用户提到处理 PDF、抽表格、给扫描页跑 OCR、把 PDF 数据转成 CSV 时使用。
argument-hint: "[PDF 路径]"
allowed-tools: Read Bash(python *) Bash(mkdir *) Bash(ls *)
effort: medium
---

## 目标

从 PDF 中抽取可用的文本和表格，保留每段内容到对应页码的追踪关系。

## 工作流程

1. 从 $ARGUMENTS 拿到 PDF 路径。
2. 判断每一页是文本型还是扫描图像。
3. 文本型页面用 pdfplumber 抽取。
4. 扫描页面用 OCR 抽取。
5. 表格按页码导出成 CSV。
6. 写一份摘要：含产出文件、跳过的页、置信度偏低的段落。

## 输出

返回：

- 抽取出的文本文件路径
- 各 CSV 文件路径
- 跳过或需人工复核的页
- 一段话摘要
```

这个 Skill 有几个优点：

- description 含清晰触发词（PDF / OCR / CSV / 抽表格）
- `argument-hint` 让手动调用更顺
- `allowed-tools` 只预批准必要命令
- `effort: medium` 和任务复杂度匹配
- 正文写流程，不写百科
- 输出格式明确

再对照一个坏版本：

```md
---
description: 帮忙处理文档。
---

PDF 是 Adobe 推出的文件格式。OCR 指光学字符识别。
请使用工具处理文件，并返回有用的结果。
```

坏在哪里？

- description 太泛：容易误触发，或该触发时不触发。
- 写百科知识：占上下文，不改变执行质量。
- 没有流程：Claude 还是要猜先后顺序。
- 没有输出契约：每次结果格式不稳定。
- 没有边界：可能动不该动的文件或命令。

这就是 Skill 写作的核心：**描述要窄，流程要清，输出要定，参考资料要外置**。

理解 Skill 的所有设计选择，可以压成一张决策矩阵——这一篇所有"为什么"汇总：

```mermaid
flowchart TB
    Core["💡 <b>Skills 的核心设计选择</b>"]

    subgraph 触发["🎯 触发方式"]
        T1["默认<b>自动</b><br/>+ 高危 opt-out<br/>责任在作者"]
    end

    subgraph 加载["📦 加载策略"]
        L1["<b>渐进式</b>三层<br/>metadata 常驻<br/>正文触发加载<br/>files 按需读"]
    end

    subgraph 表达["📝 表达层级"]
        E1["<b>prompt 层 markdown</b><br/>不是 typed function<br/>不是 flat command"]
    end

    subgraph 安全["🔒 安全模型"]
        S1["作者诚信 + 用户审查<br/>平台不兜底<br/>详见 12 篇 Plugin"]
    end

    Core --> 触发
    Core --> 加载
    Core --> 表达
    Core --> 安全

    style Core fill:#fef3c7,stroke:#f59e0b,stroke-width:3px
    style 触发 fill:#dbeafe,stroke:#3b82f6
    style 加载 fill:#dcfce7,stroke:#22c55e
    style 表达 fill:#f3e8ff,stroke:#a855f7
    style 安全 fill:#fee2e2,stroke:#ef4444
```

**4 个选择各自的"为什么不是另一种"**：

- **触发为什么不是默认全手动**：失去自动加载价值，新手记不住命令名 = 装了等于没装。
- **加载为什么不是全量注入**：50 个 Skill 全量 ≈ 50 万 token，主对话直接爆。
- **表达为什么不是 typed function**：function 强参数校验，但工作流里"这页是不是扫描件"是自然语言判断，schema 表达不了"if-else 内嵌经验"。
- **安全为什么不靠平台**：平台不知道每个 Skill 实际做什么，作者声明意图比让平台静态扫描更准确。

底层取舍：**Skill 是"用 system prompt token 换语义触发的灵活性"**。装得多 = 启动 token 成本上升 + 误触发概率上升；装得少 = 自动复用价值丢失。所以这一篇所有"description 写窄一点"、"500 行外置"、"高危 opt-out" 的劝告，本质都是在帮你对齐这个取舍。

## 11. 和 SubAgents 的连接

下一篇会讲 SubAgents（子代理 = 派分身做子任务，详见 [07 篇](/docs/claude-code/understanding/07-subagents)）。这里先提前接一条线：Skill 可以在主会话里运行，也可以在 forked subagent context（分叉子代理上下文）里运行。

为什么需要这个？

还是 PDF 例子。普通 PDF 提取，主会话直接做就行。

但如果你让 Claude **批量处理 200 个 PDF**，中间会读大量文件、跑很多命令、产出许多日志。这些过程会污染主会话上下文。

这时可以让 Skill 在子代理里跑：

```yaml
---
name: pdf-batch-workflow
description: 批量处理一整个目录的 PDF，返回抽取出的文本、CSV 文件和异常报告。
context: fork
agent: general-purpose
effort: medium
---
```

主会话只拿回结果摘要，具体处理过程留在子代理上下文里。

实际何时升级到 SubAgent，按这张决策树判断：

```mermaid
flowchart TD
    Start["拿到一个反复出现的 PDF 任务"]
    Q1{"流程能写清楚吗？<br/>步骤、判断、输出格式"}
    Q2{"主对话装得下吗？<br/>过程产出大量日志/文件"}
    Q3{"任务之间需要互相对齐吗？<br/>多角色多模块"}
    OneShot["单步 prompt 解决<br/>不要做 Skill"]
    Skill["写 Skill<br/>放在主对话执行"]
    Sub["Skill 用 context: fork<br/>跑在子代理里"]
    Teams["不用 Skill<br/>开 Agent Teams"]

    Start --> Q1
    Q1 -->|否，每次任务都不一样| OneShot
    Q1 -->|是，能模板化| Q2
    Q2 -->|装得下| Skill
    Q2 -->|装不下，过程会污染主对话| Q3
    Q3 -->|否，单点任务| Sub
    Q3 -->|是，多角色协作| Teams

    style Skill fill:#dcfce7,stroke:#22c55e,stroke-width:2px
    style Sub fill:#dbeafe,stroke:#3b82f6,stroke-width:2px
    style Teams fill:#f3e8ff,stroke:#a855f7,stroke-width:2px
    style OneShot fill:#fef3c7,stroke:#f59e0b
```

读这张图的诀窍：**Skill 是默认选项，SubAgent 是"上下文太脏"时的升级，Agent Teams 是"需要协作"时的再升级**。从下往上反着选——先问"能不能用 Skill 就解决"，再考虑分身和团队。这跟 [07 篇](/docs/claude-code/understanding/07-subagents) 讲的"SubAgent 不是任务拆分仪式" 是同一逻辑。

回到 PDF：

- 单个 PDF 处理 → Skill（主对话）
- 200 个 PDF 批量处理 → Skill + `context: fork`（SubAgent）
- 200 个 PDF + 财务 / 法务 / 技术多角色审查 + 互相确认 → 不用 Skill，开 Agent Teams（[08 篇](/docs/claude-code/understanding/08-multi-agent)）

> [!TIP]
> **提前剧透**：当一个 Skill 的执行过程很长、会读很多文件、会跑很多命令时，就开始接近 SubAgent 的使用场景。

## 12. 检验你真懂了吗

试着用自己的话回答：

1. 有人说"Skill 就是给 Claude 装插件"。你能解释为什么这个说法不准确吗？对应 §1 + §2。
2. 为什么 `description` 比正文第一段更像 Skill 的入口？一个太泛的 description 会造成什么问题？对应 §3。
3. **动手题** ⭐：现在打开一个真实项目，写一个 Skill 的 `description` 字段（≤80 字），让 Claude 在用户说"扫描发票转表格"时**触发**，但在用户说"打开这个 PDF 让我看看" 时**不触发**。写完检查：触发词覆盖了"扫描"、"发票"、"表格" 三类输入吗？有没有把"PDF" 单字作为唯一锚点（这会让"看这个 PDF" 也命中）？

> [!TIP]
> **过关标准**：能用一句话说清——**Skill 是按需加载的工作流文件：description 负责触发，正文负责流程，supporting files 负责深层资料；长期事实进 CLAUDE.md，任务流程进 SKILL.md。** 加上动手题真写过一遍 description，你才算真会。

## 13. 还没回答的问题

为了不把本篇撑爆，几个进阶问题被推到后面或官方文档：

- **多个 Skill 命名冲突时怎么办？** → §6 命名空间已点；完整规则在 [12 篇 Plugin](/docs/claude-code/understanding/12-plugins) 的 namespace 章节
- **Skill 内部能不能 `@import` 引用其它 markdown？** → 行为类似 [03 篇 CLAUDE.md](/docs/claude-code/understanding/03-memory) 的 import；具体限制看 [Anthropic skills 文档](https://code.claude.com/docs/en/skills) 当前版本
- **Skill 能不能跨会话保留状态？** → 不能直接保留；要持久化用 [03 篇 Auto Memory](/docs/claude-code/understanding/03-memory) 或自己写文件
- **Skill 跟 Permissions / Hooks 怎么联动？** → Skill frontmatter `allowed-tools` 是 Permission 的预批准入口；Skill 触发后仍受 [10 篇 Hooks](/docs/claude-code/understanding/10-operation-control) 拦截
- **Skill 怎么版本化分发？** → 单仓 Skill 跟 git 走；要打包给团队 / 社区，看 [12 篇 Plugin](/docs/claude-code/understanding/12-plugins) 的 marketplace 章节
- **Skill description 实际匹配的算法是什么？** → 当前实现是模型基于 system prompt 决策（详见 §3），具体调权细节官方未公开

把这些标记出来不是写不全，是想让你知道：**Skill 是个完整子系统，但它依赖 CLAUDE.md / Permission / Hook / Plugin 这些相邻系统**。理解 Skill 的最大杠杆，是搞清楚它跟谁联动、谁负责什么——本篇 §9 对照表和 §11 决策树就是这层联动的入口。

<details id="glossary">
<summary><b>📖 本篇术语速查表</b></summary>

- **Skill**：技能，按需加载的任务流程文件。
- **`SKILL.md`**：Skill 入口文件，frontmatter + 正文流程。
- **frontmatter**：文件头元数据，`---` 包裹的配置字段。
- **description**：触发描述，Claude 判断是否加载 Skill 的主要依据。
- **supporting files**：支持文件，Skill 目录下按需读取的参考、模板、脚本。
- **slash command**：斜杠命令，用 `/skill-name` 手动调用 Skill 的入口。
- **`disable-model-invocation`**：禁止模型自动调用，让 Skill 只能由用户手动触发。
- **`user-invocable`**：用户是否可调用，设为 false 时可作为后台知识 Skill。
- **system prompt**：系统提示，启动时 Claude 看到的指令背景，Skill metadata 注册在这里。
- **forked subagent context**：分叉子代理上下文，Skill `context: fork` 时启动的隔离上下文（详见 [07 篇](/docs/claude-code/understanding/07-subagents)）。

</details>

## 官方资料

- [Extend Claude with skills](https://code.claude.com/docs/en/skills)
- [Commands](https://code.claude.com/docs/en/commands)

## 接下来去哪

<Cards>
  <Card title="07 · 派助手去干活" href="/docs/claude-code/understanding/07-subagents">
    Skill 解决"流程复用"。下一篇讲 SubAgents：怎么把长任务放到另一张桌子上，不污染主会话。
  </Card>
  <Card title="05 · AI 怎么决定想多深（上一篇）" href="/docs/claude-code/understanding/05-thinking-depth">
    复习 effort：很多 Skill 应该把思考深度写进 frontmatter，而不是每次临场手调。
  </Card>
  <Card title="03 · 怎么记住你的习惯" href="/docs/claude-code/understanding/03-memory">
    分不清 CLAUDE.md 和 SKILL.md 时，回到记忆篇：长期身份和规则进记忆，任务流程进 Skill。
  </Card>
</Cards>

如果你只记一个判断：**凡是你第三次复制粘贴给 Claude 的流程，就该考虑写成 Skill。**
