本教程手把手带你从零创建一个自带可执行 Python 脚本的 Claude Code Skill。我们以一个真实场景为例:把 OpenAPI/Swagger 规范文件自动转成团队风格的 Markdown 接口文档。你将掌握 SKILL.md 的完整写法、scripts/ 与 references/ 的渐进披露用法、frontmatter 触发词调优,以及"技能装好了却不触发"的系统化排错闭环。适合已经跑通过 Claude Code、想让 AI 真正接管重复性文档工作的工程师。
python3(脚本依赖仅用标准库,无需额外安装)在动手之前,必须先理解 Skill 为什么是目录而不是一条提示词。这是它和 CLAUDE.md 规则最根本的区别。
Skill 采用三层渐进披露(progressive disclosure)架构:
flowchart LR
A["会话启动<br/>扫描所有 SKILL.md"] --> B["第0层<br/>只读 name+description<br/>约30-50 tokens/个"]
B --> C{"语义匹配<br/>用户意图 vs 描述"}
C -->|命中| D["第1层<br/>加载 SKILL.md 正文<br/>通常<5000 tokens"]
C -->|未命中| E["保持休眠<br/>零开销"]
D --> F["第2层<br/>按需读取 references/<br/>执行 scripts/<br/>脚本代码不进上下文"]
这里有一个关键设计哲学:会话启动时,Claude 只加载每个 Skill 的 name 和 description,大约 30–50 tokens。哪怕你装了 100 个 Skill,也只多花 5000 tokens 左右。只有当 Claude 判断某个 Skill 与当前请求相关时,才会把它的正文加载进来;而 scripts/ 里的可执行代码永远不会进入上下文——只有脚本的输出结果会。这意味着你可以挂一个 1000 行的 Python 脚本,最终却只占几个 token 的输出位。
理解了这一点,你就明白为什么要把"做什么"写进描述、把"怎么做"写进正文、把"大块参考资料"塞进 references/、把"确定性计算"丢给 scripts/。
Skill 的存放位置决定了它的作用域:
| 位置 | 作用域 | 适用场景 |
|---|---|---|
~/.claude/skills/<name>/ |
个人全局,跨所有项目 | 你自己常用、不想共享 |
.claude/skills/<name>/ |
项目级,随 git 共享 | 团队统一规范 |
插件包内 skills/ |
随插件分发 | 发布给外部用户 |
本教程我们放在项目级,方便后续提交到 git 给团队复用。在项目根目录执行:
# 创建目录骨架
mkdir -p .claude/skills/openapi-doc-gen/scripts
mkdir -p .claude/skills/openapi-doc-gen/references
# 查看结构
tree .claude/skills/openapi-doc-gen
预期输出:
.claude/skills/openapi-doc-gen
├── SKILL.md ← 必需:核心指令
├── scripts/ ← 可选:可执行脚本
└── references/ ← 可选:按需加载的参考资料
常见问题:Skill 名字只能用小写字母、数字和连字符,最长 64 字符,且不能包含
anthropic、claude等保留词。目录名要和SKILL.md里的name字段完全一致,否则会"静默加载失败"——Skill 不报错也不出现,这是新手最容易踩的坑。
SKILL.md 由两部分组成:顶部的 YAML frontmatter(用 --- 包裹)和下面的 Markdown 正文。先写 frontmatter:
---
name: openapi-doc-gen
description: 把 OpenAPI/Swagger 规范文件(.json/.yaml)转成团队风格的中文 Markdown 接口文档。当用户提到"生成接口文档""转 OpenAPI""Swagger 转文档""整理 API 文档"或需要把 .openapi.json 输出成可读文档时使用此技能。
allowed-tools:
- Bash
- Read
- Write
- Edit
metadata:
version: "1.0.0"
author: "platform-team"
---
这里每个字段都有讲究:
name:和目录名一致,全局唯一。description:整个 Skill 最重要的字段,没有之一。它同时承担"做什么"和"何时用"两个职责,必须包含用户真实会说的触发词。注意我刻意写了一串具体的说法("生成接口文档""Swagger 转文档"),而不是泛泛的"处理 API 文件"。allowed-tools(Claude Code 专有):限制这个 Skill 只能调用哪些工具。这个生成器需要跑脚本(Bash)、读规范(Read)、写文档(Write/Edit),所以列这四个。这是纵深防御,不是唯一安全手段——但能避免 Skill 越权干别的事。metadata:任意键值对,用于扩展信息,运行时不强依赖。常见问题:YAML 写错会导致整个 Skill 静默失效。三个连字符
---必须出现两次(YAML 块前后各一次)。如果你不确定 YAML 是否合法,可以先用在线 YAML 校验器过一遍。
描述写得不好,Skill 装了也永远不会被调用——这是生产环境里最高频的故障。判断一个 description 好不好的简单标准:
❌ 坏例子:description: 处理 API 文档(太模糊,没有"何时用")
✅ 好例子:把 OpenAPI/Swagger 规范文件转成中文 Markdown 接口文档。当用户提到……时使用。
好描述的公式是 做什么 + 包含哪些关键词 + 何时触发。关键词要用用户真实的口语,而不是你的内部术语。如果团队里同事口头都叫它"Swagger 文件",那描述里就该出现"Swagger",而不是只写"OpenAPI 规范"。
调试技巧:写完描述后,先用 3–5 句不同的人话去"撩"它,看它是否被触发:
"帮我把这份 swagger 整理成文档" ← 应触发
"这个 API 怎么调用,给我整理下" ← 边界情况
"重命名这个变量" ← 不应触发
把 OpenAPI 转成 Markdown 这件事,用 Python 脚本跑远比让大模型"生成"更可靠——脚本 1000 个接口也能稳定输出,而模型逐个生成既费 token 又容易漏。把核心转换逻辑写成一个独立脚本,放在 scripts/gen_doc.py:
#!/usr/bin/env python3
"""openapi-doc-gen: Convert an OpenAPI 3.x spec to team-style Markdown.
Usage: python3 gen_doc.py <input.openapi.json|yaml> [output.md]
Only uses Python standard library — no pip install needed.
"""
import json, sys, os
def load_spec(path):
with open(path, encoding="utf-8") as f:
text = f.read()
if path.endswith((".yaml", ".yml")):
# yaml is not stdlib; do a tiny fallback if PyYAML missing
try:
import yaml
return yaml.safe_load(text)
except ImportError:
raise SystemExit("ERROR: PyYAML not installed. Convert to JSON first.")
return json.loads(text)
def render(spec):
info = spec.get("info", {})
out = [f"# {info.get('title','API')}", ""]
out.append(f"> 版本:{info.get('version','—')} ")
if info.get("description"):
out.append(f"> {info['description']}")
out.append("\n## 接口列表\n")
paths = spec.get("paths", {})
for path, methods in paths.items():
for method, op in methods.items():
if method.lower() not in ("get","post","put","delete","patch"):
continue
tag = (op.get("tags") or ["默认"])[0]
summary = op.get("summary", op.get("operationId","未命名"))
out.append(f"### `{method.upper()} {path}`")
out.append(f"- 标签:{tag}")
out.append(f"- 说明:{summary}")
params = op.get("parameters") or []
if params:
out.append("- 参数:")
for p in params:
out.append(f" - `{p['name']}` ({p.get('in','?')}, "
f"{'必填' if p.get('required') else '可选'}): "
f"{p.get('description','')}")
out.append("")
return "\n".join(out)
def main():
if len(sys.argv) < 2:
print(__doc__); sys.exit(1)
spec = load_spec(sys.argv[1])
md = render(spec)
out_path = sys.argv[2] if len(sys.argv) > 2 else "API_DOC.md"
with open(out_path, "w", encoding="utf-8") as f:
f.write(md)
print(f"OK -> {out_path} ({len(md)} chars)")
if __name__ == "__main__":
main()
赋予可执行权限并独立测试(确认脚本本身没问题,再交给 Skill):
chmod +x .claude/skills/openapi-doc-gen/scripts/gen_doc.py
# 准备一份测试用规范
cat > /tmp/petstore.openapi.json <<'EOF'
{"openapi":"3.0.0","info":{"title":"Pet Store","version":"1.0.0"},
"paths":{"/pets":{"get":{"tags":["宠物"],"summary":"列出所有宠物",
"parameters":[{"name":"limit","in":"query","required":false,"description":"返回数量"}]},
"post":{"summary":"新增宠物"}}}}
EOF
python3 .claude/skills/openapi-doc-gen/scripts/gen_doc.py /tmp/petstore.openapi.json /tmp/out.md
cat /tmp/out.md
预期输出:
OK -> /tmp/out.md (286 chars)
# Pet Store
> 版本:1.0.0
## 接口列表
### `GET /pets`
- 标签:宠物
- 说明:列出所有宠物
- 参数:
- `limit` (query, 可选): 返回数量
### `POST /pets`
- 标签:默认
- 说明:新增宠物
设计要点:脚本刻意只用标准库(除可选 PyYAML),这样在任何干净环境都能跑。Skill 在沙箱里默认无网络,所以不要假设能
pip install。
frontmatter 写完后,下面就是给 Claude 的操作指令正文。这是第1层内容,要写成祈使句,清晰可执行:
---
name: openapi-doc-gen
description: 把 OpenAPI/Swagger 规范文件(.json/.yaml)转成团队风格的中文 Markdown 接口文档。当用户提到"生成接口文档""转 OpenAPI""Swagger 转文档""整理 API 文档"或需要把 .openapi.json 输出成可读文档时使用此技能。
allowed-tools: [Bash, Read, Write, Edit]
metadata: {version: "1.0.0", author: "platform-team"}
---
# OpenAPI 接口文档生成
## 何时使用
当用户需要把 OpenAPI 3.x 规范(.json / .yaml / .yml,常含 "openapi" 或 "swagger" 字段)
转成可读 Markdown 文档时使用本技能。
## 操作步骤
1. 先用 Read 工具读取规范文件,确认是 OpenAPI 3.x(顶层有 `openapi` 或 `swagger` 字段)。
- 若是更老的 Swagger 2.0,先告知用户并参考 references/swagger2-migration.md 做升级。
2. 用 Bash 执行本技能自带的转换脚本:
`python3 scripts/gen_doc.py <规范文件路径> <输出.md>`
脚本路径相对于本技能目录。脚本只用标准库,无需安装依赖。
3. 用 Read 检查生成结果。若用户要求"分组按 tag 排序""加示例""加鉴权说明"等增强,
读取 references/doc-style-guide.md 获取团队风格规范,再二次加工。
4. 用 Write 写出最终文档,默认文件名 API_DOC.md,除非用户指定。
## 边界
- 规范里含 `$ref` 外部引用时,先提示用户需先内联(bundle),脚本不自动解析外部 ref。
- 单个文件超过 500 个接口时,分章节写入,避免单文件过大。
注意正文里没有把整个 Python 源码贴进来——那会白白占上下文。我们只告诉 Claude"去跑 scripts/gen_doc.py",这正体现了第2层"代码不进上下文,只进输出"的优势。
团队可能有几十条文档风格细则(标题层级、错误码表格式、鉴权段落写法……)。这些不该塞进 SKILL.md,否则每次触发都白白加载。把它们拆成独立文件,在正文里按需引用:
references/doc-style-guide.md(节选):
# 团队接口文档风格规范
## 标题层级
- 一级标题用接口名,如 `# Pet Store API`
- 每个接口用三级标题 `### METHOD path`
- 字段表格用 Markdown 表格,列:参数名 | 位置 | 类型 | 是否必填 | 说明
## 必须包含的段落
每个接口需含:用途说明、请求参数表、响应示例、错误码映射。
鉴权接口在标题后加 `🔒 需鉴权` 标记。
## 错误码表
| code | 含义 | 处理建议 |
|------|------|----------|
| 400 | 参数错误 | 检查必填项 |
| 401 | 未授权 | 刷新 token |
references/swagger2-migration.md 存放老版本升级说明。
这样设计的好处:90% 的请求只会跑脚本(加载 SKILL.md + 执行脚本),只有用户要"按团队风格增强"时才会读取 references/doc-style-guide.md。渐进披露的本质就是:用不到的东西,一个 token 都不花。
Skill 的发现扫描只在 Claude Code 启动时发生一次。新增或修改 Skill 后必须重启:
# 退出当前会话后重新进入项目目录
claude
进入后先确认 Skill 被识别:
/skills
应能看到 openapi-doc-gen 出现在列表里。然后用一句自然语言触发:
帮我把 petstore.openapi.json 转成接口文档
观察 Claude 的思考过程会显示它读取了 Skill(类似 "Reading openapi-doc-gen..."),然后调用 Bash 执行脚本,最后写出 API_DOC.md。
如果 /skills 里看不到这个 Skill,按顺序排查:
---、缩进、引号。YAML 解析失败会静默跳过。openapi-doc-gen ≠ OpenApiDocGen。claude/anthropic、不能大写。skills/openapi-doc-gen/nested/SKILL.md。如果 Skill 出现了却不被触发,问题几乎都在 description:
用 openapi-doc-gen 技能把这份规范转成文档。显式能触发、自然语言不能,就是描述词不够准。最终的 Skill 完整结构:
.claude/skills/openapi-doc-gen/
├── SKILL.md
├── scripts/
│ └── gen_doc.py
└── references/
├── doc-style-guide.md
└── swagger2-migration.md
端到端演示一次复杂场景:用户丢来一份 30 个接口的真实规范,并要求"按团队风格加错误码表"。
用户:把 api/v1/openapi.json 转成接口文档,按咱们团队风格加上错误码说明
Claude(思考):识别到"转成接口文档""团队风格"→ 触发 openapi-doc-gen
1. Read api/v1/openapi.json → 确认 OpenAPI 3.0
2. Bash: python3 scripts/gen_doc.py api/v1/openapi.json API_DOC.md
→ OK -> API_DOC.md (8421 chars)
3. 因为要求"团队风格",Read references/doc-style-guide.md
4. Edit API_DOC.md:补错误码表、加 🔒 鉴权标记、统一标题层级
5. Write 完成,输出 API_DOC.md
整个过程,Python 脚本的 60 行源码一行都没进上下文,团队风格规范只在第 3 步被需要时才加载。这就是渐进披露 + 脚本封装的威力:能力很强,上下文很省。
当 Skill 在团队内验证成熟,可以进一步打包成 Plugin 发布到团队市场。关键是把 .claude/skills/openapi-doc-gen/ 迁移到插件包的 skills/ 目录,配一个 plugin.json 清单,再通过 /plugin 安装。这样别人一行命令就能用上你封装好的文档生成能力,连脚本和参考规范一起带走。
flowchart TD
S[".claude/skills/<br/>本地验证"] -->|"成熟后"| P["plugin/skills/<br/>打包"]
P --> M["插件市场<br/>/plugin install"]
M --> T["团队成员<br/>一键复用"]
S -.->|"日常迭代"| S
scripts/;大段规范 → references/;触发与指挥 → SKILL.md;存在感 → frontmatter。references/ 目录的官方指引references/ 资源目录与渐进披露的说明[!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 等场景全覆盖。
