LLM アプリや AI エージェントをいざ本番に乗せようとすると、最後に必ず引っかかるのが「品質をどう保証し続けるのか」という問いです。デモでは賢く答えていたのに、プロンプトを少し直したら別のケースが壊れた、モデルのバージョンを上げたら回答の傾向が変わった。こうした事故を、担当者が手動で確認する目視と勘に頼って防いでいるチームは少なくありません。
私たち FIXIT は AI 駆動開発のクリエイティブスタジオとして、Claude Code や Cursor、そして AI エージェントを実プロジェクトに組み込んで運用してきました。その経験から言えるのは、LLM アプリの品質はモデルの賢さよりも、出力をどう測り、どこで回帰を止めるかという評価の仕組みでほぼ決まるということです。本記事では、評価データセットの設計から自動採点、回帰テストの CI 連携、本番ログの還流までを実装視点で整理し、属人化しない LLMOps の型を示します。
なぜ LLM アプリに評価ハーネスが必要か
通常のソフトウェアであれば、同じ入力に対して同じ出力が返るという決定性が前提にあり、ユニットテストが一度通れば、その挙動は変更を加えるまで固定されます。LLM アプリはこの前提が成り立ちません。同じプロンプトでも出力は揺れ、温度設定を 0 に近づけても完全な再現はしにくく、モデルのアップデートで振る舞いが変わることもあります。
つまり LLM アプリの品質は、一点を確認して合格にできる性質のものではなく、代表的な入力の集合に対して「望ましい振る舞いをどれだけ満たしているか」という分布として捉える必要があります。評価ハーネスとは、この分布を継続的に測り、変更のたびに回帰を検出する仕組みのことです。
ハーネスがないと何が起きるか。プロンプトの改善は「直してみて、いくつか手で試して、良さそうだからリリース」という運用になります。この運用は次の 3 つの問題を抱えます。
- 手で試すケースは担当者が覚えている範囲に偏り、過去に直した不具合の再発を見逃します。
- 品質基準が担当者の頭の中にあるため、人が変われば基準も変わります。
- 改善が怖くなります。何かを直すと何かが壊れるかもしれないという不安が、プロンプトやモデルの更新を止めてしまうのです。
評価ハーネスを先に用意しておくと、この 3 つが反転します。回帰は自動で検出され、基準はデータセットとして明文化され、改善は安全に試せるようになります。AI 駆動開発で速度を上げながら品質を守る考え方そのものは AI 駆動開発の品質保証 — テストとガードレールの全体像 で体系化していますが、LLM アプリではその検証の中核を担うのが評価ハーネスです。
flowchart LR
D["評価データセット<br/>(ゴールデンセット)"]
R["対象を実行<br/>(プロンプト/モデル/エージェント)"]
S["自動採点<br/>(ルール + LLM as a judge)"]
G["合格判定<br/>(基準と比較)"]
L["本番ログ"]
D --> R --> S --> G
L -.還流.-> D
評価データセットの作り方とゴールデンセット管理
評価ハーネスの価値は、突き詰めればデータセットの質で決まります。どんなに採点の仕組みが洗練されていても、測る対象のケースが現実とずれていれば意味がありません。
最初に作るのは、各ケースが「入力」「期待される振る舞い」「メタ情報」を持つ構造化されたデータセットです。期待値を一字一句の正解文字列として持つのは、表現の揺れが許される LLM の出力には向きません。代わりに、満たすべき条件を構造化して持たせます。
// eval/dataset/golden.jsonl の 1 行
{
"id": "refund-policy-001",
"category": "返金ポリシー",
"input": {
"user": "購入から 40 日経った商品を返品したいのですが可能ですか?",
},
"expectations": {
"must_include": ["返品期限は 30 日", "40 日経過のため対象外"],
"must_not_include": ["返金可能です", "受け付けます"],
"rubric": "返品期限を超過しているため対象外だと正確に伝え、代替案 (有償修理の案内) を提示しているか",
},
"meta": {
"difficulty": "medium",
"source": "production-log",
"added": "2026-01-04",
},
}このように must_include / must_not_include のような機械的に判定できる条件と、rubric のような judge に渡す採点基準を分けて持たせるのがポイントです。前者はルールベースで、後者は LLM as a judge で採点するという後述の使い分けが、この構造によって自然に決まります。
ゴールデンセットは「正解の集合」を意味し、品質の基準そのものになるため、運用の規律が要ります。実務で守っているのは次のような点です。ケースは必ずレビューを経て追加し、誰が何の意図で入れたかを meta に残します。データセットはコードと同じく Git で管理し、変更は PR で行います。難易度や業務カテゴリのタグを付け、どの領域が手薄かを後から分析できるようにします。そして重要なのが、本番で起きた失敗を必ずゴールデンセットに取り込むことです。一度起きた不具合を二度と再発させないという、回帰テストの原則をそのまま LLM アプリに持ち込みます。
件数は最初から大きくする必要はありません。中核となる業務シナリオを 30〜50 件カバーするところから始め、本番ログと不具合報告を還流させながら数百件規模へ育てていくのが現実的です。テスト先行で品質の土台を作る発想は AI 駆動 TDD - テストを AI に先に書かせる開発フロー と通じるもので、評価データセットは LLM アプリにおける受け入れテストに相当します。
採点方法の選び方 - ルールベースと LLM as a judge
採点をどう実装するかで、ハーネスの信頼性とコストが大きく変わります。基本方針は、機械的に判定できるものはルールベースで安く確実に測り、主観や文脈が絡むものだけ LLM as a judge に任せる、という使い分けです。
ルールベースで測れるものは意外と多くあります。特定のキーワードを含むか含まないか、JSON として正しくパースできるか、スキーマに沿っているか、数値が許容範囲に収まっているか、禁止表現を使っていないか。これらは LLM を呼ぶまでもなく、コードで判定できます。安価で速く、判定がぶれないため、測れるものはまずここで測りきります。
// eval/scorers/rule-based.ts
import { z } from "zod";
type Case = {
expectations: {
must_include?: string[];
must_not_include?: string[];
schema?: z.ZodTypeAny;
};
};
export function scoreByRules(output: string, c: Case) {
const checks: { name: string; passed: boolean }[] = [];
for (const phrase of c.expectations.must_include ?? []) {
checks.push({ name: `include:${phrase}`, passed: output.includes(phrase) });
}
for (const phrase of c.expectations.must_not_include ?? []) {
checks.push({
name: `exclude:${phrase}`,
passed: !output.includes(phrase),
});
}
if (c.expectations.schema) {
const parsed = c.expectations.schema.safeParse(safeJson(output));
checks.push({ name: "schema", passed: parsed.success });
}
const passed = checks.every((x) => x.passed);
return { passed, checks };
}
function safeJson(s: string) {
try {
return JSON.parse(s);
} catch {
return null;
}
}ルールでは測れないのが、文章としての自然さ、要件への沿い方、トーンの適切さといった主観の入る軸です。ここで LLM as a judge を使います。判定モデルに対象の出力と採点基準を渡し、合否と理由を構造化して返させます。
// eval/scorers/llm-judge.ts
const JUDGE_PROMPT = `あなたは厳格な採点者です。
以下の「採点基準」を満たしているかだけを判定してください。基準にないことは加点も減点もしないでください。
# 採点基準
{rubric}
# 評価対象の出力
{output}
# 出力形式 (JSON のみ)
{ "passed": true | false, "score": 0-5 の整数, "reason": "判定理由を 1〜2 文で" }`;
export async function scoreByJudge(output: string, rubric: string) {
const res = await callJudgeModel(
JUDGE_PROMPT.replace("{rubric}", rubric).replace("{output}", output),
);
return JSON.parse(res) as { passed: boolean; score: number; reason: string };
}judge を信頼するには、judge 自身を検証する仕組みが要ります。判定理由を必ず出力させて根拠を残すこと、採点基準を曖昧にせず具体的な合格条件として書くこと、そして人間が採点した数十件を judge にも採点させ、両者の一致率を定期的に測ることです。一致率が落ちてきたら judge のプロンプトを見直します。judge を入れて終わりにせず、judge の品質を管理対象に含めるのが、LLM as a judge を実務で使い続けるための条件です。
プロンプト・モデル更新の回帰テストと CI 組み込み
評価ハーネスが最も力を発揮するのが、プロンプトやモデルを更新するときの回帰検出です。LLM アプリの変更は、コードの変更と違って影響範囲が読みにくく、一か所の改善が別の箇所を壊しがちです。だからこそ、変更のたびに自動で全体を測り直す価値があります。
実装としては、プロンプトテンプレート、judge の採点ロジック、エージェントの定義といった「振る舞いを左右するファイル」を変更する PR で評価ハーネスを実行し、ゴールデンセットの合格率が基準を下回ったらマージをブロックします。GitHub Actions であれば、対象パスの変更をトリガーにジョブを動かす形になります。
# .github/workflows/llm-eval.yml
name: llm-eval
on:
pull_request:
paths:
- "prompts/**"
- "agents/**"
- "eval/**"
jobs:
evaluate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- name: Run eval (CI subset)
run: npm run eval -- --suite=ci --threshold=0.9
env:
MODEL_API_KEY: ${{ secrets.MODEL_API_KEY }}ここで実務上の勘所が 2 つあります。1 つは、CI では全件を回さないことです。数百件のケースを毎 PR でモデル呼び出しすると、実行時間とコストが現実的でなくなります。代表的なケースを集めた小さめのサブセットを CI で回し、全件は夜間バッチで評価する二段構えにします。もう 1 つは、落ちたケースを差分として PR に残すことです。合格率の数字だけでは、レビュアーはどこが壊れたか分かりません。前回からスコアが下がったケースの入力と出力をコメントとして貼り出すと、影響範囲を見て安全にマージできます。
合格基準の置き方も設計が要ります。LLM の出力は揺れるため、合格率 100% を毎回要求すると、本質的でない揺れで CI が落ち続けます。中核ケースは 100% を要求し、難易度の高いケースは合格率の下限を 90% のように設定する、といった段階を設けると運用が安定します。レビュー不要で安全にマージできる状態を機械ゲートで作るという発想は、コードレビューにも共通します。AI 生成コードのレビュー設計は AI コードレビューの設計 で扱っています。
本番のログを評価に還流させる LLMOps の運用
リリース前に用意したゴールデンセットは、開発者が想像できた範囲のケースしか含みません。実際のユーザーは、こちらの想定を超える入力を投げてきます。評価ハーネスを現実に追従させ続けるには、本番のログを評価に還流させる回路が要ります。これが LLMOps を「一度作って終わり」ではなく「回し続ける」運用に変える鍵です。
回路はおおむね次のように回ります。本番の入力と出力、そしてユーザーのフィードバック (低評価ボタン、やり直し、問い合わせへのエスカレーションなど) を収集します。その中から、低評価や失敗が集中するパターンを定期的に抽出します。抽出したケースについて、本来どう振る舞うべきかを人間が定義し、レビューを経てゴールデンセットに追加します。追加されたケースは次回以降の回帰テストで自動的に検証され続けます。
flowchart LR
P["本番運用"]
C["ログ + フィードバック収集"]
T["失敗パターン抽出<br/>(週次レビュー)"]
A["期待値を定義<br/>(人間)"]
GS["ゴールデンセットへ追加"]
H["評価ハーネスで継続検証"]
P --> C --> T --> A --> GS --> H
H -.改善.-> P
この運用で注意すべきは個人情報の扱いです。本番ログには氏名や連絡先といった機微な情報が含まれることがあり、そのまま評価データセットに入れると、リポジトリに個人情報が紛れ込みます。ログを評価に使う前段に、マスキングや匿名化の処理を必ず挟みます。AI を扱うプロジェクト全体のデータの取り扱いは AI 駆動開発のセキュリティ でも整理しています。
週次でこの還流を回しているチームと、リリース時のテストケースのまま放置しているチームでは、半年後の品質の安定度がはっきり分かれます。本番でしか分からないケースこそ、評価ハーネスにとって最も価値あるデータだからです。AI エージェントを継続運用する体制をどう作ったかは AI エージェントによる運用自動化の実証事例 でも触れています。
コストと精度のトレードオフをダッシュボードで可視化
評価ハーネスを運用に乗せると、品質と並んで無視できなくなるのがコストです。全件を高性能なモデルで評価し、judge にも同じモデルを使えば、評価のたびに相応の費用と時間がかかります。逆にコストを削りすぎて評価頻度を落とすと、回帰の検出が遅れます。ここはトレードオフであり、勘で運用するのではなく数字で見える状態にするのが LLMOps の要点です。
可視化したい指標は、品質側とコスト側に分けて捉えると整理しやすくなります。品質側は、ゴールデンセット全体の合格率、カテゴリ別の合格率、judge と人間の一致率、そして本番の低評価率です。コスト側は、1 回の評価あたりのトークン消費と費用、評価の実行時間、そして本番 1 リクエストあたりのコストです。これらを 1 つのダッシュボードに並べ、プロンプトやモデルを変更した時点をマーカーで重ねると、「精度を上げたらコストがどれだけ増えたか」「コストを削ったら品質がどこまで落ちたか」という関係が一目で分かります。
| 指標 | 種別 | 見るポイント |
|---|---|---|
| ゴールデンセット合格率 | 品質 | 変更の前後で下がっていないか |
| カテゴリ別合格率 | 品質 | どの業務領域が手薄か |
| judge と人間の一致率 | 品質 | judge 自体が信頼できるか |
| 評価あたりコスト・時間 | コスト | CI に組み込める範囲に収まっているか |
| 本番 1 リクエストのコスト | コスト | 精度向上が単価をどれだけ押し上げたか |
このダッシュボードがあると、意思決定が変わります。たとえば「judge を安価なモデルに変えても人間との一致率が落ちないなら、評価コストを下げてよい」「あるカテゴリの合格率が低いので、次のスプリントはそこにケースと改善を集中する」といった判断が、感覚ではなくデータで下せるようになります。重要なのは単一の数字ではなく、品質とコストの関係を継続的に見ることです。本番側の費用を品質を保ったまま下げる具体策は LLM アプリのコスト最適化 にまとめています。
評価ハーネスをセット納品する AI 駆動開発の進め方
ここまで述べてきた評価データセット、自動採点、回帰テストの CI 連携、本番ログの還流、そしてダッシュボードは、一つひとつは難しい技術ではありません。難しいのは、これらを最初から「セット」として組み込み、開発のリズムの中で回し続けることです。後から評価を足そうとすると、すでに動いている本番に手を入れる必要が出て、優先度が下がったまま放置されがちです。
私たちが AI エージェントや LLM アプリを開発するときは、機能を作るのと同じタイミングで評価ハーネスを設計し、納品物にハーネスとゴールデンセット、CI 連携、運用の手順までを含めます。これは発注側にとって、引き継いだ後も品質を自分たちで測り続けられる状態が手に入ることを意味します。プロンプトを改善するたびに何が良くなり何が壊れたかが数字で分かり、モデルのバージョンが上がっても回帰を恐れずに追従できる。属人的な目視確認から、仕組みで品質を回す LLMOps への移行です。
進め方としては、まず中核となる業務シナリオでゴールデンセットを 30〜50 件作り、ルールベースと LLM as a judge の採点を実装します。次にそれを CI に組み込み、プロンプトやモデルの変更で自動的に回帰を検出できる状態にします。並行して本番ログの収集とマスキングの仕組みを用意し、リリース後はログの還流でゴールデンセットを育てていきます。この型を最初に敷いておくと、その後の改善のすべてが安全かつ高速になります。評価ハーネスを最初から仕込む発想は検証フェーズの設計と地続きで、検証段階からの組み込み方は AI エージェントの PoC の進め方 でも整理しています。
評価の仕組みを「あれば良いもの」ではなく開発の前提として組み込むことが、AI 駆動開発で LLM アプリの品質を落とさずに速度を出すための条件です。
AI エージェントや LLM アプリの評価・運用設計でお悩みなら、評価ハーネスのセット納品まで含めてご支援できます。まずは AI エージェント開発サービス から無料相談をご利用ください。現状のプロンプトや運用体制をうかがい、どこから評価ハーネスを組み込めば品質が安定するかを一緒に整理します。

