Pydantic AI Model Evaluations 模型評估

上一篇文章中,我們討論了如何使用 TestModel 進行單元測試 (Unit Testing)。單元測試的重點在於檢查「程式邏輯 (Code Logic)」是否正確,例如:工具是否被呼叫、參數傳遞是否正確、系統提示有沒有寫錯字。

然而,單元測試無法告訴你:「AI 寫的這篇總結文章夠不夠好?」或是「AI 有沒有產生幻覺 (Hallucination) 提供錯誤的財務建議?」

這時候,我們需要另一種測試維度,稱為 模型評估 (Evaluations,簡稱 Evals)

為什麼需要 Evals?

大型語言模型 (LLM) 是一個充滿不確定性的黑盒子。當你修改了系統提示(加了一句 "請用更禮貌的語氣"),或是將底層模型從 gpt-4o-mini 升級為 gpt-4o 時,你怎麼知道這些改動是整體提升了品質,還是顧此失彼造成了其他情境退步?

Evals 的目的就是透過系統化、自動化的方式,對 AI 代理人在大量測試案例 (Dataset) 上的表現進行「打分」。只有擁有可靠的 Evals 系統,開發團隊才能充滿信心地反覆迭代系統提示與模型參數。

認識 Pydantic Evals

Pydantic Evals 是 Pydantic AI 生態圈中專門負責模型評估的子模組。

它提供了以下幾項核心功能,幫助你建立自動化的評估管線:

  1. 資料集管理 (Dataset Management): 幫助你載入與管理測試題庫(例如數百個過去的真實客服對話紀錄,以及對應的完美人類解答)。
  2. 各式評估器 (Evaluators): 提供了多種打分機制。有些評估器是基於傳統規則(如字數長度、是否包含特定關鍵字),而更進階的評估器則是利用另一個強大的 LLM 來當作裁判 (LLM Judge)。
  3. 線上評估 (Online Evaluation): 與 Pydantic Logfire 深度整合,讓你不只在開發階段跑測試,還能在系統上線後,持續對真實使用者的生產環境對話進行即時抽樣打分,監控模型品質是否隨著時間衰退 (Drift)。
  4. 並發與效能 (Concurrency): 當你有 1,000 筆測試案例需要評估時,Pydantic Evals 提供了高效的非同步執行引擎,大幅縮短跑完全部 Evals 所需的時間。

安裝 Pydantic Evals

Pydantic Evals 是一個獨立的套件,你需要額外安裝它:

pip install pydantic-evals

如果需要與 Logfire 整合來記錄評估結果,可以安裝包含 Logfire 的版本:

pip install 'pydantic-evals[logfire]'

核心概念與快速上手

要使用 Pydantic Evals,你需要了解幾個核心概念:

  • 資料集 (Dataset):一組測試案例的集合,也可以包含共用的評估器。
  • 測試案例 (Case):單一個測試情境,包含輸入值 (inputs)、預期輸出 (expected_output),以及該案例專屬的評估器。
  • 評估器 (Evaluator):用來對任務輸出結果進行打分或驗證的函數。
  • 評估報告 (EvaluationReport):執行評估後產生的詳細結果報告。

確定性驗證 (Deterministic Validation) 範例

雖然 Evals 通常用於測試 AI 系統,但這個框架可以測試任何函數。以下是一個簡單的文字轉換函數評估範例,展示了如何使用內建的規則評估器:

from pydantic_evals import Case, Dataset
from pydantic_evals.evaluators import Contains, EqualsExpected

# 1. 建立包含測試案例的資料集
dataset = Dataset(
    name='uppercase_tests',
    cases=[
        Case(
            name='uppercase_basic',
            inputs='hello world',
            expected_output='HELLO WORLD',
        ),
        Case(
            name='uppercase_with_numbers',
            inputs='hello 123',
            expected_output='HELLO 123',
        ),
    ],
    evaluators=[
        EqualsExpected(),  # 檢查輸出是否與 expected_output 完全一致
        Contains(value='HELLO', case_sensitive=True),  # 檢查輸出是否包含 "HELLO"
    ],
)

# 2. 定義待測函數 (這裡以簡單的 Python 函數為例,實務上會是你的 AI Agent)
def uppercase_text(text: str) -> str:
    return text.upper()

# 3. 執行同步評估
report = dataset.evaluate_sync(uppercase_text)

# 4. 印出評估報告
report.print()

執行後,你會看到類似以下的精美終端機輸出報告,顯示每個測試案例是否通過了所有的斷言 (Assertions) 以及執行時間:

                  Evaluation Summary: uppercase_text
┏━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━┓
┃ Case ID                 ┃ Assertions ┃ Duration ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━┩
│ uppercase_basic         │ ✔✔         │     10ms │
├─────────────────────────┼────────────┼──────────┤
│ uppercase_with_numbers  │ ✔✔         │     10ms │
├─────────────────────────┼────────────┼──────────┤
│ Averages                │ 100.0% ✔   │     10ms │
└─────────────────────────┴────────────┴──────────┘

各式評估器 (Evaluators) 實作

在 Pydantic Evals 中,評估器 (Evaluator) 是負責對 AI 模型的輸出結果進行「打分」的元件。根據任務的性質,Pydantic Evals 支援多種不同的評估策略。

內建與自定義的規則評估器 (Built-in & Custom Evaluators)

最簡單的評估方式是基於傳統的程式邏輯(Deterministic 確定性的規則)。Pydantic Evals 內建了許多實用的評估器,例如前面範例用到的 EqualsExpectedContains,或是檢查型別的 IsInstance

from pydantic_evals import Case, Dataset
from pydantic_evals.evaluators import Contains, IsInstance

dataset = Dataset(
    name='dict_validation',
    cases=[
        Case(inputs={'data': 'required_key present'}, expected_output={'result': 'success'}),
    ],
    evaluators=[
        IsInstance(type_name='dict'), # 檢查輸出是否為字典型別
        Contains(value='required_key'), # 檢查是否包含特定內容
    ],
)

除了內建評估器,你也可以寫自定義的 Python 函數來當作評估器。這類評估器的優點是執行速度極快,且不需額外花費 API 成本。但在面對諸如「語氣是否友善」、「內容是否具有創意」這類主觀問題時,規則評估器就無能為力了。

大語言模型裁判 (LLM Judge)

這正是 LLM Judge 登場的時刻。LLM Judge 的概念非常聰明:我們使用一個更聰明、能力更強的大語言模型(例如 o1-preview 或是 gpt-4o)來作為裁判,去評分另一個較小或較便宜的模型(例如 gemini-1.5-flash 或本地模型)的輸出結果。

在 Pydantic Evals 中,你可以直接使用內建的 LLMJudge 評估器,輕鬆將主觀的評分標準 (Rubric) 交給強大的模型來判斷:

from pydantic_evals import Case, Dataset
from pydantic_evals.evaluators import LLMJudge

dataset = Dataset(
    name='llm_judge_test',
    cases=[
        Case(inputs='法國的首都哪裡?', expected_output='巴黎'),
    ],
    evaluators=[
        # 使用 LLM Judge 進行主觀評估
        LLMJudge(
            rubric='回答必須準確且具有幫助性', # 評分標準
            include_input=True,
            model='openai:gpt-4o', # 指定裁判模型
        )
    ],
)

透過 LLM Judge,你可以讓自動化評估覆蓋到那些難以用傳統程式碼量化的「模糊地帶」。當你對待測 Agent 的系統提示做出了微調後,只要跑一次 LLM Judge 的批量測試,就能立刻從幾十個案例的分數變化中,客觀地得知這個改動是好是壞。搭配 Pydantic Logfire 將每次的評分結果與原因可視化,團隊就能建立起數據驅動 (Data-Driven) 的 Prompt Engineering 流程。

效能測試 (Performance Testing)

除了驗證輸出的正確性,Pydantic Evals 也允許你針對系統的回應時間進行效能測試。你可以使用 MaxDuration 評估器來確保你的 AI 應用程式符合效能要求:

from pydantic_evals import Case, Dataset
from pydantic_evals.evaluators import MaxDuration

dataset = Dataset(
    name='performance_test',
    cases=[
        Case(inputs='test input', expected_output='test output'),
    ],
    evaluators=[
        MaxDuration(seconds=2.0), # 限制執行時間不得超過 2 秒
    ],
)

基於區段的評估 (Span-Based)

在某些進階場景中,我們不僅要評估「最終答案」,還要評估「過程」。傳統的評估器只看輸入與輸出,如果 Agent 靠「瞎猜」或是「呼叫了錯誤的工具但剛好得到正確答案」,傳統評估器是無法發現的。

Pydantic Evals 的 Span-Based (基於區段) 評估,允許你透過分析執行過程中產生的 OpenTelemetry Spans(區段紀錄)來評估系統的行為。這通常需要搭配 logfire 來捕捉這些執行軌跡。

例如,一個 RAG 系統 (檢索增強生成) 的流程包含:

  1. 提取關鍵字
  2. 搜尋向量庫
  3. 統整資料並生成答案

你可以使用 HasMatchingSpan 評估器來確保 Agent 確實執行了「搜尋向量庫」這個步驟,而不是直接產生幻覺回答:

import logfire
from pydantic_evals import Case, Dataset
from pydantic_evals.evaluators import HasMatchingSpan

# 1. 設定 logfire 以捕捉執行區段 (Spans)
logfire.configure(send_to_logfire='if-token-present')

# 2. 建立包含 Span 評估器的資料集
dataset = Dataset(
    name='rag_span_test',
    cases=[Case(inputs='請幫我總結最新的公司休假規定')],
    evaluators=[
        # 確保執行過程中,有呼叫到名稱包含 'vector_search' 的區段 (例如搜尋工具)
        HasMatchingSpan(
            query={'name_contains': 'vector_search'},
            evaluation_name='retrieved_docs', # 評估項目名稱:是否有檢索文件
        ),
        # 確保執行過程中,沒有發生錯誤
        HasMatchingSpan(
            query={'not_': {'has_attributes': {'error': True}}},
            evaluation_name='no_errors', # 評估項目名稱:無錯誤發生
        ),
    ],
)

這種細緻的評估方法能幫助你精準驗證 Agent 的行為合約(例如:是否呼叫了特定工具、是否在規定時間內完成、是否發生了重試),而不僅僅是檢查最終輸出的文字。