AREX Feed Article
Claude Code 新增 claude plugin eval:给插件跑分,并与无插件基线对比
Claude 的官方开发者账号 在 9 月 11 日 19:56 UTC(北京时间 9 月 12 日凌晨)发布一条四帖线程,宣布 Claude Code 新增 claude plugin eval:插件或 skill 的作者可以为它建立带评分的测试用例,先带着插件跑一遍,再在没有插件的情况下重跑同一批用例,用两次结果的区别判断插件起了什么作用。按官方说法,这个功能的用途是「看清你的插件带来了多少价值,或者它是否需要继续打磨」。
推文附带的官方文档页 写明了门槛与代价:运行 claude plugin eval 需要 Claude Code v2.1.269 或更新版本;每个用例默认在「有插件」和「没有插件」两条臂上各跑 3 次;每一次运行、每一个由模型打分的检查项,都是账户上真实的模型调用,计入订阅计划的用量或 API 账单。
用例由 Claude 起草,--bare 留给想手写的人
给出的流程是:在插件的文件夹里运行 claude plugin eval init,把好输出和坏输出长什么样告诉 Claude,再带上几条真实 prompt,Claude 据此起草测试用例与检查项,试跑(pilot)整套用例,并告诉用户完整跑一遍的成本。接着运行 claude plugin eval,说明终端会显示每个用例在有插件和没有插件时的得分,并生成一份含完整细节的 HTML 报告。
文档补上了中间步骤。如果 Claude Code 还没信任这个目录,它会先问 Trust this plugin directory?,回答之后打开一个交互式会话:Claude 读插件、问什么样才算好结果、提出应当触发与不应当触发插件的 prompt、为每个 prompt 设计检查项并试跑一次确认行为,最后在 evals/ 下按 prompt 写入一个个用例目录。退出会话用 /exit 或 Ctrl+D 回到 shell;已经开着 Claude Code 会话的人,也可以直接在那个会话里让 Claude 运行 claude plugin eval init。
不想让 Claude 起草的话,claude plugin eval init --bare <name> 只写一套空模板、不运行任何东西。用例本身是普通文件:evals/ 下一个子目录就是一个用例,里面有 prompt.md(frontmatter 设运行上限、可用的工具和模型,正文是发给 Claude 的 prompt)、可选的 case.yaml 和 graders/ 目录。推文里写的运行命令是 claude plugin eval,文档里的写法是站在插件根目录执行 claude plugin eval .,也就是跑完 eval 目录下的全部用例。
打分:四种检查不花钱,两种要调裁判模型
文档把检查项(grader)定义为对 Claude 产出的「通过/不通过」判断:可以对回复做正则匹配,可以看某个工具是否被调用,也可以交给第二个模型按评分标准判断。六种类型里,regex、tool_used、tool_order、file_exists 从会话记录和文件里算出来、不额外花钱;llm 和 baseline 要调一个裁判模型,计入成本,裁判默认是一个小而快的模型,可以用 --judge-model 换成更强的模型。文档明确说不支持自定义代码检查项。
计分口径是:一次运行的分数等于这次运行里通过的检查项占比(可以给检查项设权重),一个用例的分数是它各次运行分数的均值;用例达到 --threshold 才算通过,这个阈值默认是 1.0。llm 检查项由裁判模型投三次票,至少两次 PASS 才算通过。
花销的估算方式在文档里写得很直白:一套用例大致产生「用例数 × 运行次数」次带插件的 agent 运行,加上同样多的无插件运行,再加上每个 llm 或 baseline 检查项每次运行 3 次短裁判调用。终端摘要里的 COST 列就是这些模型调用的标价估算。
提醒评测会调用模型,所以消耗 token、结果也会有波动,完整运行前先用 --runs 1 试跑。文档补充了另一面:单次运行噪声大,只适合在迭代时低成本看一个用例,任何改动都要在默认的三次运行下确认再信。推文下有追问,这套评分是否也把插件自身的 token 成本算进去,还是只算输出质量;文档把两件事分开记录,分数由检查项的通过情况决定,成本作为标价估算单独列一行。
WITH、W/OUT 与 Δ:分数高不等于插件有用
文档用专门一节解释基线(baseline):插件得高分本身说明不了插件帮了忙,因为 Claude 不用插件可能做得一样好。所以默认每个用例都会在没有插件的情况下重跑一遍,得到 WITH 和 W/OUT 两个分数,两者的差 Δ 就是插件带来的增量;如果一个用例在有插件和无插件时都是 1.0,它的通过就不是插件带来的。两条臂的分数和 Δ 会同时出现在摘要表和报告里。
为了让两条臂可比,tool_used: Skill 这类「技能被调用过」的检查项不计入任何一臂的分数,只在有插件那一臂作为触发指示显示;文档说明这样做是因为这类检查在无插件时永远不可能通过,计入会把 Δ 抬高。如果一个用例里所有检查项都属于这一类别,它们就照常计分。--ablation none 只跑有插件那一臂,在迭代检查项时可以省掉一半成本。
文档还点出了一个最常见的首次发现:Δ 接近零,同时那个检查技能是否被调用的检查项失败,意味着 Claude 没有在自然措辞下选中这个 skill,要改的通常是 skill 的描述,改完重跑同一套用例再比较。在推文下把这条命令称为对「安慰剂插件」的清理:「如果去掉你的 skill,agent 的表现一样,那恭喜,你只是多写了一段上下文。」
报告:终端表格、可离线打开的 HTML、私有 artifact
套件跑完后终端先给摘要表,列为 CASE、WITH、W/OUT、Δ、RUNS、COST 和 NOTES,其中 NOTES 显示权重最高的失败检查项的解释,或者该臂的错误信息。文档配的示例报告里,一个叫 commit-helper v1.2.0 的插件在 3 个用例上得到套件分数 94.4%,无插件基线 61.1%,Δ 为 +33.3 分,3 个用例中 2 个变好、1 个持平、0 个退化,18 次运行耗时 2 分 22 秒,花费 0.93 美元。
report.html 是单个自包含文件、不发起外部请求,可以从磁盘直接打开,也可以作为 CI 产物带出去。报告顶部是套件层面的结论:套件分数、相对基线的 Δ、达标用例数,以及「每一次运行的所有检查项都通过」的运行占比;往下逐个用例展开,失败的检查项默认展开并附解释,llm 检查项还会给出裁判的投票和它读到的证据片段,不计入分数的检查项带有插件触发指示。每一轮至少跑出一个用例的运行,都会在 eval 目录下写 results/<时间戳>/,里面除报告外还有 aggregate-result.json,那是一个 schemaVersion 为 1 的版本化文档、字段用 camelCase,供 CI 脚本解析。
报告的去向分三种:如果以 claude.ai 订阅身份登录且账户支持 artifacts,它会作为私有 artifact 发布,终端多打一行 Published: 链接;--no-publish 让它留在本地;由 Claude Code 会话发起的运行默认不发布,可以加 --publish-report 强制发布。用 API key 认证时不会出现 Published: 行,本地文件就是报告。
只评测信得过的插件:hooks 和 MCP 服务器按你的身份跑
线程本身给了两条使用提醒:插件的 hooks 与 MCP 服务器会以用户身份运行,所以只应评测可信的插件;试用前先运行 claude update。
文档解释了这背后的信任模型。第一次对某个目录跑评测时,Claude Code 要先问 Trust this plugin directory?,在 git 仓库里回答 yes 会连带信任整个仓库;当标准输入输出不是终端,或者在 --json 下运行时,它无法提问,会把这次运行直接拒绝(退出码 1),CI 里需要传 --trust-plugin 由使用者自己承担这个判断。用名字指定已安装插件或 skills 目录插件时跳过这个提示。
隔离的做法是给每次运行一个一次性的 home 目录、工作目录和 Claude Code 配置,被测 agent 作为 claude -p 子进程运行,只加载被测插件;用户设置、CLAUDE.md、其他已安装插件、个人 MCP 服务器都不会加载,大多数 shell 环境变量也不传进去;用例定义对被测 agent 不可见,它读不到 eval 目录,也就看不到用例的 prompt、检查项或兄弟用例;运行里 Artifact 工具是关闭的。文档同时写清了这条边界的限度:隔离限制的是被测 agent 能碰到什么,并不构成针对插件自身代码的防线,文档的说法是「套件跑通说明不了插件是否安全」。当插件带的 hooks 不是你写的,或者你启动了它的真实 MCP 服务器时,除非在容器或 CI runner 这类隔离环境里运行,分数只能当作参考,因为 hooks 和服务器运行在 agent 沙箱之外,可能改到检查项要读的文件。
权限默认收得很紧:只有一组只读工具可用,Bash、Write、Edit、WebFetch、WebSearch 这类需要在 --allow-tools 里显式授予,没授予就从会话里移除,Claude 无法调用;插件自己的 MCP 服务器默认不启动,除非传 --allow-real-servers 或 --mocks off,启动后的工具还要按名字授权,而被 mock 顶替的工具不需要授权。授予 Bash 后,命令跑在 Claude Code 的 OS 级沙箱里,写入被限制在本次运行的工作目录,home 目录和 Claude Code 配置不可读。
当 CI 门禁用:退出码、花销上限和服务端开关
文档给出了 CI 的推荐用法:用 --json results.json 写出结果用于归档,靠退出码判定成败;传 --trust-plugin 让任务不会卡在信任提示上;固定 --model 与 --judge-model,避免模型换代被误读成插件回归;--no-publish 让报告留在本地;用 --max-cost-usd 设花销上限,这个上限管的是标价成本估算而非订阅额度,在每次运行开始前检查,已经起跑的运行会跑完,因此实际花销可能略微超过上限。
退出码的含义是:0 表示所有用例都达到阈值;1 表示有用例低于阈值、用例文件加载失败、找不到用例、运行无法启动、目录不受信任又没有传 --trust-plugin,或者选项非法;2 表示部分运行,可能是撞到花销上限,或者凭据在第一次运行前被拒,此时 JSON 里带 partial: true;130 是被中断,143 是被终止。写或发布 HTML 报告失败不会改变退出码。
有一个容易误读的坑:如果运行途中撞上订阅计划的用量上限或 API 速率限制,之后的每次运行都会以这个错误结束,仍然按已有产出打分(通常为 0),而套件照常跑完、不会被标记成 partial,结果看起来就像一次真实的回归。文档建议先查 NOTES 列或 JSON 里的错误字段,确认是不是额度问题,再决定什么时候重跑。
文档列出的两条错误信息说明这个命令还带着服务端开关。plugin eval is currently in early access 表示本地构建早于该命令的正式可用,运行 claude update 后在新会话里重试;plugin eval is currently unavailable 表示 Anthropic 已在服务端把这个命令关掉,本地没有任何开关能打开它,只能更新后换个时间再试。