本教程聚焦 Claude Code Skill 最核心的架构原则——渐进式披露(Progressive Disclosure):让 SKILL.md 保持瘦小,把细节拆进参考文件、把大段逻辑沉淀为 bundled 脚本黑盒,按需加载、用完即弃,从而在保留强大能力的同时不撑爆上下文窗口。适合已经会创建基础 Skill、想让自己的 Skill 从"能跑"走向"能长期维护且不拖累每次会话"的开发者。
claude --version 可输出版本)SKILL.md,含 name/description 两个字段很多人误以为"Skill = SKILL.md 全部内容一次性塞进上下文"。事实并非如此。Skill 本质是一个目录,SKILL.md 只是入口,真正的运行时加载是分层的:
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 |
正文明确引用、且当前任务确实需要时 | 任意大,但不触发就不加载 |
官方
关键认知: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." 这句话就是渐进式披露的精髓。
新建目录 ~/.claude/skills/api-docs/,写入最小 SKILL.md:
---
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(可能几千行)只有在用户真的问端点时才会被读取。
在 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。多个文件之间互不连带。
endpoints.md 一次读完。
解决:① 文件别太大,超 ~1500 行就再拆;② 正文里写明"定位到对应章节后即可停止读取";③ 文件开头放紧凑的章节锚点目录。当任务涉及"遍历大量数据 / 复杂解析 / 调用外部 CLI"时,与其让 Claude 在上下文里逐行推理,不如写成一个脚本,让 Claude 只看它的用法和输出。
scripts/export-openapi.py 顶部写清楚 --help 自述:
#!/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 怎么调用、看什么输出,不要贴脚本源码:
## 批量导出 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 确认参数 → 执行 → 读输出行 → 完成。脚本内部成百上千行解析逻辑全程未进入上下文。
cat 脚本源码"研究一下"。
解决:这是渐进式披露最大的反模式。在 SKILL.md 用加粗明确写 "DO NOT read the source until you try running the script first"(沿用官方 webapp-testing 的措辞),并要求"先 --help"。cross-cutting.md,但你观察 Claude 是否会顺手把 endpoints.md 也读进来);/context(或观察工具调用序列)确认:未被引用的 references 文件没有出现在读取列表里。一个设计良好的分层 Skill,回答窄问题时只发生 1 次文件读取;只有当问题横跨多个主题时,才会按需追加读取。
/context 里看到 Skill 把整个 scripts/ 目录源码都拉了进来。
解决:检查 SKILL.md 是否误贴了脚本代码块;把代码块删掉,换成"运行 --help"的指令。背景:某团队有一个 migration-helper Skill,SKILL.md 长达 2300 行——把所有数据库迁移规则、各表 schema、回滚脚本全堆在一起。结果每次只要触发该 Skill,上下文立刻吃掉 ~8000 token,且很多会话其实只问"这一步要不要加索引"。
重构动作:
references/rules.md——命名、事务、零停机等硬规则;
- references/schemas/——每个业务域一个文件(users.md、orders.md…);
- references/rollback.md——回滚与灰度策略;scripts/diff-schema.py,正文只留 --help + 用法;--help,不要读源码。效果对比:
| 指标 | 重构前 | 重构后 |
|---|---|---|
| 触发即加载 token | ~8000 | ~1200(仅 SKILL.md) |
| 问窄问题额外读取 | 0(已全加载) | 1 个对应 references 文件 |
| 修改某表 schema 影响范围 | 全文搜 2300 行 | 改 schemas/该域.md 一处 |
这个案例说明:渐进式披露不仅是"省 token",更是把维护成本从"一个巨型文件"分散到"按主题切分的小文件",越改越轻松。
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: 完成并报告路径
/context 体积是否显著小于"未分层版本";② 是否正确使用 bundled script 黑盒(Claude 未读源码即完成任务);③ 参考文件是否按主题切分、有无重复漂移。pdf、webapp-testing 等是渐进式披露的最佳范本,重点看它们如何用一句"see REFERENCE.md"把细节路由出去name/description 的最低要求与命名约定name/description 最低 frontmatter 要求与模板说明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 等场景全覆盖。
