Skip to Content
Claude Code Skill 指南Evaluation评测体系的完整方法论

评测体系的完整方法论

对比式评测

第 4 章讲过一个核心公式:

Skill 价值 = 有 Skill 时的表现 - 没有 Skill 时的表现

方法论的第一条:每个 eval 必须同时跑 with_skill 和 without_skill,不能只跑一个。

跑完之后的产出放在固定的目录结构里:

eval-results/ iteration-1/ eval-detect-missing-header/ with_skill/ outputs/ transcript.json # AI 的完整对话记录 result.md # AI 的最终输出 grading.json # 断言评判结果 without_skill/ outputs/ transcript.json result.md grading.json eval-fix-wrong-year/ with_skill/ ... without_skill/ ... summary.json # 本轮汇总:pass_rate、delta、失败断言列表 iteration-2/ ...

为什么要这个结构?因为你会反复迭代。第一轮 eval 发现 pass_rate 只有 0.60,改了 Skill 再跑第二轮变成 0.75,再改再跑第三轮到 0.90。每一轮的结果都需要留存,这样你才能看到趋势,而不是每次都在猜”上次好像是 0.7 来着”。

summary.json 的结构很简单:

{ "iteration": 1, "date": "2025-04-01", "skill_version": "abc1234", "results": { "with_skill": { "total": 10, "passed": 8, "pass_rate": 0.80 }, "without_skill": { "total": 10, "passed": 4, "pass_rate": 0.40 }, "delta": 0.40 }, "failed_assertions": [ { "eval": "complex-pr", "assertion": "指出未处理的 Promise rejection", "reason": "AI 提到了错误处理但未定位到具体行号" } ] }

双角色评测

跑完 eval 得到 AI 的输出,你需要评判它。这里有两个容易忽略的层面。

角色一:评输出质量(Grader)

这是直觉上的评测——AI 的输出对不对。

给 grader 的输入是:eval 的 prompt + AI 的输出 + 断言列表。grader 逐条判断每个断言是否通过。

断言:"输出中包含 src/utils/format.ts" AI 输出:"检查发现以下文件缺少 header:src/utils/format.ts, src/components/Button.tsx" 判定:PASS

这个角色可以由 LLM 自动完成。大部分断言是客观的——输出里有没有某个文件名、有没有提到某个安全风险、格式对不对。

Grader 的 7 步流程

skill-creator 里的 grader 子代理把评测拆成了严格的 7 步,每一步都有明确的产出:

  1. 读完整 transcript——不是只看最终输出,而是看 AI 的整个推理过程。如果 AI 走了弯路但碰巧得到了正确答案,grader 要能发现这一点。只看结果不看过程,等于放过了一个定时炸弹。
  2. 检查实际输出——对照用户的期望,逐项核对。输出里该有的东西有没有,不该有的东西有没有混进来。
  3. 评判每条断言——Pass 还是 Fail,必须给出具体 evidence。“看起来不错”不算 evidence,“输出第 3 行包含了预期的函数签名 async function deploy(config: DeployConfig)”才算。没有 evidence 的 Pass 和没评一样。
  4. 提取额外发现——断言没覆盖到但 grader 注意到的问题。比如 AI 输出了一段不相关的警告信息,或者漏掉了一个边界情况。这些发现会反馈到下一轮断言设计中。
  5. 检查执行者备注——如果 AI 在执行过程中留下了备注(“这个 API 似乎已经废弃了”),grader 要纳入考量。这些备注有时候比最终输出更有信息量。
  6. 批判断言质量——这条断言是不是太弱了?是不是永远都会通过?是不是在测试实现细节而非行为?这一步是整个设计的点睛之笔。没有它,你可能写了一堆永远绿灯的断言,自我感觉良好,实际上什么都没测出来。
  7. 写评测报告——结构化输出,供 aggregate_benchmark.py 后续聚合。报告格式固定,包含每条断言的判定、evidence、额外发现、断言质量评价。

第 6 步值得展开说。常见的弱断言有三种模式:

  • 永真断言:“输出包含文本”——只要 AI 说了任何话就通过
  • 格式依赖断言:“第三行是 XXX”——输出多加一行空行就挂了,测的是格式而非语义
  • 模糊断言:“输出的建议是有用的”——grader 自己也说不清什么叫”有用”

grader 标记出这些问题之后,你在下一轮迭代时修正断言,再重跑 eval。断言质量和 Skill 质量是同步提升的。

角色二:评断言质量(Meta-Grader)

这个角色更重要也更容易被忽略。

你写的断言本身靠谱吗?

三种常见问题:

断言太弱:“输出中包含代码审查结果”——AI 只要说了任何跟 review 相关的话就通过了。这种断言通过了也不说明什么。

断言太强:“输出的第三行必须是 src/utils/format.ts:1 - Missing license header”——格式稍有变化就失败。你在测 Skill 还是在测 AI 的格式精确度?

断言不可验证:“输出的建议是有用的”——什么叫”有用”?grader 也说不清。

Meta-grader 的工作是标记这些有问题的断言,然后你人工修正。这个步骤在第一轮 eval 时尤其重要——你对断言质量的判断会随着经验积累而提升,但一开始写的断言大概率有问题。

为什么需要双角色?防止”假阳性自信”。如果你的断言太弱,pass_rate 会虚高。你看到 0.90 以为 Skill 很好,实际上是断言没有区分度。

盲比(Comparator)

你改了 Skill,跑了一轮 eval,pass_rate 从 0.80 升到了 0.85。很好。但 pass_rate 只测客观指标——断言通过了几条。有些质量维度不好用断言表达:输出的组织是否清晰?建议是否具体到可以直接操作?语气是否符合团队风格?

盲比解决这个问题。

做法:把 with_skill 和 without_skill 的两个输出匿名呈现给评审者(可以是人,也可以是 LLM),标记为”输出 A”和”输出 B”。评审者不知道哪个是加了 Skill 的。

评分维度:

维度子项说明
内容正确性指出的问题是否真实存在
内容完整性是否覆盖了所有应检查的点
内容准确性文件路径、行号是否准确
结构组织问题是否分类清晰
结构格式是否易于阅读和后续处理
结构可操作性看完能不能直接改代码

评完分之后再揭示来源。如果匿名状态下评审者一致选了”输出 A 更好”,揭示后发现 A 是 with_skill 的——Skill 有效。如果选的是 without_skill 的——你的 Skill 可能在帮倒忙。

盲比消除的偏差很重要。人有一个倾向:自己改过的东西总觉得更好。你花了两小时调 Skill 的指令措辞,下意识会觉得改完的输出”明显好多了”。盲比让你诚实面对结果。

Comparator 的评分标准

分维度评分不是随便分的。内容维度和结构维度各自回答一个核心问题:

内容维度——“说的对不对?“

子项评判标准典型失分场景
正确性指出的问题是否真实存在,有没有无中生有AI 把合理的代码标记为 bug
完整性是否覆盖了所有应检查的点,有没有遗漏关键问题安全漏洞没被发现
准确性文件路径、行号、变量名是否准确路径写错、行号偏移

结构维度——“好不好用?“

子项评判标准典型失分场景
组织问题是否分类清晰,有没有逻辑分组所有问题堆在一起没有分类
格式是否易于阅读和后续处理(复制、搜索)纯文本墙,没有代码块标记
可操作性看完能不能直接改代码,而不是还要去查文档”建议优化性能”但不说怎么优化

为什么要分两个维度?因为改进 Skill 时经常出现此消彼长的情况。你改了 prompt 措辞让 AI 更精确地定位问题(内容维度提升),但输出变成了密密麻麻的技术术语堆砌(结构维度下降)。单一分数会掩盖这种退步,分维度评分能把它暴露出来。

comparator 对每个维度分别给出 A/B 的优劣判定和理由。最终汇总时,如果两个维度方向一致(A 在内容和结构上都更好),结论很清晰;如果方向相反,就需要人工权衡——这个 Skill 的使用场景更看重正确性还是可读性?

模式分析(Analyzer)

grader 和 comparator 回答的是”这一轮 eval 结果怎么样”。但跑了三五轮之后,你需要回答一个更高层的问题:跨轮次看,有没有反复出现的模式?

这就是 analyzer 的工作。它是 skill-creator 的 agents/ 目录下第三个子代理,做的是事后的模式识别。

analyzer 的输入是多轮 eval 的汇总数据(summary.json)和 grader 报告。它要找的东西包括:

  • 顽固断言:连续三轮都失败的同一条断言。这说明当前的 SKILL.md 指令在某个方向上有结构性缺陷,小修小补解决不了,需要重新审视 Skill 的设计思路。
  • 退步模式:第 N 轮通过了但第 N+1 轮又失败的断言。这说明你的修改引入了回归,得检查两轮之间改了什么。
  • 弱断言集中区:grader 标记为”太弱”的断言如果集中在某一类测试用例上,说明你对这类场景的预期定义不够清晰。
  • 维度偏科:如果内容维度持续提升但结构维度持续下降,说明你在迭代中只关注了”说对”而忽略了”说好”。

analyzer 的输出是一份诊断报告,指出最需要关注的 2-3 个模式,并给出改进方向的建议。注意它只给方向,不给具体的 SKILL.md 修改——具体怎么改是你的决策,analyzer 只负责把问题摆到台面上。

这个角色在前两轮 eval 时用处不大(数据太少),但从第三轮开始价值会明显体现。如果你发现自己在第五轮还在跟同一个问题纠缠,大概率是因为没有跳出来看模式。

人工审查补位

自动 eval 抓客观问题:断言通过没有、格式对不对、有没有遗漏的检查项。

但有一类问题自动 eval 抓不到:技术正确但没用

AI 审查一段代码,指出”这个函数的圈复杂度是 15”。从技术上说完全正确。但作为 review 意见,这句话毫无意义——开发者需要知道的是”这个函数需要拆分,建议把验证逻辑提取到 validateInput() 中”。

人工审查就是补这个缺口。review 最近 5 次 eval 的输出,找那些”断言通过了但其实不够好”的地方。

人工反馈的格式要具体、可操作:

eval: complex-pr 问题:AI 指出了 3 个正确的问题,但建议太笼统。 具体:"建议添加错误处理" → 应该说 "在 fetchUser() 的 catch 块中, 建议将 error 上报到 Sentry 而不是只 console.log,参考 known-pitfalls.md 中的监控规范"。 操作:在 SKILL.md 中加一条指令 —— "给出修复建议时,指出具体的修改方式和引用的团队规范"。

迭代改进循环

评测不是一次性的。改 Skill → 跑 eval → 看结果 → 再改 → 再跑。这个循环什么时候停?

信号源有三个:

  1. 失败的断言:最直接。哪条断言没通过,去看 AI 的输出到底差在哪
  2. 人工反馈:断言通过了但人觉得不够好的地方
  3. transcript:AI 的推理过程。有时候输出正确但推理路径错误——这次碰巧对了,下次不一定

把这三个信号喂给 LLM,让它生成改进建议:

以下是 code-review Skill 的当前 eval 结果: - 失败断言:[列表] - 人工反馈:[列表] - transcript 中的异常推理:[列表] 请分析这些信号,给出具体的 SKILL.md 修改建议。

LLM 给出的建议不一定全对,但它能帮你快速定位方向。改完之后在 iteration-N+1 重跑 eval。

停止条件:

  • 连续两轮人工反馈为空(没有新的改进点)
  • pass_rate 连续两轮无显著提升(波动在 0.02 以内)
  • delta 已经足够大(比如 with_skill 比 without_skill 高 0.30 以上)

到了这个阶段,Skill 就进入维护期了。不需要持续迭代,只需要在业务变化时更新规则,然后跑一轮 eval 确认没退化。

实战:为 code-review 编写完整 evals.json

[ { "name": "simple-function", "description": "审查一个简单的工具函数", "prompt": "review src/utils/formatCurrency.ts", "workspace": "test-simple-function", "assertions": [ "指出函数缺少对 NaN 输入的处理", "指出函数没有处理负数的显示格式", "不误报:函数命名符合 camelCase 规范" ] }, { "name": "complex-pr", "description": "审查一个涉及多文件的 PR", "prompt": "review 当前 PR 的所有变更", "workspace": "test-complex-pr", "assertions": [ "检查了所有变更文件,不遗漏", "指出 src/api/userService.ts 中的未处理 Promise rejection", "指出 src/hooks/useData.ts 中 useEffect 缺少依赖数组", "按文件分组输出,不是一整坨" ] }, { "name": "security-vuln", "description": "审查包含安全漏洞的代码", "prompt": "review src/components/CommentBox.tsx", "workspace": "test-security-vuln", "assertions": [ "指出 dangerouslySetInnerHTML 的 XSS 风险", "给出具体的修复方式(使用 DOMPurify)", "说明风险场景(用户可以注入恶意脚本)" ] }, { "name": "react-component", "description": "审查一个 React 组件", "prompt": "review src/components/UserProfile.tsx", "workspace": "test-react-component", "assertions": [ "指出组件中使用了 any 类型", "指出 useEffect 的清理函数缺失(存在内存泄漏风险)", "给出的类型建议是具体的接口定义,不是泛泛的'请加类型'" ] } ]

四个测试用例,覆盖四种典型场景:简单函数、复杂 PR、安全漏洞、React 组件。每个用例 3-4 条断言,总共 14 条。

注意第三条断言的写法——“不误报”也是一种断言。你不仅要测 AI 能找到问题,还要测它不会无中生有。一个把所有代码都标红的 linter 没有价值。

Last updated on