你改了一版 SKILL.md,跑一次感觉更好了。这句「感觉」值多少钱?答案是零——除非你有基线、有断言、有重复次数。
Anthropic 在 Skill authoring best practices 里把「先建评估,再写文档」加粗放在评估章节第一行,然后给了五步、给了 JSON 结构——紧接着一条 Note 说:目前没有内置的方式来跑这些评估,用户要自己搭。于是这一整节被绝大多数人跳过了。
但这条 Note 现在已经落后于工具本身:Claude Code 的 claude plugin eval 已经是一个带基线对照、带多种打分器、带 HTML 报告的完整 runner(本文写稿时处于 early access)。本期把三条可走的路径全部点名、给命令、给实测到的文件结构,最后回答那个真问题:怎么知道一次改动是真提升,而不是模型今天心情好。
# 路径 A:内置 runner。先长出一个用例骨架(在 plugin / skills 目录根下跑)
claude plugin eval init --bare my-first-case
# → 生成 evals/my-first-case/prompt.md 和 evals/my-first-case/graders/criteria.md
# 填完之后,对当前目录跑三遍,并写一份自包含 HTML 报告
claude plugin eval . --runs 3 --report ./eval-report.html
# 路径 B:装官方 skill-creator,让它替你把整套流程跑完
/plugin marketplace add anthropics/claude-plugins-official
/plugin install skill-creator@claude-plugins-official
/reload-plugins
# 然后直接说:evaluate my summarize-changes skill with skill-creator
# 路径 C:兜底。在 .claude/settings.local.json 里把 skill 关掉,开新会话再跑一遍
{ "skillOverrides": { "summarize-changes": "off" } }
# 跑之前先做一次五秒体检:frontmatter 有没有静默解析失败
claude plugin validate .claude/skills
init --bare 吐出来的模板小得让人意外——prompt.md 的头部只有 max_turns 和 allowed_tools,graders/criteria.md 的头部只有 type 和 weight,正文各一句 TODO。用例就是目录,不是配置文件:一个用例一个文件夹,里面一份 prompt 加一个 graders/。
plugin eval 本文写稿时(Claude Code v2.1.263)仍在 early access。账号没开通时,它会把参数解析完再退出,只打印一行「plugin eval is currently in early access」——什么都不跑,也不报错到你会注意的程度。别在 CI 里静默依赖它,先手动确认它真的跑起来了。claude plugin eval 的 --ablation 默认值是 with-without——只要目标解析出一个 plugin,它就自动加跑一条「没有这个 plugin」的臂,然后报告分数差。为什么必须默认打开?因为模型本来就很强。你的 skill 说「写 commit message 要用 conventional commits」,模型不带 skill 也可能这么写——不跑对照臂,这一分你会算到自己头上。skill-creator 那边把同一件事说得更死:with-skill 和 baseline 两组必须「在同一个 turn 里 spawn」,不许先跑完带 skill 的、过两天再补基线。
这是最见功力的一处。在 with-without 模式下,标了 with-only 的打分器(包括 tool_used: Skill)被当成「plugin 有没有被点着」的指示器,而不是分数的一部分。理由很直白:没装 skill 的那条臂当然不可能触发 skill,把这条算进分差等于白送自己一分。触发率和输出质量必须分开量——skill-creator 为此专门有一个 description tuning 步骤,生成 should-trigger 和 should-not-trigger 两组 prompt,量命中率,然后反过来改 description。
把一个非法的 type 塞进 grader,CLI 会把合法值全吐出来:regex | tool_order | tool_used | file_exists | llm | baseline。前四个是纯程序判定,不花钱也不引入方差;后两个是模型调用(--judge-model 默认 haiku)。这条分界线直接决定了你该怎么设计用例:能把成功标准写成「产出了 output.txt」「先 Read 后 Edit」「输出里出现了 feat(」的,就别写成一句让 LLM 去意会的「输出应当专业且清晰」。这也正是官方评估文档那条原则——宁可要更多题、判得糙一点,也不要更少题、人工精判。
--runs 的默认值是 case.runs ?? 3。单次跑赢在 agent 评估里几乎没有信息量,因为同一个 prompt 两次跑出的轨迹可能完全不同。skill-creator 把这一步做到了报表里:python -m scripts.aggregate_benchmark 产出的 benchmark.json / benchmark.md,给每种配置写的是 pass_rate、时间、token 的 mean ± stddev 以及 delta。看到 delta 先看 stddev——差值淹在标准差里的,那不叫提升。
agents/analyzer.md 专门要求做一遍 analyst pass,就是去抓这两种病:non-discriminating assertion(不管有没有 skill 都通过)和 high-variance eval(同配置跑三次结果乱跳,多半是题目本身 flaky)。官方推荐开两个实例:Claude A 帮你写和改 skill,Claude B 装着它去干真活,你把 B 的翻车现场带回给 A。文档举的例子很具体——「我让 B 出区域销售报表,它写了查询但忘了过滤测试账号,skill 里明明提了这条规则」,A 的回应可能是把 always 改成 MUST,或者调整章节顺序。
「fresh session」这四个字是硬要求,不是修辞:官方明说,写 skill 时留在会话里的上下文会掩盖 SKILL.md 本身的缺口——你以为它读懂了,其实它是记得你刚才口头说过。claude plugin eval 和 skill-creator 都用子代理跑用例,正是为了每条用例都拿到干净上下文。
claude plugin validate(v2.1.233+)——只查 frontmatter YAML 能不能解析。它抓的是一种极隐蔽的故障:YAML 挂了,skill 正文照样加载,但 metadata 是空的,于是 /skill-name 手动调还能用,自动触发永远不会发生。跑 eval 之前先跑它,五秒。/skill-doctor(v2.1.252+)——量的是使用度和 context 开销,不是质量。它列出每个 skill 花了多少 context、被调用过几次,并标出从来没被触发过的。交互式会话里结果开在 /plugin 的 Stats 页,-p 非交互模式下直接打文本。注意它跑不了的地方:Remote Control 连接下会直接回「Skill usage reports are not available on this connection.」,得回到会话真正运行的那台机器的终端上跑。skillOverrides——四档可见性(on / name-only / user-invocable-only / off),写在 .claude/settings.local.json,/skills 菜单里按空格循环、Esc 保存。当成穷人版消融开关用。边界:它对 plugin skill 无效,那些得走 /plugin。/skill-doctor 当质量评估。「从没被触发过」的 skill 通常不是没用,是 description 没进筛选——这是触发问题,该去改 description、跑 should-trigger 那组题,而不是去改正文。反过来,一个每次都触发、每次都帮倒忙的 skill,在 /skill-doctor 里看起来非常健康。llm 和 baseline 两种打分器还各是一次模型调用。claude plugin eval 给了 --max-cost-usd 做硬顶,撞顶时中止并返回部分结果(exit 2),超支被限制在一次 agent run 内,且那一次的付费打分器会被跳过、免费打分器照常打分。第一次跑一定要带上它。--threshold(默认 1.0,低于就 exit 1)和 --json 摆明是给流水线用的,但账号没开通时命令直接退出。要进 CI,先确认它在 CI 那个身份下真的能跑。npm install -g promptfoo,起手 promptfoo init --example getting-started;支持 LLM-as-judge 断言,官方主打 CI 集成。什么时候别用:它不懂 Claude Code 的 skill 触发机制——它能评「这段 prompt 的输出好不好」,评不了「这个 skill 该不该被点着」。pip install inspect-ai,自带 model-graded scorer 和两百多个预置评估。什么时候别用:它是研究级基建,为的是横评模型能力;拿它评一份 SKILL.md 是高射炮打蚊子。skillOverrides 关掉,肉眼比。什么时候够用:skill 只有你一个人用、改动是纯文案、或者你只是想确认它到底触没触发。先做这三件:① claude plugin validate .claude/skills——五秒,先确认 frontmatter 没静默挂掉;② claude plugin eval init --bare <name> 生成骨架,把成功标准尽量写成 regex / file_exists / tool_used 这三种免费打分器,再 claude plugin eval . --runs 3 --max-cost-usd <你的上限>;③ 两周后跑一次 /skill-doctor,把从没被触发过的挑出来单独治 description。
按场景选路径:要一份能给人看的报表和 mean ± stddev → 装 skill-creator@claude-plugins-official;要进 CI、要 exit code → claude plugin eval --threshold(先确认 early access 已开通);要评的是纯 prompt 质量而不是 skill 触发 → promptfoo;只是想知道有没有变好 → skillOverrides: off 开两个新会话手动跑。
读数备查:--runs 默认 3 · --ablation 默认 with-without · --threshold 默认 1.0 · --judge-model 默认 haiku · 免费打分器 regex / tool_order / tool_used / file_exists,收费的 llm / baseline · 用例目录默认 evals/ · 结果默认落在 evals/results/<timestamp>/。
看报表的顺序:先看 stddev,再看 delta;delta 淹在 stddev 里就当没提升。两条臂都满分的题,删掉重写——它没有区分度。
tool_used: Skill)。它是触发指示器,不计入分差。skillOverrides 四档表、/skill-doctor(v2.1.252+)与它在 Remote Control 下的报错、claude plugin validate(v2.1.233+)python -m scripts.aggregate_benchmark、grading.json 必须用 text/passed/evidence 三个字段名、eval-viewer/generate_review.py;仓库据 GitHub 页 2026-09 约 17.51 万 star / 2.07 万 forkagents/grader.md(判断言)、agents/comparator.md(盲测 A/B)、agents/analyzer.md(找无区分度与高方差的题)、references/schemas.md(evals.json / grading.json 的确切结构)、eval-viewer//plugin install skill-creator@claude-plugins-official 装的就是它npm install -g promptfoopip install inspect-ai