来源:掘金 - 光辉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.py | skill-creator | 可视化对比产出 + 收集人工反馈 |
| aggregate_benchmark.py | skill-creator | 聚合 pass_rate / token / time 统计 |
| run_loop.py | skill-creator | 自动化 description 优化循环 |
| CHECK_REGISTRY 模式 | Philipp Schmid | 确定性检查的注册与调度 |
| codex exec —json | OpenAI | 输出 JSONL 事件流,可精确检查执行过程 |
| —output-schema | OpenAI | 约束 LLM judge 的输出格式为 JSON Schema |
| Blind comparison | skill-creator | 给独立 agent 两个匿名产出做 A/B 判断 |
相关页面
- openclaw — OpenClaw Agent 系统
- agent-continual-learning — Agent 持续学习三层框架
- 2606.02060_span-level-error-localization — Agent 轨迹错误定位
- 2604.11641_codetracer — Agent 状态追踪架构