评估 Skill 输出质量
本页为评估指南的非官方中文翻译。
评估 Skill 输出质量
Section titled “评估 Skill 输出质量”你写了一个 Skill,在某个提示上尝试过,它似乎有效。但它在各种提示、边界情况下、并且在没有 Skill 的情况下都能稳定地做得更好吗?运行结构化评估(evals)可以回答这些问题,并为你提供一个系统改进 Skill 的反馈循环。
设计测试用例
Section titled “设计测试用例”一个测试用例由三部分组成:
- Prompt / 提示:一个真实的用户消息 — 用户实际会输入的那类内容。
- Expected output / 预期输出:对成功是什么样子的可读描述。
- Input files / 输入文件(可选):Skill 需要处理的输入文件。
将测试用例存储在 Skill 目录下的 evals/evals.json 中:
{ "skill_name": "csv-analyzer", "evals": [ { "id": 1, "prompt": "I have a CSV of monthly sales data in data/sales_2025.csv. Can you find the top 3 months by revenue and make a bar chart?", "expected_output": "A bar chart image showing the top 3 months by revenue, with labeled axes and values.", "files": ["evals/files/sales_2025.csv"] }, { "id": 2, "prompt": "there's a csv in my downloads called customers.csv, some rows have missing emails — can you clean it up and tell me how many were missing?", "expected_output": "A cleaned CSV with missing emails handled, plus a count of how many were missing.", "files": ["evals/files/customers.csv"] } ]}编写好的测试提示的技巧:
- 从 2-3 个测试用例开始。 在看到第一轮结果之前不要过度投入。之后你可以扩展测试集。
- 变换提示。 使用不同的措辞、详细程度和正式程度。有些提示应当随意(“嘿,你能帮我清理这个 csv 吗”),有些提示应当精确(“解析 data/input.csv 中的 CSV,删除 B 列为空的行,并将结果写入 data/output.csv”)。
- 覆盖边界情况。 至少包含一个测试边界条件的提示 — 格式错误的输入、不寻常的请求,或 Skill 指令可能存在歧义的情况。
- 使用真实的上下文。 真实用户会提及文件路径、列名和个人上下文。像”处理这份数据”这样的提示过于模糊,无法测试任何有用的东西。
暂时不必定义具体的通过/失败检查 — 只要有提示和预期输出即可。在看到第一轮运行的输出后,你将添加详细的检查(称为断言)。
核心模式是针对每个测试用例运行两次:一次 使用 Skill,一次 不使用 Skill(或使用之前的版本)。这为你提供了一个可供比较的基线。
在你的 Skill 目录旁边的工作区目录中组织评估结果。每次完整地通过评估循环都有自己的 iteration-N/ 目录。其中,每个测试用例都有一个包含 with_skill/ 和 without_skill/ 子目录的评估目录:
csv-analyzer/├── SKILL.md└── evals/ └── evals.jsoncsv-analyzer-workspace/└── iteration-1/ ├── eval-top-months-chart/ │ ├── with_skill/ │ │ ├── outputs/ # 运行产生的文件 │ │ ├── timing.json # tokens 和耗时 │ │ └── grading.json # 断言结果 │ └── without_skill/ │ ├── outputs/ │ ├── timing.json │ └── grading.json ├── eval-clean-missing-emails/ │ ├── with_skill/ │ │ ├── outputs/ │ │ ├── timing.json │ │ └── grading.json │ └── without_skill/ │ ├── outputs/ │ ├── timing.json │ └── grading.json └── benchmark.json # 汇总统计你手工编写的主要文件是 evals/evals.json。其他 JSON 文件(grading.json、timing.json、benchmark.json)在评估过程中生成 — 由智能体、脚本或你生成。
每次评估运行都应从一个干净的上下文开始 — 没有先前运行或 Skill 开发过程的遗留状态。这确保智能体仅遵循 SKILL.md 所告诉它的内容。在支持子智能体的环境中(Claude Code 就是这种情况),这种隔离是自然实现的:每个子任务都从零开始。如果没有子智能体,请为每次运行使用单独的会话。
对于每次运行,请提供:
- Skill 路径(或基线则不提供 Skill)
- 测试提示
- 任何输入文件
- 输出目录
以下是你会为单个带 Skill 的运行给智能体的指令示例:
Execute this task:- Skill path: /path/to/csv-analyzer- Task: I have a CSV of monthly sales data in data/sales_2025.csv. Can you find the top 3 months by revenue and make a bar chart?- Input files: evals/files/sales_2025.csv- Save outputs to: csv-analyzer-workspace/iteration-1/eval-top-months-chart/with_skill/outputs/对于基线,请使用相同的提示但不带 Skill 路径,并保存到 without_skill/outputs/。
改进现有 Skill 时,请使用之前的版本作为基线。在编辑之前对其进行快照(cp -r <skill-path> <workspace>/skill-snapshot/),将基线运行指向该快照,并将输出保存到 old_skill/outputs/ 而不是 without_skill/。
捕获计时数据
Section titled “捕获计时数据”计时数据让你能够比较 Skill 相对于基线所花费的时间和 tokens — 一个大幅提升输出质量但使 tokens 用量增加三倍的 Skill,与一个既更好又更便宜的 Skill,是不同的权衡。每次运行完成时,请记录 token 数量和耗时:
{ "total_tokens": 84852, "duration_ms": 23332}译者注:在 Claude Code 中,当子智能体任务完成时,任务完成通知 包含
total_tokens和duration_ms。请立即保存这些值 — 它们不会在其他地方持久化。
断言是关于输出应包含或实现什么的可验证陈述。在看到第一轮输出后再添加它们 — 在 Skill 运行之前,你通常不知道”良好”是什么样的。
良好的断言:
"输出文件是有效的 JSON"— 可通过编程验证。"条形图具有带标签的坐标轴"— 具体且可观察。"报告至少包含 3 条建议"— 可数。
弱断言:
"输出是好的"— 过于模糊,无法评分。"输出恰好使用了短语 'Total Revenue: $X'"— 过于脆弱;正确但措辞不同的输出也会失败。
并非所有内容都需要断言。一些质量(写作风格、视觉设计、输出是否”感觉正确”)难以分解为通过/失败检查。这些最好在 人工审核 时发现。将断言留给那些可以客观检查的事项。
将断言添加到 evals/evals.json 中的每个测试用例:
{ "skill_name": "csv-analyzer", "evals": [ { "id": 1, "prompt": "I have a CSV of monthly sales data in data/sales_2025.csv. Can you find the top 3 months by revenue and make a bar chart?", "expected_output": "A bar chart image showing the top 3 months by revenue, with labeled axes and values.", "files": ["evals/files/sales_2025.csv"], "assertions": [ "The output includes a bar chart image file", "The chart shows exactly 3 months", "Both axes are labeled", "The chart title or caption mentions revenue" ] } ]}对输出进行评分
Section titled “对输出进行评分”评分意味着根据实际输出评估每个断言,并以具体证据记录 PASS 或 FAIL。证据应引用或参考输出,而不仅仅是陈述观点。
最简单的方法是将输出和断言交给 LLM,并要求它评估每个断言。对于可以通过代码检查的断言(有效的 JSON、正确的行数、具有预期维度的文件存在),请使用验证脚本 — 对于机械检查,脚本比 LLM 判断更可靠,并且可以在迭代之间复用。
{ "assertion_results": [ { "text": "The output includes a bar chart image file", "passed": true, "evidence": "Found chart.png (45KB) in outputs directory" }, { "text": "The chart shows exactly 3 months", "passed": true, "evidence": "Chart displays bars for March, July, and November" }, { "text": "Both axes are labeled", "passed": false, "evidence": "Y-axis is labeled 'Revenue ($)' but X-axis has no label" }, { "text": "The chart title or caption mentions revenue", "passed": true, "evidence": "Chart title reads 'Top 3 Months by Revenue'" } ], "summary": { "passed": 3, "failed": 1, "total": 4, "pass_rate": 0.75 }}- PASS 需要具体证据。 不要凭空臆断。如果某个断言说”包含摘要”而输出有一个标题为”Summary”的部分,其中只有一句含糊的句子,那么这是 FAIL — 标签在那里但实质内容缺失。
- 检查断言本身,而不仅仅是结果。 在评分过程中,注意断言是否过于容易(无论 Skill 质量如何都始终通过)、过于困难(即使输出良好也始终失败),或无法验证(无法仅从输出进行检查)。针对下一次迭代修复这些问题。
译者注:要比较两个 Skill 版本,请尝试 盲测比较:将两个输出交给 LLM 评判,而不透露哪个来自哪个版本。评判者根据其自己的评分标准,对组织、格式、可用性、润色等整体质量进行评分,避免关于哪个版本”应该”更好的偏见。这补充了断言评分:两个输出可能都通过所有断言,但在整体质量上有显著差异。
一旦迭代中的每次运行都已评分,就为每个配置计算汇总统计信息,并将其保存到评估目录旁边的 benchmark.json(例如 csv-analyzer-workspace/iteration-1/benchmark.json):
{ "run_summary": { "with_skill": { "pass_rate": { "mean": 0.83, "stddev": 0.06 }, "time_seconds": { "mean": 45.0, "stddev": 12.0 }, "tokens": { "mean": 3800, "stddev": 400 } }, "without_skill": { "pass_rate": { "mean": 0.33, "stddev": 0.10 }, "time_seconds": { "mean": 32.0, "stddev": 8.0 }, "tokens": { "mean": 2100, "stddev": 300 } }, "delta": { "pass_rate": 0.50, "time_seconds": 13.0, "tokens": 1700 } }}delta 告诉你 Skill 的成本(更多时间、更多 tokens)以及它带来的收益(更高的通过率)。一个增加 13 秒但将通过率提高 50 个百分点的 Skill 可能是值得的。一个为提高 2 个百分点而使 tokens 用量翻倍的 Skill 则可能不值得。
译者注:标准差(
stddev)仅在每个评估有多个运行的情况下才有意义。在只有 2-3 个测试用例和单次运行的早期迭代中,请关注原始通过计数和 delta — 随着你扩展测试集并多次运行每个评估,统计指标将变得有用。
汇总统计可能掩盖重要的模式。在计算基准之后:
- 删除或替换在两种配置中始终通过的断言。 这些不会告诉你任何有用的信息 — 模型在没有 Skill 的情况下也能很好地处理它们。它们会抬高带 Skill 的通过率,却无法反映实际的 Skill 价值。
- 调查在两种配置中始终失败的断言。 要么是断言有问题(要求模型无法完成的事情)、测试用例太难,要么是断言检查的东西不正确。在下一次迭代之前修复这些问题。
- 研究那些在使用 Skill 时通过、而不使用时失败的断言。 这就是 Skill 明显增加价值的地方。理解 为什么 — 是哪些指令或脚本产生了差异?
- 当结果在不同运行中不一致时,加强指令。 如果同一个评估有时通过有时失败(在基准中表现为高
stddev),则该评估可能是 flaky(对模型随机性敏感),或者 Skill 的指令可能足够模糊,以致模型每次都会对其进行不同的解释。添加示例或更具体的指导以减少歧义。 - 检查时间和 token 离群值。 如果一个评估的耗时是其他评估的 3 倍,请阅读其执行轨迹(模型在运行期间所做操作的完整日志)以找出瓶颈。
由人工审核结果
Section titled “由人工审核结果”断言评分和模式分析能发现很多问题,但它们只检查了你事先想到要编写断言的内容。人工审核者带来全新的视角 — 找出你没有预料到的问题、注意那些技术上正确但偏离要点的情况,或发现难以用通过/失败检查表达的问题。对于每个测试用例,请同时查看实际输出和评分。
记录每个测试用例的具体反馈,并将其保存在工作区中(例如,作为评估目录旁边的 feedback.json):
{ "eval-top-months-chart": "The chart is missing axis labels and the months are in alphabetical order instead of chronological.", "eval-clean-missing-emails": ""}“图表缺少坐标轴标签”是可操作的;“看起来很差”则不是。空反馈意味着输出看起来不错 — 该测试用例通过了你的审核。在 迭代改进 Skill 步骤中,请将你的改进重点放在你收到具体意见的测试用例上。
迭代改进 Skill
Section titled “迭代改进 Skill”在评分和审核之后,你拥有三类信号:
- 失败的断言 指向具体的差距 — 一个缺失的步骤、一个不清晰的指令,或 Skill 未处理的情况。
- 人工反馈 指向更广泛的质量问题 — 方法不对、输出结构不良,或 Skill 产生了技术正确但无用的结果。
- 执行轨迹 揭示了 为什么 事情出错。如果智能体忽略了某条指令,则该指令可能存在歧义。如果智能体在不具生产力的步骤上花费了时间,则这些指令可能需要简化或删除。
将这些信号转化为 Skill 改进的最有效方法是,将这三者以及当前的 SKILL.md 一起交给 LLM,并要求它提出修改建议。LLM 可以综合失败断言、审核者意见和轨迹行为中的模式,而手动连接这些模式会很繁琐。在提示 LLM 时,请包含以下指导原则:
- 从反馈中归纳总结。 Skill 将被用于许多不同的提示,而不仅仅是测试用例。修复方案 SHOULD 广泛地解决潜在问题,而不是为特定示例添加狭窄的补丁。
- 保持 Skill 精简。 少而精的指令往往胜过详尽的规则。如果轨迹显示存在浪费的工作(不必要的验证、不需要的中间输出),请删除这些指令。如果在添加更多规则后通过率停滞不前,则 Skill 可能过于受约束 — 尝试删除指令并查看结果是保持还是改善。
- 解释原因。 基于推理的指令(“执行 X,因为 Y 往往会导致 Z”)比僵化的指令(“始终执行 X,绝不执行 Y”)更有效。当模型理解目的时,会更可靠地遵循指令。
- 打包重复的工作。 如果每次测试运行都独立编写了类似的辅助脚本(图表构建器、数据解析器),那么这就是一个信号:应将该脚本打包到 Skill 的
scripts/目录中。有关如何执行此操作,请参阅 使用脚本。
- 将评估信号和当前的
SKILL.md交给 LLM,并要求它提出改进建议。 - 审查并应用这些更改。
- 在新的
iteration-<N+1>/目录中重新运行所有测试用例。 - 对新结果进行评分并汇总。
- 由人工审核。重复此过程。
当你对结果满意、反馈始终为空,或者在迭代之间不再看到有意义的改进时,请停止。
译者注:
skill-creatorSkill 可以自动化此工作流的许多部分 — 运行评估、断言评分、汇总基准,并提供结果供人工审核。