Claude Media
/claude-api hillclimbで評価データを使いモデルとeffortを探索する

/claude-api hillclimbで評価データを使いモデルとeffortを探索する

/claude-api hillclimbは評価をtrainとtestに分け、モデル・effort・プロンプトの変更を1周ずつ試します。build-evalとの違い、前提条件、分割の考え方、停止条件、結果の読み方をまとめます。

/claude-api hillclimbは評価を使って設定変更を反復探索するコマンド

/claude-api hillclimbは、既存の評価(eval)に対してアプリを反復改善するClaude Codeのコマンドです。評価データをtrain(訓練)とtest(検証)に分け、モデル・effort・プロンプトなどの変更を1周ずつ試します。失敗したtrainの事例を読んで次の変更を決め、最終的な設定はtestの成績で採否を決めます。

狙いは2種類あります。スコアを上げることと、スコアを保ったままコストやレイテンシを下げることです。コスト削減が目的なら、モデルを一段安いものへ替え、足りなくなった分をプロンプトで補う探索になります。同じ/claude-apiスキルには、APIバージョンを上げるapi-versionも加わりました(v2.1.307の更新)。

使える条件は次のとおりです。

  • Claude Code v2.1.259以降(skillsドキュメントがbuild-evalとhillclimbに示す要件)
  • コマンドラインから実行でき、固定のプロンプト集に対してスコアを出す評価スクリプトがあること

コスト削減の全体像はClaude Platformのコストを削減する3つの実装レバーにあります。この記事は、その3つ目にあたるhillclimbの手順だけを掘り下げます。評価の設計原則と公式の2つの事例の読み方はevalの設計とhillclimbの過学習対策にまとめています。

build-evalとhillclimbの違いは何か

build-evalは評価データそのものを作り、hillclimbは既にある評価に対してアプリを反復改善します。順序はbuild-evalが先です。評価が無いままhillclimbを始めても、改善を測る物差しがありません。

build-evalを実行すると、Claudeはインタビューしながら評価をコードベースの中に作ります。入力と採点器の2か所で承認を求めて止まります。進め方はclaude-apiのbuild-evalとhillclimbにまとめています。

ラウンド1の前に決まること

hillclimbはスキルの手順書(eval-hillclimb.md)に沿って進みます。最初の数ステップは、ループを始める前の確認です。

評価が実行できるかを確かめる

Claudeはまず、評価コマンドとその結果の出力先を尋ねます。評価の各実行では、ケースごとの全文トランスクリプト、使用モデル、トークン使用量、採点結果を残す必要があります。合計スコアだけでは足りません。

再試行(retry)の扱いも確認されます。再試行でやっと通ったケースは、既定では失敗として数えます。再試行の分もコストに計上されます。

評価が改善を検出できるかを確かめる

次に、評価そのものが信頼できるかを調べます。ラウンド1の前に、次の3つの数字がユーザーに示されます。

数字意味
ノイズフロア意味現在のケース数と反復数での、差の信頼区間の半幅
余地(ヘッドルーム)意味満点とベースラインの差
動く価値のある最小の改善幅意味ユーザーが採用を決められる最小の差

ノイズフロアが余地や最小改善幅より大きいと、ループは本物の改善を示せません。その場合は、反復数を増やす、ケースを増やす、より細かい指標に替えるといった提案が先に出ます。

そのほかに、次の検査が入ります。

  • 採点結果の合計を、ケース別の生データから自分で再計算する
  • ベースラインで最も低い数ケースを読み、採点が公正か、正解値が正しいかを確かめる
  • 応答に含まれるmodelを見て、想定のモデルで動いたかを確かめる

出来すぎた数字は、手で照合するまで測定バグとして扱われます。

スコアが依存する仕組みの配線確認も、ラウンド1の前に入ります。メモリへの書き込みやツール呼び出しのように、スコアを左右する仕組みが実際に効いているかを、1回きりの試走で確かめます。評価が「仕組みは何もしない」ような結果を返したときは、「モデルにはできない」と結論づける前に配線を疑う順序です。サブエージェントを使う構成では、いじるモデル・effort・プロンプトが全サブエージェントへ届き、トレースにそのターンが残ることも確かめます。

何を最適化するかを選ぶ

目的は選択式で聞かれます。スコアを上げる、リクエストあたりのコストを下げる、レイテンシを下げる、別モデルへ移ってスコアを取り戻す、のいずれかです。

答えは3つのことを決めます。分析役が狙う主指標、停止条件の指標、そして保護対象の指標です。たとえばコストを目標にしたなら、正解率は「ノイズの範囲を超えて悪化してはいけない」制約に変わります。目標は_state.jsonに記録され、再開したセッションが勝手にスコア改善へ戻ることはありません。

何ラウンドで止めるかを決める

停止条件は選択式です。「プラトーまで(推奨)」は、目標がdeltaを下回る改善を連続Kラウンド繰り返す(Kは3以上)か、保護指標がノイズの外で悪化するまで続けます。「1ラウンドずつ」は、提案・実行・報告のたびに確認が入ります。「Nラウンド」は、N周したところで続行を尋ねます。

deltaはノイズフロアより上に置きます。評価が測れる幅より細かい閾値では、停止条件が正直に働かないためです。コスト削減が目標なら、キャッシュの状態確認、prompt-audit、モデルとeffortの階段、凍結したモデル上でのプロンプト調整の順に進める専用ガイド(cost-hillclimb.md)も読み込まれます。

どこを触ってよいかを決める

次に、変更してよい対象を聞かれます。システムプロンプト、スキルや指示ファイル、ツール説明、モデルやeffort・thinking・max_tokensなどのAPIパラメータ、エージェントループ本体が候補です。触ってはいけない範囲も同時に確認されます。この禁止リストは厳守されます。

評価を実行するスクリプトや採点コードは「ハーネスのパス」として登録します。登録したファイルが承認後に書き換わると、実行が止まります。知らないうちに評価が変わることを防ぐ仕組みで、止まる条件の詳細は後の節で触れます。

trainとtestの分割はどう決まるか

hillclimbの信頼性は分割で決まります。分析役は失敗事例を読み、その失敗を直す変更を出します。その事例は設計上、成績が上がりやすくなります。だから報告する数字は、分析役が一度も読んでいないケースから出す必要があります。

既定はtrain/testの2分割

くらべる

既定の2分割

分析役が読む

train

各ラウンドで、分析役がトランスクリプトを読むケースです。失敗の分析と次の変更案の材料になります。

分析役は開かない

test

毎ラウンド採点はしますが、分析役は決して開きません。勝ったラウンドを選ぶ基準であり、最終的な成績になります。

分割は、tags[0]で層別したランダム抽出で行います。ベースラインのスコアで分けてはいけません。低得点のケースだけをtrainに集めると、分析役は極端な失敗にだけ最適化し、それらは再実行すれば平均へ戻ります。trainは伸びるのにtestが動かない場合は、この兆候です。

ベースライン測定のあと、trainとtestの平均がノイズの範囲で一致するかを確認します。ずれていれば、ラウンド1の前に引き直します。分割は一度決めたら変えません。

testの件数と誤差の関係

二値の正解率では、95%信頼区間の半幅はおよそ1/sqrt(n·反復数)です。手順書はこの数字を自分のデータ件数で示し、件数と反復数のどちらを増やすかをユーザーに選ばせるよう指示しています。

数字

testの件数と誤差(反復2回)

  • test 25件

    約±14pt

    手順書の例

  • test 50件

    約±10pt

    手順書の例

半幅の目安。件数と反復数のどちらを増やしても縮みます

この誤差の読み方はモデル評価の統計的手法にも詳しくあります。

分割しないほうがよい場合

ケース数が少ない評価や、順位付けのようにケースをまたいで成立する指標では、分割しません。全件を毎ラウンド採点し、反復でノイズを抑えます。このとき各ラウンドの数字は「方向を示す値(directional)」として扱われます。公開用の数字にはなりません。

約150件以上と多い場合は、勝者選びに使う検証用スライスを別に切り、testを最後まで温存する3分割も選べます。

1ラウンドで起きること

守られるルールは2つです。読むのはtrainのトランスクリプトだけであること。評価セットと予算を、ユーザーに戻らず変えないことです。

手順

1ラウンドの流れ

  1. 1

    分析する

    次の変更が自明なら分析役を省き、vN/change.mdを直接書きます。自明でなければ、まっさらな分析役を1つ起動します。

  2. 2

    適用する

    変更は既定でClaudeが適用します。顧客向け文面や法務・医療の領域では、差分を実行前に見せる承認モードを選べます。適用の前には、意味のない一般論を削るde-fluffが毎回入ります。差分はchange.patchに保存し、後から戻せるようにします。

  3. 3

    実行する

    全件を毎ラウンド実行します。件数を絞ると、過去のラウンドと比べられなくなるためです。effortのようなサーバー側で検証される設定は、まず1ケースだけ流して受理されるかを確かめます。

  4. 4

    記録する

    結果はvN/に書き、_state.jsonを更新します。全ケースが揃う前にスコアを報告せず、途中経過ならN/totalと明示します。

分析役はtrainだけを読む

分析役に渡されるのは、trainのトランスクリプトと採点結果だけです。メインのセッションは、変更を選ぶ段階ではトランスクリプトを読まず、スコアだけを見ます。testの情報が提案へ漏れない構造です。

手順書が分析役に課す方針は3つです。

  • 失敗の「内容」でなく「振る舞い」を書く。trainの固有名詞や語句をプロンプトに貼ると、trainだけ伸びる過学習の変更になる
  • 何かを削る提案では、回復するケースだけでなく、悪化すると予想するケースも名指しする
  • 早い段階なら、同じ文の言い換えでなく、ツール説明やAPIパラメータなど別のレバーを試してよい

「1ラウンド1仮説」は、この方針とは別のラウンド全体の規則です。無関係な修正を束ねると、効いた部分も戻すべき部分も分からなくなります。

評価で測れない小さな言い換えには周回を使わない

効果が見えない変更に周回を使わない規則は、v2.1.284で挙動が改善されています。この版から、hillclimbは評価で測れないほど小さいプロンプトの言い換えにラウンドを使わなくなりました。

背景にはノイズフロアの考え方があります。効果がノイズフロアより小さい変更は、残るか戻るかが偶然で決まります。そのため、失敗の原因を根から直す変更(節の書き直し、欠けているルールやツールの追加、effortの変更)を優先します。一文の言い換えは対象外です。差分の長さでなく、効果で測ります。

効果を出せる量にも上限があります。ある振る舞いを直して得られるのは、その振る舞いで落としているケースの分までです。落とし分がノイズフロアに届かないなら、変更を膨らませず、停滞として扱います。

無人で回すときの許可と整合性ゲート

評価はrun_in_backgroundのBash 1回として走ります。分析役のようなサブエージェントで包みません。完了通知を待つあいだも会話は使えます。通知が来たら、通知を信じる前にディスクを確認します。vN/results.jsonlがケース数×反復数の行数あり、vN/summary.jsonがあるかを見ます。行が足りなければ同じコマンドを再実行します。再実行は(ケース, 反復)単位で冪等なので、途中から続きます。

最初の無人ラウンドの前に、Claudeは実行コマンドそのものを見せ、セッション中の許可を求めます。この許可が、ループの安全境界です。許可したコマンドが実行することは、すべて承認済みの扱いになるためです。許可範囲は評価ランナーの1コマンドに絞り、依存関係はnpm ciとロックファイルで固定します。

ハーネスの整合性ゲートは、この許可の上に重ねた変更検知にすぎません。ランナーは自分自身、隣と実行ディレクトリのロックファイル、harness_pathsのファイルのハッシュを取り、最後に--approve-harnessで記録した値と違えば終了コード2で止まります。node_modules/、インタプリタやSDKのバージョン、harness_pathsにないファイルは対象外です。--approve-harnessは無人ラウンドから渡さず、ユーザーが自分で実行します。ヘッドレス(-pやSDK)では会話を空けておく意味がないため、評価は前景で実行されます。

進み具合を尋ねると、ランナーの進捗行(k/N done ... ~Ns left)から1行で答えます。この行は30秒ごとに出力され、vN/progress.txtにも写されます。

記録と採否

状態は次のような構成です(公式ガイドの既定レイアウトの抜粋)。

.claude/hillclimb/<flow>/
  _state.json      # ラウンド番号・分割ID・best・goal
  narrative.md     # 各ラウンドの効果を要約した実行サマリ
  baseline/        # results.jsonl / summary.json / traces/
  v1/              # change.md / change.patch / 結果一式
  v2/

_state.jsonのgoalには最適化の狙いが入ります。コスト削減を狙うときの記録例は次のとおりです。

{
  "goal": {"target": "cost_usd", "direction": "lower", "hold": ["pass"]},
  "approve_each_round": false,
  "train_ids": ["case_01"],
  "test_ids": ["case_02"],
  "harness_paths": ["eval/run-eval.mjs", "eval/grade.mjs"]
}

narrative.mdの先頭には、ラウンドごとの状態表(変更の1行説明、test、train、1ターンあたりの秒数、出力トークン、ツール呼び出し、1回あたりのドル、累計支出)が置かれます。ラウンドごとに全体を書き直す要約なので、再開したセッションも1画面で経過をつかめます。

採否のルールは明快です。trainが上がってtestが上がらないなら、trainで読んだケースへの過学習として変更を戻します。trainも下がったなら、次に積む前に戻します。trainが下がってtestだけ上がった場合は、反復が少ないときのノイズとして扱い、再実行で同じ傾向が出たときだけ残します。

探索が止まったときの分類

数ラウンドの間、testがノイズの帯を越えなくなったら、内容の追加をやめて失敗を分類します。残った失敗の原因が、対象のプロンプトや設定だとは限らなくなるからです。ここで分析役が、trainの失敗を原因別に分けます。

  • 採点側の不一致: 出力は正しく見えるのに採点が誤りにしている。採点を直し、全ラウンドを保存済みの出力から再採点する
  • 構造の問題: 内容は存在するのにモデルが到達していない。ファイルを整理し直す
  • ばらつき: 同じコードでの再実行の差が、ラウンド間の差と同じ大きさ。反復を増やすか目標を変える

このほか、失敗が小さな塊に分かれて散らばっている場合は、一括で軽く網羅する「幅出しの周回」も提案されます。

失敗の混ぜ方にも規則があります。ケースがモデルの出力を得る前にエラーで落ちたなら、直すまで分母から外します。安全上の拒否も、能力の失敗と平均してはいけません。報告では失敗を拒否、ハーネスまたはサービングのエラー、タイムアウト、本物の能力不足に分けて数えます。

結果はtestの差で読む

終了時、コードベースはtestで勝った版に置かれます。見るべきはtestのスコアがベースラインから勝者へどれだけ動いたかです。trainの伸びは過程であって、成果ではありません。信頼区間つきで報告され、差がノイズの範囲内なら、マージを勧めないと明言する設計です。

ブログの例が、この読み方を具体的に見せています。カスタマーサポートのベンチマークで、Opus 4.8を既定の高effortで走らせた状態から出発しました。

  1. Opus 5を低effortで試し、prompt-auditで必須のツール呼び出し儀式・スクラッチパッド手順・矛盾するルールを削った。train正解率は98.9%で、1チケットあたり2.6セントになった
  2. Sonnet 5の低effortへ下げた。1セントとさらに安くなったが、正解率は88.9%まで落ちた
  3. 失敗したtrainのチケットを読み、ルーティング規則と返金上限の相互参照をプロンプトに追加した。Sonnet 5で98.9%に戻り、コストは同じ

最後に、探索が一度も見なかった14件のtestチケットで採点しました。最終設定は90.5%で、元の設定の78.6%を上回り、コストは約5分の1です。

注目点は、trainの98.9%でなくtestの90.5%が結果として示されていることです。 最終報告は、勝った版の推奨変更、ベースラインとの比較、信頼してよい理由、試したが採らなかった変更の4部で書かれます。トランスクリプトの詳細は報告に再掲せず、report.htmlを指します。

レポートの出力

各ラウンドの後、report.htmlが作られます。全機能入りのビューアが使える場合は、全ラウンドのスコア推移、トランスクリプトの比較、ラウンドごとの差分を1ファイルで見られます。軽量版のビルダーでは、サマリー表とケースごとの行が出て、各トレースファイルへのリンクが付きます。ラウンド後の報告には、このパスが末尾に添えられます。

レポート以外の表示がほしい場合は、report.htmlの隣に追加のページとして作らせます。v2.1.284以降、その追加ページは外部を読み込まない単一のローカルファイルとして生成されます。社内のドキュメントに貼るときに、ネットワークへ依存しない形です。

コストを見積もるには

ラウンドごとに全件を評価するため、ループ全体のコストはベースラインを含めて(ラウンド数 + 1) × 反復数 × 1回あたりのコストが目安です。採点にモデルを使うなら、判定の呼び出し分も加えます。

予算を聞かれたときは、ケースごとのusageを合計し、価格表でドルへ換算し、実際に1回流した所要時間を測って見積もります。負担を減らす手段は、ラウンド数を減らす、反復を減らす、判定モデルを安くする、判別力の高いケースに絞ってから最後に全件で確認する、の4つです。ただし既定では、ラウンド中に評価セットを黙って絞ることはしません。予算は目安であり壁ではありません。上限に近づいてスコアが伸びているときは、延長を提案する設計です。

評価なしで進める/claude-api cost-optimizeは、同じ目的のチェックリストです。prompt-auditもこの段取りの一部です。

導入時に確認したい注意点

  • 正解データを隠す: 参照回答や採点基準は、モデルの文脈から読めない場所に置く。プロンプトへ「答えを見るな」と書いても防御にならない
  • レビュー役を決める: 承認モードにするか、自動適用にするか。顧客向けの文面を含むなら承認モードが選択肢になる
  • 1つのループでは1つの評価を使う: 評価の中身を途中で変えると、過去のラウンドと比べられない
  • 評価を作る: 評価が無いなら、先に/claude-api build-evalで作る。評価の設計自体はプロンプト評価の実践ガイドが参考になる

モデルを替える移行が目的なら、移行ガイドの内容も踏まえて変更が提案されます。effortの意味や設定はmodelSettingsのeffortをモデル別に編集する方法に整理されています。

呼び出しと、使うかどうかの切り分け

呼び出しは、評価が用意できているプロジェクトで/claude-api hillclimbを実行するだけです。

/claude-api hillclimb

あとはClaudeが評価コマンドの場所、目標、対象範囲、停止条件を順に尋ね、計画の承認を求めます。承認後に分割とベースラインの測定が始まります。

評価が数十件でノイズフロアが改善の見込みより大きいなら、走らせる前にケースか反復を増やす判断になります。差を本物と読めるのは、testを守り、ノイズフロアを先に測るからです。探索の途中の挙動は版で変わっており、v2.1.284の変更はリリースノートにあります。

参照した一次ソース

この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。

  1. claude.com/blog/reducing-cost-and-improving-performance-with-claude-p…
  2. code.claude.com/docs/en/commands
  3. github.com/anthropics/skills/blob/main/skills/claude-api/shared/evals…
  4. code.claude.com/docs/en/skills
この記事を共有:XはてブLinkedIn