<!-- title: 给Skill造量化基准测试体系 -->

# Claude Code Skill 开发实战：用 eval 闭环给技能做量化基准测试

## 概述

本教程教你用官方 `skill-creator` 内置的 **eval/benchmark 闭环**，把"写完 SKILL.md 凭感觉发布"升级成"写测试用例 → 对比有无 Skill 的输出 → 量化 pass rate/耗时/token → 看 reviewer 回填反馈 → 迭代改进"。学完你将能为任意自定义 Skill 建立可回归、可量化、可比对的质量护栏。适合已会写基础 Skill、想给团队交付稳定技能的中高级开发者。

## 前置条件

- 已安装 Claude Code（CLI 或 IDE 扩展），会用 `/skill-test` 之外的基础流程
- 已读过至少一个 SKILL.md，理解 `name`/`description` frontmatter
- 安装了官方 `skill-creator` 插件（含 `scripts/`、`eval-viewer/`、`agents/`、`references/` 目录）
- Python 3.10+（eval 聚合与 reviewer 脚本依赖）
- 能跑 Claude Code 子代理（subagent），用于并行执行测试运行

## 为什么需要 eval 闭环：告别"hope and pray"

社区里有个普遍做法：写好 SKILL.md，随手测一句，发布，然后"向 LLM 之神祈祷它能在真实场景触发"。问题是——**你根本不知道三件事**：

1. **Skill 该触发时是否真的触发了？**（欠触发是当前模型的通病）
2. **Skill 让输出变好了还是变差了？**（没有对照组）
3. **模型升级后这个 Skill 是否回归了？**（无基准可比）

2026 年 3 月 3 日 Anthropic 升级了 `skill-creator`，正式给出工程化答案：给 Skill 写测试用例（evals）、同时跑 **with-skill** 和 **baseline**（无 Skill 或旧版 Skill）两组、用脚本量化对比、用浏览器 reviewer 收集人类反馈、迭代下一轮。这套机制本质上是为 Skill 做 TDD + A/B 测试 + 回归测试。

它特别适合**输出客观可验证的 Skill**：文件转换、数据抽取、代码生成、固定工作流（如 commit message、分支命名、PR 描述生成）。对于风格化、艺术化的 Skill（写作风格、设计审美），更适合定性评审，不要硬套断言。

## 核心机制：with-skill vs baseline 双轨并行

eval 闭环的精髓是**对照实验**。每个测试用例同时孵化两个子代理：

| 运行类型 | 新建 Skill 场景 | 改进已有 Skill 场景 |
|---------|----------------|-------------------|
| **with_skill** | 加载当前 Skill | 加载改后版本 |
| **baseline** | 不加载任何 Skill | 加载改前快照（`cp -r` 备份的 `skill-snapshot/`） |

两组用**完全相同的 prompt 和输入文件**，结果分目录存放。这样你看到的"Skill 贡献值"= `with_skill` 与 `baseline` 的差值（`delta`）。没有 baseline，"效果好不好"就是一句空话。

关键纪律：**所有运行必须在同一轮（same turn）一起孵化**，不要先跑 with-skill 再回头补 baseline。一起启动，它们会差不多同时完成，不浪费等待时间。

## 工作流总览

```mermaid
flowchart TD
    A["1. 捕获意图<br/>访谈+研究"] --> B["2. 写 SKILL.md 草稿<br/>name/description/正文"]
    B --> C["3. 写 evals.json<br/>2-3 个真实测试 prompt"]
    C --> D["4. 同轮孵化双轨运行<br/>with_skill + baseline"]
    D --> E["5. 边跑边写 assertions<br/>客观可验证的断言"]
    E --> F["6. 捕获 timing.json<br/>tokens + duration"]
    D --> F
    F --> G["7. 打分 grading.json<br/>text/passed/evidence"]
    G --> H["8. aggregate_benchmark<br/>benchmark.json/md"]
    H --> I["9. 生成 reviewer<br/>Outputs + Benchmark 两页"]
    I --> J["10. 人类回填 feedback.json"]
    J --> K{"满意?"}
    K -- 否 --> L["11. 泛化改进<br/>去 overfit/删冗余"]
    L --> C
    K -- 是 --> M["12. description improver<br/>优化触发准确率"]
    M --> N["13. 扩大测试集<br/>更大规模再跑一轮"]
    N --> C
```

## 分步详解

### 第 1 步：写一个客观可验证的 Skill

实战案例我们做一个 `branch-namer`——根据改动描述生成规范化的 git 分支名（`feat/auth-jwt-login` 这种）。它输出固定格式，极易断言，非常适合演示 eval 闭环。

在你的 skills 目录下建结构：

```
branch-namer/
└── SKILL.md
```

`SKILL.md` 草稿：

```markdown
---
name: branch-namer
description: Generate a conventional-style git branch name from a change description. Make sure to use this skill whenever the user is about to create a branch, mentions branch naming, starts new work, or asks "what should I call this branch", even if they don't explicitly say "branch name".
---

# Branch Namer

Turn a free-text change description into a branch name following the
`<type>/<short-kebab-slug>` convention.

## Rules
1. Pick a type from: feat, fix, docs, refactor, test, chore, perf.
2. Lowercase the slug, join words with hyphens, drop stopwords.
3. Keep total length under 40 characters.
4. Return ONLY the branch name, nothing else.

## Example
Input: "I'm adding JWT-based login"
Output: feat/auth-jwt-login
```

注意 description 里那句"Make sure to use this skill whenever..."——这是官方推荐的**"略微 pushy"写法**，对抗模型的欠触发倾向。这一句在你后面跑 eval 时会直接体现为触发率的提升。

### 第 2 步：写测试用例（先不写断言）

在 Skill 目录下建 `evals/evals.json`，只放 prompt，**先不写 assertions**（断言在运行进行中再起草，节省时间）：

```json
{
  "skill_name": "branch-namer",
  "evals": [
    {
      "id": 1,
      "prompt": "我要开始做用户登录功能，用 JWT",
      "expected_output": "形如 feat/auth-jwt-login 的分支名",
      "files": []
    },
    {
      "id": 2,
      "prompt": "修复用户列表分页的崩溃 bug",
      "expected_output": "形如 fix/user-list-pagination 的分支名",
      "files": []
    },
    {
      "id": 3,
      "prompt": "重命名一批变量，纯重构不改行为",
      "expected_output": "形如 refactor/rename-variables 的分支名",
      "files": []
    }
  ]
}
```

设计原则：prompt 要**像真实用户会说的话**，而不是测试人员腔。把这三条拿给用户确认："这几条测试用例对吗，要不要加？"

### 第 3 步：同轮孵化双轨运行

在与 Skill 同级的位置建工作区 `branch-namer-workspace/`，按 `iteration-N/eval-name/` 组织。**不要预先建好所有目录**，边跑边建。

对每个 eval，同一轮内孵化两个子代理。给子代理的指令模板：

```
执行此任务：
- Skill 路径: ./branch-namer
- 任务: <eval prompt>
- 输入文件: none
- 输出保存到: ./branch-namer-workspace/iteration-1/feat-auth-jwt-login/with_skill/outputs/
- 需保存的输出: 最终分支名字符串
```

baseline 子代理用**相同 prompt**，但**不传 Skill 路径**，输出存到 `without_skill/outputs/`。如果你是在**改进**已有 Skill，则先 `cp -r branch-namer ../skill-snapshot/`，把 baseline 指向快照，输出存到 `old_skill/outputs/`。

每个 eval 目录写一个 `eval_metadata.json`（用描述性名字而非 `eval-0`）：

```json
{
  "eval_id": 1,
  "eval_name": "feat-auth-jwt-login",
  "prompt": "我要开始做用户登录功能，用 JWT",
  "assertions": []
}
```

### 第 4 步：运行进行中，起草断言

不要干等运行结束。趁这段时间起草**客观可验证、带描述性名字**的断言。好断言读起来就像基准查看器里的体检项，扫一眼就懂。

填入 `eval_metadata.json` 和 `evals.json`：

```json
{
  "eval_id": 1,
  "eval_name": "feat-auth-jwt-login",
  "prompt": "我要开始做用户登录功能，用 JWT",
  "assertions": [
    {
      "text": "输出是单个分支名，无多余解释文字"
    },
    {
      "text": "类型前缀属于 feat/fix/docs/refactor/test/chore/perf 之一"
    },
    {
      "text": "slug 全小写，词间用连字符连接"
    },
    {
      "text": "总长度小于 40 字符"
    }
  ]
}
```

### 第 5 步：捕获 timing（唯一时机）

每个子代理任务完成时，你会收到一个带 `total_tokens` 和 `duration_ms` 的通知。**这是唯一捕获时机**——通知不持久化。每到一个处理一个，写到运行目录的 `timing.json`：

```json
{
  "total_tokens": 84852,
  "duration_ms": 23332,
  "total_duration_seconds": 23.3
}
```

### 第 6 步：打分

全部跑完后，孵化一个 grader 子代理（读 `agents/grader.md`），对每个运行评估每条断言。**能用脚本判定的断言优先写脚本**——正则匹配分支名格式、长度校验这类，脚本比"肉眼瞄"更快、更可靠、可复用。

结果写入每个运行目录的 `grading.json`。**字段名必须是 `text`/`passed`/`evidence`**（不是 `name`/`met`/`details`），查看器依赖这组精确字段名：

```json
{
  "expectations": [
    {
      "text": "输出是单个分支名，无多余解释文字",
      "passed": true,
      "evidence": "输出为 feat/auth-jwt-login，无前后缀文字"
    },
    {
      "text": "类型前缀属于 ... 之一",
      "passed": true,
      "evidence": "feat 在允许列表内"
    }
  ]
}
```

### 第 7 步：聚合成基准

从 skill-creator 目录运行聚合脚本：

```bash
python -m scripts.aggregate_benchmark \
  ./branch-namer-workspace/iteration-1 \
  --skill-name branch-namer
```

产出 `benchmark.json` 和 `benchmark.md`，含每种配置（with_skill / without_skill）的 `pass_rate`、`time`、`tokens`，给出 **mean ± stddev** 和 **delta**（贡献值）。**with_skill 版本要排在它对应 baseline 之前**。

接着做一次**分析师 pass**（读 `agents/analyzer.md`）：挖掘聚合统计掩盖的模式——比如某条断言加不加 Skill 都过（无区分度）、某个 eval 方差高（可能 flaky）、耗时与 token 的取舍。

### 第 8 步：启动 reviewer，收人类反馈

```bash
nohup python <skill-creator-path>/eval-viewer/generate_review.py \
  ./branch-namer-workspace/iteration-1 \
  --skill-name "branch-namer" \
  --benchmark ./branch-namer-workspace/iteration-1/benchmark.json \
  > /dev/null 2>&1 &
VIEWER_PID=$!
```

迭代 2+ 时加 `--previous-workspace ./branch-namer-workspace/iteration-1`，查看器会折叠展示上一轮输出做对比。无显示环境的协同/headless 场景用 `--static output.html` 生成独立 HTML，反馈以 `feedback.json` 下载。

查看器两个页签：
- **Outputs**：逐个看测试用例，prompt / 输出 / 上一轮输出 / 形式化评分 / 反馈文本框，左右键翻页，点"Submit All Reviews"保存
- **Benchmark**：pass rate、耗时、token 的量化对比 + 每 eval 明细 + 分析师观察

用户看完回来告诉你后，读 `feedback.json`：

```json
{
  "reviews": [
    {"run_id": "feat-auth-jwt-login-with_skill", "feedback": "", "timestamp": "..."},
    {"run_id": "refactor-rename-variables-with_skill",
     "feedback": "输出带了多余解释，破坏了规则1", "timestamp": "..."}
  ],
  "status": "complete"
}
```

**空反馈 = 用户觉得没问题**。把改进火力集中在有具体抱怨的用例上。用完关掉查看器：`kill $VIEWER_PID 2>/dev/null`。

## 改进心法：泛化而非过拟合

拿到反馈后，这是闭环的心脏。两条铁律：

**1. 泛化。** 这个 Skill 最终可能被使用百万次、横跨无数 prompt。你和用户反复迭代的只是几个样本，是因为样本快、用户熟。如果改进只对这几个样本有效，Skill 就废了。别塞零碎的 overfit 补丁，也别堆压迫性的"MUST"。遇到顽固问题，换个比喻、换种工作模式再试——试错成本很低，也许就撞上金子了。

**2. 保持 prompt 精简。** 删掉没在起作用的指令。**读 transcript（对话记录），不只看最终输出**——如果发现 Skill 让模型在某处浪费大量时间做无用功，就砍掉诱导这部分的那段指令，看会发生什么。

## 收尾：优化触发与扩大规模

Skill 满意后（顺序灵活），跑 **description improver**（官方提供独立脚本），专门优化 description 的触发准确率——解决"该触发没触发"和"不该触发乱触发"的问题。

最后**扩大测试集**，用更多样本更大规模再跑一轮，验证 Skill 的泛化性而非过拟合到最初几个用例上。

## 常见问题与排坑

| 问题 | 原因 | 解决 |
|-----|------|------|
| 查看器报字段缺失 | `grading.json` 用了 `name`/`met` | 改回 `text`/`passed`/`evidence` |
| timing 数据丢失 | 没在通知到达时立即处理 | 每个通知单独处理，勿批量 |
| Skill 总不触发 | description 太保守 | 加"Make sure to use this skill whenever..."句式 |
| 某断言加不加 Skill 都过 | 无区分度 | 删掉或改成更有区分力的断言 |
| benchmark delta 是负的 | Skill 反而拖累输出 | 读 transcript 找让模型绕弯的指令并精简 |
| 无显示器环境打不开 | `webbrowser.open()` 不可用 | 用 `--static out.html` 生成独立 HTML |

## 训练营适用性评估

- **适用类型**：进阶 Skill 开发训练营、AI 工程质量保证（QA）训练营、Agent 可观测性训练营
- **不适合**：零基础入门营（需先掌握 SKILL.md 基础）、纯创意写作营（主观输出不宜强断言）
- **预计教学时长**：1.5–2 小时讲授 + 2 小时实操（学员自带一个客观输出的 Skill 跑完整闭环，至少 2 个 iteration）
- **考核建议**：要求学员提交 `benchmark.md`，且 with_skill 的 pass_rate delta 显著为正、含至少一轮"泛化改进"的迭代记录

## 延伸阅读

- 官方 `skill-creator` 完整 SKILL.md：理解完整流程与 `references/schemas.md` 全 schema
- Progressive Disclosure（三层按需加载）：让大 Skill 不爆上下文
- Plugin 打包发布：把验证过的 Skill 发到团队市场
- `agents/grader.md` / `agents/analyzer.md`：打分器与分析师的评判标准
- description improver：触发率优化的独立脚本

## 参考来源

1. [skill-creator SKILL.md（官方源码）](https://github.com/anthropics/claude-plugins-official/blob/main/plugins/skill-creator/skills/skill-creator/SKILL.md) — 完整的 eval/benchmark 闭环流程、目录结构、schema 与迭代心法的权威定义
2. [Improving skill-creator: Test, measure, and refine Agent Skills（Anthropic 官方博客, 2026-03-03）](https://claude.com/blog/improving-skill-creator-test-measure-and-refine-agent-skills) — 该 eval/benchmark 能力正式发布的公告与设计动机
3. [Claude Code Skill Creator: How to Test and Benchmark Your Skills（nathanonn）](https://www.nathanonn.com/claude-code-skill-creator-guide/) — "hope and pray" 方法论痛点与实操视频/图文指南
4. [Skill Creator Complete Guide（smartscope.blog）](https://smartscope.blog/en/ai-development/skills/skill-creator-complete-guide/) — 2026 年 1 月的 Skill/Plugin 安装与市场机制综述

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