LiteLLMとDeepEvalでLangGraphエージェントの応答品質を自動評価してみる

LiteLLMとDeepEvalでLangGraphエージェントの応答品質を自動評価してみる

2026.05.17

はじめに

データ事業本部のkobayashiです。

前回はLangSmithで「実行履歴の可視化と一致度ベースの評価」を扱いました。前回の記事の中でも evaluate() に独自の llm_judge 関数を並べて LLM-as-a-judge を一部試していますが、今回は DeepEval を使って、回答関連性・忠実性・ハルシネーションといった LLM特有の品質指標 をライブラリ標準のメトリクスで網羅的に評価する方法を紹介します。

https://dev.classmethod.jp/articles/python-litellm-langsmith/

DeepEvalはLLM-as-a-judge型の評価メトリクスを多数提供する Python ライブラリで、LangGraphエージェントの応答品質を自動評価するのに最適です。

DeepEval の主要メトリクス

メトリクス 評価する内容
AnswerRelevancyMetric 入力質問に関連した回答か
FaithfulnessMetric RAGコンテキストに忠実か
HallucinationMetric 矛盾する情報が含まれていないか
ContextualPrecisionMetric 検索結果の関連性
ContextualRecallMetric 検索結果の網羅性
BiasMetric / ToxicityMetric バイアス・有害性

これらは内部で別のLLMを「審判」として使うメトリクス(LLM-as-a-judge)です。

環境

Python 3.13
litellm 1.83.14
langgraph 1.1.10
langchain-litellm 0.6.4
deepeval 2.0.0
pytest 8.0.0
$ uv pip install litellm langgraph langchain-litellm deepeval pytest
$ export OPENAI_API_KEY="sk-..."  # DeepEvalの審判モデル用
$ export ANTHROPIC_API_KEY="sk-ant-..."

エージェントの応答品質を評価

agent_eval.py
"""DeepEval で LangGraph エージェントの応答品質を評価する。

メトリクス:
- AnswerRelevancyMetric: 質問に関連した回答か
- FaithfulnessMetric: コンテキストに忠実か(ハルシネーションがないか)
- HallucinationMetric: 矛盾した情報を含まないか
"""

from deepeval import evaluate
from deepeval.metrics import (
    AnswerRelevancyMetric,
    FaithfulnessMetric,
    HallucinationMetric,
)
from deepeval.test_case import LLMTestCase
from langchain.agents import create_agent
from langchain_core.tools import tool
from langchain_litellm import ChatLiteLLM

@tool
def search_doc(query: str) -> str:
    """社内ドキュメントを検索します。"""
    docs = {
        "有給": "有給休暇は入社6ヶ月後に10日付与されます。",
        "リモート": "リモートワークは週3日まで可能です。",
    }
    for k, v in docs.items():
        if k in query:
            return v
    return "情報が見つかりませんでした"

llm = ChatLiteLLM(model="openai/gpt-5-mini")
agent = create_agent(model=llm, tools=[search_doc])

def run_agent(question: str) -> tuple[str, list[str]]:
    """エージェントを実行し、(回答, 取得したコンテキスト) を返す。"""
    result = agent.invoke({"messages": [{"role": "user", "content": question}]})
    final = str(result["messages"][-1].content)
    # ツール出力(コンテキスト)を抽出
    contexts = [
        str(m.content) for m in result["messages"] if getattr(m, "type", None) == "tool"
    ]
    return final, contexts

# 評価対象のテストケース
test_cases: list[LLMTestCase] = []
for q, expected in [
    ("有給休暇のルールを教えて", "入社6ヶ月後に10日付与"),
    ("リモートワークの可能日数は?", "週3日まで可能"),
]:
    answer, contexts = run_agent(q)
    test_cases.append(
        LLMTestCase(
            input=q,
            actual_output=answer,
            expected_output=expected,
            retrieval_context=contexts,
            context=contexts,
        )
    )

# 評価実行: judge モデルは OPENAI_API_KEY を使った DeepEval デフォルト
# (高性能な GPT が自動選択される)。
# 別プロバイダーに切替えたい場合は LiteLLMModel を import して
# AnswerRelevancyMetric(model=LiteLLMModel(model="anthropic/claude-sonnet-4-6"))
# のように渡せる。
results = evaluate(
    test_cases=test_cases,
    metrics=[
        AnswerRelevancyMetric(threshold=0.7),
        FaithfulnessMetric(threshold=0.7),
        HallucinationMetric(threshold=0.5),
    ],
)
print(results)

ポイントを整理します。

LLMTestCase

DeepEvalの評価単位はLLMTestCaseで、以下の情報を持ちます。

  • input: ユーザーの入力
  • actual_output: エージェントの実際の出力
  • expected_output: 期待される出力(任意)
  • retrieval_context: RAGで検索したコンテキスト(FaithfulnessMetric用)
  • context: 一般的な参照コンテキスト(HallucinationMetric用)

コンテキスト抽出

LangGraphエージェントの場合、messagesからtype == "tool"のメッセージを取り出すことで、エージェントが参照した情報(ツール出力)をretrieval_contextとして渡せます。これがあると Faithfulness を正しく評価できます。

閾値

threshold=0.7は0.7以上で合格扱い。CIで使う場合は失敗時にビルドを落とせます。

実行結果は以下のようになります。

$ python agent_eval.py
 You're running DeepEval's latest Answer Relevancy Metric! (using gpt-5.4, strict=False, async_mode=True)...
 You're running DeepEval's latest Faithfulness Metric! (using gpt-5.4, strict=False, async_mode=True)...
 You're running DeepEval's latest Hallucination Metric! (using gpt-5.4, strict=False, async_mode=True)...

======================================================================

Metrics Summary

  - Answer Relevancy (score: 1.0, threshold: 0.7, evaluation model: gpt-5.4, reason: The score is 1.00 because the response appears fully relevant to the question, with no irrelevant statements noted.)
  - Faithfulness (score: 1.0, threshold: 0.7, evaluation model: gpt-5.4, reason: The score is 1.00 because there are no contradictions, so the actual output appears fully consistent with the retrieval context.)
  - Hallucination (score: 0.0, threshold: 0.5, evaluation model: gpt-5.4, reason: The score is 0.00 because the actual output fully aligns with the context, correctly stating that remote work is possible up to 3 days per week.)

For test case:
  - input: リモートワークの可能日数は?
  - actual output: 原則、週3日まで可能です。
  - expected output: 週3日まで可能
  - retrieval context: ['リモートワークは週3日まで可能です。']

======================================================================

Overall Metric Pass Rates
Answer Relevancy: 100.00% pass rate
Faithfulness:     100.00% pass rate
Hallucination:    100.00% pass rate

 Evaluation completed 🎉! (time taken: 30.32s | token cost: 0.1013725 USD)
» Test Results (2 total tests):
   » Pass Rate: 100.0% | Passed: 2 | Failed: 0

判定モデルとして DeepEval が自動的に gpt-5.4 を選択しています(OpenAI APIキーが設定されているため)。Hallucination のスコアは「低いほど良い」指標なので0.0でPASSになっています。

DeepEval は最後に 総トークンコスト(今回は約 $0.10)まで表示してくれるため、CI で評価コストの予算管理がしやすい点が嬉しいポイントです。

pytest で CI に組み込む

DeepEvalはpytestと統合されており、assert_testでテスト関数として書くと CI に組み込めます。

test_agent_pytest.py
"""pytest 経由で DeepEval を回す。CIに組み込みやすい。"""

import pytest
from deepeval import assert_test
from deepeval.metrics import AnswerRelevancyMetric, FaithfulnessMetric
from deepeval.test_case import LLMTestCase
from langchain.agents import create_agent
from langchain_core.tools import tool
from langchain_litellm import ChatLiteLLM

@tool
def search_doc(query: str) -> str:
    """社内ドキュメントを検索します。"""
    return "有給休暇は入社6ヶ月後に10日付与されます。"

llm = ChatLiteLLM(model="openai/gpt-5-mini")
agent = create_agent(model=llm, tools=[search_doc])

@pytest.mark.parametrize(
    ("question", "expected_substr"),
    [
        ("有給休暇は何日?", "10日"),
        ("入社後いつから有給?", "6ヶ月"),
    ],
)
def test_answer_quality(question: str, expected_substr: str) -> None:
    result = agent.invoke({"messages": [{"role": "user", "content": question}]})
    answer = str(result["messages"][-1].content)
    contexts = [
        str(m.content) for m in result["messages"] if getattr(m, "type", None) == "tool"
    ]
    test_case = LLMTestCase(
        input=question,
        actual_output=answer,
        expected_output=expected_substr,
        retrieval_context=contexts,
    )
    assert_test(
        test_case,
        [
            AnswerRelevancyMetric(threshold=0.7),
            FaithfulnessMetric(threshold=0.7),
        ],
    )

実行結果は以下のようになります。

$ pytest test_agent_pytest.py -v
============================= test session starts ==============================
plugins: deepeval-3.9.9, asyncio-1.3.0, langsmith-0.7.38, ...
collected 2 items

test_agent_pytest.py::test_answer_quality[有給休暇は何日?-10日] PASSED  [ 50%]
test_agent_pytest.py::test_answer_quality[入社後いつから有給?-6ヶ月] PASSED  [100%]

=================== 2 passed, 1 warning in 60.21s (0:01:00) ====================

AnswerRelevancyMetricFaithfulnessMetric の両方を満たした2件がPASSしました。実行時間は約60秒(メトリクスごとに判定LLMを内部で呼ぶため、1テストあたり25秒前後)です。CIで実行する場合はこの時間と判定LLMのトークンコストを見越したビルド予算が必要です。

通常のpytestコマンドで動きますが、deepeval test run コマンドを使うとレポートが綺麗にまとまり、deepeval loginしておけば結果が Confident AI(DeepEvalのSaaS)にも送られます。

LangSmith との使い分け

観点 LangSmith DeepEval
強み トレース可視化、本番監視 LLM特有メトリクスのライブラリ充実
データセット UIで管理 コード中心
評価方法 任意関数(自由度高) 既製メトリクスが豊富
CI統合 可能 pytestネイティブ

両者は併用できます。LangSmithでトレース・監視、DeepEvalで品質評価という組み合わせが現実的です。

LiteLLMでのモデル使い分け

ロール モデル 理由
ReActエージェント(被評価対象) openai/gpt-5-mini 評価される側。安価モデルで動かすことで「安いモデルでも品質が出るか」を測る
DeepEval judge LLM DeepEvalデフォルト(gpt-5.4) 評価する側。判定品質を担保するため高性能モデルが選択される

DeepEval は「評価される側」と「評価する側(judge)」で別のモデルを使うのが標準です。LiteLLMで被評価モデルを安価なGPT-5-miniに固定し、judgeはDeepEvalデフォルトの高性能モデル(今回は gpt-5.4)に任せることで、コスト最適化したエージェントの品質を高品質モデルでチェックするという構造が自然に組めます。

被評価モデルを anthropic/claude-sonnet-4-6 に切り替えて再実行すれば、同じjudgeでGPTとClaudeの品質スコアを横並びで比較できます。

まとめ

DeepEval で LangGraph エージェントの応答品質を評価する方法を、evaluate() 直叩きと pytest 統合の両方で紹介しました。

LLMTestCase に LangGraph の tool メッセージを retrieval_context として渡すことで、AnswerRelevancyMetric / FaithfulnessMetric / HallucinationMetric といった LLM-as-a-judge 型のメトリクスがそのまま使え、assert_test で書けば pytest ベースの CI にもそのまま組み込めます。LangSmith は実行履歴の可視化と運用監視、DeepEval は応答品質のメトリクス評価、と役割分担して併用するのが現実的で、LiteLLM で被評価モデルだけを差し替えれば同じ judge で複数モデルの品質スコアを横並び比較できる点も、本構成の大きな利点です。

最後まで読んでいただきありがとうございました。


生成AI活用はクラスメソッドにお任せ

過去に支援してきた生成AIの支援実績100+を元にホワイトペーパーを作成しました。御社が抱えている課題のうち、どれが解決できて、どのようなサービスが受けられるのか?4つのフェーズに分けてまとめています。どうぞお気軽にご覧ください。

生成AI資料イメージ

無料でダウンロードする

この記事をシェアする

関連記事