本教程教你用官方 skill-creator 内置的 eval/benchmark 闭环,把"写完 SKILL.md 凭感觉发布"升级成"写测试用例 → 对比有无 Skill 的输出 → 量化 pass rate/耗时/token → 看 reviewer 回填反馈 → 迭代改进"。学完你将能为任意自定义 Skill 建立可回归、可量化、可比对的质量护栏。适合已会写基础 Skill、想给团队交付稳定技能的中高级开发者。
/skill-test 之外的基础流程name/description frontmatterskill-creator 插件(含 scripts/、eval-viewer/、agents/、references/ 目录)社区里有个普遍做法:写好 SKILL.md,随手测一句,发布,然后"向 LLM 之神祈祷它能在真实场景触发"。问题是——你根本不知道三件事:
2026 年 3 月 3 日 Anthropic 升级了 skill-creator,正式给出工程化答案:给 Skill 写测试用例(evals)、同时跑 with-skill 和 baseline(无 Skill 或旧版 Skill)两组、用脚本量化对比、用浏览器 reviewer 收集人类反馈、迭代下一轮。这套机制本质上是为 Skill 做 TDD + A/B 测试 + 回归测试。
它特别适合输出客观可验证的 Skill:文件转换、数据抽取、代码生成、固定工作流(如 commit message、分支命名、PR 描述生成)。对于风格化、艺术化的 Skill(写作风格、设计审美),更适合定性评审,不要硬套断言。
eval 闭环的精髓是对照实验。每个测试用例同时孵化两个子代理:
| 运行类型 | 新建 Skill 场景 | 改进已有 Skill 场景 |
|---|---|---|
| with_skill | 加载当前 Skill | 加载改后版本 |
| baseline | 不加载任何 Skill | 加载改前快照(cp -r 备份的 skill-snapshot/) |
两组用完全相同的 prompt 和输入文件,结果分目录存放。这样你看到的"Skill 贡献值"= with_skill 与 baseline 的差值(delta)。没有 baseline,"效果好不好"就是一句空话。
关键纪律:所有运行必须在同一轮(same turn)一起孵化,不要先跑 with-skill 再回头补 baseline。一起启动,它们会差不多同时完成,不浪费等待时间。
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
实战案例我们做一个 branch-namer——根据改动描述生成规范化的 git 分支名(feat/auth-jwt-login 这种)。它输出固定格式,极易断言,非常适合演示 eval 闭环。
在你的 skills 目录下建结构:
branch-namer/
└── SKILL.md
SKILL.md 草稿:
---
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 时会直接体现为触发率的提升。
在 Skill 目录下建 evals/evals.json,只放 prompt,先不写 assertions(断言在运行进行中再起草,节省时间):
{
"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 要像真实用户会说的话,而不是测试人员腔。把这三条拿给用户确认:"这几条测试用例对吗,要不要加?"
在与 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):
{
"eval_id": 1,
"eval_name": "feat-auth-jwt-login",
"prompt": "我要开始做用户登录功能,用 JWT",
"assertions": []
}
不要干等运行结束。趁这段时间起草客观可验证、带描述性名字的断言。好断言读起来就像基准查看器里的体检项,扫一眼就懂。
填入 eval_metadata.json 和 evals.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 字符"
}
]
}
每个子代理任务完成时,你会收到一个带 total_tokens 和 duration_ms 的通知。这是唯一捕获时机——通知不持久化。每到一个处理一个,写到运行目录的 timing.json:
{
"total_tokens": 84852,
"duration_ms": 23332,
"total_duration_seconds": 23.3
}
全部跑完后,孵化一个 grader 子代理(读 agents/grader.md),对每个运行评估每条断言。能用脚本判定的断言优先写脚本——正则匹配分支名格式、长度校验这类,脚本比"肉眼瞄"更快、更可靠、可复用。
结果写入每个运行目录的 grading.json。字段名必须是 text/passed/evidence(不是 name/met/details),查看器依赖这组精确字段名:
{
"expectations": [
{
"text": "输出是单个分支名,无多余解释文字",
"passed": true,
"evidence": "输出为 feat/auth-jwt-login,无前后缀文字"
},
{
"text": "类型前缀属于 ... 之一",
"passed": true,
"evidence": "feat 在允许列表内"
}
]
}
从 skill-creator 目录运行聚合脚本:
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 的取舍。
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:
{
"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 |
benchmark.md,且 with_skill 的 pass_rate delta 显著为正、含至少一轮"泛化改进"的迭代记录skill-creator 完整 SKILL.md:理解完整流程与 references/schemas.md 全 schemaagents/grader.md / agents/analyzer.md:打分器与分析师的评判标准[!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 等场景全覆盖。
