来源:掘金 - 光辉GuangHui | 2026-05-22 参考文献:Anthropic 工程博客、skill-creator、Philipp Schmid、OpenAI 开发者博客

为什么需要评估 Skill?

Skill 和代码一样是生产资产,但大多数 Skill 的质量保障停留在”跑几遍看看感觉”的阶段。Philipp Schmid 的统计很直白:SkillsBench 在 6,300 多个仓库中发现了超过 47,000 个 Skill,几乎没人在测试它们,大部分还是 AI 生成的。

你不会不写测试就发布代码,为什么写 Skill 就可以不做评估?

Eval(evaluation,评估)做的事情很简单:给 agent 一个 prompt,记录它做了什么,然后按一组规则打分。像轻量级的端到端测试——跑 agent、录过程、对结果。

核心循环

Skill 评估不是一次性动作,而是一个迭代循环。四篇文章虽来自不同生态(Anthropic / Google-Gemini / OpenAI-Codex),但收敛到了同一个骨架:

定义成功 → 手动试跑 → 构建 prompt 集 → 搭评估框架 → 跑评估 → 人工审查 → 改 Skill → 重跑 → ……

Phase 0:先定义成功,再动手

在写任何 eval 代码之前,先用可测量的语言写下”成功”长什么样。

OpenAI 的文章把成功拆成四个维度,Philipp Schmid 用三个维度(合并了 Outcome 和 Process)。综合来看:

维度问的问题具体示例
Outcome(结果)产出能用吗?代码能编译、文件能打开、API 返回有效响应、npm run dev 能启动
Process(过程)agent 走对路了吗?触发了正确的 Skill、按预期顺序执行命令、没有跳步
Style(风格)产出符合约定吗?用了正确的 SDK import、model ID 没过期、命名规范一致
Efficiency(效率)过程划算吗?没有反复装同一个依赖、token 消耗合理、没有命令死循环

前两个是底线——不通过就别谈别的。后两个区分”能用”和”好用”。

关键原则:评结果,不评路径

Agent 经常走出你没预料到的路线但结果完全正确。Philipp Schmid 强调:不要惩罚通往正确答案的非预期路径。

两次运行可能产出完全一样的正确结果,但一个烧了 3 倍 token——效率维度会捕捉到这个差异,不需要在结果维度上扣分。

Phase 1:手动试跑,暴露隐藏假设

不要一上来就写自动化。先手动跑 3-5 次,这一步的目的不是打分,而是发现你没预料到的问题。

OpenAI 的文章把隐藏假设分成三类:

类型示例
触发假设”搭个 React demo”该触发 setup-demo-app 但没触发;“给现有项目加 Tailwind”不该触发但触发了
环境假设Skill 假设目录是空的;假设 npm 已安装;假设特定操作系统
执行假设agent 跳过了 npm install;配置 Tailwind 的顺序和预期不同

每一个手动修复都是一个未来的 eval case。

Phase 2:构建 Prompt 集

10-20 个 prompt 足以启动。之后从真实失败中逐步扩充。

四种 Prompt 类型:

类型目的示例
显式触发直接点名 Skill,验证基本功能Create a demo app using the $setup-demo-app skill
隐式触发描述场景但不提 Skill 名,验证 description 的匹配能力Set up a minimal React demo app with Tailwind
上下文触发加入领域噪音,验证在现实 prompt 中的鲁棒性Create a small demo app to showcase the Responses API
负面控制相邻但不该触发的场景,捕捉误触发Add Tailwind styling to my existing React app

负面测试的关键

四篇文章不约而同强调了同一点:负面测试是最容易被忽略但最有价值的部分。

  • 差的负面测试:“Write a fibonacci function” — 作为 PDF Skill 的负面测试毫无价值,明显不相关
  • 好的负面测试:“Add Tailwind styling to my existing React app” — 和 setup-demo-app Skill 共享关键词(Tailwind、React),但意图完全不同(增量修改 vs 从头搭建)

负面测试的价值在于暴露 description 写得太宽泛的问题。

Phase 3:搭建评估框架

评估框架 = 运行机制 + 评分机制。

3.1 运行机制:永远带 Baseline

Anthropic 的 skill-creator 要求每个 prompt 跑两个版本:

配置什么时候用
with_skill加载目标 Skill
baseline(无 Skill)创建新 Skill 时:不加载任何 Skill,测”模型本来就会多少”
baseline(旧版 Skill)改进现有 Skill 时:加载旧版本,测”改了之后是否真的更好”

两个版本要同时启动,不要先跑完 with_skill 再补 baseline。

3.2 确定性检查:快、可靠、可解释

确定性检查是评估框架的骨架。它用正则匹配、文件存在性、命令序列等手段,覆盖所有能机械判定的维度。

优势:

  • 快——毫秒级,零额外成本
  • 可解释——失败时直接看原始输出就能定位原因
  • 可复现——同样的输入永远给同样的结果

3.3 LLM-as-Judge:覆盖确定性检查够不着的地方

有些维度用正则搞不定——代码结构是否合理、设计是否美观、命名是否地道。这时引入第二个模型做评委。

Philipp Schmid 和 OpenAI 都给出了同一个做法:用结构化输出约束评委模型的响应格式,让结果可解析、可追踪、可对比。

要点:

  • 拆成独立维度分别打分,不要只给一个总分——总分掩盖了具体哪里出了问题
  • 评委模型和被测 agent 用不同的 session,避免自我验证
  • 只在确定性检查覆盖不了时才用 LLM judge——它更慢、更贵、结果有波动

3.4 人工审查:不可替代的最后一环

自动化检查告诉你”对不对”,人工审查告诉你”好不好”。

Anthropic 的 skill-creator 为此做了一个专门的 eval-viewer 工具:

  • Outputs 标签页:逐个查看每个 prompt 和产出,留下文字反馈
  • Benchmark 标签页:pass rate、token 用量、执行时间的聚合对比

反馈规则很简单:空反馈 = 认可,有文字 = 需要改进。

Phase 4:执行评估循环

目录结构

skill-workspace/
  iteration-1/
    eval-basic-generation/
      with_skill/
        outputs/
        timing.json
        grading.json
      without_skill/
        outputs/
        timing.json
        grading.json
      eval_metadata.json
    benchmark.json
    benchmark.md
    feedback.json
  iteration-2/
    ...

一轮迭代的完整步骤

Step 1:并行启动所有运行 对每个 prompt,同时启动 with_skill 和 baseline 两个运行。

Step 2:等待期间起草 assertions 不要干等。趁运行还没结束,起草或更新检查项。

Step 3:运行完成后立即记录 timing 数据

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

Step 4:评分 + 聚合 + 分析

  • 用确定性检查 + 可选 LLM judge 对每个运行评分
  • 聚合为 benchmark:pass_rate、mean ± stddev、与 baseline 的 delta
  • 找出聚合数字掩盖的问题

Step 5:展示给用户,收集反馈

改进 Skill 的四个原则

skill-creator 给出了改 Skill 时最重要的思维方式:

原则说明
A. 从反馈中泛化,不要为特定 case 打补丁理解反馈背后的 pattern,写出通用的指令
B. 保持精简,删掉没拉动效果的指令读 agent 的执行 transcript,删掉让 agent 浪费时间做无用功的指令
C. 解释 why,而不是堆 MUST / ALWAYS当代 LLM 有 theory of mind。告诉它”为什么这样做”比命令它”必须这样做”有效得多
D. 提取重复工作如果多个 test run 都独立写了同一个 helper script——把它打包进 Skill 的 scripts/ 目录

Phase 5:Description 优化

name + description 是 Skill 的触发机制——agent 看到用户的 prompt 后,拿它和所有已安装 Skill 的 description 做匹配,决定是否加载。

Philipp Schmid 的案例中,单独改 description 就修复了 7 个失败中的 5 个。

Description 决定了触发率——触发都不对,Skill 内容写得再好也没用。

Description 写法要点

  • 以 “Load when…” 开头——这是给 agent 的路由指令,不是给人读的文档
  • 描述用户的意图,不是 Skill 的功能——用户不会说”我需要一个 PR 监控 Skill”,他们会说”babysit my PR”、“make sure this lands”
  • 适度拓宽触发范围——当前模型倾向于 under-trigger(该触发时不触发),description 可以稍微”主动”一些
  • 50 词以内——这个成本每次会话、每个用户都在付

Phase 6:毕业与退役

Eval 的生命周期

一个 eval case 的角色会随时间变化:

阶段角色说明
初期Capability eval(能力检验)通过率从低往高爬,每次改进都能看到数字变化
成熟期Regression eval(回归检验)通过率稳定在 ~100%,职责变成”守住已有成果,防止倒退”

Philipp Schmid 的说法:eval 从”给你一座山去爬”毕业为”确保你不滑下来”。

Skill 退役检测

不加载 Skill,重跑所有 eval。如果依然全部通过——model 已经内化了这个 Skill 的能力,可以下线。

随着基础模型升级,某些 Skill 会变成多余负担:占 context 窗口、增加 Skill 间的触发冲突、拖慢响应。定期做退役检测,让 Skill 库保持精简。

工具链速览

工具/方法来源用途
eval-viewer / generate_review.pyskill-creator可视化对比产出 + 收集人工反馈
aggregate_benchmark.pyskill-creator聚合 pass_rate / token / time 统计
run_loop.pyskill-creator自动化 description 优化循环
CHECK_REGISTRY 模式Philipp Schmid确定性检查的注册与调度
codex exec —jsonOpenAI输出 JSONL 事件流,可精确检查执行过程
—output-schemaOpenAI约束 LLM judge 的输出格式为 JSON Schema
Blind comparisonskill-creator给独立 agent 两个匿名产出做 A/B 判断

相关页面