LLMの評価とは、決めておいた入力と合否の基準を使って、出力の良し悪しを毎回同じ物差しで測ることです。 まず20〜30件のテストケース表と合否基準を作り、プロンプトを変えるたびに同じ表で採点します。

この記事には、そのまま表計算ソフトに貼れるテストケース表(20件の記入例つき)、5段階の採点基準(ルーブリック)、評価役のモデルに渡す採点プロンプトのテンプレートを載せています。完全一致率やキーワード含有率を計算する Python コードは、実行結果つきです。

確認環境:Ubuntu 24.04、Python 3.11.15(標準ライブラリのみ。確認日 2026-10-06)。図は Mermaid(@mermaid-js/mermaid-cli 12.0.0)で描画できることを確かめています。記事中の「回答」と「評価役の点数」は、採点と集計の手順を示すために用意した説明用のデータで、モデルが出力したものではありません。採点の考え方は、Anthropic と OpenAI の公式ドキュメントを今回開いて確認した範囲を出典つきで書いています。

この記事の結論

  • 最初に作るのはテストケース表です。入力・期待する要素・採点方法・合否基準を1行にまとめ、20〜30件から始めます。
  • プロンプトを変えたら、必ず同じ表で採点し直します。 全体の合格率だけでなく、「前は合格だったのに不合格になったケース」を見ます。
  • 採点方法は3種類。コードで判定(最も速く確実)、モデルで採点(柔軟)、人が採点(最も丁寧)。コードで測れる項目から先に埋めます。
  • モデルに採点させるときは、採点基準を具体的に書き、人の採点と照らしてから件数を増やします。
  • 指標はタスクで選びます。 分類は完全一致、案内文は必須要素の有無、要約や文章の質はモデルによる採点が出発点です。

LLMの評価指標 早見表

指標 測るもの 正解データ 自動化 得意 気をつける点
完全一致 出力が正解と同じ文字列か 必要 できる 分類、抽出、選択式 余分な語が1つ付くだけで不一致になる
キーワード含有 必須の要素が入っているか 必要(要素の一覧) できる 案内文、手順の回答 要素は入っているが内容が不適切、を見分けられない
禁止語チェック 入っていてはいけない語が無いか 必要(語の一覧) できる 境界ケース、断るべき質問 言い換えた表現はすり抜ける
形式チェック JSON として読めるか、文字数が範囲内か 不要 できる 構造化出力、要約の長さ 中身の正しさは測れない
BLEU・ROUGE 参照文と語の並びがどれだけ重なるか 必要(参照文) できる 翻訳、要約の機械的な比較 正しい言い換えが低く、数字だけ違う誤りが高く出る
埋め込み類似度 参照文と意味が近いか 必要(参照文) できる(埋め込みモデルが必要) 言い換えを許したい回答、回答の一貫性 「似ている」とみなすしきい値を、自分のデータで決める必要がある
モデルによる採点 採点基準に沿った点数や合否 あると安定する できる 文章の質、口調、要約の過不足 採点基準があいまいだと点がぶれる。人の採点との照合が必要
人手評価 人が読んで判断 不要 できない 基準づくり、最終確認 時間がかかる。採点者ごとの差が出る

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「LLMの評価方法と評価指標一覧|テストケース表のテンプレート付き」

評価の進め方:7つの手順

flowchart LR
    A[1. 合格の基準を決める] --> B[2. テストケース表を作る<br>20〜30件]
    B --> C[3. 採点方法を決める<br>コード・モデル・人]
    C --> D[4. 今のプロンプトで採点<br>基準の点を記録]
    D --> E[5. プロンプトを変える]
    E --> F[6. 同じ表で採点し直す]
    F --> G{悪化した<br>ケースはあるか}
    G -- ある --> E
    G -- ない --> H[7. 採用して表にケースを追加]
    H --> E
手順 やること できあがるもの
1 「何ができれば合格か」を文章で決める 合格の基準(例:合格率90%以上、境界ケースは全件合格)
2 入力と期待する要素を表にする テストケース表
3 行ごとに採点方法を決める 採点方法の列
4 今のプロンプトで全件を採点する 比較の基準になる点
5 プロンプトを1か所だけ変える 変更の記録
6 同じ表で全件を採点し直す 変更前後の比較
7 悪化が無ければ採用し、新しく見つかった入力を表に足す 育っていく表

Anthropic の公式ドキュメントは、評価の設計について「タスクに合わせる(端のケースも入れる)」「できるだけ自動で採点できる形にする」「少数の丁寧な手採点より、やや粗くても件数の多い自動採点を優先する」という3つの原則を挙げています。OpenAI の公式ガイドも、目的を決める → データを集める → 指標を決める → 実行して比べる → 続ける、という流れを示しています。どちらも、先に基準とデータを用意してから、変更のたびに同じもので測るという点で共通しています。

テストケース表のテンプレートと記入例(20件)

題材は、架空の「社内の備品予約についての問い合わせに答えるアシスタント」です。回答の根拠になるルールは次のとおりとします。

予約は社内ポータルの備品ページから行う。利用日の前日17時までに申請し、担当者の承認で確定する。
1人2台まで。返却は利用した当日中に総務の棚へ。付属のケーブルもそろえて返す。
延長は、次の予約が入っていなければポータルからできる。会議室や食堂など備品以外は対象外。

列の定義

列 書くこと 例
ID 変わらない番号。結果の比較に使う T09
分類 ケースの種類(通常・境界・対象外など) 案内
入力 モデルに渡す質問や文章 予約はいつまでに申請すればよいですか
採点方法 完全一致/要素をすべて含む/モデルが採点/人が採点 要素をすべて含む
期待する要素 正解のラベル、または回答に必ず入っているべき語 前日、17時
含めない語 入っていたら不合格にする語 可能です
結果 プロンプトの版ごとの合否や点数 合格
メモ 不合格の理由、気づいたこと 締め切りに触れていない

記入例(結果は後述のコードで採点したもの)

ID 分類 入力 採点方法 期待する要素 含めない語 結果A 結果B
T01 分類 プロジェクターを来週火曜に借りたい 完全一致 予約 ― 合格 合格
T02 分類 借りていたケーブルを戻したい 完全一致 返却 ― 合格 合格
T03 分類 明日まで使い続けたい 完全一致 延長 ― 不合格 合格
T04 分類 食堂は何時まで開いていますか 完全一致 対象外 ― 合格 合格
T05 分類 予約を取り消したい 完全一致 取消 ― 不合格 合格
T06 分類 ホワイトボードの空きを知りたい 完全一致 予約 ― 合格 合格
T07 分類 返す場所がわからない 完全一致 返却 ― 合格 合格
T08 分類 パスワードを忘れた 完全一致 対象外 ― 不合格 合格
T09 案内 予約はいつまでに申請すればよいですか 要素をすべて含む 前日、17時 ― 合格 合格
T10 案内 予約はどこから行いますか 要素をすべて含む ポータル、備品ページ ― 合格 合格
T11 案内 1人で何台まで借りられますか 要素をすべて含む 2台 ― 合格 合格
T12 案内 返却はどこにすればよいですか 要素をすべて含む 総務、棚 ― 不合格 合格
T13 案内 返却の期限はいつですか 要素をすべて含む 当日 ― 合格 合格
T14 案内 延長はできますか 要素をすべて含む 次の予約、ポータル ― 合格 不合格
T15 案内 申請したらすぐ使えますか 要素をすべて含む 承認 ― 合格 合格
T16 案内 ケーブルは別に返してもよいですか 要素をすべて含む そろえて ― 合格 合格
T17 境界 3台借りたいのですが 要素をすべて含む 2台 可能です、できます 合格 合格
T18 境界 当日の朝に予約したい 要素をすべて含む 前日、17時 可能です 合格 不合格
T19 対象外 会議室の予約方法を教えて 要素をすべて含む 対象外、総務 ポータルの備品ページ 合格 合格
T20 対象外 おすすめのランチは 要素をすべて含む 対象外 ― 不合格 合格

表計算ソフトに貼れるタブ区切り

下のブロックをコピーして表計算ソフトに貼ると、列に分かれます。このまま testcases.tsv という名前で保存すれば、次の章のコードで採点できます。expected と forbidden は、複数あるときに | で区切ります。answer_a と answer_b は、プロンプトAとBで得た回答を貼る列です(ここでは説明用の回答を入れてあります)。

id  category    input   method  expected    forbidden   answer_a    answer_b
T01 分類  プロジェクターを来週火曜に借りたい   exact   予約      予約  予約
T02 分類  借りていたケーブルを戻したい  exact   返却      返却  返却
T03 分類  明日まで使い続けたい  exact   延長      予約  延長
T04 分類  食堂は何時まで開いていますか  exact   対象外     対象外 対象外
T05 分類  予約を取り消したい   exact   取消      取消です。   取消
T06 分類  ホワイトボードの空きを知りたい exact   予約      予約  予約
T07 分類  返す場所がわからない  exact   返却      返却  返却
T08 分類  パスワードを忘れた   exact   対象外     その他 対象外
T09 案内  予約はいつまでに申請すればよいですか  keywords    前日|17時      利用日の前日17時までに申請してください。   前日の17時までにポータルから申請してください。
T10 案内  予約はどこから行いますか    keywords    ポータル|備品ページ      社内ポータルの備品ページから予約できます。   社内ポータルの備品ページから予約できます。
T11 案内  1人で何台まで借りられますか  keywords    2台      1人2台までです。   1人につき2台まで借りられます。
T12 案内  返却はどこにすればよいですか  keywords    総務|棚        総務に返してください。 総務の棚へ戻してください。
T13 案内  返却の期限はいつですか keywords    当日      利用した当日中に返却してください。   利用した当日中に返却してください。
T14 案内  延長はできますか    keywords    次の予約|ポータル       次の予約が入っていなければ、ポータルから延長できます。 ポータルから延長できます。
T15 案内  申請したらすぐ使えますか    keywords    承認      申請後、担当者の承認で確定します。   担当者の承認後に確定します。
T16 案内  ケーブルは別に返してもよいですか    keywords    そろえて        付属のケーブルもそろえて返してください。    ケーブルも本体とそろえて返してください。
T17 境界  3台借りたいのですが  keywords    2台  可能です|できます   1人2台までのため、3台は予約できません。   1人2台までです。3台目は別の方の名義で申請してください。
T18 境界  当日の朝に予約したい  keywords    前日|17時  可能です    予約は前日17時までです。当日の予約は受け付けていません。   当日の予約も可能です。
T19 対象外 会議室の予約方法を教えて    keywords    対象外|総務  ポータルの備品ページ  備品予約の対象外です。総務へお問い合わせください。   備品予約の対象外のため、総務へお問い合わせください。
T20 対象外 おすすめのランチは   keywords    対象外     備品予約についての質問にお答えしています。   この質問は対象外です。

ケースの選び方

分類 入れるもの 件数の目安(20件の場合の例)
通常 よく来る質問、代表的な入力 12〜14件
境界 上限を超える依頼、締め切り後の依頼など、断る・条件を付ける必要があるもの 3〜4件
対象外 答えてはいけない、または別の窓口へ案内する質問 2〜3件
過去につまずいた入力 利用中に見つかった、うまく答えられなかった入力 見つかるたびに追加

件数が少ないうちは、1件の重みが大きくなります。20件なら1件で5ポイント、30件なら約3.3ポイント、100件なら1ポイント動きます。20件で「90%から95%に上がった」は、1件の差です。件数が少ない段階では、合格率の数字よりもどのケースが変わったかを見ます。

完全一致率とキーワード含有率を計算するコード

上の testcases.tsv を読み、プロンプトAとBの回答を同じ基準で採点します。標準ライブラリだけで動きます。

# evaluate.py  テストケース表(TSV)を読み、プロンプトAとBの回答を同じ基準で採点する
import csv
import sys
import unicodedata


def normalize(text: str) -> str:
    """全角・半角をそろえ、前後の空白と末尾の句点を取る"""
    return unicodedata.normalize("NFKC", text).strip().rstrip("。.")


def exact_match(answer: str, expected: str) -> bool:
    return normalize(answer) == normalize(expected)


def keyword_coverage(answer: str, expected: str) -> float:
    """期待する要素(| 区切り)のうち、回答に含まれている割合"""
    keywords = [k for k in expected.split("|") if k]
    hit = sum(1 for k in keywords if normalize(k) in normalize(answer))
    return hit / len(keywords)


def has_forbidden(answer: str, forbidden: str) -> bool:
    return any(w and normalize(w) in normalize(answer) for w in forbidden.split("|"))


def judge(case: dict, answer: str) -> tuple[bool, str]:
    """1件を採点して (合否, 理由) を返す"""
    if has_forbidden(answer, case["forbidden"]):
        return False, "禁止語を含む"
    if case["method"] == "exact":
        ok = exact_match(answer, case["expected"])
        return ok, "" if ok else f"期待={case['expected']} 回答={answer}"
    coverage = keyword_coverage(answer, case["expected"])
    return coverage == 1.0, "" if coverage == 1.0 else f"要素の含有率 {coverage:.0%}"


def main(path: str) -> None:
    with open(path, encoding="utf-8", newline="") as f:
        cases = list(csv.DictReader(f, delimiter="\t"))

    results = {}
    for name in ("a", "b"):
        results[name] = [judge(c, c[f"answer_{name}"]) for c in cases]

    print(f"テストケース {len(cases)}件")
    for name in ("a", "b"):
        passed = sum(ok for ok, _ in results[name])
        print(f"プロンプト{name.upper()}: 合格 {passed}/{len(cases)} = {passed / len(cases):.0%}")

    print("\n採点方法ごとの内訳")
    for method, label in (("exact", "完全一致率"), ("keywords", "キーワード含有(全要素あり)率")):
        idx = [i for i, c in enumerate(cases) if c["method"] == method]
        for name in ("a", "b"):
            passed = sum(results[name][i][0] for i in idx)
            print(f"  {label} {name.upper()}: {passed}/{len(idx)} = {passed / len(idx):.0%}")

    print("\nAで合格、Bで不合格になったケース(悪化)")
    for c, (ok_a, _), (ok_b, why) in zip(cases, results["a"], results["b"]):
        if ok_a and not ok_b:
            print(f"  {c['id']} [{c['category']}] {why}")

    print("\nAで不合格、Bで合格になったケース(改善)")
    for c, (ok_a, why), (ok_b, _) in zip(cases, results["a"], results["b"]):
        if not ok_a and ok_b:
            print(f"  {c['id']} [{c['category']}] Aの理由: {why}")


if __name__ == "__main__":
    main(sys.argv[1] if len(sys.argv) > 1 else "testcases.tsv")

実行結果(python evaluate.py):

テストケース 20件
プロンプトA: 合格 15/20 = 75%
プロンプトB: 合格 18/20 = 90%

採点方法ごとの内訳
  完全一致率 A: 5/8 = 62%
  完全一致率 B: 8/8 = 100%
  キーワード含有(全要素あり)率 A: 10/12 = 83%
  キーワード含有(全要素あり)率 B: 10/12 = 83%

Aで合格、Bで不合格になったケース(悪化)
  T14 [案内] 要素の含有率 50%
  T18 [境界] 禁止語を含む

Aで不合格、Bで合格になったケース(改善)
  T03 [分類] Aの理由: 期待=延長 回答=予約
  T05 [分類] Aの理由: 期待=取消 回答=取消です。
  T08 [分類] Aの理由: 期待=対象外 回答=その他
  T12 [案内] Aの理由: 要素の含有率 50%
  T20 [対象外] Aの理由: 要素の含有率 0%

結果の読み方

  • 全体では A 75% → B 90% に上がりましたが、悪化が2件あります。 T14 は延長の条件(次の予約が入っていなければ)が抜け、T18 は当日の予約を「可能です」と案内しています。合格率だけ見て B を採用すると、この2件を見落とします。
  • T05 は「取消です。」と答えたため、完全一致では不合格でした。末尾の句点は正規化で取り除いていますが、「です」は残ります。分類のように完全一致で測るタスクは、プロンプトで「ラベルだけを出力する」と指定するか、出力を構造化します。
  • T17 の B は「1人2台までです。3台目は別の方の名義で申請してください。」で合格になりました。必須の「2台」を含み、禁止語も無いためです。しかし、ルールに無い抜け道を案内しており、内容としては適切ではありません。キーワードの有無では測れない部分で、次の章の「モデルによる採点」か人の確認が必要になります。

5段階の採点基準(ルーブリック)

文章の質のように、コードで判定しにくい項目は、採点基準を言葉で決めます。各点数に「どういう回答か」を書くのが要点です。

点 基準 例(T12「返却はどこにすればよいですか」)
5 必要な要素をすべて含み、誤りが無く、そのまま利用者に見せられる 総務の棚へ、当日中に戻すよう案内している
4 必要な要素をすべて含み、誤りが無い。言い回しや長さに小さな改善点がある 要素は正しいが、同じ内容を2回述べている
3 誤りは無いが、必要な要素が1つ欠けている 「総務に返してください」(棚に触れていない)
2 必要な要素が複数欠けている、またはルールに無い内容が混ざっている 「受付に預けてもかまいません」と書いている
1 ルールと矛盾している、または質問に答えていない 「返却は不要です」

合否で使う場合は「4点以上を合格」のように線を決めておきます。採点者が複数いるときは、各点数の例を2〜3件ずつ用意して、全員が同じ例を見てから採点を始めると、点のばらつきが小さくなります。OpenAI の公式ガイドも、人が採点するときは点数ごとの例を示すことを勧めています。

表計算ソフトに貼れるタブ区切り

点   基準
5   必要な要素をすべて含み、誤りが無く、そのまま利用者に見せられる
4   必要な要素をすべて含み、誤りが無い。言い回しや長さに小さな改善点がある
3   誤りは無いが、必要な要素が1つ欠けている
2   必要な要素が複数欠けている、またはルールに無い内容が混ざっている
1   ルールと矛盾している、または質問に答えていない

評価役のモデルに渡す採点プロンプトのテンプレート

モデルに採点させる方法は、LLM-as-a-judge とも呼ばれます。Anthropic の公式ドキュメントは、モデルによる採点のこつとして「詳しく明確な採点基準を持つ」「合否や1〜5の点数など、決まった形で出力させる」「点数を出す前に理由を考えさせる」の3点を挙げています。次のテンプレートは、この3点を満たす形にしてあります。

あなたは回答の採点者です。下の「ルール」「質問」「期待する要素」「回答」を読み、
「採点基準」に従って回答を1〜5点で採点してください。

<ルール>
{回答の根拠になる資料やルールの全文}
</ルール>

<質問>
{テストケースの入力}
</質問>

<期待する要素>
{回答に入っているべき要素。無ければ「指定なし」}
</期待する要素>

<回答>
{採点する回答}
</回答>

<採点基準>
5: 必要な要素をすべて含み、誤りが無く、そのまま利用者に見せられる
4: 必要な要素をすべて含み、誤りが無い。言い回しや長さに小さな改善点がある
3: 誤りは無いが、必要な要素が1つ欠けている
2: 必要な要素が複数欠けている、またはルールに無い内容が混ざっている
1: ルールと矛盾している、または質問に答えていない
</採点基準>

採点の手順:
1. 回答に含まれる主張を1つずつ取り出し、ルールに書かれているかを確かめる。
2. 期待する要素が回答に入っているかを確かめる。
3. 採点基準のどの点に当てはまるかを決める。
回答の長さや丁寧さだけで点を上げない。ルールに無い内容は、もっともらしくても減点する。

出力は次のJSONだけにしてください。reason には点数の理由を1文で書きます。
{"id": "{テストケースのID}", "score": 1〜5の整数, "reason": "理由"}

{ } の部分を、テストケースごとに差し替えて使います。

出力形式の指定例

評価役には、次の形の1行を返すよう指定します(モデルの出力ではなく、指定する形の例です)。

{"id": "T12", "score": 3, "reason": "返却先の棚に触れていない"}

テンプレートの調整表

やりたいこと 変える場所
合否だけ知りたい 採点基準を「合格/不合格」の2段階にし、score を "pass" か "fail" にする
2つのプロンプトのどちらが良いか知りたい 回答Aと回答Bを並べて渡し、「どちらが採点基準をより満たすか」を選ばせる
観点ごとに見たい 正確さ・過不足・口調など、観点ごとに採点基準を分けて別々に採点する
点がぶれる 各点数の回答例を採点基準に足す。同じ回答を複数回採点して、ぶれの大きさを確かめる

OpenAI の公式ガイドは、評価役のモデルを使うときは、自由な点数付けよりも2つを比べる方法や合否の判定のほうが安定しやすいとしています。5段階でぶれが大きいときは、まず合否の2段階に絞るのが確実です。

評価役の点数を人の採点と照らすコード

評価役のモデルに任せる前に、同じ回答を人も採点して、どれだけ合うかを確かめます。次のコードは、評価役が返した JSON の行と、人が付けた点を照らし合わせます。

# judge_check.py  評価役のモデルの点数(1〜5)を、人の採点と照らし合わせる
import json
import statistics

# 評価役に指定した出力形式(JSON)を、1件1行で保存したものと想定した説明用のデータ
judge_lines = """
{"id": "T09", "score": 5, "reason": "必要な要素をすべて含む"}
{"id": "T12", "score": 3, "reason": "返却先の棚に触れていない"}
{"id": "T14", "score": 4, "reason": "条件を含むが敬語が不統一"}
{"id": "T15", "score": 5, "reason": "承認が必要なことを明記"}
{"id": "T16", "score": 4, "reason": "要素は含むがやや冗長"}
{"id": "T17", "score": 5, "reason": "上限を示して断っている"}
{"id": "T18", "score": 2, "reason": "締め切りと矛盾する案内"}
{"id": "T19", "score": 4, "reason": "対象外と案内先を示す"}
{"id": "T20", "score": 4, "reason": "対象外であることは伝わる"}
{"id": "T11", "score": 5, "reason": "上限を正しく回答"}
""".strip().splitlines()
human = {"T09": 5, "T12": 3, "T14": 3, "T15": 5, "T16": 4, "T17": 5, "T18": 1, "T19": 4, "T20": 2, "T11": 5}

PASS_LINE = 4   # 4点以上を合格とする
judge = {}
for line in judge_lines:
    row = json.loads(line)
    if not (isinstance(row["score"], int) and 1 <= row["score"] <= 5):
        raise ValueError(f"score が 1〜5 の整数ではありません: {row}")
    judge[row["id"]] = row["score"]

ids = sorted(human)
same = sum(judge[i] == human[i] for i in ids)
within1 = sum(abs(judge[i] - human[i]) <= 1 for i in ids)
same_pass = sum((judge[i] >= PASS_LINE) == (human[i] >= PASS_LINE) for i in ids)
print(f"件数: {len(ids)}")
print(f"点数が完全に一致: {same}/{len(ids)} = {same / len(ids):.0%}")
print(f"差が1点以内:     {within1}/{len(ids)} = {within1 / len(ids):.0%}")
print(f"合否({PASS_LINE}点以上)が一致: {same_pass}/{len(ids)} = {same_pass / len(ids):.0%}")
print(f"平均点  評価役: {statistics.mean(judge[i] for i in ids):.1f}  人: {statistics.mean(human[i] for i in ids):.1f}")
print("合否が食い違ったケース:", [i for i in ids if (judge[i] >= PASS_LINE) != (human[i] >= PASS_LINE)])

実行結果(python judge_check.py):

件数: 10
点数が完全に一致: 7/10 = 70%
差が1点以内:     9/10 = 90%
合否(4点以上)が一致: 8/10 = 80%
平均点  評価役: 4.1  人: 3.7
合否が食い違ったケース: ['T14', 'T20']

この説明用のデータでは、点数の完全な一致は70%ですが、合否の一致は80%で、食い違いは T14 と T20 の2件でした。評価役の平均点(4.1)が人(3.7)より高いので、評価役が甘めに付けていることも分かります。こういうときは、食い違ったケースの回答を採点基準の例に足して、もう一度照らします。OpenAI の公式ガイドは、評価役が人の採点と合うようになってから件数を増やすことを勧めています。

5つの評価方法の違いと選び方

方法 仕組み 速さ 柔軟さ 向いている出力
文字列一致(完全一致・含有・正規表現) コードで文字列を比べる 最も速い 低い ラベル、数値、決まった語を含む案内
BLEU・ROUGE 参照文との語の並びの重なりを数える 速い 低い 参照文に近い表現が期待される翻訳・要約
埋め込み類似度 回答と参照文をベクトルにして近さを測る 速い 中 言い換えを許したい短い回答
モデルによる採点 採点基準を渡して、別のモデルに採点させる 中 高い 文章の質、要約の過不足、口調
人手評価 人が読んで採点する 遅い 最も高い 基準づくり、評価役の確認、最終判断

BLEU は機械翻訳の自動評価の方法として2002年に、ROUGE は要約の自動評価のためのパッケージとして2004年に発表されました(出典は記事末尾)。どちらも参照文との重なりを数える考え方です。

重なりを数える指標の性質をコードで確かめる

BLEU や ROUGE そのものではありませんが、同じ「重なりを数える」考え方を、文字2つ組(2-gram)で簡略化して試します。

# overlap.py  文字2-gramの重なり(BLEUやROUGEの考え方を簡略化したもの)で、言い換えと誤答を比べる
from collections import Counter


def bigrams(text: str) -> Counter:
    return Counter(text[i:i + 2] for i in range(len(text) - 1))


def overlap_f1(reference: str, answer: str) -> float:
    ref, ans = bigrams(reference), bigrams(answer)
    common = sum((ref & ans).values())          # 両方に出てくる2-gramの数
    if common == 0:
        return 0.0
    precision = common / sum(ans.values())      # 回答のうち、参照にもある割合
    recall = common / sum(ref.values())         # 参照のうち、回答にもある割合
    return 2 * precision * recall / (precision + recall)


reference = "予約は利用日の前日17時までに申請してください。"
answers = {
    "同じ文": "予約は利用日の前日17時までに申請してください。",
    "言い換え(内容は正しい)": "使う日の前の日、17時が締め切りです。",
    "数字だけ違う(内容は誤り)": "予約は利用日の前日19時までに申請してください。",
}
for label, answer in answers.items():
    print(f"{overlap_f1(reference, answer):.2f}  {label}")

実行結果:

1.00  同じ文
0.20  言い換え(内容は正しい)
0.91  数字だけ違う(内容は誤り)

内容が正しい言い換えは 0.20、締め切りの時刻が違う誤答は 0.91 になりました。重なりを数える指標は、表現が参照文に近いかどうかを測るもので、内容の正しさを直接測るものではありません。数字や固有名詞が大事な回答では、キーワード含有や完全一致と組み合わせます。

タスク別の判断基準表

タスク まず使う方法 足す方法
分類・ラベル付け 完全一致 取り違えたラベルの組み合わせを数える
項目の抽出(日付、数量、名前) 完全一致(項目ごと) 形式チェック
JSON などの構造化出力 形式チェック(読み込めるか、必須の項目があるか) 項目ごとの完全一致
ルールにもとづく案内・問い合わせ回答 キーワード含有+禁止語チェック モデルによる採点(ルールに無い内容の検出)
要約 モデルによる採点(過不足・誤り) 文字数の形式チェック、必須要素の含有
翻訳 モデルによる採点、または参照訳との重なり 用語集どおりかのキーワード含有
文章の作成(メール、報告文) モデルによる採点(観点別) 人手評価(抜き取り)
断るべき依頼への対応 禁止語チェック モデルによる合否判定

条件から選ぶ判断表

条件 選ぶ方法
正解が1つの文字列に決まる 完全一致
正解の言い方は複数あるが、入るべき要素は決まっている キーワード含有
言い方も要素も決めにくいが、良し悪しは説明できる モデルによる採点
良し悪しの基準そのものがまだ言葉になっていない 人手評価から始めて、基準を言葉にする
毎回の変更で全件を回したい コードで判定できる方法を主にする

確かめて分かったこと

  • 合格率が上がっても、悪化したケースが含まれていました。 説明用のデータで A 75% → B 90% のとき、T14 と T18 は合格から不合格に変わっています。変更前後の比較は、ケース単位で出力する形にしておくと見落としません。
  • 完全一致は「取消です。」を不一致にしました。 句点を取り除く正規化だけでは「です」が残ります。正規化を増やすより、出力をラベルだけに絞る指定のほうが確実です。
  • キーワード含有は、ルールに無い抜け道の案内(T17 の B)を合格にしました。 必須の語が入っていて禁止語が無ければ通るためです。境界ケースには、モデルによる採点か人の確認を足します。
  • 重なりを数える指標は、正しい言い換えを 0.20、時刻だけ違う誤答を 0.91 と採点しました。 数字が1文字違うだけの誤りは、高い値のまま通ります。
  • 20件の表では、1件の合否で合格率が5ポイント動きます。 小さな差は、件数を増やしてから判断します。
  • Python の :.0% は、5/8(62.5%)を 62% と表示しました。 ちょうど半分の端数は偶数側に丸められます。報告に載せる数値は「5/8」のように件数も併記すると、取り違えがありません。

よくある質問

Q. LLMの評価とは何をすることですか? A. 決めておいた入力に対する出力を、決めておいた基準で採点することです。入力と基準を固定しておくことで、プロンプトやモデルを変えたときに良くなったか悪くなったかを比べられます。

Q. テストケースは何件から始めればよいですか? A. 20〜30件から始められます。通常の入力に加えて、境界ケースと対象外の質問を必ず数件ずつ入れ、利用中に見つかったうまく答えられない入力をその都度足していきます。

Q. BLEUやROUGEとモデルによる採点の違いは? A. BLEU や ROUGE は参照文との語の重なりを数え、モデルによる採点は採点基準に沿って内容を判断します。参照文に近い表現が求められる場面では前者、言い方が自由で内容の過不足を見たい場面では後者が向いています。

Q. モデルによる採点は信頼できますか? A. 採点基準を具体的に書き、人の採点と照らして合うことを確かめた範囲で使えます。まず10〜20件を人と評価役の両方で採点し、合否の一致を見てから件数を増やします。

Q. プロンプトを直したのに評価が下がったときは? A. どのケースが合格から不合格に変わったかを先に見ます。変更を1か所ずつに分けて採点し直すと、どの変更が影響したかを切り分けられます。

まとめ

  • まずテストケース表と合否基準を作る。 20〜30件、通常・境界・対象外を混ぜます。
  • プロンプトを変えるたびに、同じ表で採点し直す。 合格率と一緒に、悪化したケースの一覧を見ます。
  • コードで測れるものはコードで。 完全一致、キーワード含有、禁止語、形式チェックは標準ライブラリだけで書けます。
  • 文章の質は採点基準を言葉にして、モデルに採点させる。 点数ごとの基準と、決まった出力形式、理由を先に考えさせる指示を入れます。
  • 評価役のモデルは、人の採点と照らしてから任せる。
  • 重なりを数える指標は、内容の正しさを直接は測らない。 数字や固有名詞は別の方法で確かめます。
  • 指標はタスクで選ぶ。 迷ったら判断基準表の「まず使う方法」から始めます。

参考・出典

すべて 2026-10-06 に閲覧しました。

  • Anthropic「Define success criteria and build evaluations」 https://platform.claude.com/docs/en/test-and-evaluate/develop-tests (評価の設計の原則、3つの採点方法、モデルによる採点のこつ)
  • OpenAI「Evaluation best practices」 https://developers.openai.com/api/docs/guides/evaluation-best-practices (評価の進め方、評価役のモデルの使い方、人の採点との照合)
  • Papineni ほか「Bleu: a Method for Automatic Evaluation of Machine Translation」ACL 2002 https://aclanthology.org/P02-1040/
  • Lin「ROUGE: A Package for Automatic Evaluation of Summaries」Text Summarization Branches Out, 2004 https://aclanthology.org/W04-1013/

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「LLMの評価方法と評価指標一覧|テストケース表のテンプレート付き」

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?