首页 › tutorials › 造接口契约Skill:扫路由秒出文档

概述

本教程带你从零亲手打造一个名为 api-contract-doc 的 Claude Code Skill——它能扫描项目里的路由/控制器代码,自动反向工程出一份符合 OpenAPI 3.0 规范的接口契约文档。通过这个实战案例,你将掌握 Skill 的底层机制(目录结构、SKILL.md、渐进式披露、脚本挂载)、skill-creator 加速器、frontmatter 的触发调优,以及调试发布全流程。适合已跑通 Claude Code、想让 AI 按团队规范重复执行复杂任务的后端开发者与工程效率工程师。

前置条件

Step 1:先把 Skill 的本质看透

很多人把 Skill 想得很神秘,其实它就是一个带约定结构的文件夹。Claude Code 启动时会把三个位置的 Skill 注册进会话:

级别 路径 适用场景
个人级 ~/.claude/skills/<name>/SKILL.md 跨项目复用,比如你的 commit 规范
项目级 <project>/.claude/skills/<name>/SKILL.md 团队共享,随仓库走
插件级 通过 plugin marketplace 安装 第三方能力

一个 Skill 的标准目录长这样:

api-contract-doc/
├── SKILL.md              # 唯一必需:入口文件
├── scripts/              # 可选:可执行脚本
│   └── extract-endpoints.js
└── reference/            # 可选:按需加载的详细资料
    ├── openapi-conventions.md
    └── team-api-style.md

核心机制是"渐进式披露"(Progressive Disclosure):Claude 启动时只读取每个 Skill 的 SKILL.md(理想控制在 500 行以内),把 token 成本压到最低;只有当判断出某个 Skill 与当前任务相关、且需要更深细节时,才会去 Read reference 子目录里的文件。这就是为什么你能塞进几千行规范文档而不爆上下文。

flowchart TD
    A[Claude Code 启动] --> B[扫描三个位置的 skills 目录]
    B --> C[只读每个 SKILL.md 的<br/>frontmatter + 正文摘要]
    C --> D{当前任务是否匹配某 Skill?}
    D -->|否| E[跳过该 Skill 不占上下文]
    D -->|是| F[加载完整 SKILL.md 正文]
    F --> G{需要更深细节?}
    G -->|是| H[按需 Read reference 文件]
    G -->|需要执行确定性操作| I[Bash 调用 scripts 脚本]
    H --> J[按规范产出结果]
    I --> J

Step 2:用 skill-creator 一键生成骨架(对比手工)

Claude Code 自带一个 skill-creator 官方 Skill,专门帮你生产新 Skill。两种起步方式:

方式 A:加速器(推荐新手)

直接在会话里说:

/skill-creator 帮我创建一个叫 api-contract-doc 的 skill,
功能是扫描项目路由代码生成 OpenAPI 3.0 接口文档

skill-creator 会问你几个澄清问题,然后自动生成目录结构、frontmatter 草稿和正文框架。它的价值在于保证文件结构、字段名符合规范,免去查文档。

方式 B:纯手工(本教程采用,便于讲透每一处)

mkdir -p ~/.claude/skills/api-contract-doc/scripts
mkdir -p ~/.claude/skills/api-contract-doc/reference
touch ~/.claude/skills/api-contract-doc/SKILL.md

手工的好处是你完全掌控每一行。下面的步骤都基于手工方式推进。

Step 3:写 frontmatter——Skill 的"命门"

SKILL.md 顶部是一段 YAML frontmatter,用 --- 包裹。其中只有 description 决定 Skill 会不会被触发,这是新手最容易踩的坑。

---
name: api-contract-doc
description: 扫描项目路由或控制器源码,反向工程生成符合 OpenAPI 3.0 规范的接口契约文档(YAML/JSON)。当用户要"生成接口文档""出 API 文档""补接口说明""把代码转成 OpenAPI/Swagger""整理接口契约"时使用。支持 Express/Koa/Fastify 风格路由。
---

编写 description 的三条铁律:

  1. 第一句写"做什么":让 Claude 在做意图匹配时有清晰锚点。
  2. 铺满触发关键词:把用户真实会说出口的词都塞进去——"生成接口文档""API 文档""OpenAPI""Swagger""接口契约"。Claude 靠语义相似度判断,关键词越准召回越高。
  3. 点明适用范围:列出支持的框架(Express/Koa/Fastify),既帮召回,也防止跨语言误触发。

常见问题:Skill 写好却从不自动触发?99% 是 description 没写出用户嘴里的词。改 description,不需要重启,下一次会话即生效。

可选字段还有 allowed-tools(限制该 Skill 只能用哪些工具,如 Read, Bash)、version。本案例暂不限制,让 Claude 自由组合工具。

Step 4:写正文——把"专家心法"写成可执行流程

frontmatter 负责"何时用",正文负责"怎么用"。正文要写得像给一个聪明但不了解你项目的新同事的操作手册:明确步骤、明确分支、明确产出格式。

# 接口契约文档生成

## 何时使用
当用户希望从现有路由/控制器代码反向生成接口文档时激活本 Skill。

## 工作流程

1. **定位路由**:先用 Grep/Glob 找到项目的路由定义文件。
   - Express:搜索 `app\.(get|post|put|delete|patch)\(` 或 `router\.(get|post)`
   - Koa:搜索 `router\.(get|post)` 或 `\.ctx\.`
   - 若用户指定了目录,优先扫该目录。

2. **粗扫出端点清单**:调用本 Skill 的脚本生成机器可读草稿:
   ```bash
   node ~/.claude/skills/api-contract-doc/scripts/extract-endpoints.js <路由文件或目录>
   ```
   脚本会输出 JSON 数组,每项含 method、path、handlerName。

3. **补全语义信息**:脚本只能给出骨架,请求/响应 schema、描述、示例需要你阅读
   handler 函数体与相关 model 定义后补全。

4. **套用团队规范**:补全时务必遵循 reference/team-api-style.md 的命名与错误码约定;
   OpenAPI 字段写法遵循 reference/openapi-conventions.md。

5. **产出**:默认输出 openapi.yaml 到项目根目录,并打印一段摘要给用户确认。

## 注意
- 路径参数(如 /users/:id)要转成 OpenAPI 的 {id} 写法。
- 遇到无法静态推断的字段,标注 TODO 而非编造。

注意第 2 步和第 4 步:把"机械活"交给脚本,把"判断活"留给模型。这是高质量 Skill 的设计哲学——确定性的事情用代码保证一致,需要语义理解的事情交给 LLM。

Step 5:用 reference/ 实现"按需深潜"

把几百行的 OpenAPI 规范要点和团队接口规范塞进 SKILL.md 会撑爆日常上下文。把它们拆到 reference 子目录,正文只在用到时点名引用。

reference/openapi-conventions.md(节选):

# OpenAPI 3.0 字段约定

- paths 下每个操作必填:summary、operationId、responses
- operationId 用 camelCase,全局唯一
- 响应体统一走 components/schemas 复用,避免内联
- 错误响应统一引用 ProblemDetails(RFC 7807)结构
- 字段 description 必填,用中文,一句话讲清用途

reference/team-api-style.md(节选):

# 团队接口风格

- URL 一律小写,单词用连字符分隔:/user-profiles 而非 /userProfiles
- 列表接口必须支持 page、pageSize 分页参数
- 时间字段用 ISO 8601 字符串,字段名以 At 结尾:createdAt、updatedAt
- 业务错误码 5 位数字,前两位标识模块

为什么这样组织?因为这两份资料只在"补全语义信息"那一步才需要。Claude 平时根本不读它们,只有在执行该 Skill 且走到第 4 步时,才会按正文指引 Read 这两个文件。一个 Skill 配 2000 行资料,日常启动成本几乎为零。

Step 6:用 scripts/ 挂载确定性脚本

这是本 Skill 区别于"纯提示词 Skill"的关键。脚本用 Node 写,扫描 Express 路由生成端点骨架。

scripts/extract-endpoints.js(完整可运行):

#!/usr/bin/env node
// 从 Express/Koa 路由文件中静态提取端点骨架,输出 JSON
const fs = require('fs');
const path = require('path');

const target = process.argv[2];
if (!target) {
  console.error('用法: extract-endpoints.js <文件或目录>');
  process.exit(1);
}

const files = [];
function collect(p) {
  const stat = fs.statSync(p);
  if (stat.isFile() && /\.(js|ts)$/.test(p)) files.push(p);
  else if (stat.isDirectory()) {
    for (const f of fs.readdirSync(p)) collect(path.join(p, f));
  }
}
collect(path.resolve(target));

// 匹配 app.get('/x', ...) / router.post('/y', ...)
const routeRe = /\.(get|post|put|delete|patch)\(\s*['"`]([^'"`]+)['"`]/g;
const paramRe = /:([A-Za-z_]\w*)/g;

const endpoints = [];
for (const file of files) {
  const src = fs.readFileSync(file, 'utf8');
  let m;
  while ((m = routeRe.exec(src)) !== null) {
    const method = m[1].toUpperCase();
    const rawPath = m[2];
    // Express :id -> OpenAPI {id}
    const openapiPath = rawPath.replace(paramRe, '{$1}');
    endpoints.push({
      method,
      path: openapiPath,
      sourceFile: path.relative(process.cwd(), file),
      handlerName: null, // 由模型阅读函数体补全
    });
  }
}

console.log(JSON.stringify({ count: endpoints.length, endpoints }, null, 2));

赋可执行权限:

chmod +x ~/.claude/skills/api-contract-doc/scripts/extract-endpoints.js

预期结果:对一个含 router.get('/users/:id', getUser) 和 router.post('/orders', createOrder) 的文件运行,输出:

{
  "count": 2,
  "endpoints": [
    { "method": "GET", "path": "/users/{id}", "sourceFile": "routes/users.js", "handlerName": null },
    { "method": "POST", "path": "/orders", "sourceFile": "routes/orders.js", "handlerName": null }
  ]
}

常见问题:脚本在会话里报"permission denied"?记得 chmod +x;Windows 下用 node scripts/extract-endpoints.js 显式调用解释器即可。

Step 7:安装、触发与调试

安装:因为是个人级路径(~/.claude/skills/),无需额外安装命令,新开会话即自动注册。项目级 Skill 同理,把目录放进 <project>/.claude/skills/ 即可。

触发有两种方式:

  1. 自动触发(设计目标):直接说"帮我把这个项目的接口整理成 OpenAPI 文档",Claude 读到 description 里的关键词自动激活。
  2. 显式触发:在会话里输入 /api-contract-doc(斜杠 + Skill 名)强制调用,用于验证 Skill 是否注册成功。

调试三连:

实战案例:跑通一个迷你商城项目

准备一个最小 Express 项目 mini-shop/routes/orders.js:

const router = require('express').Router();

// 创建订单
router.post('/orders', (req, res) => {
  const { userId, amount } = req.body;
  res.json({ id: 1001, userId, amount, createdAt: '2026-07-12T08:00:00Z' });
});

// 查询订单详情
router.get('/orders/:id', (req, res) => {
  res.json({ id: req.params.id, status: 'paid' });
});

module.exports = router;

在项目根目录启动 Claude Code,只说一句话:

帮我把 mini-shop 的接口整理成 OpenAPI 文档,放到根目录 openapi.yaml。

预期发生的事:

  1. Claude 激活 api-contract-doc,Grep 定位到 routes/orders.js。
  2. Bash 调 extract-endpoints.js mini-shop/routes 拿到端点骨架。
  3. Read 两份 reference 文件套用规范。
  4. 阅读两个 handler 补全 schema,产出 openapi.yaml:
openapi: 3.0.3
info:
  title: Mini Shop API
  version: 1.0.0
paths:
  /orders:
    post:
      summary: 创建订单
      operationId: createOrder
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                userId: { type: integer, description: 下单用户ID }
                amount: { type: number, description: 订单金额(元) }
      responses:
        '200':
          description: 创建成功
          content:
            application/json:
              schema:
                type: object
                properties:
                  id: { type: integer }
                  userId: { type: integer }
                  amount: { type: number }
                  createdAt: { type: string, format: date-time, description: 创建时间 }
  /orders/{id}:
    get:
      summary: 查询订单详情
      operationId: getOrderById
      parameters:
        - in: path
          name: id
          required: true
          schema: { type: integer }
      responses:
        '200':
          description: 查询成功
          content:
            application/json:
              schema:
                type: object
                properties:
                  id: { type: integer }
                  status: { type: string, description: 订单状态 }

整个过程你只说了一句话,规范、命名、错误码全部对齐团队风格——这就是 Skill 把"重复教 AI"的成本一次性摊销掉的威力。

进阶:评估、发布与协同

评估:用 skill-creator 的 eval 能力,准备 5-10 条真实 prompt(不同说法请求接口文档),跑一遍看触发率与产出质量。description 改一轮、脚本正则补一轮,指标就能稳定上升。

发布:项目级 Skill 直接提交进仓库 .claude/skills/,团队 clone 即用;要跨组织共享,可封装成 plugin 发布到 marketplace。

与 CLAUDE.md / Hooks 协同:把"接口变更必须同步更新 openapi.yaml"写进项目 CLAUDE.md,再用一个 PostToolUse Hook 在检测到 routes/ 改动时提示运行本 Skill,就形成了"改接口→自动催更文档"的闭环。

延伸阅读

训练营适用性评估

参考来源

  1. Claude Skills 开发完全指南(GitHub) — 涵盖文件结构、SKILL.md 编写、Scripts、Reference、调试部署与完整示例的中文开发指南。
  2. Claude Code Skills 完全指南:用 SKILL.md 打造自定义工作流 — 含 SKILL.md 编写方法、即用模板与 Skills/CLAUDE.md/Hooks 协同技巧。
  3. 使用 skills 扩展 Claude — Claude Code 官方文档 — 创建、管理与共享 skills 的权威说明,含调用控制与子代理执行机制。
  4. Claude Code Skills 完全指南(2026 最新版)— 知乎 — 涵盖手动安装、热门 Skills 推荐与离线部署场景。

[!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学通信知识星球海报-扫码加入