Lesson CH09-L02
Pydantic Evals
ゴールデンデータセットでAgent出力を継続評価する。
- 読了目安
- 11 min
- Colab目安
- 16 min
- 合計
- 27 min
- 前提
- Logfire
関連: 公式ドキュメント
一行サマリ
Case(inputs=, expected_output=) を集めて Dataset(cases=[...], evaluators=[...]) を作り、dataset.evaluate_sync(task) で Agent を全ケースに走らせて自動採点 + レポート出力。Pydantic 流の 「LLM アプリのユニットテスト」 が pydantic-evals パッケージで完結する。
ヒーロー: 「LLM のユニットテスト」を書く
Agent を本番投入したら 「プロンプトを 1 文字直したら別のケースが壊れた」 という回帰が必ず起きます。pydantic-evals は テストケースの集合 (Dataset) に対して 複数の評価軸 (Evaluators) を組み合わせ、スコア + レポート を出す仕組みです。pytest + 構造化レポートのイメージ。
概念: 4 つの基本パーツ
| パーツ | 役割 |
|---|---|
Case(name=, inputs=, expected_output=, metadata=) | 1 件のテストケース |
Dataset(cases=[...], evaluators=[...]) | ケース集合 + 共通の評価器 |
Evaluator | 採点ロジック。IsInstance / Equals / Contains / LLMJudge / カスタム |
dataset.evaluate_sync(task) | task 関数を全 case に適用してレポート生成 |
ビルトイン Evaluator は 決定的 (確率的でない) 検証 を担当。LLMJudge は次レッスン (Ch9-L03) で詳しく扱います。
コード: 3 つのパターン
パターン 1: 最小例 — 翻訳 Agent をビルトイン Evaluator で評価
from pydantic_ai import Agent
from pydantic_ai.models.google import GoogleModel
from pydantic_evals import Case, Dataset
from pydantic_evals.evaluators import IsInstance
translator = Agent(
GoogleModel('gemini-3-flash-preview'),
instructions='与えられた英語を自然な日本語に訳してください。',
)
cases = [
Case(name='hello', inputs='Hello, world!', expected_output='こんにちは、世界!'),
Case(name='cat', inputs='I have a cat.', expected_output='私は猫を飼っています。'),
Case(name='thanks', inputs='Thank you very much.', expected_output='どうもありがとうございます。'),
]
dataset = Dataset(cases=cases, evaluators=[IsInstance(type_name='str')])
def task(inputs: str) -> str:
return translator.run_sync(inputs).output
report = dataset.evaluate_sync(task)
report.print(include_input=True, include_output=True)ポイント:
Caseは inputs / expected_output が肝心。metadataで観察用情報を添えるtaskは inputs を受けて Agent の output を返す関数IsInstanceは str を返したか の最低限チェックreport.print(...)で ケース単位の表形式レポート
パターン 2: カスタム Evaluator で「正解語を含むか」をチェック
ビルトインに無い検証は @dataclass class XxxEvaluator(Evaluator): で書きます。
from dataclasses import dataclass
from pydantic_evals.evaluators import Evaluator, EvaluatorContext
@dataclass
class ContainsExpected(Evaluator):
"""expected_output の文字列が output に部分一致するか。"""
async def evaluate(self, ctx: EvaluatorContext[str, str]) -> float:
if ctx.expected_output is None:
return 0.0
return 1.0 if ctx.expected_output in ctx.output else 0.0
dataset = Dataset(
cases=cases,
evaluators=[IsInstance(type_name='str'), ContainsExpected()],
)
report = dataset.evaluate_sync(task)
report.print(include_input=True, include_output=True)EvaluatorContext は ctx.inputs / ctx.output / ctx.expected_output / ctx.metadata を持ちます。0.0 〜 1.0 の float を返すのが約束。
パターン 3: 構造化出力 Agent を評価
output_type=BaseModel の Agent も同じ枠組みで評価できます。
from pydantic import BaseModel
from pydantic_ai import Agent
from pydantic_ai.models.google import GoogleModel
from pydantic_evals import Case, Dataset
from pydantic_evals.evaluators import Evaluator, EvaluatorContext
from dataclasses import dataclass
class Sentiment(BaseModel):
polarity: str # positive / neutral / negative
agent = Agent(
GoogleModel('gemini-3-flash-preview'),
output_type=Sentiment,
instructions='文の感情を判定し polarity に positive/neutral/negative を入れてください。',
)
@dataclass
class PolarityMatches(Evaluator):
async def evaluate(self, ctx: EvaluatorContext[str, Sentiment]) -> float:
return 1.0 if ctx.output.polarity == ctx.expected_output else 0.0
cases = [
Case(inputs='今日の発表は最高でした!', expected_output='positive'),
Case(inputs='残念な結果に終わりました。', expected_output='negative'),
Case(inputs='通常通り進んでいます。', expected_output='neutral'),
]
dataset = Dataset(cases=cases, evaluators=[PolarityMatches()])
def task(inputs: str) -> Sentiment:
return agent.run_sync(inputs).output
dataset.evaluate_sync(task).print(include_input=True, include_output=True)構造化出力 (Ch4) と組み合わせれば、「fields 単位での厳密検証」 がそのまま自動化できます。
観察: レポート出力と CI 連携
report.print(...) はターミナル向けの表形式表示。report.cases で 個別のスコア / output / failure 詳細 にアクセスでき、CI で assert report.average_score > 0.8 のような閾値チェックが書けます。
report = dataset.evaluate_sync(task)
avg = sum(c.evaluators[0].score for c in report.cases) / len(report.cases)
assert avg >= 0.8, f'平均スコアが基準未満: {avg:.2f}'Logfire 連携 (pip install pydantic-evals[logfire]) を使うと Web 上でケースごとのトレース も見られます。
まとめ
Case+Dataset+Evaluatorで LLM アプリのユニットテスト を書ける- ビルトイン Evaluator (
IsInstance/EqualsExpected等) は決定的検証 - カスタム Evaluator は
@dataclass class X(Evaluator):+async def evaluate(ctx) -> float - 構造化出力 (Ch4) と組み合わせれば fields 単位の厳密検証
- CI で
report.average_score >= 0.8のような閾値チェックを回す
次レッスンでは LLM-Judge — 別 LLM に採点させる主観品質評価のパターンと、その落とし穴を扱います。
Colab で実際に動かす
本レッスンの内容を Google Colab 上で実行できるノートブックを用意しています。下のボタンから自分のColab環境に開けます (要 Google アカウント / GOOGLE_API_KEY)。
notebooks/ch09/02-pydantic-evals.ipynb