DeepEvalによる単体テストベースのLLMパフォーマンス評価手法

DeepEvalと生成AIで挑むLLMテスト自動化:泥臭いデータ作成をコード化する実践レシピ

約10分で読めます
文字サイズ:
DeepEvalと生成AIで挑むLLMテスト自動化:泥臭いデータ作成をコード化する実践レシピ
目次

この記事の要点

  • DeepEvalによるLLMの単体テスト自動化を実現
  • プロンプトやモデル変更時のパフォーマンス評価を効率化
  • テストデータ生成と評価基準定義の課題をAIで解決

「回答がなんとなく良くなった気がする」。

実務の現場で対話AIのチューニングを続けていると、この「なんとなく」という言葉の危うさが課題となる瞬間があります。PoC(概念実証)の段階では許容されがちなこの感覚も、いざ本番環境への導入や、継続的な改善フェーズに入った途端、最大のボトルネックへと変わる傾向があります。

「先週のプロンプト修正で、以前正しく答えられていた質問が答えられなくなっていないか?」
「RAGの検索ロジックを変えたら、回答のトーンまで変わってしまった気がする」

これらを毎回手動でチャットして確認するのは、効率的な開発プロセスとは言えません。開発現場に必要なのは、ソフトウェア開発における単体テスト(Unit Test)と同じ確実性と再現性を持った、LLMのための評価基盤です。

この記事では、PythonベースのLLM評価フレームワーク「DeepEval」を活用し、評価プロセスをコード化(Evaluation as Code)する方法を解説します。特に、多くの開発現場で課題となる「評価用データセット(Golden Dataset)の作成」をAIに任せ、自動化するテクニックに焦点を当てます。

実用的なプロンプトと実装コードを紹介します。ぜひ、実際の開発環境での検証に役立ててください。

1. 本記事の活用ガイド:DeepEvalによる「評価のコード化」とは

なぜ今、DeepEvalのようなツールに注目すべきなのでしょうか。それは、LLMチャットボットの品質保証(QA)を、従来のソフトウェアテストの文脈に落とし込めるからです。

なぜ単体テストベースの評価が必要なのか

従来のLLM評価は、スプレッドシートに質問と回答を並べ、目視で判定するスタイルが主流でした。この方法には、以下のような欠点があります。

  • 再現性がない: 評価のタイミングや判定基準のブレが生じやすく、客観的な評価が難しい。
  • スケーラビリティがない: 大量のテストケースを毎回手動で確認するには膨大な時間がかかる。
  • CI/CDに組み込めない: コード変更のたびに自動実行できないため、デグレに気づきにくい。

DeepEvalは、Pythonの標準的なテストライブラリである pytest と統合されており、以下のようなコードでLLMの評価を実行できます。

from deepeval import assert_test
from deepeval.test_case import LLMTestCase
from deepeval.metrics import AnswerRelevancyMetric

def test_answer_relevancy():
    test_case = LLMTestCase(
        input="DeepEvalとは何ですか?",
        actual_output="DeepEvalはLLMの評価フレームワークです。",
        expected_output="DeepEvalはLLMアプリケーションのためのオープンソース評価フレームワークです。"
    )
    metric = AnswerRelevancyMetric(threshold=0.7)
    assert_test(test_case, [metric])

このように、評価ロジックをコードとして管理できるのが強みです。Gitでバージョン管理もでき、チーム内でのレビューも容易になります。

DeepEval導入のボトルネックは「データセット作成」にある

しかし、ここで多くの開発チームが課題に直面します。
LLMTestCase に渡すデータ(質問と正解のペア)を大量に誰が作成するのか」という問題です。

仕様書やドキュメントを読み込み、ユーザーの発話パターンを想定し、理想的な回答を用意する。この「Golden Dataset」の作成には多大な手間がかかります。ここをクリアしない限り、自動テストは実現できません。

本記事のプロンプトテンプレートの使い方

そこで本記事では、この作業をAIに任せるためのプロンプトテンプレートを紹介します。以下のステップで進めます。

  1. データ生成: プロンプトを使って、仕様書からテストケース(JSON)を生成する。
  2. 評価定義: 独自の評価基準(トーンや禁止事項)をG-Eval用のコードに変換する。
  3. 実装: 生成されたデータをDeepEvalに流し込み、テストを実行する。

それでは、まずはAIにどのようなデータを作らせるべきか、その構造から見ていきましょう。

2. プロンプト設計の基本:評価可能なデータ構造を作る

単に「テストデータを作成して」とAIに指示しても、DeepEvalで即座に使える形式にはなりません。ツールが求めるデータ構造を理解し、そこから逆算してプロンプトを設計することが重要です。

DeepEvalが求める入力データ形式(input, actual_output, expected_output)

DeepEvalの LLMTestCase クラスは、主に以下の4つの要素を必要とします。

  • input: ユーザーの入力(質問、プロンプト)。
  • actual_output: 対象のLLMアプリが生成した実際の回答(テスト実行時に取得)。
  • expected_output: 理想的な回答(正解データ)。これとの類似度を測る場合に使用。
  • retrieval_context: RAGの場合、回答生成に使用した検索ドキュメントのチャンク。

AIに生成させるべきは、テスト実行前の静的なデータ、つまり inputexpected_output、そしてRAGのテストであれば retrieval_context(またはその元となるドキュメント箇所)です。

ハルシネーション検知に必要なコンテキスト情報の抽出

特にRAGの評価で重要な指標が「Faithfulness(忠実性)」です。これは「回答が検索したコンテキストに基づいているか(ハルシネーションを起こしていないか)」を測ります。

このテストデータを生成する場合、単に質問と回答を作るだけでなく、「この質問はこのドキュメントのこの部分(コンテキスト)に基づいて回答すべき」という紐付け情報もセットで生成させる必要があります。ここがプロンプト設計のポイントになります。

「曖昧な要件」を「明確なテストケース」に変換するコツ

業務要件として「丁寧に対応すること」と定義されていても、そのままではテストコードに落とし込めません。

  • NG: 「丁寧に対応する」
  • OK: 「ユーザーが不満を示している場合、まずは謝罪の言葉(『申し訳ございません』等)を冒頭に含めること」

このように、プロンプトを通じて「曖昧な仕様」を「検証可能な条件」に変換させる意識を持ちましょう。AIに指示を出す際も、この変換ステップを明示的に含めることで、質の高いテストケースが得られます。

3. テンプレート①:仕様書からテストケースを一括生成する

プロンプト設計の基本:評価可能なデータ構造を作る - Section Image

では、実際のデータ作成プロセスを見ていきましょう。製品マニュアルや要件定義書(テキストデータ)を入力とし、DeepEvalで利用可能なJSON形式のテストケースを出力するプロンプトです。

【用途】ドキュメントベースのGolden Dataset作成

このプロンプトは、RAGシステムの精度検証や、チャットボットの回答品質テストに使用する「正解データセット」を構築するために使います。手作業では膨大な時間がかかる作業を、大幅に効率化することが期待できます。

【プロンプト例】要件定義書から入出力ペアを抽出

以下のプロンプトを、最新のLLMに入力します。[ここにドキュメントのテキストを貼り付け] の部分に対象のテキストを配置します。

なお、開発環境としてVS CodeとGitHub Copilotを利用している場合は、ブラウザにテキストを貼り付ける代わりに、@workspace コマンドを使ってプロジェクト内の仕様書ファイル(例: specs.md)を直接参照させるとよりスムーズです。最新のエージェント機能はコンテキスト理解に優れており、大規模なドキュメントでも高精度な抽出が可能です。

# 指示
あなたはQAエンジニアです。以下の[ドキュメント]に基づき、LLMチャットボットの単体テストに使用するテストケースを作成してください。
出力はDeepEvalで読み込み可能なJSON形式のリストにしてください。

# ドキュメント
"""
[ここにドキュメントのテキストを貼り付け]
"""
(※GitHub Copilotの場合は @workspace /path/to/specs.md を参照するよう指示を変更)

# 要件
1. 以下の3種類の質問タイプをバランスよく含めてください。
   - Fact Retrieval: ドキュメントに明記されている事実を問う単純な質問。
   - Reasoning: ドキュメント内の複数の情報を組み合わせて推論が必要な質問。
   - Negative: ドキュメントには記載されていない情報に関する質問(回答は「情報がありません」となるべきもの)。
2. 各テストケースは以下のフィールドを持つオブジェクトにしてください。
   - input: 想定されるユーザーの質問
   - expected_output: ドキュメントに基づく理想的な回答
   - context: 回答の根拠となるドキュメント内の具体的な文章(抜粋)

# 出力フォーマット(JSON)
[
  {
    "input": "...",
    "expected_output": "...",
    "context": ["..."]
  }
]

【カスタマイズ】エッジケースの含有率調整

実際の対話フローでは、ユーザーの発話パターンは多岐にわたります。「Reasoning」や「Negative」の割合を増やすよう指示を追加することで、より厳格なテストセットを作成できます。たとえば、「複雑な言い回しや、文脈が曖昧な質問を3割含めてください」と指示するのも効果的です。

特に推論能力の高い最新モデルを利用する場合、こうした複雑な指示への追従性が高いため、より質の高いエッジケース生成が期待できます。

【出力例】JSON形式でのテストデータセット

このプロンプトを実行すると、以下のようなJSONが得られます。これを test_data.json として保存します。

[
  {
    "input": "プレミアムプランの月額料金はいくらですか?",
    "expected_output": "プレミアムプランの月額料金は1,980円(税込)です。",
    "context": ["料金プラン: スタンダードは980円、プレミアムは1,980円(税込)です。"]
  },
  {
    "input": "Linux環境でインストールできますか?",
    "expected_output": "申し訳ありませんが、現在のバージョンはWindowsとmacOSのみ対応しており、Linuxには対応していません。",
    "context": ["動作環境: Windows 10以降、macOS 11以降。Linuxは非対応。"]
  }
]

このJSONファイルをPythonコードで読み込めば、TestCase のリストが効率的に完成します。

4. テンプレート②:カスタム評価指標(G-Eval)の定義

DeepEvalには「回答の関連性」や「忠実性」といった標準メトリクスがありますが、実務では「ブランドに沿ったトーンか」「ユーザーに不快感を与えないか」といった独自の業務要件に基づく基準が必要になることがあります。
これを実現するのが G-Eval です。G-Evalは、LLMを使ってLLMを評価(LLM-as-a-Judge)するための仕組みです。

【用途】自社特有の品質基準(トーン、禁止事項)の数値化

抽象的な「ブランドボイス」や「コンプライアンス基準」を、LLMが採点可能なステップに変換します。「自然で親しみやすい」を「スコア4.5」といった数値に変えることが可能です。

【プロンプト例】評価基準(Criteria)の言語化プロンプト

評価指標の作成自体もAIを活用して効率化できます。評価基準の言語化は、対話設計においても難易度の高いタスクだからです。

# 指示
あなたはAI評価の専門家です。以下の[評価観点]に基づき、DeepEvalのG-Evalで使用する「評価基準(Criteria)」と「評価ステップ(Evaluation Steps)」を定義してください。

# 評価観点
「親しみやすさ(Friendliness)」
- ユーザーに寄り添った共感的な表現があるか。
- 専門用語を避け、平易な言葉を使っているか。
- ロボット的ではなく、人間味のある対話ができているか。

# 出力フォーマット
1. Criteria: 評価の定義(1-2文)
2. Evaluation Steps: 1〜5点のスコアを付けるための具体的な確認手順(箇条書き)

【出力例】DeepEvalのG-Eval用パラメータコード

AIが出力した内容を、そのままPythonコードに落とし込みます。

from deepeval.metrics import GEval
from deepeval.test_case import LLMTestCaseParams

friendliness_metric = GEval(
    name="Friendliness",
    criteria="回答がユーザーに共感的で、専門用語を避けた平易な言葉で構成されているか。",
    evaluation_steps=[
        "回答に共感を示す言葉(『大変ですね』『承知いたしました』等)が含まれているか確認する。",
        "専門用語が使われている場合、直後にわかりやすい説明があるか確認する。",
        "文体が硬すぎず、親しみやすいトーンであるか判定する。",
        "上記に基づき、1〜5のスケールでスコアを算出する(5が最も親しみやすい)。"
    ],
    evaluation_params=[LLMTestCaseParams.INPUT, LLMTestCaseParams.ACTUAL_OUTPUT],
)

これで、「親しみやすさ」という曖昧な指標を定量的にテストできるようになります。

5. テンプレート③:RAG精度検証用の「嘘」データ生成

テンプレート②:カスタム評価指標(G-Eval)の定義 - Section Image

システムがハルシネーションを見抜けるか、あるいはシステム自身が誤った情報を出力しないかを確認するには、エッジケースを含んだデータセットが必要です。

【用途】Faithfulness(忠実性)テスト用の対照実験データ

「コンテキストにはAと書いてあるのに、回答でBと言っていないか」を検知できるかテストします。これは、評価指標自体の信頼性を確認するメタ評価にも使えます。

【プロンプト例】コンテキストと矛盾する回答の生成

# 指示
以下の[正しい情報]をもとに、あえて矛盾する「誤った回答(Hallucination)」を作成してください。
これはRAGシステムの検知機能をテストするためのデータです。

# 正しい情報
「当社サービスの営業時間は平日9:00〜18:00です。土日祝日は休業となります。」

# 作成すべきデータ
1. input: 営業時間に関する質問
2. context: 上記の[正しい情報]
3. hallucinated_output: コンテキストと矛盾する誤った回答(例:土日も営業している等)

【出力例】検知すべき失敗パターンのテストケース

{
  "input": "土曜日は営業していますか?",
  "context": ["当社サービスの営業時間は平日9:00〜18:00です。土日祝日は休業となります。"],
  "hallucinated_output": "はい、土曜日も10:00から17:00まで営業しております。"
}

このデータを test_case として作成し、FaithfulnessMetric で評価を実行します。もしこのテストケースが「合格(Pass)」になってしまったら、評価指標の設定やモデル自体に問題があると考えられます。

6. 実践ワークフロー:CI/CDへの組み込みと運用

最後に、これらを組み合わせて自動化パイプラインを構築します。ここまで準備できれば、あとは実装のフェーズです。

生成したデータセットを用いたPytest実行コード例

test_chatbot.py というファイルを作成し、JSONデータを読み込んでテストを実行する構造にします。

import json
import pytest
from deepeval import assert_test
from deepeval.test_case import LLMTestCase
from deepeval.metrics import AnswerRelevancyMetric, FaithfulnessMetric

# 生成したJSONデータを読み込む
with open("test_data.json", "r", encoding="utf-8") as f:
    test_data = json.load(f)

@pytest.mark.parametrize("data", test_data)
def test_chatbot_quality(data):
    # 実際のチャットボットAPIを呼び出して回答を取得する関数(仮)
    # actual_output = my_chatbot_api.generate(data["input"])
    actual_output = "ここに実際のLLMからの回答が入ります" 

    test_case = LLMTestCase(
        input=data["input"],
        actual_output=actual_output,
        expected_output=data["expected_output"],
        retrieval_context=data.get("context", [])
    )

    # 評価指標の設定
    relevancy = AnswerRelevancyMetric(threshold=0.7)
    faithfulness = FaithfulnessMetric(threshold=0.8)

    # テスト実行
    assert_test(test_case, [relevancy, faithfulness])

GitHub Actionsでの自動テスト設定

GitHub Actionsを使えば、プルリクエストが作られるたびにこのテストを走らせることができます。

name: LLM Quality Check
on: [pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Set up Python
        uses: actions/setup-python@v2
        with:
          python-version: '3.9'
      - name: Install dependencies
        run: pip install deepeval pytest
      - name: Run DeepEval tests
        env:
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
        run: pytest test_chatbot.py

失敗したテストケースの分析とプロンプト改善ループ

テストが失敗(Fail)した場合、DeepEvalは詳細なレポートを出力します。「なぜスコアが低かったのか」という理由もLLMが言語化してくれるため、それを分析してプロンプトやRAGの検索ロジック、対話フローを修正します。
このユーザーテストと改善のサイクルを高速に回せることこそが、Evaluation as Codeの価値です。

まとめ:自動化で「本質的な対話設計」に時間を割こう

出力フォーマット - Section Image 3

DeepEvalと生成AIを組み合わせることで、これまで多大な時間を要していた評価データの作成とテスト実行を、大幅に短縮できます。

しかし、ツールはあくまで手段です。自動生成されたテストケースが本当にユーザーの発話パターンを網羅しているか、定義した評価指標(G-Eval)が業務要件と合致しているか、最終的な判断を行うことが重要です。

テストデータの作成コストを理由に評価を妥協する段階を抜け出し、「どのようなテストケースを追加すれば、より自然で効果的な対話AIになるか」を追求するステージへ進むことが求められます。

継続的なA/Bテストや改善を通じて、実用的なチャットボットを構築していきましょう。

DeepEvalと生成AIで挑むLLMテスト自動化:泥臭いデータ作成をコード化する実践レシピ - Conclusion Image

コメント

コメントは1週間で消えます
コメントを読み込み中...