Evalエンジニアリング入門、LLMのデグレを検知する評価の作り方
Evalエンジニアリングとは、LLM[1](ChatGPTのような文章を生成するAI)の回答の良し悪しを、毎回同じ物差しで測り続ける仕組みを作る仕事です。そしてデグレ[2]とは、直したせいで別のところが動かなくなること。この記事が扱うのは、その2つを軸にした「AIの回答が壊れたことに、どうやって気づくか」です。
たとえば、社内規程についての問い合わせに答えるAIを運用しているとします。回答がおかしいと指摘を受けたのでプロンプト[3]を1行直し、手元で3件試す。直したいケースが通ったのでマージ[4]する。
数日後、別の質問への回答が崩れていると利用者から報告が来ます。以前は正しく答えられていた質問です。今回の修正が原因なのかも、いつからそうなっていたのかも分かりません。
出力が確率的である以上、「この3件が通った」は品質の証明になりません。測るべきは、固定した入力セットの合格率が変更の前後でどう動いたかです。
手元で数件試す確認が限界になる理由と、Evalエンジニアリングの守備範囲
「この3件が通ったなら、たぶん大丈夫」
確かめられたのは、その3件だけです。直したかった1件が直っても、別の質問が壊れたことは見えません。デグレはプログラムの改修でも起きますが、LLMでは出力が毎回変わるぶん、気づくのがずっと難しくなります。
しかもLLMは、同じ入力に同じ文字列を返しません。assert answer == "..." で期待値を固定できず、単体テスト[5]の書き方が使えないのです。代わりに使うのが pass rate(合格率)[6]、固定した入力セットのうち何件が合格したかの割合になります。
プロンプトエンジニアリング[7]が出力を良くする作業なのに対し、Evalエンジニアリングは良くなったのかを測り続ける仕組みを作る作業です。守備範囲は2つあります。手元に固定したデータで測る評価(offline eval)と、本番で実際に使われた記録に対して測る評価(online eval)です。リリース前に走らせるテストと、稼働後の監視ダッシュボード。その両方を持つのに近いと考えてください。
独立した仕事として語られるのは、評価軸が多次元で、モデルが予告なく更新され、判定器そのものの校正コストがかかるからです。2つ目には実測もあり、2023年公開の比較ではGPT-4の3月版と6月版で素数判定の正答率が97.6%から2.4%へ落ちました(arXiv)。
なお「evals engineering」という呼び名の出どころは、一次情報では特定できていません。2024年3月29日公開のHamel Husainの記事が、評価の仕組みが無いことをAIプロダクト失敗の主因に挙げた例として確認できる程度です(hamel.dev)。
評価データセットを本番ログと失敗事例から起こす、件数と分布の決め方
測定に使う質問と回答のセットを、評価データセットと呼びます。これはゼロから作文するのではなく、本番ログから起こします。
ここからは、社内規程を検索してその内容をもとにLLMが答える仕組み(RAG)[8]を題材にします。答えを作る前に社内文書を引いてくるため、失敗の原因が検索側と生成側に分かれるのが特徴です。ログに残すのは、質問文、検索で引いた参照文書(文書ID、章、版数)、回答と引用した文書IDの3点。これが揃っていないと、どちら側の失敗なのかを切り分けられません。
次が error analysis[9](失敗した出力を読み、種類ごとにラベルを貼る作業)です。検索が引けていない、回答が文書と食い違う、出典がない、古い版を答えている。この4つのように分けて数え、多い順から評価項目へ起こします。読む件数は最低100件、新しい失敗カテゴリが約20件連続で出なくなったら打ち切ってよいという目安があります(hamel.dev、2025年6月公開)。
「結局、何件あればいいのか?」Anthropicの公式ドキュメントは「量を質より優先する」としたうえで、感情分析1,000件、医療チャットボット500件、要約200件、カスタマーサポート100件という用途別の例を挙げています(Anthropic、2026年8月時点)。例示であって、社内文書RAGの推奨値ではありません。

出典: Anthropic「Define success criteria and build evaluations」(2026年8月時点)
配分は3層で考えます。
| 入れるもの | 目的 |
|---|---|
| 頻出する質問(ログから頻度順に) | 日常の品質を守る |
| まれだが影響の大きい質問(労務・情報管理の規程から) | 事故を防ぐ |
| すでに壊れた既知の失敗(ラベル付きの失敗ログ) | 再発を検知する |
つまり平均を測る分と、事故を防ぐ分と、直したものが戻らないことを確かめる分を別々に確保します。デグレの検知(回帰テスト)[10]に使うセットは凍結してください。中身が動くと、合格率が下がった原因が実装なのかデータなのか分からなくなります。新しい失敗は追加用セットへ入れ、区切りで昇格させます。
決定的な評価とLLM-as-a-judge、どの観点をどちらに割り振るか
では、何を見て合格と呼ぶのでしょうか。観点ごとに判定方法を決めますが、その前に判定のやり方が2種類あることを押さえてください。
1つが決定的な評価です。「この文書IDは実在するか」のようにプログラムで白黒つけられるもので、何度実行しても同じ結果が返ります。もう1つが LLM-as-a-judge[11]、日本語にすれば「LLMに採点役をやらせる方法」です。回答が参照文書に忠実かどうかのように、正解の文をひとつに決められない観点を、別のLLMに合否判定させます。
どちらを使うかは、観点ごとに割り振ります。
| 観点 | 判定方法 | そうする理由 |
|---|---|---|
| 出力形式(JSON) | 決められた形になっているかの検査 | 正解が構造で書ける |
| 引用IDの実在 | 引いた文書の一覧と照合 | 存在しないIDを機械的に弾ける |
| 出典への忠実さ | LLM-as-a-judge | 正解文が一意に書けない |
| 質問に答えているか | LLM-as-a-judge | 言い回しが無数にある |
正解を一意に書ける観点を先にコードへ落とし、残りだけをjudgeに渡します。コードレビューで、書式のような機械的に見られる部分をツールに任せ、人は設計を見るのと同じ分担です。引用IDの実在確認なら、次のように「回答が挙げた文書IDが、実際に引いてきた文書の中に全部あるか」を見るだけで済みます。
def cited_ids_exist(answer: dict, retrieved: list[dict]) -> bool:
allowed = {d["doc_id"] for d in retrieved}
return set(answer["citations"]) <= allowed
retrieved が検索で引いてきた文書、answer["citations"] が回答に書かれた引用IDです。1行目で引いてきた文書のID一覧を作り、2行目で「回答の引用IDが、そのID一覧にすべて収まっているか」を判定しています(<= は大小ではなく、集合として含まれるかを表します)。存在しないIDが1つでもあれば不合格です。回答が800字以内か、応答が10秒以内かも、同じようにコードで書けます。
judgeへ回すのは、faithfulness[12](忠実さ、回答の各文が参照文書で裏づけられているか)のように、正解文を一意に書けない観点だけにしてください。その前に、判定基準を日本語で書き下ろします。「参照文書に無い数値が出たら不合格」「文書の要約は合格」と境界まで書いておかないと、人と食い違ったときに原因を特定できません。
LLM-as-a-judgeを作り、人手ラベルとの一致率で校正する
judgeが正しく採点できているかは、人が付けた合否と突き合わせて確かめます。この人が付けた合否を人手ラベル[13]と呼び、判定器を合わせ込んでいく作業が校正[14]です。まずは、書き下ろした基準をそのまま判定用プロンプトに載せるところから始めます。
あなたは社内文書QAの回答を検査します。
参照文書: {{context}}
質問: {{question}}
回答: {{answer}}
判定基準:
- 事実・数値・条番号が参照文書で裏づけられていれば合格
- 参照文書に無い数値や固有名詞が1つでも出たら不合格
- 参照文書の要約・言い換え、「記載がありません」は合格
裏づけを確認できなかった箇所を1つずつ列挙し、
最終行に PASS または FAIL だけを出力してください。
二値に寄せる、理由を先に書かせる、境界例を基準に含める、出力のばらつきを決める設定(温度)[15]を0付近に下げる。この4つが要点です。
判定器そのものも評価対象です。自分たちで合否を付けた50件から100件ほどにjudgeをかけ、人手ラベルとの一致率を測ります。ただし一致率は、たまたま一致した分も含んでしまいます。それを差し引いた指標が Cohen's kappa[16] で、0.61〜0.80なら相当な一致とされます(Landis & Koch, 1977)。MT-benchの検証では、判定器にしたGPT-4と人間の一致率は85%、人間同士は81%でした(arXiv、2023年)。
同じ検証は癖も報告しています。提示順を入れ替えて再判定したとき判定が一貫していた割合はGPT-4で65.0%(位置バイアス)[17]、意味を変えず長くしただけの回答を高く評価した割合は8.7%(冗長性バイアス)[18]でした。順序を入れ替えて両方向で判定する、2つの回答を比べさせるのではなく基準に照らして1つずつ判定する、採点役には回答を作ったのとは別系列のモデルを使う。この3つで手当てしてください。
校正は一度では終わりません。食い違った事例を読み、判定基準の文を書き直して測り直します。この反復を回してください。

出典: arXiv「Judging LLM-as-a-Judge with MT-Bench and Chatbot Arena」(NeurIPS 2023)
判定プロンプトを3回作り直して専門家との一致率90%超に到達した事例もあります(hamel.dev、2025年3月公開)。
CIに組み込み、本番ログを評価セットへ戻す
判定器が信頼できるようになったら、変更のたびに自動で走る仕組み(CI)[19]へ載せます。ただし全件を毎回回すと、時間もAPIの利用料も見合いません。動かすのに時間のかかるテストと同じで、段階を3つに分けてください。
| タイミング | 回すもの | 目的 |
|---|---|---|
| 変更を出すたび | 最小限の確認用セット(数十件) | 数分で明らかな破壊を止める |
| 本流へ取り込むとき・夜間 | 全件を1周 | 合格率の推移を記録する |
| モデル更新・大幅な改訂 | 全件を複数回 | ばらつき込みで差を見る |
評価はテスト実行ツール(ここではPythonのpytest)[20]で書き、CIの設定から呼びます。大事なのは、落ちたケースの質問・参照文書・回答を実行結果のファイルとして残すことです。
- run: pytest evals/ -m smoke -n 8 --json-report --json-report-file=result.json
- uses: actions/upload-artifact@v4
if: always()
with: { name: eval-failures, path: "result.json\nartifacts/failed_cases/" }
前半が評価の実行です。-m smoke で最小限のセットだけに絞り、-n 8 で8件を同時に走らせ、結果を1つのJSONファイルに書き出します。後半が結果の保存で、if: always() を付けているのは、落ちたときこそ中身を残したいからです。
合格率の数字だけでは、何が壊れたか分かりません。前回の測定結果と突き合わせ、合格から不合格へ変わったケースの一覧を、変更のレビュー画面に出すところまで作ります。止める基準も、絶対値ではなく前回との比較で決めます。決定的な評価は1件でも落ちたら止めます。judgeによる合格率は、低下幅と件数の両方で判断してください(60件で1件の反転は誤差、5件なら調査対象)。1件の上下で赤くなる設定にすると、チームがCIを無視するようになります。天気予報の警報と同じで、鳴りすぎる警報は誰も見なくなります。
「これ、毎回回したらいくらかかるのか?」Claude Haiku 4.5は入力100万トークン[21]あたり1米ドル(約159円)、出力100万トークンあたり5米ドル(約797円)。まとめて非同期で処理するBatch API[22]なら5割引き、毎回同じ前置きを使い回すプロンプトキャッシュ[23]が効いた分は基本価格の0.1倍です(Anthropic、2026年8月19日時点。1米ドル=約159.39円で換算)。急がない夜間の全件実行は、Batch APIへ回してください。
ただしCIの合格率が上がっても、本番のユーザー体験が良くなったとは限りません。日次の応答から一定割合(例えば5%)を無作為に抜いて校正済みのjudgeにかけ、低評価が付いた・短時間で聞き直された・窓口へエスカレーションされたセッションは全部拾います。

本番で見つかった失敗が、翌週のCIで検知できる状態になっていること。この往復が続いて初めて、評価が本番の品質に追いつきます。
仕組みが機能しなくなる3つの原因
スコアは上がったのに本番が良くならないとき、原因は3つのどれかです。
1つ目が評価セットへの過適合[24]です。同じデータの失敗を見ながら直し続けると、そのデータだけ通る書き方に寄ります。開発中は見ない検証用のセットを別に持ち、節目でだけ回して差を確かめてください。
2つ目が基準そのものの緩みです。判定プロンプトを直すたびに合格率が上がっているなら、動いているのは品質ではないかもしれません。judgeを触ったら、一致率を測り直してください。
3つ目が件数不足です。10件で合格率が7割から8割になっても、1件の当たり外れで動く範囲でしょう。3ポイントの差を「偶然ではない」と言い切るには、独立した設問が約969件必要という計算例もあります(arXiv、2024年11月。ベンチマーク比較を想定した計算なので、そのまま回帰テストの必要数ではありません)。件数を増やせないときは、同じ設問を複数回実行して平均を取ります。同じ論文のモデルでは、4回実行すればばらつきが約半分になります。
評価の仕組みを作り始めるなら、どこから手をつけるか
ここで示したのは入口です。それでも、最初の3つは今週のうちに動かせるのではないでしょうか。
- 本番ログから失敗した回答を集め、種類でラベルを付ける。100件を目安に読み、新しい種類が出てこなくなったら止める
- 多い種類から小さな評価データセットを起こし、決定的に判定できる観点から自動化する。スキーマ検証と引用IDの実在確認は、その日のうちに書ける
- 判断が要る観点を1つだけjudgeにして、人手ラベルとの一致率を測る。社内文書RAGなら忠実さから始める
この3つが回り始めてから、CIへの接続と本番ログの還流へ進みます。合格率という共通の数字があれば、書き換えるたびに「他を壊していないか?」と手を止める必要がなくなります。壊すのが怖くて改善を止めていた理由が消えるところから、評価の仕組みは効き始めます。
用語の注釈
- LLM 大量の文章を読み込ませて、次に来る言葉を確率で選びながら文章を作るAI。Large Language Model(大規模言語モデル)の略で、ChatGPTやClaudeがこれにあたります。 ↩
- デグレ degrade(デグレード)の略。ある不具合を直したせいで、これまで動いていた別の部分が動かなくなることです。回帰やリグレッションとも呼びます。 ↩
- プロンプト AIへ渡す指示文。役割、守ってほしい条件、出力の形などを言葉で書きます。設定値をつまみで決めるかわりに、文章で与えていると考えてください。 ↩
- マージ 手元で直したコードを、みんなが使う本流のコードへ取り込むこと。取り込んだ時点で、その変更は利用者全員に届きます。 ↩
- 単体テスト プログラムの部品ごとに「この入力ならこの出力」を書いて確かめる検査。出力が毎回変わるLLMには、この形がそのままでは使えません。 ↩
- pass rate(合格率) 用意した質問のうち、判定に通った件数の割合。60件中54件が通れば90%です。1件ずつの正誤ではなく、この割合の増減を見ます。 ↩
- プロンプトエンジニアリング 指示文を書き換えて、AIの出力を良くする作業。良くなったのかを測る側の仕事がEvalエンジニアリングです。 ↩
- RAG Retrieval-Augmented Generationの略。答えを作る前に社内文書などを検索し、引いてきた文書をもとにLLMが回答する作りです。失敗の原因が検索側と生成側に分かれます。 ↩
- error analysis(失敗分析) 失敗した出力を実際に読み、原因の種類ごとにラベルを貼って数える作業。多い種類から評価項目へ起こします。 ↩
- 回帰テスト 一度直した不具合が元へ戻っていないかを確かめる検査。使うデータを固定しておかないと、合格率が下がった原因が実装なのかデータなのか分からなくなります。 ↩
- LLM-as-a-judge 採点役を別のLLMに任せる方法。judgeは採点役の意味で、正解の文をひとつに決められない観点に使います。 ↩
- faithfulness(忠実さ) 回答の内容が、引いてきた参照文書で裏づけられているか。文書に無い数値や固有名詞が混じっていないかを見ます。 ↩
- 人手ラベル 人が目で見て付けた合否。採点役のLLMが正しく採点できているかは、この人の判断と突き合わせて確かめます。 ↩
- 校正 ここでは文章の校正ではなく、採点役の目盛りを人の判断へ合わせ込む作業(calibration)を指します。測りが狂っていないか確かめて直すことです。 ↩
- 温度(temperature) 出力のばらつきを決める設定。0に近いほど毎回似た答えになり、上げるほど表現が散らばります。採点役には0付近を使います。 ↩
- Cohen's kappa 2者の判定がどれだけ一致しているかを表す指標。偶然の一致を差し引くのが特徴で、0が偶然並み、1が完全一致です。 ↩
- 位置バイアス 見せる順番によって判定が変わる癖。順序を入れ替えて両方向で判定すると見つかります。 ↩
- 冗長性バイアス 中身が同じでも、長い回答のほうを高く評価してしまう癖です。 ↩
- CI Continuous Integrationの略。コードを変更するたびに、決めた検査を自動で走らせる仕組みです。検査に落ちれば、本流への取り込みを止められます。 ↩
- pytest Pythonでテストを書いて動かすための道具。評価もこの形で書き、CIの設定から呼び出します。 ↩
- トークン LLMが文章を扱うときの単位。単語より少し細かく、日本語なら1文字が1〜2トークン程度です。利用料はこの数で決まります。 ↩
- Batch API 急ぎでない処理をまとめて非同期で流す仕組み。すぐに結果が要らない代わりに、料金が安くなります。 ↩
- プロンプトキャッシュ 毎回同じ前置きを使い回すとき、その部分を安く処理する仕組みです。 ↩
- 過適合 同じデータの失敗ばかり見て直し続けた結果、そのデータでだけ点が取れる状態になること。手元の点は上がっても、本番は良くなりません。 ↩