0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PydanticAIの評価フレームワークPydantic Evalsを使ってみた

Posted at

はじめに

 最近、評価駆動開発が注目を集めています。これは、AIシステムの振る舞いを保証し、改善の方向性を明確にする上で欠かせない手法です。今回は、エージェントフレームワークPydanticAIが提供する評価ツール「Pydantic Evals」を活用し、LLMの推論能力を検証する方法を使ってみました。

今回のタスク:コインパーキング料金計算

 今回は、コインパーキングの看板画像から料金体系を正確に読み取り、ユーザーの駐車時間に対する料金を算出する処理を題材とします。例えば、「金曜21時から翌日の7時までとめた時の料金はいくらか」といった質問に正しく答えることができるか調べます。

Pydantic Evalsのデータモデル

https://ai.pydantic.dev/evals/#pydantic-evals-data-model
Pydantc Evalsのデータモデルは以下のようになります。

Dataset (1) ──────────── (Many) Case
│                        │
│                        │
└─── (Many) Experiment ──┴─── (Many) Case results
     │
     └─── (1) Task
     │
     └─── (Many) Evaluator
  • ケース(Case):入力と期待出力(正解)のセット
    • ただし、LLMアプリでは出力の文章が適切であるか正解データを利用せず、LLMに評価させること(LLM as a Judge)もあるため、正解データの設定は必須でありません
  • 評価器(Evaluator):ケースの出力が期待通りか否かを判定する処理
    • 例えば、出力が正解と一致しているか、長さが適切か、処理時間は許容範囲内か、出力テキストはわかりやすいか、といった処理を記述します。 
  • データセット(Dataset):複数のケースと評価器をいれたもの
  • 実験(Experiment):データセットに対して処理を実行する一連の工程。

使ってみる

評価対象の処理の作成

 まず評価対象の処理を実装します。評価対象として、gemini-2.5-flashモデルを使用したエージェントを実装しました。ついでに、入力と出力のデータクラスも実装します。

from pydantic import BaseModel
from pydantic_ai import Agent, BinaryContent, ModelSettings, NativeOutput
from pydantic_ai.models.google import GoogleModel
from pydantic_ai.providers.google import GoogleProvider

class AgentInput(BaseModel):
    image_path: Path
    query: str

class AgentResponse(BaseModel):
    thinking_process: str = ""
    fee: int

def run_agent(agent_input: AgentInput) -> AgentResponse:
    provider = GoogleProvider(api_key=settings.GEMINI_API_KEY)
    model = GoogleModel(
        "gemini-2.5-flash", settings=ModelSettings(temperature=0.0), provider=provider
    )
    agent = Agent(
        model,
        output_type=NativeOutput(AgentResponse),
        system_prompt=(
            "あなたは、コインパーキングの看板画像を読み取り、ユーザーの質問に答える有能なアシスタントです。\n"
            "看板に記載されている情報を読み取り、ユーザーの金額に関する質問に答えてください。"
        ),
    )
    image_path = agent_input.image_path
    query = agent_input.query
    with image_path.open("rb") as f:
        image_data = f.read()
    image_input = BinaryContent(data=image_data, media_type="image/jpeg")
    result = agent.run_sync([image_input, query])
    return result.output

評価ケースの作成

 評価ケースを3つ作成します。簡単なケースから、日をまたぐ少し複雑なケースを作成します。

from pydantic_evals import Case

case1 = Case(
    name="weekday_2hours",
    inputs=AgentInput(
        image_path=Path("./data/sample.jpg"),
        query="平日の12時から14時までとめた時の料金はいくらですか?",
    ),
    expected_output=AgentResponse(fee=1980)
)

case2 = Case(
    name="saturday_6hours",
    inputs=AgentInput(
        image_path=Path("./data/sample.jpg"),
        query="土曜の18時から24時までとめた時の料金はいくらですか?",
    ),
    expected_output=AgentResponse(fee=1390),
)

case3 = Case(
    name="weekday_to_holiday_overnight",
    inputs=AgentInput(
        image_path=Path("./data/sample.jpg"),
        query="木曜の21時から7時までとめた時の料金はいくらですか?なお翌日は祝日です。",
    ),
    expected_output=AgentResponse(fee=400)
)

評価器を作る

 料金が適切であるか評価するためのFeeMatchクラスを実装します。

from pydantic_evals.evaluators import Evaluator, EvaluatorContext

@dataclass
class FeeMatch(Evaluator):
    def evaluate(self, ctx: EvaluatorContext) -> bool:
        output: AgentResponse = ctx.output
        expected: AgentResponse = ctx.expected_output
        return output.fee == expected.fee

データセットを作る

ケースと評価器をまとめて、評価データセットを作成します。

from pydantic_evals.evaluators.common import IsInstance

from pydantic_evals import Dataset
dataset = Dataset(
    name="ParkingFeeDataset",
    cases=[case1, case2, case3],
    evaluators=[
        IsInstance(type_name="AgentResponse"),
        FeeMatch(),
    ],
)

評価の実行

評価を実行します。

report = dataset.evaluate_sync(run_agent)
report.print(include_input=True, include_output=True)
評価結果(長いため省略)
                                           Evaluation Summary: run_agent                                           
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━┓
┃ Case ID                     ┃ Inputs                      ┃ Outputs                     ┃ Assertions ┃ Duration ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━┩
│ weekday_2hours              │ image_path=PosixPath('data/ │ thinking_process='ユーザー  │ ✔✔         │     6.1s │
│                             │ sample.jpg')                │ は平日の12時から14時までの  │            │          │
│                             │ query='平日の12時から14時ま │ 駐車料金を尋ねています。看  │            │          │
│                             │ でとめた時の料金はいくらで  │ 板の料金体系を確認します。  │            │          │
│                             │ すか?'                     │ 平日は「月〜土」に該当し、1 │            │          │
│                             │                             │ って、330円 × 6単位 =       │            │          │
│                             │                             │ 1980円となります。'         │            │          │
│                             │                             │ fee=1980                    │            │          │
├─────────────────────────────┼─────────────────────────────┼─────────────────────────────┼────────────┼──────────┤
│ saturday_6hours             │ image_path=PosixPath('data/ │ thinking_process='ユーザー  │ ✔✔         │     9.0s │
│                             │ sample.jpg')                │ は土曜日の18時から24時まで  │            │          │
│                             │ query='土曜の18時から24時ま │ の駐車料金を尋ねています。  │            │          │
│                             │ でとめた時の料金はいくらで  │ 看板の「月〜土曜」の料金体  │            │          │
│                             │ すか?'                     │ 系を確認します。駐車時間帯  │            │          │
│                             │                             │ は18:00から24:00です。この  │            │          │
│                             │                             │ 時間帯は、基本料金と最大料  │            │          │
│                             │                             │ 金の適用時間帯にまたがって  │            │          │
│                             │                             │ います。まず、18:00から19:0 │            │          │
│                             │                             │ 0までの1時間(60分)の料金  │            │          │
│                             │                             │ を計算します。基本料金は20  │            │          │
│                             │                             │ 分330円なので、60分では3単  │            │          │
│                             │                             │ 位となり、330円 × 3 =       │            │          │
│                             │                             │ 990円です。次に、19:00から2 │            │          │
│                             │                             │ 4:00までの5時間の料金を計算 │            │          │
│                             │                             │ します。この時間帯は「19:00 │            │          │
│                             │                             │ 〜8:00」の最大料金400円が適 │            │          │
│                             │                             │ 用されます。したがって、19: │            │          │
│                             │                             │ 00から24:00までの料金は400  │            │          │
│                             │                             │ 円となります。合計料金は、1 │            │          │
│                             │                             │ 8:00〜19:00の料金と19:00〜2 │            │          │
│                             │                             │ 4:00の料金を合算し、990円 + │            │          │
│                             │                             │ 400円 = 1390円です。'       │            │          │
│                             │                             │ fee=1390                    │            │          │
├─────────────────────────────┼─────────────────────────────┼─────────────────────────────┼────────────┼──────────┤
│ weekday_to_holiday_overnig… │ image_path=PosixPath('data/ │ thinking_process='ユーザー  │ ✔✗         │    19.5s │
│                             │ sample.jpg')                │ は木曜の21時から翌日7時まで │            │          │
│                             │ query='木曜の21時から7時ま  │ の駐車料金を質問しています  │            │          │
│                             │ でとめた時の料金はいくらで  │ 。翌日は祝日であるという情  │            │          │
│                             │ すか?なお翌日は祝日です。' │ 報が与えられています。看板  │            │          │
│                             │                             │ の料金体系を確認します。\n\ │            │          │
│                             │                             │ n1.                         │            │          │
│                             │                             │ **駐車開始日(木曜)の料金  │            │          │
│                             │                             │ 計算**:                     │            │          │
│                             │                             │ 木曜の21時から24時までの駐  │            │          │
│                             │                             │ 車です。これは「月〜土」の  │            │          │
│                             │                             │ 「19:00〜8:00」の時間帯に該 │            │          │
│                             │                             │ 当し、この時間帯の最大料金  │            │          │
│                             │                             │ は400円です。この3時間(21: │            │          │
│                             │                             │ 00〜24:00)の基本料金は20分 │            │          │
│                             │                             │ 330円で計算すると2970円にな │            │          │
│                             │                             │ りますが、最大料金400円が適 │            │          │
│                             │                             │ 用されます。\n2.            │            │          │
│                             │                             │ **翌日(祝日)の料金計算**: │            │          │
│                             │                             │ 翌日(祝日)の0時から7時ま  │            │          │
│                             │                             │ での駐車です。これは「日・  │            │          │
│                             │                             │ 休日」の「19:00〜8:00」の時 │            │          │
│                             │                             │ 間帯に該当し、この時間帯の  │            │          │
│                             │                             │ 最大料金は400円です。この7  │            │          │
│                             │                             │ 時間(0:00〜7:00)の基本料  │            │          │
│                             │                             │ 金は20分330円で計算すると69 │            │          │
│                             │                             │ 30円になりますが、最大料金4 │            │          │
│                             │                             │ 00円が適用されます。\n\n看  │            │          │
│                             │                             │ 板には「タイムズパーキング  │            │          │
│                             │                             │ の最大料金は繰り返し適用さ  │            │          │
│                             │                             │ れます」と記載されています  │            │          │
│                             │                             │ 。これは、日をまたぐ場合や  │            │          │
│                             │                             │ 、最大料金の適用期間を超え  │            │          │
│                             │                             │ て駐車した場合に、再度最大  │            │          │
│                             │                             │ 料金が適用されることを意味  │            │          │
│                             │                             │ します。今回のケースでは、  │            │          │
│                             │                             │ 木曜の夜間料金と祝日の夜間  │            │          │
│                             │                             │ 料金がそれぞれ独立して適用  │            │          │
│                             │                             │ されると解釈するのが妥当で  │            │          │
│                             │                             │ す。\n\nしたがって、木曜の  │            │          │
│                             │                             │ 夜間料金として400円、祝日の │            │          │
│                             │                             │ 夜間料金として400円が適用さ │            │          │
│                             │                             │ れ、合計料金は400円 + 400円 │            │          │
│                             │                             │ = 800円となります。'        │            │          │
│                             │                             │ fee=800                     │            │          │
├─────────────────────────────┼─────────────────────────────┼─────────────────────────────┼────────────┼──────────┤
│ Averages                    │                             │                             │ 83.3% ✔    │    11.5s │
└─────────────────────────────┴─────────────────────────────┴─────────────────────────────┴────────────┴──────────┘

 ケース1,2は正しく評価できていますが、ケース3は適切でないようです。期待する正解は400円でしたが、エージェントは800円と回答しました。 これは、エージェントが「24時(日付変更)」を境に料金計算をリセットし、2日分の夜間最大料金を足し合わせてしまったためです。改善するにはモデルをflashからproにすることや、プロンプトを修正する必要がありそうです。

まとめ

 今回は Pydantic Evalsを活用して、コインパーキング料金計算エージェントの評価駆動開発を実践してみました。実際にツールを使ってみて、以下のような点に気づきました。

評価のデータモデルが良い

  • DatasetやCaseなどの評価データモデルがよくできており、テストケースの管理や評価ロジックをシンプルかつ抽象度高く記述できます
  • そのおかげで、実装がすっきりまとまりました

インフラ実装の手間が減る

  • 複数テストケースを並列で評価する処理などを自前で実装する必要がなく、手間が大幅に削減されました
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?