Claude Code skill-creatorの品質評価アーキテクチャ解説
TL;DR
Claude Codeのskill-creatorはスキル出力の品質評価とDescriptionトリガー精度評価の2つの評価系を持つ。前者はスキルあり/なしの並列実行→自動採点→ブラインド比較→人間レビューの多段パイプライン。後者はtrain/test分割+extended thinkingによるDescription改善の自動ループ。LLMで作ったものをLLMで評価するという再帰的な構造に、過学習防止やバイアス排除の古典的な手法を組み込んでいる点が興味深い。
背景:LLMで作ったものをどう評価するか
Claude CodeのSkillsは、特定タスク向けの知識と手順をパッケージ化したものだ。skill-creatorはそのSkillを自動生成・改善するメタスキルで、内部に本格的な品質評価パイプラインを持っている。
LLMベースのツールが生成する成果物の品質をどう計測し、改善ループに乗せるかは、いまエンジニアリングの現場で広く問われている課題だ。skill-creatorのソースコード(~/.claude/skills/skill-creator/)を読むと、この問題に対する一つの解が見えてくる。
skill-creatorは2種類の品質評価を持つ。
- スキル出力の品質評価 — スキルを使った場合と使わなかった場合の出力を比較する
- Descriptionトリガー精度評価 — SKILL.mdのdescriptionフィールドが適切なクエリで発火し、無関係なクエリではスルーするかを検証する
それぞれ見ていこう。
1. スキル出力の品質評価
全体フロー
まず全体像を示す。
flowchart TD
A["テストケース作成"] --> B["並列実行<br>with/without skill"]
B --> C["Grader Agent<br>自動採点"]
C --> D["集計<br>benchmark.json"]
D --> E["Analyzer<br>パターン分析"]
E --> F["人間レビュー<br>HTML viewer"]
F -->|"フィードバック"| G["スキル改善"]
G -->|"次のイテレーション"| A
テストケースに対してスキルあり/なしで並列実行し、自動採点→集計→パターン分析→人間レビューという多段パイプラインで品質を計測する。これを反復して改善を回す。
テストケース実行:A/Bテストの基盤
各テストケースに対して2つのサブエージェントを並列で起動する。
with_skill: スキルありで実行without_skill(またはold_skill): ベースラインとして実行
出力は iteration-N/eval-ID/{with_skill,without_skill}/outputs/ に保存され、タスク完了時の total_tokens と duration_ms が timing.json に記録される。
単にPASS/FAILを見るだけでなく、トークン数や実行時間も記録する点に注意してほしい。スキルが品質を上げても、それが10倍のトークン消費を伴うなら実用上は問題がある。品質とコストのトレードオフを定量化する仕組みが最初から組み込まれている。
Grader Agent:採点だけでなくevalの品質も問う
自動採点を担当するGrader Agent(agents/grader.md)は、assertionのPASS/FAIL判定に加え、3つの追加的な品質チェックを行う。
| チェック | 内容 |
|---|---|
| Claims抽出・検証 | 出力から暗黙の主張を抽出し、事実/プロセス/品質の3タイプに分類して検証する |
| User Notes参照 | executorが残した不確実性やワークアラウンドの記録を考慮する |
| Eval自体の批評 | 「このassertionは弱すぎる」「カバーされていない重要なアウトカムがある」等のフィードバックを出す |
3番目の「Eval自体の批評」が特に面白い。テストの品質をテスト自身が評価するという再帰的な構造だ。「このassertionは弁別力がない」「もっと重要な観点が抜けている」というフィードバックをGraderが出すことで、評価基準自体の改善が回る。
出力は grading.json として保存され、expectations、summary、claims、eval_feedbackの4つのフィールドを含む。
集計とパターン分析
scripts/aggregate_benchmark.py が全run結果からmean ± stddev、min、maxを算出し、with_skill vs without_skillのdelta(pass_rate、time_seconds、tokens)を計算する。出力は benchmark.json と benchmark.md。
ここまでは定量的な集計だが、次のAnalyzer Agent(agents/analyzer.md)が統計だけでは見えないパターンを検出する。
- 両configで常にpassするassertion → 弁別力がない(テストとして無意味)
- 両configで常にfailするassertion → 能力外か、テスト自体が壊れている
- 高分散のeval → flakyテストの可能性
- 時間/トークンのトレードオフ → 品質は上がったがコストが過大になっていないか
flakyテストの自動検出は地味だが重要だ。LLMの出力は本質的に確率的なので、同じプロンプトでも結果がブレる。その分散を計測し、信頼区間の中で議論する姿勢がこのシステムの根幹にある。
ブラインド比較:バイアスの排除
オプションとして、agents/comparator.md と agents/analyzer.md を使ったブラインド比較も用意されている。
- Blind Comparator: 2つの出力をA/Bとして提示し、どちらがどのスキルによるものかを隠す
- ルブリック採点を行う(Content: 正確性/完全性/精度、Structure: 構成/フォーマット/可用性、各1-5点)
- 勝者決定後、Post-hoc Analyzerが「なぜ勝ったか」を分析し改善提案を生成する
「どちらがスキルありか」を隠して採点させるのは、LLMベースの評価で陥りがちな確証バイアスを排除する定番の手法だ。LLMは「こちらが改善版です」と伝えると、無意識に高い点をつけてしまう傾向があるので、この対策は合理的だと思う。
人間レビュー:最後は人間が見る
eval-viewer/generate_review.py がHTMLビューアを生成する。2タブ構成で、Outputsタブではテストケースごとの出力表示とフィードバック入力欄、前回出力との比較が可能。Benchmarkタブでは定量比較の統計サマリーとanalyzerのノートが表示される。
フィードバックは feedback.json に保存され、空フィードバック=問題なしとして扱う。完全自動化ではなく、人間の判断を組み込むポイントを明示的に設けている点は、現実的な設計だ。
2. Descriptionトリガー精度評価
なぜDescriptionが重要なのか
Claude CodeのSkillsは、ユーザーのクエリに対して自動的にトリガーされる仕組みを持つ。このトリガーの精度を決めるのがSKILL.mdの description フィールドだ。descriptionが曖昧だと関連クエリで発火せず、逆に広すぎると無関係なクエリでも発火してしまう。
トリガー検出の仕組み
scripts/run_eval.py が核心を担う。処理フローは以下の通り。
sequenceDiagram
participant Eval as run_eval.py
participant CLI as claude -p
participant Stream as ストリーム解析
Eval->>CLI: クエリ実行(stream-json)
CLI->>Stream: ストリームイベント
Stream->>Eval: tool_use検出
Note over Eval: 各クエリを3回実行
Eval->>Eval: trigger_rate算出
Note over Eval: 閾値0.5超でトリガー判定
各クエリをデフォルト3回ずつ実行し、trigger_rateを算出する。閾値(デフォルト0.5)を超えたらトリガーと判定。複数回実行して確率的なブレを吸収する設計だ。
最適化ループ:train/test分割で過学習を防ぐ
scripts/run_loop.py が最適化ループを回す。
flowchart TD
A["eval set"] --> B["60/40 train/test分割<br>(stratified)"]
B --> C["トリガー評価"]
C -->|"trainで全pass"| D["早期終了"]
C -->|"失敗あり"| E["description改善"]
E --> F["新description生成"]
F --> C
ここでの設計上の要点はbestの選定をtestスコアで行うことだ。trainセットでの性能が上がっても、testセットで下がっていれば採用しない。これは機械学習における過学習防止の基本中の基本だが、LLMのプロンプト最適化でもまったく同じ問題が発生する。特定のクエリ例に過度にフィットしたdescriptionは、未知のクエリに対して脆弱になる。
stratified分割を採用しているのも重要だ。should_trigger=trueとfalseの比率がtrain/testで偏らないようにしている。
Description改善:extended thinkingで汎化を図る
scripts/improve_description.py はAnthropic APIを直接呼び出してdescriptionを改善する。extended thinking(budget 10,000トークン)を使い、以下の情報をプロンプトに含む。
- 現在のdescription
- 失敗したトリガー(should_trigger=trueなのに発火しなかった)
- 誤トリガー(should_trigger=falseなのに発火した)
- 過去の試行履歴(ただしtestスコアは隠す)
- スキル本文
過去の試行履歴にtestスコアを含めない点に注目してほしい。これによりモデルがtestセットに間接的にフィットすることを防いでいる。
生成されたdescriptionには制約が課される。
- 100-200語に収める
- 具体的クエリの列挙ではなく、意図のカテゴリで汎化する
- 1024文字を超えた場合は再度APIで短縮する
「具体的クエリの列挙ではなく意図のカテゴリで汎化する」という制約は賢い。「ユーザーが’テストを書いて’と言ったら発動」のような具体例の列挙は、見たことのないクエリに対応できない。「テスト自動化に関する支援を提供する」のような抽象的なカテゴリ記述のほうが、未知のクエリへの汎化性能が高い。
設計パターンのまとめ
skill-creatorの評価アーキテクチャから抽出できる設計パターンを整理する。
| 観点 | 手法 | なぜ重要か |
|---|---|---|
| 過学習防止 | train/test分割、bestはtestスコアで選定 | プロンプト最適化でも過学習は発生する |
| 統計的信頼性 | 各クエリ複数回実行、mean ± stddevで評価 | LLMの出力は確率的にブレる |
| 人間判断の統合 | HTMLビューア + feedback.json | 完全自動化は品質の天井が低い |
| バイアス排除 | ブラインド比較(A/Bでどちらか隠す) | LLMは「改善版」と聞くと甘く採点する |
| eval品質管理 | Graderがassertion自体の弱点を指摘 | 悪いテストで良い結果が出ても意味がない |
| 改善の自動化 | extended thinkingでdescriptionを反復改善 | 人間が毎回書き直すのはスケールしない |
| コスト計測 | トークン数・実行時間を常に記録 | 品質向上がコスト爆発を伴わないか監視する |
これらのパターンは、skill-creatorに限らず、LLMベースのシステムの品質評価全般に応用できる。
おわりに
skill-creatorの評価アーキテクチャを見ていて感じたのは、「LLMの評価」という新しい問題に対して、機械学習やソフトウェアテストの古典的な手法が驚くほどそのまま適用できるということだ。train/test分割、ブラインド比較、flakyテスト検出、テスト品質のメタ評価——どれも目新しい概念ではないが、LLMの文脈で組み合わせることで強力な品質保証パイプラインになる。
一方で、人間レビューのポイントを明示的に残しているのも印象的だ。LLMでLLMを評価するという再帰構造には本質的な限界があり、最終的な品質判断は人間が行うという割り切りは現実的だと思う。
完全自動化を目指すのではなく、自動化できる部分と人間の判断が必要な部分を明確に切り分ける。この設計思想は、LLMを使ったツール開発全般に通じる原則ではないだろうか。
References
以上。