AI エージェントの evaluation (evals) 入門 — 「それっぽく動く」を数値で担保する方法

AI エージェントの evaluation (evals) 入門 — 「それっぽく動く」を数値で担保する方法

AI エージェントを本番運用し始めた瞬間、必ず直面する問題があります:

「このエージェント、本当に大丈夫?」

人間がちょっと触って「それっぽく動く」ことは分かる。でも、1 万回使われたら何パーセント失敗するのか、どういう入力で破綻するのかは、見えない。

伝統的なソフトウェアテスト(unit test / integration test)では、AI エージェントの品質を担保できません。AI エージェント用の評価(evaluation、略して evals) という別の仕組みが必要です。

本稿では、

  1. なぜ unit test では足りないのか
  2. Evals の 3 層構造(Unit evals / Integration evals / End-to-End evals)
  3. LLM-as-a-judge の使い方と落とし穴
  4. 統計的に意味のある評価サンプル数
  5. 本番モニタリングと evals の関係
  6. 実装パターン(最小構成)

を整理します。

なぜ unit test では足りないのか

伝統的なソフトウェアテスト

test("add function", () => {
  expect(add(1, 2)).toBe(3);
  expect(add(0, 0)).toBe(0);
});

これは、入力と期待出力が確定 しているので動きます。add(1, 2) の答えは 3 で、永遠にそう。

AI エージェントの出力は非決定

test("agent writes a Python function", () => {
  const result = await agent.run("Write a Python function that reverses a string");
  // ... 何をチェックすればいい?
});

LLM の出力は、

単純な expect(x).toBe(y) では捉えられません。

評価の軸が複数ある

AI エージェントの「良さ」は 1 次元ではありません:

全部を 1 つの数値にまとめると、どこが悪いか分からなくなります。

Evals の 3 層構造

ソフトウェアテストに unit / integration / E2E があるように、evals にも層があります。

Layer 1: Unit evals — プロンプト単位の評価

対象: 1 つのプロンプトに対する 1 ターンの応答。

例:

評価方法:

サンプル数: 50-200 件、カテゴリごとに最低 20 件。

実装: GitHub の braintrust, promptfoo, openai evals 等。

Layer 2: Integration evals — ツール連鎖の評価

対象: エージェントがツールを使ってタスクを完遂できるか。1 ターンではなく、複数ターンのツール呼び出しを含む。

例:

評価方法:

サンプル数: 20-100 件。1 件あたりが重いので、unit evals より少なめ。

実装: カスタム harness が必要なことが多い。

Layer 3: End-to-End evals — ユースケース単位

対象: 実ユーザーのシナリオをエージェントが完結できるか。

例:

評価方法:

サンプル数: 10-50 件。シナリオを変えて多様性を確保。

実装: 本番環境に近い整備が必要。モックしたバックエンド、合成ユーザーなど。

LLM-as-a-judge — 使い方と落とし穴

評価の自動化で避けて通れないのが LLM-as-a-judge:別の LLM にスコア付けさせる手法。

基本パターン

async function judge(input: string, output: string, criteria: string): Promise<number> {
  const prompt = `
以下の出力を、次の基準で 1-5 のスコアで評価してください。

## 入力
${input}

## 出力
${output}

## 基準
${criteria}

スコアと理由を JSON で返してください。
`;
  const result = await claude.messages.create({ ... });
  return parseInt(result.text);
}

使いどころ

落とし穴 1: Judge のバイアス

LLM-as-a-judge には、よく知られたバイアスがあります:

対策

落とし穴 2: Reward hacking

Judge が特定のパターンを好むと、エージェントがそれを exploit するようになります。

例: 「ステップバイステップで」と書くと judge のスコアが上がると分かったら、エージェントは意味なく「ステップバイステップで」を含めるようになる。

対策

統計的妥当性のためのサンプル数

典型的な数

Rule of thumb

95% 信頼区間で ±5% の精度が欲しいなら、約 400 件が目安。

サンプル数 ≈ (1.96 / 0.05)² × 0.25 ≈ 400

ただし、評価対象が偏りがあるなら(例: タスクの難易度が様々)、カテゴリごとに 20-50 件は必要。

継続的評価

リリースごとに全 1000 件を回すとコストがかかりすぎるので、

という階層にします。

本番モニタリングと evals の関係

Evals はリリース前

本番に出す前に「品質が許容範囲か」を数値で担保する。

本番モニタリングはリリース後

実トラフィックで、

両方のループ

Evals (リリース前) → 本番 → モニタリング → 失敗事例を収集
    ↑                                             ↓
    ← Evals データセットに追加(失敗事例を eval 化)

本番で失敗したケースを、次のリリースの eval に組み込む のが、品質を上げ続けるループ。

最小実装 — 50 行で始める

Promptfoo で始める

promptfoo は、OSS の prompt eval ツール。設定は YAML 1 枚:

# promptfooconfig.yaml
prompts:
  - "Translate to Japanese: {{text}}"
providers:
  - anthropic:messages:claude-sonnet-5
  - openai:chat:gpt-5
tests:
  - vars:
      text: "Hello, world"
    assert:
      - type: contains
        value: "こんにちは"
  - vars:
      text: "I love you"
    assert:
      - type: llm-rubric
        value: "日本語として自然な愛情表現を含むこと"

npx promptfoo eval で全プロバイダ × 全テストを一括実行。結果が web UI で見られる。

自作する場合

// evals.ts
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();

type EvalCase = {
  input: string;
  judge: (output: string) => Promise<boolean>;
};

async function runEvals(cases: EvalCase[]) {
  const results = await Promise.all(cases.map(async (c) => {
    const output = await agent.run(c.input);
    const passed = await c.judge(output);
    return { input: c.input, output, passed };
  }));
  const passRate = results.filter(r => r.passed).length / results.length;
  console.log(`Pass rate: ${(passRate * 100).toFixed(1)}%`);
  return results;
}

const cases: EvalCase[] = [
  {
    input: "What is 2+2?",
    judge: (out) => Promise.resolve(out.includes("4")),
  },
  {
    input: "Translate 'cat' to Japanese",
    judge: async (out) => {
      const result = await client.messages.create({
        model: "claude-haiku-4-5-20251001",
        max_tokens: 100,
        messages: [{
          role: "user",
          content: `この翻訳は 'cat' の正しい日本語訳ですか: "${out}". yes か no で答えてください。`,
        }],
      });
      return result.content[0].text.toLowerCase().includes("yes");
    },
  },
];

runEvals(cases);

これだけで、プロンプトごとに pass/fail を自動判定できる最小構成が動きます。

MulmoTerminal / MulmoClaude での実運用

MulmoTerminal や MulmoClaude のような長時間動くエージェントでは、以下の層で evals を回します:

Layer 1: 単発プロンプトの evals

Layer 2: Tool chain の evals

Layer 3: Round table / 複雑シナリオの evals

Singularity Society BootCamp の第 4 期では、参加者も自分のプロダクトごとに eval suite を作る前提で動いています。

まとめ

「それっぽく動く」から「数値で担保」に移行する のが、AI エージェントを本番で使う覚悟です。

関連リンク


Singularity Society はテクノロジー集団として MulmoClaude / MulmoTerminal / MulmoCast を開発しています。エンジニア・起業家向けの実践プログラム BootCamp も運営しています。

この記事をシェア

関連記事

記事一覧に戻る