TT Lab
はじめる
学ぶ 学習パス コース

LLMサービング

感覚でモデルを替えると起きること

TT Labで続きを見る

一言でいうと

評価ハーネスなしに、モデルやプロンプトを変えるのは、テストなしにリファクタリングするのと同じです。よくなったという感覚と、実際によくなったことは、別です。

なぜ必要なのか

新しいモデルが出ました。いくつか尋ねてみると、答えのほうがよいです。切り替えます。2週間後、特定の種類の質問で、品質が落ちたという問い合わせが入ります。元に戻すかを判断しなければなりませんが、根拠がありません。

この状況が繰り返される理由は、LLMの出力が決定的ではなく、評価の基準が主観的だからです。ユニットテストのように、「正確にこの値」を期待できない場合が多くあります。しかも、人の記憶は、最近の数件に大きく左右されます。印象深い失敗が1つあるだけで、全体の品質に対する判断が覆ります。

それでも、測ることはできます。完璧でなくても、回帰を捉えられる程度にはなります。

評価ハーネスの4つの部分

評価セット。実際のトラフィックから抜き出した代表的な質問と、期待する結果です。30–100件あれば、回帰の検知を始めるのに十分です。重要なのは、実際の分布を反映することです。うまくいくケースだけを集めると、回帰を捉えられません。トラフィックの種類ごとの比率を合わせ、難しいと知られているケースを、最低20%入れます。

採点ツール。種類によって異なります。

出力の種類 採点方法 注意
JSON・分類・抽出 完全一致 フィールドの順序と空白を正規化して比較します
短い事実の答え キーワードを含むか、正規表現 同義語をリストで許容します
長い記述 LLMジャッジ ジャッジモデルとプロンプトをバージョン固定します
コード 実行してテストを通過 最も信頼できます

LLMジャッジには、バイアスがあります。長い答えを短い答えより高く採点し、自分と同じ系列のモデルの文体を好み、最初に見せた候補を好みます。そのため、A/B比較をさせるときは、順序をランダムに入れ替えて2回尋ね、結果が逆転したら、引き分けとして数えます。

ベースライン。現在運用中の組み合わせのスコアを、保存しておきます。これがないと、比較する対象がありません。モデル・プロンプト・温度・評価セットのバージョンをいっしょに記録しておかないと、あとで再現できません。

ゲート。新しい組み合わせのスコアが、ベースラインより一定の幅以上低ければ、失敗と判定します。

しきい値の決め方、まずノイズを測る

しきい値を勘で決めてはいけません。同じ設定で3回動かして、揺らぎの幅を先に測り、それより大きい値をしきい値に使います。

# 같은 모델·프롬프트로 3회
run 1: 0.847   run 2: 0.861   run 3: 0.839
→ 노이즈 폭 약 2.2%p → 임계는 3~4%p 로 잡는다

このコードブロックの韓国語は、同じモデルとプロンプトで3回実行した結果から、ノイズの幅が約2.2%pなので、しきい値を3–4%pにする、という意味です。

評価セットが小さいと、ノイズが大きくなります。50件の評価セットで1件が逆転すると、2%p動きます。統計的にいえば、50件で観測した0.85の95%信頼区間は、およそ0.75–0.92です。小さい評価セットで、1–2%pの差で判断してはいけません。

温度を0にしても、完全に決定的にはなりません。バッチサイズと浮動小数点の累積の順序によって、結果が変わることがあり、同じ入力に異なる出力が出ることがあります。

品質だけを測るのでは半分

品質といっしょに、レイテンシとコストも、同じハーネスで測る必要があります。

軸 測る値 ゲートの例
品質 評価セットのスコア ベースライン−3%p以下なら失敗
レイテンシ p50、p95、最初のトークンまで p95がベースラインの1.5倍を超えたら失敗
コスト 1,000件あたりのトークンコスト ベースラインの2倍を超えたら人が承認

平均ではなく、p95を見る必要があります。平均レイテンシはよいのに、p95が3倍になる変更が、実際にあります。ユーザーは、平均を体験するのではなく、自分のリクエストのレイテンシを体験します。

最初のトークンまでの時間(TTFT)も、別に測ります。ストリーミングUIでは、全体の完了時間よりも、TTFTが体感品質を左右します。

現場での姿

ハーネスをCIに入れることが決定的です。プロンプトの変更のPRで自動的に動いて、回帰ならマージがブロックされれば、人が覚えておく必要がなくなります。プロンプトをコードのように扱うことの実際の意味が、これです。

評価セットは、生きている必要があります。ユーザーの問い合わせで見つかった失敗ケースを、評価セットに追加するループを作れば、同じ失敗が2回起きません。回帰テストの原理と同じです。

ただし、罠が1つあります。評価セットに合わせてプロンプトを直し続けると、評価セットに過学習します。実際のトラフィックでは悪くなるのに、スコアだけが上がります。これを防ぐには、評価セットを2つに分けて、開発用(繰り返し見るもの)と検証用(ときどきしか見ないもの)を分離します。検証用のスコアが、開発用に追いつかなくなり始めたら、過学習のサインです。

何を評価セットに入れるのか

評価セットを作るとき、最もよくあるミスは、うまくいくものだけを集めることです。そうすると、スコアがいつも高く、回帰も捉えられません。5つの種類を、意図的に混ぜます。

  1. 代表的なケース: 実際のトラフィックで最も多い種類。全体の半分ほど。
  2. 境界のケース: 入力が空のとき、非常に長いとき、他の言語が混ざったとき。
  3. 既知の失敗: 過去に問い合わせで入ってきたもの。ここが回帰検知の核心です。
  4. 拒否すべきもの: 答えてはいけない質問。モデルがおとなしく断るかを確認します。
  5. 罠: 前提が間違った質問(「2026年のノーベル賞を取った、あの人のことだけど」)。モデルが前提を聞き返すのか、それとも作り話をするのかを見ます。

期待する結果を書くときは、1つの正解ではなく、許容する集合として書きます。

{"id": "refund-01",
 "input": "환불 언제까지 돼요?",
 "must_include": ["7일", "영업일"],
 "must_not_include": ["환불 불가", "죄송"],
 "max_tokens": 200}

このコードブロックの韓国語は、入力が「返金はいつまで可能ですか」、必ず含める語が「7日」と「営業日」、含めてはならない語が「返金不可」と「申し訳ありません」だ、という意味です。

must_not_includeが、意外と役に立ちます。品質事故の相当数は、「あってはならない言葉が出たこと」であって、「あるべき言葉が抜けたこと」ではありません。

次のラボですること

評価セットを読み込んでランナーを作り、完全一致と部分点の採点ツールをそれぞれ実装し、レイテンシSLOの違反を数え、同じ設定で3回動かしてノイズの幅を測り、ベースラインを保存したあとで回帰を判定し、最後にCIゲートのスクリプトにします。