Skip to Content
Claude Code Skill 指南Evaluation自动化评测流水线

自动化评测流水线

CI 集成:PR 修改 Skill 时自动跑 eval

手动跑 eval 能解决问题,但靠不住。忙起来就忘了,或者”改了个措辞应该没事吧”然后直接合并。

和代码测试一样——自动化才是唯一可靠的方案。

GitHub Actions 配置

# .github/workflows/skill-eval.yml name: Skill Evaluation on: pull_request: paths: - '.claude/skills/**' jobs: eval: runs-on: ubuntu-latest timeout-minutes: 30 steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 # 需要完整历史来对比 baseline - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '20' - name: Detect changed skills id: changed run: | SKILLS=$(git diff --name-only origin/main...HEAD \ | grep '^\.claude/skills/' \ | cut -d'/' -f3 \ | sort -u) echo "skills=$SKILLS" >> "$GITHUB_OUTPUT" echo "Changed skills: $SKILLS" - name: Run evals for changed skills env: ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} run: | for SKILL in ${{ steps.changed.outputs.skills }}; do SKILL_DIR=".claude/skills/$SKILL" EVAL_FILE="$SKILL_DIR/evals.json" if [ ! -f "$EVAL_FILE" ]; then echo "::warning::No evals.json found for $SKILL" continue fi echo "Running eval for $SKILL..." # 注意:以下命令是概念演示。目前没有官方的 skill-eval CLI 工具。 # 实际实现有两种方式: # 1. 用 skill-creator 的 scripts/run_eval.py(需要 Python 环境) # 2. 自己写一个简单的评测脚本,核心逻辑是: # - 启动两个 Claude Code 会话(一个加载 Skill,一个不加载) # - 把 evals.json 中的 prompt 分别发给两个会话 # - 收集输出,对比断言通过率 # 用 skill-creator 的方式(如果已安装): # python -m scripts.run_eval --skill "$SKILL_DIR" --evals "$EVAL_FILE" # # 或者用自定义脚本(见 examples/ch21-ci-pipeline/eval-runner.ts): npx tsx .claude/scripts/eval-runner.ts \ --skill "$SKILL_DIR" \ --evals "$EVAL_FILE" \ --output "eval-results/$SKILL/summary.json" done - name: Compare with baseline id: compare run: | REPORT="" EXIT_CODE=0 for SKILL in ${{ steps.changed.outputs.skills }}; do SUMMARY="eval-results/$SKILL/summary.json" BASELINE=".claude/skills/$SKILL/benchmark.json" if [ ! -f "$SUMMARY" ]; then continue fi NEW_RATE=$(jq -r '.results.with_skill.pass_rate' "$SUMMARY") DELTA=$(jq -r '.results.delta' "$SUMMARY") if [ -f "$BASELINE" ]; then OLD_RATE=$(jq -r '.pass_rate' "$BASELINE") DIFF=$(echo "$NEW_RATE - $OLD_RATE" | bc) if (( $(echo "$DIFF < -0.05" | bc -l) )); then REPORT="$REPORT\n❌ **$SKILL**: pass_rate $OLD_RATE → $NEW_RATE (退化 $DIFF)" EXIT_CODE=1 else REPORT="$REPORT\n✅ **$SKILL**: pass_rate $OLD_RATE → $NEW_RATE (delta: $DELTA)" fi else REPORT="$REPORT\n⚠️ **$SKILL**: pass_rate $NEW_RATE (无 baseline,首次评测)" fi done echo "report<<EOF" >> "$GITHUB_OUTPUT" echo -e "$REPORT" >> "$GITHUB_OUTPUT" echo "EOF" >> "$GITHUB_OUTPUT" exit $EXIT_CODE - name: Comment on PR if: always() uses: actions/github-script@v7 with: script: | const report = `${{ steps.compare.outputs.report }}`; const body = `## Skill Eval Results\n\n${report}\n\n` + `_Auto-generated by skill-eval workflow_`; // 查找已有评论并更新,避免重复评论 const comments = await github.rest.issues.listComments({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number, }); const existing = comments.data.find(c => c.body.includes('Skill Eval Results')); if (existing) { await github.rest.issues.updateComment({ owner: context.repo.owner, repo: context.repo.repo, comment_id: existing.id, body, }); } else { await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number, body, }); }

这个 workflow 做四件事:

  1. 检测哪些 Skill 被改了
  2. 对每个改了的 Skill 跑 eval(with_skill + without_skill)
  3. 和 baseline 对比,检查有没有退化
  4. 把结果评论到 PR 上

触发条件是 .claude/skills/** 路径有变更。你改业务代码不会触发这个 workflow,只有动了 Skill 才跑。

退化阈值

配置里的关键数字是 -0.05。pass_rate 下降超过 5% 就判定为退化,CI 报错。

为什么是 5% 而不是 0?因为 LLM 的输出有随机性。同一个 Skill、同一个 prompt,跑两次的结果不完全一样。允许 5% 的波动是务实的。

如果你的测试用例够多(10 条以上断言),可以把阈值收紧到 3%。用例太少的话,单条断言的随机波动就会导致 pass_rate 剧烈变化。

基线管理

基线是评测的锚点。没有基线,你只知道”这次 pass_rate 是 0.85”,不知道这是进步还是退步。

benchmark.json 版本化提交

每个 Skill 目录下放一个 benchmark.json

{ "version": "2025-04-01", "skill_commit": "abc1234", "pass_rate": 0.85, "delta": 0.40, "total_assertions": 14, "passed_assertions": 12, "eval_details": [ { "name": "simple-function", "pass_rate": 1.0 }, { "name": "complex-pr", "pass_rate": 0.75 }, { "name": "security-vuln", "pass_rate": 1.0 }, { "name": "react-component", "pass_rate": 0.67 } ] }

这个文件提交到 git,和 Skill 一起版本管理。CI 跑 eval 时就拿它做对比。

基线更新流程

不是每次 eval 跑完都更新基线。更新基线意味着”我认可当前的表现水平”。

流程:

  1. 跑 eval,确认 pass_rate 有提升
  2. 人工 review eval 结果,确认提升是真实的(不是因为断言变弱了)
  3. 更新 benchmark.json
  4. 在 commit message 中说明为什么更新基线
git add .claude/skills/code-review/benchmark.json git commit -m "chore(skill): update code-review baseline to 0.88 Added performance check rules, eval pass_rate improved from 0.85 to 0.88. Reviewed eval outputs manually, improvement is genuine."

反模式:CI 自动更新基线。这等于取消了基线的意义——永远和上一次比,永远不退化,但也永远不知道绝对水平在哪。

eval-viewer 可视化

eval 的原始数据是 JSON,不够直观。用 generate_review.py 生成交互式 HTML,可以直观地对比两次 eval 的结果。

生成方式:

python scripts/generate_review.py \ --input eval-results/code-review/ \ --output eval-results/code-review/review.html

生成的 HTML 包含:

  • 输出对比视图:左右分栏,with_skill 和 without_skill 的输出并排显示,差异高亮
  • 断言通过率:每个 eval 用例的通过率,用颜色编码(绿/黄/红)
  • 评分详情:每条断言的判定结果和理由
  • 趋势图:多个 iteration 的 pass_rate 变化曲线

这个 HTML 可以作为 CI 的 artifact 上传,reviewer 在 PR 页面直接下载查看。

在 GitHub Actions 中添加 artifact 上传:

- name: Upload eval report if: always() uses: actions/upload-artifact@v4 with: name: eval-report path: eval-results/ retention-days: 30

团队评测流程

把前面的所有环节串起来,完整流程如下:

改 Skill 本地跑 eval ├─ pass_rate 退化 → 回去改 Skill 提 PR CI 自动跑 eval ──→ 评论 PR(pass_rate 对比) ├─ CI 失败 → 回去改 Skill Owner review ├─ 看 eval report(下载 artifact) ├─ 看 Skill 变更的合理性 ├─ 对照 checklist(第 19 章) 合并到 main 更新 baseline(如果 pass_rate 提升了) 全团队生效

几个注意点:

本地 eval 是可选但强烈推荐的。 CI 跑一次 eval 可能需要几分钟(取决于测试用例数量和 API 延迟)。如果你改了 Skill 就直接提 PR 等 CI 告诉你结果,来回几次就浪费半天。在本地先跑一遍,确认没问题再提 PR。

CI eval 是强制的。 本地 eval 可能因为环境差异(不同的模型版本、不同的上下文状态)得到不同结果。CI 跑的 eval 是标准化的,所有人用同一个环境、同一个模型配置。

Owner review 不只是看 eval 数据。 eval 通过只说明客观指标没退化。Owner 还需要判断:这个变更有没有必要?会不会让 Skill 膨胀(第 17 章)?新规则的措辞够不够精确(第 7 章)?

基线更新是显式操作。 合并 PR 之后,如果 pass_rate 提升了,Owner 手动更新 benchmark.json 并提交。这个操作本身就是一个 commit,有记录可查。

最后一点

评测流水线的成本不低——每次 CI 要调 API、要等结果、要上传 artifact。但这是值得付的成本。

没有自动化评测的 Skill,就像没有自动化测试的代码。刚开始写的时候觉得”我心里有数”,三个月后就是一坨不敢动的东西。

你团队的第一个 CI eval 不需要很完善。从最关键的那一两个 Skill 开始,跑起来再逐步完善。等团队尝到甜头——某次 PR 的 CI 评论提示”pass_rate 退化了 0.12”,reviewer 一看,果然是新规则和旧规则冲突了——以后就没人会质疑”这玩意有必要吗”。

Last updated on