Skip to content

필사 모드: 評価駆動開発で最初に較正すべきなのは審査者です

日本語
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.

審査者のスコアは上がったのに問い合わせも一緒に増えた

プロンプトを一度手直ししたら、社内評価ダッシュボードの忠実度スコアが0.82から0.90に上がりました。デプロイしました。その翌週、有人対応への引き継ぎ件数が増えました。ログを開くと、回答はより長く、より丁寧で、より自信を持って間違っています。

この状況で多くのチームは「評価が足りなかった」という結論を出し、評価セットを大きくします。100件を500件に増やし、項目を5個から20個に割ります。ところが実際の原因は評価セットの大きさではありません。採点を任せたモデルが長くて丁寧な回答により高い点を付けていて、その事実を誰も確認したことがなかった、ということです。評価セットを5倍にすれば、その偏りを5倍精密に測るようになるだけです。

評価駆動開発がTDDと決定的に違う地点

Airbnbエンジニアリングが2026年7月28日に公開したEval-driven developmentは、評価を付随作業ではなく一級のエンジニアリング分野として扱うべきだと述べます。ここまではTDDと同じ掛け声です。

違うのは判定者です。TDDの判定者は assert であり、この判定者は間違えません。評価駆動開発の判定者のかなりの部分はLLMであり、この判定者は間違えます。しかもランダムに間違えるのではなく、特定の方向へ一貫して間違えます。長さ、形式、自信、自分と同じ系列のモデルへの選好といった軸です。

そのため手順がひとつ増えます。テストを先に書くだけでは足りず、テストがまっすぐな物差しで測っているかをまず確認しなければなりません。

審査者はコードではなくデプロイされたモデルです

この見方を受け入れると、審査者の扱い方が変わります。審査者プロンプトは設定ファイルではなくデプロイ物です。バージョンが付かなければならず、変わるときには回帰検査を受けなければならず、どのモデルでどの温度で走ったかが結果とともに保存されなければなりません。

Airbnbの記事が示す三層構造がここで意味を持ちます。決定的なコード検査が1層、審査者モデルが2層、人間が3層です。核心は層を分けたこと自体ではなく、3層が2層を継続的に監査するという関係です。人間はすべての出力を見ません。代わりに、審査者が人間とどれだけずれているかを見ます。

1層に何を入れるかも重要です。JSONスキーマ違反、禁止語、引用のない断定、応答長の上限のように規則で表現できるものは、すべて1層で弾くべきです。これはコストの問題ではなく信頼の問題です。規則で捕まえられる失敗を審査者に任せると、審査者が間違えたときにその失敗が通ってしまいます。決定的に判定できるものを確率的な判定者に渡すのは、いつでも損です。

ゴールデンセット50〜100件で審査者を較正する手順

Airbnbの記事は審査者の較正を五つの段階に整理します。ゴールデンセットを作り、審査者をその上で走らせ、一致度を測り、不一致を掘り下げ、定期的に再較正します。推奨される規模は「50〜100件のゴールデンセット」で、ラベル付けは「主題の専門家がラベルした20〜100行」から始めよ、と書かれています。

この数字が小さく見えるのには理由があります。ゴールデンセットはモデルの性能を測る物差しではなく、審査者の性能を測る物差しです。物差しを検査するのに標本はたくさん要らず、代わりに失敗事例が必ず混ざっていなければなりません。良い回答だけを集めたゴールデンセットでは、何ひとつ弾けない審査者でも満点を取ります。

同じ記事の実例では、最初に作った忠実度審査者は人間と78パーセント一致しました。ルーブリックを整えて例をいくつか入れたあと88パーセントに上がり、そこでようやく大規模な実行に投入されました。

一致率88パーセントという数字をそのまま信じてはいけない理由

ここでもう一段階必要です。単純な一致率は、ラベル分布が偏ると水増しされます。合格が90パーセントの評価では、無条件に合格を打つ審査者も一致率90パーセントを得ます。ですから偶然の一致を取り除いた指標を見なければならず、Airbnbの記事もコーエンのカッパやクリッペンドルフのアルファを使えと明記しています。

"""審査者の較正: 一致率だけを見ず、カッパを一緒に見る。"""
from collections import Counter
from typing import Sequence


def cohens_kappa(human: Sequence[str], judge: Sequence[str]) -> float:
    assert len(human) == len(judge) and human, "対にしたラベルが必要です"
    n = len(human)
    po = sum(h == j for h, j in zip(human, judge)) / n  # 観測一致率
    hc, jc = Counter(human), Counter(judge)
    pe = sum((hc[k] / n) * (jc[k] / n) for k in set(hc) | set(jc))  # 偶然一致率
    return 1.0 if pe == 1 else (po - pe) / (1 - pe)


def calibration_report(rows):
    """rows: [(input_id, human_label, judge_label), ...]"""
    human = [r[1] for r in rows]
    judge = [r[2] for r in rows]
    k = cohens_kappa(human, judge)
    po = sum(h == j for h, j in zip(human, judge)) / len(rows)
    # 不一致の方向まで見てはじめて偏りを捕まえられる
    lenient = sum(h == "fail" and j == "pass" for h, j in zip(human, judge))
    strict = sum(h == "pass" and j == "fail" for h, j in zip(human, judge))
    return {
        "agreement": round(po, 3),
        "kappa": round(k, 3),
        "judge_too_lenient": lenient,   # 見逃した失敗 — もっとも高くつく誤り
        "judge_too_strict": strict,
        "verdict": "ship" if k >= 0.6 and lenient == 0 else "recalibrate",
    }


rows = (
    [(f"g{i}", "pass", "pass") for i in range(80)]
    + [(f"b{i}", "fail", "pass") for i in range(8)]   # 審査者が見逃した失敗
    + [(f"c{i}", "fail", "fail") for i in range(12)]
)
print(calibration_report(rows))
# {'agreement': 0.92, 'kappa': 0.694, 'judge_too_lenient': 8, ..., 'verdict': 'recalibrate'}

一致率92パーセントなのに判定が再較正になる理由は最後の行にあります。人間が失敗と見た20件のうち8件を審査者が通しました。この審査者をそのままCIに掛ければ、チームはこれから先その8件の類型の失敗を永遠に見られなくなります。

審査者3〜5個が20〜30個より優れています

Airbnbの記事の表現をそのまま移すと、よく較正された審査者3〜5個が、雑音の混じった審査者20〜30個に勝ちます。この文は評価項目を減らせという意味ではなく、較正コストを負担できる分だけ項目を作れという意味に読むほうが正確です。

審査者ひとつを維持するコストは、プロンプトの塊ひとつではありません。ゴールデンセット50〜100件、ラベル付けに投じた専門家の時間、失敗の類型が変わるたびの再較正、そして審査者プロンプトが変わったときに回帰を確認するパイプラインまでがひと揃いです。この一式を負担できない項目は、作らないほうがましです。較正されていない審査者は、ないよりましどころか、誤った方向を指す羅針盤だからです。

エージェントは最終回答だけでは採点できません

エージェントに移ると軸がもうひとつ増えます。同じ記事は、エージェント評価が最終出力、中間の推論段階、ツール呼び出しと引数という三つの次元にまたがると整理します。

理由は単純です。最終回答が当たったという事実だけでは、その実行が再現されるかどうかは分かりません。検索を三度空振りして偶然キャッシュされた答えに当たった実行と、最初の呼び出しで正しい引数で正しいツールを呼んだ実行は、同じ点数を受け取りますが、まったく別のシステムです。前者は来週崩れます。

実務ではツール呼び出しの名前と引数を正規化して軌跡として保存し、少なくとも三つを別々に数えます。不要なツール呼び出しの数、誤った引数による再試行の数、そして同じ入力をもう一度入れたときに軌跡がどれだけ変わるかです。三つ目が特に重要です。同じ質問に対して軌跡が毎回大きく変わるなら、そのエージェントはまだやり方を知らない状態であり、今の成功率が次のデプロイで維持される保証はありません。

軌跡の採点に審査者を使うときは、ルーブリックを結果ではなく手順で書きます。「良い回答か」ではなく「この段階でこのツールを呼んだのは必要だったか」を問う形です。手順の問いは答えが短く根拠がログに残っているので、同じ審査者でも結果の問いより人間との一致度が目に見えて高く出ます。

何をゲートに掛け、何をダッシュボードにだけ置くか

最後はデプロイとの接続です。すべての指標をCIゲートに掛ければパイプラインは常時赤信号になり、何も掛けなければ評価は飾りになります。区切り線はこう引くのが実用的です。

性格配置
決定的で、違反がそのまま事故禁止表現、個人情報の露出、スキーマ違反CIゲート、1件でも失敗なら遮断
審査者ベースで較正済み忠実度、指示遵守CIゲート、基準線に対する下落幅で判定
審査者ベースで較正が未完語調、役立ち度ダッシュボード観察用、遮断禁止
分布に敏感応答長、ツール呼び出し数警報のみ、趨勢で判断

そして本番から標本を引き続き取ってこなければなりません。Airbnbの記事は、非識別化した実トラフィックの5パーセントを毎日サンプリングしていると明かしています。評価セットは作った日から古びはじめるので、新しい失敗の類型がゴールデンセットへ流れ込む経路がなければ、数か月後には昔の問題だけを繰り返し測ることになります。

評価駆動開発の最初の作業は、評価セットを作ることではありません。いま使っている審査者が人間とどれだけずれているかを数字で知ることです。その数字を知らないなら、ダッシュボードに出るすべてのスコアはまだ何の意味も持ちません。

参考資料

현재 단락 (1/62)

プロンプトを一度手直ししたら、社内評価ダッシュボードの忠実度スコアが0.82から0.90に上がりました。デプロイしました。その翌週、有人対応への引き継ぎ件数が増えました。ログを開くと、回答はより長く、...

작성 글자: 0원문 글자: 5,157작성 단락: 0/62