mcpField
S
skill-benchmark

skill-benchmark

github.com/workersio测试与质量208112 次安装23 周前更新
代码仓库

关于 skill-benchmark

skill-benchmark 是用于评估 Claude Code 技能效果的工具,通过与无技能基线对比任务结果。它结合分层评分、隔离会话、重复运行和负向对照。

主要功能

  1. 01对比启用技能与基线
  2. 02结合确定性与 LLM 评分
  3. 03隔离会话沙盒
  4. 04重复运行并设置负向对照
  5. 05分析会话记录中的行为

适用场景

  1. 01衡量技能对编码任务的影响
  2. 02用对照任务检查误报
  3. 03评估多次运行的一致性

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/workersio/skills --skill skill-benchmark

安装后在 Claude Code 中输入/skill-benchmark确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

config.example.yml
agents/
grader.md
reporter.md
runner.md
references/
CONFIG.md
DIRECTORY-STRUCTURE.md
scripts/
analyze_transcript.py
parse_stream.py
run_checks.py

详细介绍

技能基准测试

你是一个技能基准测试系统。你的工作是严格评估 Claude Code 技能是否比基准(不使用技能)提升了性能。

方法依据行业最佳实践(Anthropic 和 OpenAI 的评估指南):

  • 分层评分:先进行确定性检查,再由 LLM 评审
  • 每个会话使用独立沙箱——状态干净,不共享产物
  • 多次运行,以考虑非确定性
  • 负向对照任务,用于检测误报
  • 分析对话记录,以判断行为信号

安全声明

此基准测试会启动需要提升权限才能在无头模式下运行的嵌套 claude -p 会话。会使用以下安全敏感标志,并采取相应缓解措施:

标志 / 技术必要原因缓解措施
--dangerously-skip-permissions无头会话中没有人批准工具调用——不使用此标志,进程会永远挂起每个会话都通过 --allowedTools 限制为仅使用 Read、Write、Edit、Bash、Grep、Glob(使用技能模式下还包括 Skill)。不提供网络工具,也不允许访问 MCP。
env -u CLAUDECODE -u CLAUDE_CODE_ENTRYPOINTClaude Code 会通过这些环境变量阻止嵌套 claude -p。必须取消设置才能启动子会话。env -u 仅影响子进程的环境。父会话仍受到保护。
--append-system-prompt确保在使用技能的会话中始终调用该技能,以保证公平比较。不使用此参数时,模型往往会跳过技能。使用固定模板——仅替换 <skill_name>。不会注入外部内容或用户提供的内容。确切提示词会在步骤 1 确认时显示。
runs_without_error 命令任务验证需要执行 python3 <file> 等命令,以检查生成的代码能否运行。命令会根据安全可执行文件许可名单进行验证;若包含 Shell 元字符,则会被拒绝。不使用 shell=True。

**仅使用来自可信来源的任务文件和技能运行基准测试。**继续执行并完成步骤 1 确认,即表示你已知悉这些安全影响。

执行流程

请严格按照以下步骤操作:


步骤 1:收集输入

用户可以通过以下两种方式运行此技能:

选项 1:自定义配置——用户创建一个 config.yml:

cp .claude/skills/skill-benchmark/config.example.yml .claude/skills/skill-benchmark/config.yml
# edit config.yml
/skill-benchmark

选项 2:默认运行——无需配置:

/skill-benchmark
要执行的操作:
  1. 检查 config.yml — 按以下顺序查找:(1) 技能目录中的 config.yml;(2) ~/.claude/skills/skill-benchmark/config.yml;(3) 作为参数传入的路径。如果找到,请读取并使用其中的值。如果未找到,则使用内置默认值:

    • runner_model: sonnet
    • judge_model: opus
    • task_count: 5
    • negative_controls: 1
    • difficulties: {easy: 2, medium: 2, hard: 1}
    • runs: 1
    • max_turns: 10
    • results_dir: ./skill-bench/results
  2. 要评测哪个技能 — 如果 config.yml 中设置了 skill,则使用该值。否则,通过 AskUserQuestion 询问用户。搜索常见位置:

    • .claude/skills/<name>/SKILL.md
    • ~/.claude/skills/<name>/SKILL.md
    • 直接文件路径
  3. 任务集 — 询问用户是否有自定义任务集目录,或者是否由你根据该技能的领域自动生成任务。

  4. 确认设置 — 向用户展示最终配置(已加载或默认配置)。附上安全提示:“此基准测试将运行无头 claude -p 会话,并使用 --dangerously-skip-permissions 和受限工具(--allowedTools)。会话将在隔离的沙盒目录中执行。对于启用了技能的会话,将追加以下系统提示词:‘IMPORTANT: Before starting any work, you MUST first call the Skill tool with skill="<skill_name>" to load the relevant skill instructions.’”开始前请用户确认。

  5. 设置 $RESULTS_DIR — 使用技能名称和时间戳创建结果目录:

    RESULTS_DIR="<results_dir>/<skill_name>-$(date +%Y%m%d-%H%M%S)"
    mkdir -p "$RESULTS_DIR"
    

    后续所有路径(tasks/、sandbox/、outputs/、grades/、report.md)都放在 $RESULTS_DIR 下。不要直接在基础 results_dir 中放置文件——务必放入带时间戳的子目录中。


第 2 步:读取并分析目标技能

完整读取目标技能的 SKILL.md 文件。提取:

  • 领域:此技能涵盖什么领域?(例如,代码审查、测试、部署)
  • 能力:此技能会指导 Claude 执行哪些具体操作?
  • 触发条件:何时应使用此技能?
  • 使用的工具:此技能依赖哪些工具?

撰写简要的分析摘要——之后将用它来生成相关任务。


第 3 步:生成基准测试任务

如果未提供自定义任务集,则自动生成任务。按照评估最佳实践设计任务:

任务类别(均为必需):
  1. 正向任务 — 该技能 SHOULD 提供帮助的任务(占多数):

    • 简单(2 个任务):技能领域内的直接任务
    • 中等(2 个任务):需要深入运用技能指导的任务
    • 困难(1 个任务):最能体现该技能专门知识价值的复杂任务
  2. 负向对照(1 个任务):该技能领域之外的任务,技能不应触发或提供帮助。这有助于发现误报——如果技能会降低无关任务的表现,就应引起警惕。

任务格式

将每项任务写入 $RESULTS_DIR/tasks/task-NN-<difficulty>.md:

# Task: <descriptive-name>
difficulty: easy|medium|hard
category: <domain>
type: positive|negative-control

## Prompt
<the exact prompt that will be sent to Claude via `claude -p`>

## Expected Outcome
<clear description of what a correct response looks like>

## Verification Checks
<deterministic checks to run BEFORE LLM grading>
- file_exists: <filename that should be created>
- file_contains: <pattern> in <filename>  (or just <pattern> to search all files)
- syntax_valid: <language — run syntax checker>
- runs_without_error: <command to execute, e.g., "python3 <filename>">

## Grading Rubric
- Correctness: <specific criteria for correctness>
- Completeness: <what must be included for full marks>
- Quality: <quality expectations — best practices, clarity, etc.>

## Tags
<comma-separated tags for grouping>

任务设计规则:

  • 提示必须独立完整——任务会在全新的 claude -p 会话中运行,不会有任何先前上下文
  • 包含 Verification Checks,列出具体、确定的测试项(文件是否存在、代码是否运行、输出是否匹配)
  • 两位领域专家应能独立得出相同的通过/失败结论——如果任务含糊不清,请重写
  • 每项任务都必须可解——预期结果必须能够实现
任务验证(适用于自定义任务集)

用户提供自定义任务集时,请在执行前验证每个任务文件:

  1. 结构检查:确认文件包含必需的章节(## Prompt、## Expected Outcome、## Verification Checks、## Grading Rubric)。缺少必需章节的文件应予以拒绝。
  2. 命令检查:对每个任务运行 python3 scripts/run_checks.py --validate <task_file>。此命令会根据命令允许列表验证 runs_without_error 条目,并拒绝包含 shell 元字符的命令。
  3. 提示词审查:如果任何任务提示词包含下载外部代码、访问 URL 或从未知来源安装软件包的指令,请警告用户并要求其明确确认。
  4. 摘要:开始运行前,向用户显示:“找到 N 个任务。验证命令:[列表]。是否继续?”

**警告:**切勿运行来自不可信来源的基准测试任务。任务提示词会作为代码生成指令执行,并可在嵌套会话中使用工具。


第 4 步:运行评测会话

对每个任务,使用 claude -p 运行两个会话。每个会话都必须在各自隔离的沙盒目录中运行,以防互相干扰。

多次运行支持

如果配置中的 runs > 1,则每个任务运行 N 次。每次运行都使用独立的沙盒和输出目录。这可以考虑到大模型输出的非确定性。

  • 多次运行的目录结构(runs: 3):

    sandbox/task-01/run-1/with-skill/
    sandbox/task-01/run-1/baseline/
    sandbox/task-01/run-2/with-skill/
    sandbox/task-01/run-2/baseline/
    sandbox/task-01/run-3/with-skill/
    sandbox/task-01/run-3/baseline/
    outputs/task-01/run-1/with-skill/
    outputs/task-01/run-1/baseline/
    outputs/task-01/run-2/with-skill/
    outputs/task-01/run-2/baseline/
    ...
    grades/task-01/run-1/with-skill-grade.json
    grades/task-01/run-1/baseline-grade.json
    ...
    
  • 单次运行的目录结构(runs: 1,默认值):

    sandbox/task-01/with-skill/
    sandbox/task-01/baseline/
    outputs/task-01/with-skill/
    outputs/task-01/baseline/
    grades/task-01/with-skill-grade.json
    grades/task-01/baseline-grade.json
    

    当 runs: 1 时,为简化输出,完全跳过 run-N/ 子目录层级。

  • 多次运行的汇总:为所有运行评分后,按任务计算:

    • avg_score:所有运行的加权总分的平均值
    • best_score:各次运行加权总分中的最高分
    • worst_score:各次运行加权总分中的最低分
    • pass@k:至少有 1 次运行得分 >= 70(任务视为“可通过”)
    • pass^k:所有运行的得分均 >= 70(任务稳定通过)
    • std_dev:得分的标准差(数值越高,行为越不一致)
隔离设置

运行任何会话之前,都要为每个会话创建隔离的工作目录:

# For runs: 1 (default)
mkdir -p "$RESULTS_DIR/sandbox/task-NN/with-skill"
mkdir -p "$RESULTS_DIR/sandbox/task-NN/baseline"

# For runs: 3 (multi-run)
mkdir -p "$RESULTS_DIR/sandbox/task-NN/run-1/with-skill"
mkdir -p "$RESULTS_DIR/sandbox/task-NN/run-1/baseline"
mkdir -p "$RESULTS_DIR/sandbox/task-NN/run-2/with-skill"
# ... etc

每次调用 claude -p 时,都必须先 cd 到其专属沙盒目录。这可以避免:

  • 文件冲突(两个会话都在同一位置写入 fibonacci.py)
  • 一个会话读取另一个会话创建的文件
  • 技能版与基线运行之间共享状态
  • 同一任务不同运行之间共享状态
嵌套会话修复

**关键:**Claude Code 会设置 CLAUDECODE=1 和 CLAUDE_CODE_ENTRYPOINT=cli,以防止递归调用 claude -p。必须通过 env -u CLAUDECODE -u CLAUDE_CODE_ENTRYPOINT 取消设置这些变量,基准测试才能启动子会话。

为何安全:env -u 只会影响子进程环境,不会更改父会话的环境变量。子会话会在受限工具(--allowedTools)的隔离沙盒目录中运行。

会话命令

每次 claude -p 调用都必须包含 --dangerously-skip-permissions——否则,无头会话会一直挂起,等待人工批准工具使用。

会话 A——使用技能:

# For runs: 1 → sandbox/task-NN/with-skill, outputs/task-NN/with-skill
# For runs: 3 → sandbox/task-NN/run-R/with-skill, outputs/task-NN/run-R/with-skill
cd "$SANDBOX_DIR" && \
env -u CLAUDECODE -u CLAUDE_CODE_ENTRYPOINT \
  claude -p "<task_prompt>" \
  --output-format stream-json \
  --verbose \
  --dangerously-skip-permissions \
  --allowedTools "Skill(<skill_name>),Read,Edit,Bash,Grep,Glob,Write" \
  --append-system-prompt "IMPORTANT: Before starting any work, you MUST first call the Skill tool with skill=\"<skill_name>\" to load the relevant skill instructions. Follow whatever instructions the skill provides throughout your work." \
  --model <runner_model> \
  --max-turns <max_turns> \
  > "$OUTPUT_DIR/raw_stream.jsonl" 2>&1

为什么使用 --append-system-prompt? 如果不使用它,技能就只是一个可用工具——模型必须主动选择调用它。对于简单任务,模型往往会完全跳过该技能,直接编写代码。追加系统提示词可确保始终调用该技能,从而公平比较“使用技能说明”和“不使用技能说明”。

会话 B——基线(不使用技能):

cd "$SANDBOX_DIR" && \
env -u CLAUDECODE -u CLAUDE_CODE_ENTRYPOINT \
  claude -p "<task_prompt>" \
  --output-format stream-json \
  --verbose \
  --dangerously-skip-permissions \
  --allowedTools "Read,Edit,Bash,Grep,Glob,Write" \
  --disallowedTools "Skill" \
  --model <runner_model> \
  --max-turns <max_turns> \
  > "$OUTPUT_DIR/raw_stream.jsonl" 2>&1

为什么使用 --disallowedTools "Skill"? Skill 工具是内置工具,仅使用 --allowedTools 并不能限制它。如果不明确禁止使用它,基线模型仍可能调用该技能,导致比较结果受到干扰。

$SANDBOX_DIR 和 $OUTPUT_DIR 取决于运行次数:

  • runs: 1 → $RESULTS_DIR/sandbox/task-NN/<mode> 和 $RESULTS_DIR/outputs/task-NN/<mode>
  • runs: N → $RESULTS_DIR/sandbox/task-NN/run-R/<mode> 和 $RESULTS_DIR/outputs/task-NN/run-R/<mode>

重要:

  • 必须使用 --dangerously-skip-permissions——否则,claude -p 会一直挂起,等待权限批准,但没有人工点击“Allow”。
  • 将 <skill_name> 替换为第 1 步中的实际技能名称(例如 code-commenter)。不要将 Skill() 留空——这表示未加载任何技能,两个会话将变得完全相同。
  • 运行 claude -p 之前,务必先 cd 进入沙盒。这是隔离机制。
  • 由于你会执行 cd,输出重定向(> .../raw_stream.jsonl)请使用绝对路径。
执行策略
  • 并行运行同一任务和运行编号的会话 A 与会话 B(使用后台 Bash 命令)
  • 按顺序处理任务,避免系统不堪重负
  • 多次运行时:完成 task-01 的所有运行后,再开始 task-02
  • 对于同一任务,如果系统资源允许,可以并行运行多次
  • 每个会话完成后,解析 raw_stream.jsonl,在输出目录中生成以下三个文件:

response.json——从 JSONL 流中提取最后一个 type: "result" 事件。

transcript.json——将所有流事件收集到一个 JSON 数组中。

meta.json——从 response.json 中提取会话元数据。包含:session_id、model(取自 modelUsage 键)、skill_name、mode、stop_reason、duration_ms、duration_api_ms、num_turns、total_cost_usd 和 usage(输入/输出/缓存令牌)。scripts/parse_stream.py 脚本负责处理此提取;运行 --help 可查看完整字段列表。

如果会话失败或超时,请在 meta.json 中记录错误,并将其标记为失败运行(得分:0)。


步骤 5:为输出评分(分层评分)

采用两层评分方法:先进行确定性检查,再使用 LLM 作为评审。这样可以快速发现明显失败,并利用模型进行细致评估。

第一层:确定性检查

对每个会话输出,运行确定性检查脚本:

# For runs: 1 → sandbox/task-NN/<mode>, grades/task-NN/<mode>-checks.json
# For runs: N → sandbox/task-NN/run-R/<mode>, grades/task-NN/run-R/<mode>-checks.json
python3 "scripts/run_checks.py" \
  "$RESULTS_DIR/tasks/task-NN-<difficulty>.md" \
  "$SANDBOX_DIR" \
  "$GRADES_DIR/<mode>-checks.json"

此脚本会读取任务文件中的 ## Verification Checks 部分,并在沙盒目录中运行每项检查(file_exists、syntax_valid、runs_without_error、file_contains)。

将结果保存到 $RESULTS_DIR/grades/task-NN/<mode>-checks.json:

{
  "file_exists": true,
  "syntax_valid": true,
  "runs_without_error": true,
  "file_contains": {"def add": true, "def subtract": true},
  "all_passed": true
}

如果确定性检查失败(文件缺失、语法错误、运行时崩溃),无论 LLM 评分结果如何,该任务的正确性得分上限均为 50——代码无法正常工作。

第 2 层:LLM-as-Judge

对于每个任务,启动一个 评分子 Agent(使用 Agent 工具,subagent_type: "general-purpose",并将 model 设为评审模型)。

评分提示词必须包含:

  1. 原始任务提示词
  2. 任务文件中的预期结果
  3. 任务文件中的评分标准
  4. 要评分的实际输出(response.json 中的 result 字段)
  5. 第 1 层的确定性检查结果
  6. 要求按 0–100 分为每项标准打分并说明理由

还要告知评分 Agent,读取会话在沙盒目录($RESULTS_DIR/sandbox/task-NN/<mode>/)中创建的实际文件,以验证正确性;不要只根据文本输出评分。

独立评估每个输出(不要让评分 Agent 同时看到两个输出,以免产生比较偏差)。

评分标准和默认权重:

  • 正确性(40%):输出是否正确地解决了任务?若确定性检查失败,最高只能得 50 分。
  • 完整性(25%):是否满足了所有要求?
  • 质量(20%):代码质量、最佳实践、说明清晰度
  • 效率(15%):解决方案是否直接高效?(还要考虑 token 用量)

评分 Agent 必须返回结构化响应。要求其输出 JSON:

{
  "deterministic_checks_passed": true|false,
  "correctness": { "score": 0-100, "justification": "..." },
  "completeness": { "score": 0-100, "justification": "..." },
  "quality": { "score": 0-100, "justification": "..." },
  "efficiency": { "score": 0-100, "justification": "..." },
  "weighted_total": 0-100,
  "summary": "..."
}

将评分结果保存到对应的评分目录:

  • runs: 1 → $RESULTS_DIR/grades/task-NN/with-skill-grade.json 和 baseline-grade.json
  • runs: N → $RESULTS_DIR/grades/task-NN/run-R/with-skill-grade.json 和 baseline-grade.json

你可以使用后台 Agent 并行运行不同任务或运行的评分。


第 6 步:分析会话记录

生成报告前,分析 transcript.json 文件中的行为信号。这一点至关重要——仅凭分数无法全面反映情况。

对每个会话,运行分析脚本:

# For runs: 1 → outputs/task-NN/<mode>/
# For runs: N → outputs/task-NN/run-R/<mode>/
python3 "scripts/analyze_transcript.py" \
  "$OUTPUT_DIR/transcript.json" \
  "$OUTPUT_DIR/behavior.json"

该脚本会从 transcript.json 中提取:

  • 工具调用次数:每种工具的使用次数(Read、Write、Edit、Bash 等)
  • 重复折腾检测:会话是否陷入循环或重复尝试同一操作?(连续调用同一工具 3 次或以上)
  • 错误恢复:会话遇到错误后是否恢复,还是悄无声息地失败?

输出(behavior.json):

{
  "tool_calls": {"Read": 2, "Write": 1, "Bash": 3, "Edit": 0},
  "total_tool_calls": 6,
  "thrashing_detected": false,
  "errors_encountered": 0,
  "errors_recovered": 0
}

第 7 步:生成对比报告

完成所有评分和分析后,生成最终报告。

读取所有评分文件、meta 文件和行为文件,然后计算:

  1. 每项任务的分数:skill 与 baseline 的加权总分
  2. 每项任务的分差:skill_score - baseline_score
  3. 汇总分数:所有任务的平均值
  4. 各评分标准的汇总值:两种条件下正确性、完整性、质量、效率的平均值
  5. 确定性检查通过率:所有确定性检查均通过的任务百分比(skill 与 baseline)
  6. 负向对照结果:skill 在领域外任务上的表现如何?
  7. Token 用量和成本对比:取自 meta.json 的 total_cost_usd 和 usage 字段
  8. 行为对比:来自 behavior.json 的工具使用模式、重复折腾情况、回合效率
  9. 结论判定逻辑:
    • Delta >= +10%:使用 — skill 带来显著帮助
    • Delta 在 +3% 到 +10% 之间:可能使用 — skill 带来一定收益
    • Delta 在 -3% 到 +3% 之间:中性 — skill 几乎没有影响
    • Delta 在 -10% 到 -3% 之间:可能不使用 — skill 可能带来负面影响
    • Delta <= -10%:不要使用 — skill 带来显著负面影响

按照以下格式将报告写入 $RESULTS_DIR/report.md:

# Skill Benchmark Report: <skill-name>
Date: <YYYY-MM-DD HH:MM>
Runner Model: <model> | Judge Model: <model> | Tasks: <N> | Runs: <R>

## Verdict: <emoji> <VERDICT>
**Skill scores <X>% <higher/lower> than baseline on average.**

## Summary
| Metric | With Skill | Baseline | Delta |
|--------|-----------|----------|-------|
| Avg Score | X% | Y% | +/-Z% |
| Correctness | X% | Y% | +/-Z% |
| Completeness | X% | Y% | +/-Z% |
| Quality | X% | Y% | +/-Z% |
| Efficiency | X% | Y% | +/-Z% |

## Deterministic Check Pass Rate
| Condition | Pass Rate |
|-----------|-----------|
| With Skill | X/N tasks (Y%) |
| Baseline | X/N tasks (Y%) |

## Per-Task Breakdown
| # | Task | Type | Difficulty | Skill | Baseline | Delta | Winner |
|---|------|------|-----------|-------|----------|-------|--------|
| 1 | ... | positive | easy | X% | Y% | +/-Z% | Skill/Baseline |
| N | ... | negative | - | X% | Y% | +/-Z% | ... |

## Negative Control Results
<How did the skill perform on out-of-domain tasks? If it hurt performance, flag this.>

## Where Skill Helps
- <identified patterns where skill outperformed baseline>

## Where Skill Hurts
- <identified patterns where baseline outperformed skill>

## Behavioral Analysis
| Metric | With Skill | Baseline | Delta |
|--------|-----------|----------|-------|
| Avg Tool Calls | X | Y | +/-Z |
| Avg Turns | X | Y | +/-Z |
| Thrashing Detected | X/N | Y/N | |
| Avg Duration (s) | X | Y | +/-Z |
| Avg Cost | $X | $Y | +/-$Z |
| Total Cost | $X | $Y | +/-$Z |

## Recommendations
- <actionable suggestions based on the results>
- <suggestions for improving the skill if it underperformed>
- <flag if skill hurts negative control tasks>

向用户展示报告,并告知完整结果的保存位置。


参考资料

  • 输出目录结构 — 单次运行和多次运行模式下的完整目录树
  • 配置 — 配置格式、变量和解析方式

可用脚本

  • scripts/parse_stream.py — 解析 raw_stream.jsonl → response.json、transcript.json、meta.json
  • scripts/analyze_transcript.py — 分析 transcript.json → behavior.json(工具调用次数、反复尝试、错误)
  • scripts/run_checks.py — 根据任务文件对沙箱运行确定性验证检查

运行任意脚本并添加 --help,即可查看完整用法详情。

错误处理

  • 如果 claude -p 会话失败:记录错误,评分为 0,并继续处理剩余任务
  • 如果评分 Agent 失败:重试一次,然后评分为“未评分”,并从平均分中排除
  • 如果找不到目标技能文件:列出可用技能并请用户选择
  • 如果成功完成的任务少于 2 项:中止并报告数据不足
  • 如果确定性检查崩溃(例如,无法使用 python3):记录警告,跳过并进行 LLM 评分

评论

支持 Markdown