<!-- title: 给Skill挂脚本：造API文档生成器 -->

# 给 Skill 挂脚本：从零造一个 API 文档自动生成器

## 概述

本教程手把手带你从零创建一个**自带可执行 Python 脚本**的 Claude Code Skill。我们以一个真实场景为例：把 OpenAPI/Swagger 规范文件自动转成团队风格的 Markdown 接口文档。你将掌握 SKILL.md 的完整写法、`scripts/` 与 `references/` 的渐进披露用法、frontmatter 触发词调优，以及"技能装好了却不触发"的系统化排错闭环。适合已经跑通过 Claude Code、想让 AI 真正接管重复性文档工作的工程师。

## 前置条件

- 已安装 Claude Code（v1.0 及以上，内置 Skill 支持）
- 本机能跑 `python3`（脚本依赖仅用标准库，无需额外安装）
- 手头有一份 OpenAPI 3.x 的 JSON/YAML 规范文件用于测试
- 大致了解 YAML 与 Markdown 语法

## 第一步：先想清楚"三层渐进披露"

在动手之前，必须先理解 Skill 为什么是**目录**而不是**一条提示词**。这是它和 CLAUDE.md 规则最根本的区别。

Skill 采用三层渐进披露（progressive disclosure）架构：

```mermaid
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 目录骨架

Skill 的存放位置决定了它的作用域：

| 位置 | 作用域 | 适用场景 |
|------|--------|----------|
| `~/.claude/skills/<name>/` | 个人全局，跨所有项目 | 你自己常用、不想共享 |
| `.claude/skills/<name>/` | 项目级，随 git 共享 | 团队统一规范 |
| 插件包内 `skills/` | 随插件分发 | 发布给外部用户 |

本教程我们放在**项目级**，方便后续提交到 git 给团队复用。在项目根目录执行：

```bash
# 创建目录骨架
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 的 frontmatter

`SKILL.md` 由两部分组成：顶部的 YAML frontmatter（用 `---` 包裹）和下面的 Markdown 正文。先写 frontmatter：

```yaml
---
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 校验器过一遍。

## 第四步：打磨 description 的触发词

描述写得不好，Skill 装了也永远不会被调用——这是生产环境里**最高频的故障**。判断一个 description 好不好的简单标准：

❌ 坏例子：`description: 处理 API 文档`（太模糊，没有"何时用"）

✅ 好例子：`把 OpenAPI/Swagger 规范文件转成中文 Markdown 接口文档。当用户提到……时使用。`

好描述的公式是 **做什么 + 包含哪些关键词 + 何时触发**。关键词要用用户真实的口语，而不是你的内部术语。如果团队里同事口头都叫它"Swagger 文件"，那描述里就该出现"Swagger"，而不是只写"OpenAPI 规范"。

调试技巧：写完描述后，先用 3–5 句不同的人话去"撩"它，看它是否被触发：

```
"帮我把这份 swagger 整理成文档"   ← 应触发
"这个 API 怎么调用，给我整理下"    ← 边界情况
"重命名这个变量"                  ← 不应触发
```

## 第五步：把确定性逻辑塞进 scripts/

把 OpenAPI 转成 Markdown 这件事，**用 Python 脚本跑远比让大模型"生成"更可靠**——脚本 1000 个接口也能稳定输出，而模型逐个生成既费 token 又容易漏。把核心转换逻辑写成一个独立脚本，放在 `scripts/gen_doc.py`：

```python
#!/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）：

```bash
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`。

## 第六步：在 SKILL.md 正文里指挥 Claude 调脚本

frontmatter 写完后，下面就是给 Claude 的操作指令正文。这是**第1层**内容，要写成祈使句，清晰可执行：

```markdown
---
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层"代码不进上下文，只进输出"的优势。

## 第七步：把大块参考资料放进 references/

团队可能有几十条文档风格细则（标题层级、错误码表格式、鉴权段落写法……）。这些不该塞进 SKILL.md，否则每次触发都白白加载。把它们拆成独立文件，在正文里**按需引用**：

`references/doc-style-guide.md`（节选）：

```markdown
# 团队接口文档风格规范

## 标题层级
- 一级标题用接口名，如 `# 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 后必须重启：

```bash
# 退出当前会话后重新进入项目目录
claude
```

进入后先确认 Skill 被识别：

```
/skills
```

应能看到 `openapi-doc-gen` 出现在列表里。然后用一句自然语言触发：

```
帮我把 petstore.openapi.json 转成接口文档
```

观察 Claude 的思考过程会显示它读取了 Skill（类似 "Reading openapi-doc-gen..."），然后调用 Bash 执行脚本，最后写出 `API_DOC.md`。

## 第九步：不触发时的系统化排错

如果 `/skills` 里**看不到**这个 Skill，按顺序排查：

1. **YAML 是否合法**——三个 `---`、缩进、引号。YAML 解析失败会静默跳过。
2. **目录名是否等于 name 字段**——`openapi-doc-gen` ≠ `OpenApiDocGen`。
3. **是否重启了 Claude Code**——不重启扫描不到。
4. **name 是否含保留词或非法字符**——不能有 `claude`/`anthropic`、不能大写。
5. **SKILL.md 是否真在该目录根下**——别放成 `skills/openapi-doc-gen/nested/SKILL.md`。

如果 Skill 出现了**却不被触发**，问题几乎都在 description：

1. 描述是否同时包含"做什么"和"何时用"。
2. 触发词是否匹配用户真实口语（用"Swagger"而非只有"OpenAPI"）。
3. 试探性显式触发验证逻辑本身没问题：`用 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 升级为可分发插件

当 Skill 在团队内验证成熟，可以进一步打包成 Plugin 发布到团队市场。关键是把 `.claude/skills/openapi-doc-gen/` 迁移到插件包的 `skills/` 目录，配一个 `plugin.json` 清单，再通过 `/plugin` 安装。这样别人一行命令就能用上你封装好的文档生成能力，连脚本和参考规范一起带走。

```mermaid
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。
- **脚本优先**：凡是能用代码确定性完成的事，别让模型去"生成"，省 token 又更可靠。
- **描述为王**：花在 description 上的时间，回报远高于写更多正文。
- **小步迭代**：先跑通最小 Skill，再加脚本，再补 references，每步独立验证。

## 延伸阅读

- [Claude Code 官方 Skills 文档](https://code.claude.com/docs/en/skills) — 创建、管理与共享 Skills 的官方权威说明
- [Anthropic 官方 Skills 教程（含 references 用法）](https://claude.com/docs/skills/how-to) — 含 `references/` 目录的官方指引
- [Claude Code Skills 完整技术深度解析](https://github.com/gregpriday/claude-code-docs/blob/main/claude-code-skills.md) — 渐进披露三层架构的源码级解读
- [Claude Code Skill Frontmatter：每个 YAML 字段详解](https://allahabadi.dev/blogs/ai/claude-code-skills-frontmatter-complete-guide/) — 15 个 frontmatter 字段逐项说明

## 训练营适用性评估

- **适合训练营类型**：企业内部 AI 编程训练营、研发效能提升工作坊、平台工程团队赋能培训。偏中高级，要求学员已跑通 Claude Code 基础。
- **不适合**：纯零基础入门营（建议先完成 Round 1 安装与首跑）、纯产品/非技术受众。
- **预计教学时长**：理论讲解 30 分钟 + 动手实操 60 分钟 + 触发调优与排错 30 分钟，合计约 **2 小时**。若要求学员现场自带一份 OpenAPI 规范完成端到端转换并提交 Skill 到 git，可扩展为 **半天（3.5 小时）** 工作坊。
- **评估产出**：每位学员产出一个可触发、含至少一个脚本与一份 references 的完整 Skill，并通过 3 句自然语言触发测试。

## 参考来源

1. [Extend Claude with skills — Claude Code Docs](https://code.claude.com/docs/en/skills) — Skills 创建、管理与共享的官方文档
2. [Creating custom skills — Claude.ai Documentation](https://claude.com/docs/skills/how-to) — 官方对 `references/` 资源目录与渐进披露的说明
3. [Claude Code Skills: Complete Technical Deep-Dive (gregpriday/claude-code-docs)](https://github.com/gregpriday/claude-code-docs/blob/main/claude-code-skills.md) — 三层加载机制、frontmatter 校验规则与排错清单的技术深读
4. [Claude Code Skill Frontmatter: Every YAML Option Explained](https://allahabadi.dev/blogs/ai/claude-code-skills-frontmatter-complete-guide/) — name/description/allowed-tools/metadata 等字段的逐项示例与坑点
5. [Inside Claude Code Skills: Structure, prompts, invocation (mikhail.io)](https://mikhail.io/2025/10/claude-code-skills/) — Skill 目录结构、调用机制与多 Skill 组合的工程实践

> [!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)
