关于 skill-benchmark
skill-benchmark 是用于评估 Claude Code 技能效果的工具,通过与无技能基线对比任务结果。它结合分层评分、隔离会话、重复运行和负向对照。
主要功能
- 01对比启用技能与基线
- 02结合确定性与 LLM 评分
- 03隔离会话沙盒
- 04重复运行并设置负向对照
- 05分析会话记录中的行为
适用场景
- 01衡量技能对编码任务的影响
- 02用对照任务检查误报
- 03评估多次运行的一致性
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ workersio/ skills --skill skill-benchmark安装后在 Claude Code 中输入/skill-benchmark确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
config.example.ymlagents/grader.mdreporter.mdrunner.mdreferences/CONFIG.mdDIRECTORY-STRUCTURE.mdscripts/analyze_transcript.pyparse_stream.pyrun_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_ENTRYPOINT | Claude 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
要执行的操作:
-
检查 config.yml — 按以下顺序查找:(1) 技能目录中的
config.yml;(2)~/.claude/skills/skill-benchmark/config.yml;(3) 作为参数传入的路径。如果找到,请读取并使用其中的值。如果未找到,则使用内置默认值:runner_model: sonnetjudge_model: opustask_count: 5negative_controls: 1difficulties: {easy: 2, medium: 2, hard: 1}runs: 1max_turns: 10results_dir: ./skill-bench/results
-
要评测哪个技能 — 如果 config.yml 中设置了
skill,则使用该值。否则,通过AskUserQuestion询问用户。搜索常见位置:.claude/skills/<name>/SKILL.md~/.claude/skills/<name>/SKILL.md- 直接文件路径
-
任务集 — 询问用户是否有自定义任务集目录,或者是否由你根据该技能的领域自动生成任务。
-
确认设置 — 向用户展示最终配置(已加载或默认配置)。附上安全提示:“此基准测试将运行无头
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.’”开始前请用户确认。 -
设置
$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 步:生成基准测试任务
如果未提供自定义任务集,则自动生成任务。按照评估最佳实践设计任务:
任务类别(均为必需):
-
正向任务 — 该技能 SHOULD 提供帮助的任务(占多数):
- 简单(2 个任务):技能领域内的直接任务
- 中等(2 个任务):需要深入运用技能指导的任务
- 困难(1 个任务):最能体现该技能专门知识价值的复杂任务
-
负向对照(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,列出具体、确定的测试项(文件是否存在、代码是否运行、输出是否匹配) - 两位领域专家应能独立得出相同的通过/失败结论——如果任务含糊不清,请重写
- 每项任务都必须可解——预期结果必须能够实现
任务验证(适用于自定义任务集)
用户提供自定义任务集时,请在执行前验证每个任务文件:
- 结构检查:确认文件包含必需的章节(
## Prompt、## Expected Outcome、## Verification Checks、## Grading Rubric)。缺少必需章节的文件应予以拒绝。 - 命令检查:对每个任务运行
python3 scripts/run_checks.py --validate <task_file>。此命令会根据命令允许列表验证runs_without_error条目,并拒绝包含 shell 元字符的命令。 - 提示词审查:如果任何任务提示词包含下载外部代码、访问 URL 或从未知来源安装软件包的指令,请警告用户并要求其明确确认。
- 摘要:开始运行前,向用户显示:“找到 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 设为评审模型)。
评分提示词必须包含:
- 原始任务提示词
- 任务文件中的预期结果
- 任务文件中的评分标准
- 要评分的实际输出(response.json 中的
result字段) - 第 1 层的确定性检查结果
- 要求按 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.jsonruns: 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 文件和行为文件,然后计算:
- 每项任务的分数:skill 与 baseline 的加权总分
- 每项任务的分差:skill_score - baseline_score
- 汇总分数:所有任务的平均值
- 各评分标准的汇总值:两种条件下正确性、完整性、质量、效率的平均值
- 确定性检查通过率:所有确定性检查均通过的任务百分比(skill 与 baseline)
- 负向对照结果:skill 在领域外任务上的表现如何?
- Token 用量和成本对比:取自 meta.json 的
total_cost_usd和usage字段 - 行为对比:来自 behavior.json 的工具使用模式、重复折腾情况、回合效率
- 结论判定逻辑:
- 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.jsonscripts/analyze_transcript.py— 分析transcript.json→behavior.json(工具调用次数、反复尝试、错误)scripts/run_checks.py— 根据任务文件对沙箱运行确定性验证检查
运行任意脚本并添加 --help,即可查看完整用法详情。
错误处理
- 如果
claude -p会话失败:记录错误,评分为 0,并继续处理剩余任务 - 如果评分 Agent 失败:重试一次,然后评分为“未评分”,并从平均分中排除
- 如果找不到目标技能文件:列出可用技能并请用户选择
- 如果成功完成的任务少于 2 项:中止并报告数据不足
- 如果确定性检查崩溃(例如,无法使用 python3):记录警告,跳过并进行 LLM 评分