claude -p を評価器にして、AI の生成結果を930件自動採点した
Chord Eureka は、キーワードからコード進行を作る Web アプリです。画面には「切ない」「ジャジー」「セカンダリードミナント」のようなキーワードのボタンが69個並んでいて、押すとそのキーワードで生成できます。
あるとき、ふと疑問に思いました。このボタン、本当に全部効いているのだろうか。「ノスタルジック」と「優しい」で、出てくる進行は本当に違うのか。押しても何も変わらないボタンがあるなら、消したほうが画面がすっきりします。
確かめるには、キーワードごとに何回も生成して比べるしかありません。そこで、Claude Code の claude -p を生成器にして930件を作り、プログラムで採点しました。その方法をまとめます。
※ Chord Eureka のリポジトリは非公開なので、ソースは載せず、手法として書きます。
なぜ API ではなく claude -p なのか
本番のアプリは Claude API を呼んでいます。同じことを930回やると、そのぶん API の料金がかかります。
claude -p(Claude Code の非対話モード)なら、Claude Code のサブスクリプションの枠の中で 生成できます。プロンプトを渡すと結果を出力して終わるので、スクリプトから何百回でも呼べます。
ただし、claude -p は API と同じではありません。Claude Code 自身のシステムプロンプトや設定が乗るからです。そこで、できるだけ本番の API 呼び出しに近づけるオプションを付けました。
本番に近づけるためのオプション
1回の生成は、次のような引数で claude を起動しています。
claude -p \
--model claude-sonnet-5-5 --effort medium \
--system-prompt "<本番と同じシステムプロンプト>" \
--json-schema '<本番と同じ JSON スキーマ>' \
--output-format json \
--tools "" --strict-mcp-config --setting-sources "" \
--disable-slash-commands --no-session-persistence
それぞれの役割は次のとおりです。
| オプション | 目的 |
|---|---|
--model / --effort | 本番と同じモデル・思考量にする |
--system-prompt | Claude Code の既定のシステムプロンプトを、本番のものに置き換える |
--json-schema | 本番と同じ構造化出力にする。結果は JSON の structured_output に入る |
--output-format json | 結果を JSON で受け取る。トークン数(usage)も取れる |
--tools "" | ファイル操作などのツールを使わせない。コード進行を答えるだけなので要らない |
--strict-mcp-config | 自分の環境の MCP サーバーを読み込まない |
--setting-sources "" | ~/.claude やプロジェクトの設定(CLAUDE.md・hooks など)を読み込まない |
--disable-slash-commands | スキルやスラッシュコマンドを無効にする |
--no-session-persistence | 930件分の会話ログを残さない |
ポイントは、普段の開発用の設定を全部切る ことです。グローバルの CLAUDE.md には「日本語で答える」「キャラクターになりきる」のような指示が書いてあるので(設定ツアー参照)、そのまま読ませると生成結果に混ざりかねません。
それでも、本番と完全には一致しません。入力トークンには Claude Code の分が上乗せされますし、内部の呼び出し方も違います。なので、この測定は 「本番の傾向の近似」 として扱うことにしました。
Windows では .cmd を経由しない
Windows でひとつハマりました。npm で入れた claude は、実体が claude.cmd というラッパーです。Node.js から shell: true で呼ぶと、cmd.exe を経由するので システムプロンプトの改行や引用符が崩れます。
対策として、ラッパーの場所から本体の claude.exe を探し、シェルを通さずに直接起動しました。ユーザープロンプトは引数ではなく標準入力から渡しています。改行を含む長い文字列は、引数よりも標準入力のほうが安全です。
(今はネイティブ版の Claude Code に移行したので、ラッパーの問題自体がなくなっています。)
中断しても続きから再開できるようにする
930件となると、途中で止まることもあります。利用上限に当たる、PC がスリープする、などです。
そこで、結果は1件ごとに JSONL(1行1件の JSON)へ追記し、起動時にはファイルを読んで 成功済みの(キーワード, 回)を飛ばす ようにしました。止まっても同じコマンドを打ち直せば、続きから始まります。最初から失敗が5件続いたとき(たいてい利用上限です)は、自分で止まるようにもしています。
並列数は3にしました。結果として、930件は 約40分で、失敗0件 で終わりました。1件あたりの応答時間は中央値で約7秒です。
何を生成したか
- 既存のキーワード69語 + 追加を検討していた候補23語
- ベースライン(キーワード無し)
- それぞれ10回ずつ、8小節で生成 → 計930件
一番大事なのはベースラインです。「キーワードが効いている」とは、キーワード無しのときと比べて出力が変わっている ということだからです。比べる基準がないと、「ジャジーで7thが多い」が、キーワードのおかげなのか、もともと7thが多いだけなのか分かりません。
採点は AI にさせない
生成した進行の採点は、AI ではなく プログラムで しました。
Chord Eureka には、もともと進行を分析する機能があります。キーの推定、各コードの機能(トニック・ドミナントなど)、進行名の判定、珍しさのスコアです。これらは AI ではなく決まったロジックで計算しているので、同じ進行なら何度やっても同じ結果になります。この分析をそのまま採点に使いました。
AI に「この進行は『切ない』らしいですか?」と聞く方法もありますが、採点自体がぶれますし、930件分の料金や時間もかかります。測る側は決定的にしておく ほうが、結果を信用できます。
指標1:ベースラインからの距離
1回の生成ごとに、13の特徴を数えます。
- 7th コードの割合、テンション(9th 以上)の割合、分数コードの割合
- sus・dim・aug の割合
- キーから外れたコードの割合、セカンダリードミナントやサブドミナントマイナーの割合
- 短調で始まるか、キーが C 以外か、有名な進行名が含まれるか
- 珍しさのスコア
キーワードごとに10回の平均を取り、ベースラインとの距離を出します。特徴ごとに単位がばらばらなので、各特徴を「語どうしのばらつき(標準偏差)」で割ってから距離を測りました。距離が大きいほど、そのキーワードが出力を変えている ということです。
指標2:遵守率(理論用語のキーワード)
「ツーファイブ」「偽終止」「ペダルポイント」のような理論用語は、入っているかどうかを判定できます。そこで、語ごとに判定ルールを書きました。
- ツーファイブ:マイナーのコードから、4度上のメジャーかセブンスのコードへ進む箇所がある
- 偽終止:V(または V7)から VIm へ進む箇所がある
- ペダルポイント:同じベース音が3つ以上続き、上のコードは変わっている
10回のうちルールを満たした割合が遵守率です。
分かったこと
システムプロンプトのくせが一番強かった
まず驚いたのはベースラインです。キーワード無しで生成すると、7th コードが99%、キーは100% C でした。
システムプロンプトに「テンションや7thも使って豊かに」という趣旨の指示があり、それが強く効いていたのです。7th を外せたのは「明るい」「ポップ」「シンプル」「ロック」のような語だけでした。キーワードの効き目を測るつもりが、先にプロンプト自体のくせが見えました。
効いていないボタンがあった
距離が小さく、ほぼベースラインと同じ出力になる語がありました。
| 語 | 距離 |
|---|---|
| 神聖な | 1.24 |
| ノスタルジック | 1.78 |
| ロマンチック | 1.86 |
| 優しい | 1.98 |
一番効きが強い「オーギュメントコード」「無機質」は 9.5 前後なので、かなり小さい値です。
逆に、同じカテゴリでほぼ同じ出力になる語の組 も見つかりました。「希望に満ちた」と「壮大」(距離0.77)、「切ない」と「哀愁」(1.17)などです。
判断は人がする
数字が出たら、あとは自分で決めました。
- 削除(6語):効きの弱い語と、ほぼ同じ出力の組の片方
- 追加(5語):候補23語のうち、効いていて既存と被らないもの(サブドミナントマイナー・半音進行・偽終止・疾走感・フォーク)
- 補強(1語):「神聖な」はプロンプトに語の意味(sus・プラガル終止など)を補足した。再測定すると距離は 1.24 → 3.26 になり、7th の割合も84% → 30% に下がった
距離が小さくても、その言葉で押したい人がいる語は残す、という判断もありえます。「ジャンル名として検索される語は残す」なども、数字だけでは決められません。測定は判断材料を出すところまで で、決めるのは自分です。
注意したこと
- 1語10件はノイズが大きい:距離1.5未満の差は誤差かもしれない、と割り切って読みました。差が小さいものの判断は保留寄りにしています
- 判定ルールの厳しさで遵守率が変わる:「循環コード」の遵守率は40%でしたが、ルールを「前半4つと後半4つのルートが同じ」と厳しめにしたせいもあります。低い遵守率は、AI のせいかルールのせいかを確かめてから扱いました
- あくまで近似:本番は API、測定は
claude -pなので、細かい数字の差には意味を持たせすぎないようにしました
同じ仕組みで、モデルの比較もできた
この仕組みは一度作ると使い回せます。10月に新しいモデル(Sonnet 5.5)が出たときは、モデルと思考量を引数で切り替えられるようにして、旧モデルと比べました。応答の速さ、出力トークン、そして「同じキーワードで同じ進行が出ていないか」まで、同じ集計で並べられます。
その結果どうなったかは、AI コード進行アプリを2年運用して分かったこと に書きました。
生成 AI を使ったアプリは、プロンプトやモデルを変えたときに 良くなったのか悪くなったのか が分かりにくいものです。claude -p で数をこなし、採点はプログラムでする。この組み合わせで、手元のサブスクリプションの範囲で、数字を見ながら判断できるようになりました。
この記事をシェア