<!-- title: 渐进式披露：Skill分层加载 -->

## 概述

本教程聚焦 Claude Code Skill 最核心的架构原则——**渐进式披露（Progressive Disclosure）**：让 SKILL.md 保持瘦小，把细节拆进参考文件、把大段逻辑沉淀为 bundled 脚本黑盒，按需加载、用完即弃，从而在保留强大能力的同时不撑爆上下文窗口。适合已经会创建基础 Skill、想让自己的 Skill 从"能跑"走向"能长期维护且不拖累每次会话"的开发者。

## 前置条件

- 已安装 Claude Code 并能正常运行（`claude --version` 可输出版本）
- 写过至少一个最简 Skill：一个目录 + 一个 `SKILL.md`，含 `name`/`description` 两个字段
- 了解 YAML frontmatter 基本语法
- 本机有 Python 3 或 Node 可用于 bundled 脚本

---

## 一、先认清 Skill 的三层加载结构

很多人误以为"Skill = SKILL.md 全部内容一次性塞进上下文"。事实并非如此。Skill 本质是**一个目录**，SKILL.md 只是入口，真正的运行时加载是分层的：

```mermaid
flowchart TD
    A[用户一句话触发<br/>'name + description' 匹配] --> B[只加载 SKILL.md 正文]
    B --> C{正文里是否<br/>提到某个参考文件?}
    C -->|否| D[就地完成任务<br/>上下文最省]
    C -->|是| E[按需读取 references/REFERENCE.md]
    E --> F{是否需要<br/>复杂运算?}
    F -->|否| G[基于参考文件完成任务]
    F -->|是| H[执行 bundled script 黑盒<br/>只读 stdout 结果]
    H --> G
```

这三层可以理解为"菜单 → 食材库 → 厨房"：

| 层级 | 内容 | 何时进入上下文 | 典型体积 |
|------|------|----------------|----------|
| 元数据层 | `name` + `description`（frontmatter） | 用户每轮触发判定时 | 几十~一两百 token |
| 正文层 | SKILL.md 的 Markdown 指令、决策树、最小示例 | Skill 被触发时一次加载 | 控制在 ~500 行内 |
| 按需层 | `REFERENCE.md`、专题文件、`scripts/*.py` | 正文明确引用、且当前任务确实需要时 | 任意大，但**不触发就不加载** |

> 官方 `pdf` Skill 的正文只放最常用操作，末尾留一句："For advanced features… see REFERENCE.md. If you need to fill out a PDF form, read FORMS.md."——这就是把"按需层"显式路由出去的范本。

**关键认知**：bundled script 的源码**默认不进上下文**。官方 `webapp-testing` Skill 直白地写道：*"These scripts can be very large and thus pollute your context window. They exist to be called directly as black-box scripts rather than ingested into your context window."* 这句话就是渐进式披露的精髓。

---

## 二、步骤一：写一个克制的 SKILL.md（只放路由与决策树）

### 操作

新建目录 `~/.claude/skills/api-docs/`，写入最小 SKILL.md：

```markdown
---
name: api-docs
description: Use this skill when the user asks about the internal REST API,
  including endpoint signatures, request/response schemas, auth, and error
  codes. Also use when generating client SDK snippets or debugging a 4xx/5xx
  from our API. Triggers on phrases like "API 文档", "endpoint", "接口字段",
  "返回结构", "错误码".
---

# 内部 REST API 指南

## 先走决策树

\`\`\`
用户提问 → 是查某个具体端点吗?
  ├─ 是 → 读 references/endpoints.md，定位端点，按 schema 回答
  └─ 否 → 是问鉴权/错误码/限流这类横切问题?
      ├─ 是 → 读 references/cross-cutting.md
      └─ 否 → 是要生成客户端调用示例?
          └─ 是 → 读 references/sdk-patterns.md，套用模板
\`\`\`

## 速查表（高频，直接放这里，省一次跳转）

| 任务 | 去哪 |
|------|------|
| 某端点字段定义 | references/endpoints.md |
| 鉴权 / 限流 / 错误码 | references/cross-cutting.md |
| 生成 SDK 片段 | references/sdk-patterns.md |
| 批量导出全部端点为 OpenAPI | python scripts/export-openapi.py --help |

> 任何 references/ 文件未读到之前，不要凭记忆作答。
```

### 预期结果

触发该 Skill 后，进入上下文的只有上面这一两百行——而 `endpoints.md`（可能几千行）只有在用户真的问端点时才会被读取。

### 常见问题

- **问题**：description 写得太宽（如"回答一切 API 问题"），导致不相关场景也被触发、白白加载正文。
  **解决**：description 必须同时写清**做什么**和**何时触发**，并列举触发词，把边界划死。
- **问题**：把所有端点细节直接铺进 SKILL.md 正文。
  **解决**：正文只留"路由表 + 决策树 + 高频速查"，其余一律下沉。

---

## 三、步骤二：把细节拆进 references/ 参考文件

### 操作

在 skill 目录下建 `references/`，每个文件聚焦一个主题，命名即语义：

```
api-docs/
├── SKILL.md
├── references/
│   ├── endpoints.md      # 每个端点：method / path / 参数 / 响应 schema
│   ├── cross-cutting.md  # 鉴权、限流、错误码、分页约定
│   └── sdk-patterns.md   # curl / Python / JS 调用模板
└── scripts/
    └── export-openapi.py
```

参考文件内部仍可再用"分层"：`endpoints.md` 开头放目录索引，正文按端点分节。

### 预期结果

问"GET /users 支持哪些过滤参数"时，只会读 `endpoints.md` 中 `/users` 那一节；问"429 怎么回事"时，只会读 `cross-cutting.md`。多个文件之间互不连带。

### 常见问题

- **问题**：Claude 没有按需读，而是把整个 `endpoints.md` 一次读完。
  **解决**：① 文件别太大，超 ~1500 行就再拆；② 正文里写明"定位到对应章节后即可停止读取"；③ 文件开头放紧凑的章节锚点目录。
- **问题**：参考文件彼此重复。
  **解决**：横切概念（如错误码）只放一个地方，其他文件用"见 cross-cutting.md#error-codes"交叉引用，避免多处维护漂移。

---

## 四、步骤三：把大段逻辑沉淀为 bundled script 黑盒

当任务涉及"遍历大量数据 / 复杂解析 / 调用外部 CLI"时，与其让 Claude 在上下文里逐行推理，不如写成一个脚本，让 Claude 只看它的**用法和输出**。

### 操作

`scripts/export-openapi.py` 顶部写清楚 `--help` 自述：

```python
#!/usr/bin/env python3
"""
导出内部 API 为 OpenAPI 3.1 JSON。

用法:
  python scripts/export-openapi.py --out openapi.json
  python scripts/export-openapi.py --tag users --out users.json
  python scripts/export-openapi.py --help
"""
import argparse, json, sys

def build():
    # ... 从源数据生成 spec ...
    return {"openapi": "3.1.0", "paths": {}}  # 占位

def main():
    p = argparse.ArgumentParser(description="导出 OpenAPI 规范")
    p.add_argument("--out", required=True, help="输出文件路径")
    p.add_argument("--tag", help="只导出某标签下的端点")
    a = p.parse_args()
    spec = build()
    with open(a.out, "w", encoding="utf-8") as f:
        json.dump(spec, f, ensure_ascii=False, indent=2)
    print(f"已导出 {len(spec['paths'])} 个端点 → {a.out}")

if __name__ == "__main__":
    main()
```

在 SKILL.md 正文里，只告诉 Claude **怎么调用、看什么输出**，不要贴脚本源码：

```markdown
## 批量导出 OpenAPI

先看用法：`python scripts/export-openapi.py --help`
按标签导出：`python scripts/export-openapi.py --tag users --out users.json`
成功会打印 `已导出 N 个端点 → users.json`，只需读取该 stdout 即可，无需打开脚本源码。
```

### 预期结果

用户说"把我们 users 相关接口导出成 OpenAPI"，Claude 直接 `--help` 确认参数 → 执行 → 读输出行 → 完成。脚本内部成百上千行解析逻辑**全程未进入上下文**。

### 常见问题

- **问题**：Claude 想先 `cat` 脚本源码"研究一下"。
  **解决**：这是渐进式披露最大的反模式。在 SKILL.md 用加粗明确写 **"DO NOT read the source until you try running the script first"**（沿用官方 webapp-testing 的措辞），并要求"先 `--help`"。
- **问题**：脚本输出过于啰嗦，把日志全打到 stdout。
  **解决**：脚本只把**结论性数据**打到 stdout（一行可读摘要或结构化 JSON），冗长日志走 stderr，避免污染上下文。
- **问题**：脚本报错时 Claude 不知道怎么修。
  **解决**：脚本对常见错误给出人类可读的退出信息（如"缺少 OPENAPI_SRC 环境变量，请在 .env 配置"），而不是裸 traceback。

---

## 五、步骤四：守住上下文——验证"未触发即未加载"

### 操作（自检手法）

1. 在 SKILL.md 正文里，把每个按需文件的引用写成一目了然的清单；
2. 触发 Skill 后，刻意问一个**不需要**任何参考文件的问题（例如"你们 API 用哪个鉴权方案？"——答案应来自 `cross-cutting.md`，但你观察 Claude 是否会顺手把 `endpoints.md` 也读进来）；
3. 用 `/context`（或观察工具调用序列）确认：未被引用的 references 文件**没有**出现在读取列表里。

### 预期结果

一个设计良好的分层 Skill，回答窄问题时只发生 1 次文件读取；只有当问题横跨多个主题时，才会按需追加读取。

### 常见问题

- **问题**：`/context` 里看到 Skill 把整个 `scripts/` 目录源码都拉了进来。
  **解决**：检查 SKILL.md 是否误贴了脚本代码块；把代码块删掉，换成"运行 `--help`"的指令。
- **问题**：分层后 Claude 反而变"笨"，老是漏读必要文件。
  **解决**：决策树要覆盖全部入口分支，速查表要把"高频任务→文件"映射写全；宁可路由表长一点，也不要让 Claude 猜。

---

## 六、实战案例：把一个臃肿 Skill 重构为分层结构

**背景**：某团队有一个 `migration-helper` Skill，SKILL.md 长达 2300 行——把所有数据库迁移规则、各表 schema、回滚脚本全堆在一起。结果每次只要触发该 Skill，上下文立刻吃掉 ~8000 token，且很多会话其实只问"这一步要不要加索引"。

**重构动作**：

1. **抽骨架**：SKILL.md 只保留"迁移总原则 + 决策树 + 高频速查表"，压到约 180 行；
2. **拆参考**：
   - `references/rules.md`——命名、事务、零停机等硬规则；
   - `references/schemas/`——每个业务域一个文件（users.md、orders.md…）；
   - `references/rollback.md`——回滚与灰度策略；
3. **沉淀脚本**：把"比对线上与代码 schema 差异"这种重逻辑写进 `scripts/diff-schema.py`，正文只留 `--help` + 用法；
4. **加纪律声明**：正文顶部加粗 *未读取对应 references 文件前，不得凭记忆判断 schema*；以及 *脚本先 `--help`，不要读源码*。

**效果对比**：

| 指标 | 重构前 | 重构后 |
|------|--------|--------|
| 触发即加载 token | ~8000 | ~1200（仅 SKILL.md） |
| 问窄问题额外读取 | 0（已全加载） | 1 个对应 references 文件 |
| 修改某表 schema 影响范围 | 全文搜 2300 行 | 改 `schemas/该域.md` 一处 |

这个案例说明：渐进式披露不仅是"省 token"，更是**把维护成本从"一个巨型文件"分散到"按主题切分的小文件"**，越改越轻松。

---

## 七、Mermaid：一次完整会话的加载时序

```mermaid
sequenceDiagram
    participant U as 用户
    participant C as Claude
    participant FS as Skill 目录(文件系统)

    U->>C: "GET /users 支持哪些过滤参数?"
    Note over C: description 命中 → 加载 api-docs/SKILL.md
    C->>C: 读决策树 → 命中"查端点"
    C->>FS: 读取 references/endpoints.md(仅 /users 节)
    FS-->>C: 该端点字段定义
    C-->>U: 回答(全程未读 scripts/、未读 cross-cutting.md)

    U->>C: "把 users 接口导出成 OpenAPI"
    C->>FS: python scripts/export-openapi.py --tag users --out users.json
    FS-->>C: stdout: "已导出 12 个端点 → users.json"
    Note over C: 脚本源码未进入上下文
    C-->>U: 完成并报告路径
```

---

## 训练营适用性评估

- **适合训练营类型**：Claude Code 进阶开发营、AI 工程化 / Prompt 工程营、企业内部"Agent 能力建设"工作坊。要求学员已具备基础 Skill 编写经验，属于"Day 2 / Day 3"进阶课题。
- **预计教学时长**：讲授 45 分钟 + 动手重构 60 分钟 + 分组互测 30 分钟，合计约 2.5 小时。
- **考核点**：① 学员提交的 Skill 触发后 `/context` 体积是否显著小于"未分层版本"；② 是否正确使用 bundled script 黑盒（Claude 未读源码即完成任务）；③ 参考文件是否按主题切分、有无重复漂移。
- **延伸练习**：让学员把自己的某个 Skill 接入 Evals，对比"分层前 vs 分层后"在窄问题上的响应准确率与 token 消耗，把架构收益量化。

---

## 延伸阅读

- Anthropic 官方 Skills 仓库——`pdf`、`webapp-testing` 等是渐进式披露的最佳范本，重点看它们如何用一句"see REFERENCE.md"把细节路由出去
- Agent Skills 规范站点 agentskills.io——了解跨工具通用的 Skill 标准
- 官方支持文档《How to create custom skills》——frontmatter `name`/`description` 的最低要求与命名约定
- "Equipping agents for the real world with Agent Skills"（Anthropic Engineering 博客）——渐进式披露设计理念的原理解读
- 本系列前序：《三段式机制：从零吃透 Skill》讲触发机制，《Skill 脚本化：写 agent 友好的脚本》讲黑盒脚本细节，本文承接二者、聚焦"分层加载"这一架构主线

## 参考来源

1. [Anthropic Skills 官方仓库 README](https://github.com/anthropics/skills) — Skill 的目录结构、`name`/`description` 最低 frontmatter 要求与模板说明
2. [anthropics/skills · webapp-testing SKILL.md](https://github.com/anthropics/skills/blob/main/skills/webapp-testing/SKILL.md) — bundled script 作为"黑盒、勿读源码、先 --help"的官方范本，是本文核心引用
3. [anthropics/skills · pdf SKILL.md](https://github.com/anthropics/skills/blob/main/skills/pdf/SKILL.md) — 通过 "see REFERENCE.md / FORMS.md" 演示参考文件按需路由的官方写法
4. [Claude Code Skill Frontmatter: Every YAML Option Explained](https://allahabadi.dev/blogs/ai/claude-code-skills-frontmatter-complete-guide/) — frontmatter 各字段（name、description 及可选字段）的逐项释义与示例
5. [How to create custom skills（Anthropic 支持）](https://support.claude.com/en/articles/12512198-creating-custom-skills) — 创建自定义 Skill 的官方步骤与命名规范

---

*51学通信原创制作 · 站长爱卫生（微信：gprshome201101）*

> [!tip] 51学通信知识星球
> **星主：爱卫生**（微信：gprshome201101）——公众号「51学通信」创办者，19 年通信行业运维与讲师经验，著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书（各大电商平台有售）。
> **星球定位**：深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域，主打运维岗位与信令分析。
> **当前规模**：5200 篇主题 / 990 个回答，198 元/年。
> **加入福利**：加入星球后请加爱卫生微信，拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题，另有群内分享。
> **AI 加持**：51学通信知识星球已作为 skill 接入 WorkBuddy，使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例，做总结、找文档和 PPT 等场景全覆盖。

![51学通信知识星球海报-扫码加入](https://wiki.500nian.com/images/zhixingqiu_qrcode.png)
