claude plugin evalの使い方 — 現実的なケースの書き方とCI組み込み
claude plugin evalでプラグインの挙動を検証する実践ガイド。プロンプトとgraderの設計、no-pluginとの差分測定、CIゲート化までを扱います。
claude plugin evalは、Claude Codeのプラグインを実際に動かして採点するコマンドです。コマンド自体の起動方法・主要フラグ・終了コードはClaude Code v2.1.269のリリースノートで扱っています。本記事はその先、どんなケースを書けば意味のある検証になるかに絞ります。現実的なプロンプトの作り方、graderの設計、no-pluginベースラインとの差分の読み方、CIへの組み込み方です。
claude plugin evalとは
claude plugin evalは、プラグインのevalスイートを実行して結果を採点するコマンドです。ケースは現実的なプロンプト1つと、grader(採点基準)1つ以上の組み合わせで、graderは応答のregexマッチ・特定ツールが呼ばれたか・第二のモデルによるルーブリック判定のいずれかです。用途は3つあります。プラグインが正しい結果へClaudeを導く確率の測定、プラグイン変更時や新モデル登場時のリグレッション検知、プラグインなしとの比較で「何が効いているか」を見ることです。
実行にはClaude Code v2.1.269以降が要ります。claude --versionで確認し、古ければclaude updateで上げます。gitが入っている環境ではgit 2.31以降も必要です。古いgitではフックや資格情報ヘルパーを無効化する環境変数(GIT_CONFIG_COUNT)が効かず、実行前に拒否されます。この検証自体はv2.1.283で追加されたもので、それ以前のバージョンはgitのバージョンを確認せずに実行していました。
対象はplugin.jsonか.claude-plugin/plugin.jsonを持つプラグインディレクトリ、またはskills-directoryプラグインです。evalの実行もjudge graderの呼び出しも、実際のモデル課金になります。プランの使用量かAPI課金として計上されるため、CIで頻繁に回す前にコストを見積もっておく必要があります。
evalスイートの構成を決める
evalスイートはプラグイン内のevals/ディレクトリに置きます。ケース1つがサブディレクトリ1つで、中にprompt.md(プロンプト本体)とgraders/(採点基準)を持ちます。作り方は2通りです。
1つ目はclaude plugin eval initです。プラグイン直下で実行すると対話セッションが開き、プラグインを読んでどんなプロンプトが妥当かを尋ね、graderを設計し、試しに1回走らせてからファイル一式を書き出します。ケースをまとめて用意したいときの既定の方法です。
2つ目は手動作成です。空のテンプレートから書きたい場合は--bareを使います。
claude plugin eval init --bare first-caseこれでevals/first-case/prompt.mdとevals/first-case/graders/criteria.mdが空の状態で作られます。evals/が別ツールに使われている場合は置き場所を変えられます。plugin.jsonに"experimental": { "evals": "quality/evals" }を書くか、実行時に--eval-dir quality/evalsを渡します。
現実的なプロンプトをprompt.mdに書く
prompt.mdのfrontmatterには、そのケースの実行条件を書きます。本文はClaudeがそのまま受け取るプロンプトです。
---
max_turns: 10
allowed_tools: [Read, Glob, Grep, Skill]
---
Write me a commit message for this change: I renamed getUser to fetchUser and updated the three call sites.スキル名を書かずに、ユーザーが打つであろう自然な依頼文で書きます。「〇〇スキルを使って」ではなく「このコミットメッセージを書いて」と頼み、Claudeがそのスキルを自分の判断で選ぶかどうかを見ます。@pathのようなファイル添付記法は展開されないので、Claudeにファイルを読ませたいならallowed_toolsにツールを許可します。
各runは空のワークスペースから始まります。ファイルやgitリポジトリが要るケースは、case.yamlにcontext.scaffold_scriptでBashスクリプトを指定します(実行は--scaffoldを渡したときだけ)。会話の続きから始めたいケースはcontext.history_fileに.jsonlのトランスクリプトを渡すと、プロンプトが「その続きの発言」として扱われます。max_turnsは既定10、timeout_secondsは既定300秒で、時間のかかるタスクは緩めに設定しないと打ち切りがそのままスコアを下げます。
runはユーザー設定・project-level設定を一切読み込みません。個人のCLAUDE.md・フック・MCPサーバー・他の導入済みプラグイン・メモリー・スキルはすべて不在です。プラグインが何か前提を必要とするなら、プラグイン自体に同梱するか、scaffold_scriptで作るか、EVAL_*で始まる環境変数として渡すしかありません。ケースがなぜか失敗するときは、まず「普段の環境にだけあるもの」に依存していないかを疑うのが早道です。
graderを設計する — 何を測るか、どう安定させるか
graderには6種類あり、性質が2つに分かれます。regex・tool_used・tool_order・file_existsはトランスクリプトとファイルから機械的に判定するのでコストがかかりません。llmとbaselineはjudgeモデルを呼ぶので追加コストが発生します。
| grader | 何を見るか | コスト |
|---|---|---|
regex | 何を見るか応答やトランスクリプトの正規表現一致 | コストなし |
tool_used | 何を見るか特定ツールが呼ばれた回数・入力 | コストなし |
tool_order | 何を見るか2つのツール呼び出しの前後関係 | コストなし |
file_exists | 何を見るかrun中に作られたファイルの有無 | コストなし |
llm | 何を見るかjudgeモデルによるルーブリック判定(3票中2票でPASS) | コストあり |
baseline | 何を見るか参照トランスクリプトとの相対比較 | コストあり |
llm graderはモデルに聞く以上、runごとに答えがぶれます。読ませる文章が長いほどぶれも増えます。安定させるコツは4つあります。
- 長い出力(生成ファイルなど)はファイル全体を
regexで機械的にチェックします。llm graderは短い応答用に、PASS/FAIL条件を具体的に書いたルーブリックだけに使います - 1ケースに「結果そのもの」を見るgrader(最終応答やファイル)と「そこに至る手順」を見るgrader(
tool_usedやtool_order)を1つずつ置きます tool_used: Skillが通っているのにΔがマイナスなら、まずjudgeモデルを疑います。--judge-model sonnetのような強いモデルで再実行し、ルーブリックを絞り込みます- ビルドやテストの成否を確かめたいときは、Claudeに実行させて結果をファイルへ書かせます。そのファイルを採点しつつ、
tool_usedのinput_matchで実際にコマンドが走ったことも確認します
プラグインがMCPツールを呼ぶ場合、実サービスなしでも検証できます。evals/mocks/<server>/<tool>.mdにツールごとのモックを置くと、runは実サーバーを起動せずモックから応答します。モックにはexpect:で入力の形を指定でき、違反するとrunはスコア0で中断されます。呼び出し内容そのものを採点したいときはtarget: mock_callsを指定したgraderを使います。この仕組みはプラグインに同梱されたMCPサーバーを配布しているプラグインで特に効きます。
no-pluginベースラインとの差分(Δ)を読む
1回のスコアだけでは、プラグインが本当に効いているのか分かりません。Claudeがプラグインなしでも同じ結果を出せるかもしれないからです。そこで各ケースは既定で2アーム実行されます。プラグインありのwith-armと、プラグインなしのwithout-arm(no-pluginベースライン)です。両者のスコア差がΔで、プラスならプラグインが貢献したことになります。両方とも1.0なら、そのケースはプラグインが無くても通っています。
2アーム実行では、一部のgraderが両アームで公平に比較できません。「スキルが呼ばれたか」を見るgraderはプラグインなしでは原理的に通らないため、そのままではΔを水増ししてしまいます。Claude Codeは次のgraderを自動的にスコア計算から除外し、with-armでは合否の参考表示だけに使います。
toolがSkillのtool_usedgrader- プラグイン自身が宣言するMCPサーバーへの
target: mock_callsのregexやfocus: mock_callsのllm arm: with-onlyを指定したgrader
「スキルを絶対に呼ばせない」ことを確認したいケースでは、逆にarm: bothを指定して両アームで強制的に採点させ、min: 0とmax: 0を組み合わせます。比較が要らない反復作業では--ablation noneでwith-armだけを実行でき、コストは半分になります。ただし除外の仕組みごと働かなくなるため、同じ--thresholdでも合否が変わることがあります。
CIにゲートとして組み込む
CIで回すときは、結果をJSONで残し終了コードでビルドを失敗させます。信頼プロンプトで止まらないよう--trust-pluginを付け、モデルは固定し、コストには上限を置きます。
claude plugin eval . \
--trust-plugin \
--json results.json \
--threshold 0.8 \
--model claude-sonnet-5 \
--judge-model claude-haiku-4-5 \
--no-publish \
--max-cost-usd 20--modelをCIで固定するのは、モデルの既定更新をプラグインの退行と誤認しないためです。--judge-modelも同じ理由で固定します。終了コードの意味や課金上限の扱いはClaude Code v2.1.269のリリースノートにまとめてあるので、そちらを参照してください。ここで押さえておきたいのは運用面の3点です。CI環境にはANTHROPIC_API_KEYなどの資格情報が要ること。--trust-pluginを付けないとチェックアウトディレクトリが未信頼のまま拒否されること。CIでの雛形作成は対話プロンプトを出せないのでclaude plugin eval init --bare <name>を使うことです。トレンドを追うグラフには、コスト上限で打ち切られたpartial: trueの結果や、judgeが省略されたskippedPaidGradersのrunを混ぜないようにします。
skill-creator evalsとどう違うか
似た名前の仕組みがClaude Codeにはもう1つあります。1つのスキルを対話セッション内で素早く反復するための、skill-creatorプラグインのeval機能です。両者は別物です。claude plugin evalはプラグイン全体を対象にした公式コマンドで、evals/ディレクトリのケース形式を使い、CIでのゲート化を前提にしています。skill-creatorのevalはevals/evals.jsonという独自形式を使い、開発中のセッション内でその場のスキルを比較する用途に寄っています。どちらもプラグインなしとの比較でスコアを出す発想は共通ですが、片方のケースファイルをもう片方が読むことはありません。すでにskill-creatorでスキルを仕上げていても、配布するプラグインとしてCIでの継続的な検証をしたいなら、claude plugin evalのスイートを別に用意することになります。ファイルの構文・スキーマだけを確かめたいなら、挙動を採点するclaude plugin evalではなくclaude plugin validateが対象です。
よくあるつまずき
- Δが常にゼロ付近:
tool_used: Skillのgraderが失敗しているなら、多くの場合はスキルのdescriptionが自然な言い回しにマッチしていません。プロンプトの表現に合わせてdescriptionを調整し、再実行して比べます - 全ケースが0点になる: 作成されたパスの一覧を返す
filesを指定していて、本当はファイルの中身を見たい設定に多い症状です。{ source: file, path: <path> }に直します。file_existsはrun中に新規作成されたファイルしか数えないので、既存ファイルをClaudeが編集しただけのケースではtool_usedでEditを確認します - regexがトレースにマッチしない:
targetの既定はlast_messageで、トレース全体ではありません。traceを対象にする場合はJSONの1行1メッセージなので、引用符は\"として現れます。正規表現の構文はJavaScript準拠で、大小文字を無視したいならflags: iを使い、インラインの(?i)は使えません - usage-limitやrate-limitで途中から全部0点になる: 途中でプランの使用上限やAPIのレート制限に達すると、それ以降のrunはエラーで終わりほぼ0点になります。スイート自体は
partial扱いにならず終わるため、見かけ上のリグレッションに見えます。NOTES列やJSONのerrorにある制限メッセージを確認してから、上限リセット後に再実行します - Bashやサンドボックスの前提を見落とす:
--allow-toolsでBashを許可すると、runはOSレベルのサンドボックス内で動きます。ネイティブのWindowsにはサンドボックスのバックエンドが無いためWSL2で実行し、Linuxでは事前にbubblewrapとsocatを用意します
まとめ
claude plugin evalでプラグインを検証する要は、ケースの質にあります。スキル名を伏せた自然なプロンプトを書き、結果を見るgraderと手順を見るgraderを1つずつ組み合わせ、no-pluginベースラインとのΔで「プラグインが本当に効いているか」を確認します。CIに組み込むときはモデルとjudgeモデルを固定し、コスト上限を置いて、--trust-pluginで信頼プロンプトを回避します。1つのスキルを素早く試したいだけならskill-creatorのevalで十分です。配布するプラグイン全体を継続的に検証したいなら、claude plugin evalのスイートを別に用意する価値があります。プラグインの配布・マーケットプレイスの仕組みはClaude Codeプラグインマーケットプレイスの必須化と自動更新設定にまとめました。公式マーケットプレイスに並ぶプラグインの傾向はClaude Code公式マーケットプレイスのプラグイン一覧で扱っています。