news 2026/9/13 12:44:29

Pydantic Evals 数据集序列化完全指南:YAML/JSON 持久化、Schema 生成与自定义 Evaluator

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Pydantic Evals 数据集序列化完全指南:YAML/JSON 持久化、Schema 生成与自定义 Evaluator

Pydantic Evals 数据集序列化完全指南:YAML/JSON 持久化、Schema 生成与自定义 Evaluator

【免费下载链接】pydantic-aiHow Python does AI. Agents, realtime voice, image generation, embeddings. Every model, every interface, typed end to end.项目地址: https://gitcode.com/GitHub_Trending/py/pydantic-ai

导读

本文聚焦 Pydantic Evals 的Dataset序列化能力,讲解如何将测试用例数据集保存为 YAML 与 JSON 文件、如何在保存时自动生成 JSON Schema 以驱动 IDE 的自动补全与校验,以及如何处理自定义 Evaluator 的序列化与反序列化。读完本文,你将掌握to_file()/from_file()的完整参数体系、三种 Evaluator 简写形式、Schema 路径模板,并能独立排查"自定义 Evaluator 无法加载""格式推断失败"等常见问题。

概述:两种格式,一套机制

Pydantic Evals 支持将数据集序列化到文件,共两种格式:

  • YAML.yaml.yml)—— 人类可读、注释友好、git diff 干净,适合绝大多数场景;
  • JSON.json)—— 结构化、机器可读,适合程序化生成与严格结构约束。

两种格式共同具备以下能力:

  • 自动生成 JSON Schema,供 IDE 自动补全与校验(yaml-language-server$schema机制);
  • 自定义 Evaluator 的序列化 / 反序列化(通过custom_evaluator_types注册);
  • 带泛型参数的类型安全加载Dataset[InputsT, OutputT, MetadataT].from_file(...))。

从源码看,序列化的核心实现在 pydantic_evals/pydantic_evals/dataset.py:

  • DEFAULT_SCHEMA_PATH_TEMPLATE = './{stem}_schema.json'定义了默认 Schema 文件命名模板({stem}会被替换为数据集文件名主干);
  • _YAML_SCHEMA_LINE_PREFIX = '# yaml-language-server: $schema='是 YAML 文件首行注释的前缀;
  • 内部通过_CaseModel/_DatasetModel两个extra='forbid'的 Pydantic 模型完成严格的序列化校验,且_DatasetModel显式声明了$schema字段别名(alias='$schema'),避免加载时因多余的$schema键触发校验失败。

YAML 格式:从保存到加载

基础示例

from typing import Any from pydantic_evals import Case, Dataset from pydantic_evals.evaluators import EqualsExpected, IsInstance # Create a dataset with typed parameters dataset = Datasetstr, str, Any, ], evaluators=[ IsInstance(type_name='str'), EqualsExpected(), ], ) # Save to YAML dataset.to_file('my_tests.yaml')

执行后会产生两个文件:

  1. my_tests.yaml—— 数据集本体;
  2. my_tests_schema.json—— 供 IDE 使用的 JSON Schema(由默认模板./{stem}_schema.json推导,即{stem}=my_tests)。

YAML 输出内容

# yaml-language-server: $schema=my_tests_schema.json name: my_tests cases: - name: test_1 inputs: hello expected_output: HELLO evaluators: - IsInstance: str - EqualsExpected

注意几个细节(均有源码依据):

  • 首行的# yaml-language-server: $schema=...注释由to_file()依据_YAML_SCHEMA_LINE_PREFIX自动写入;
  • Evaluator 默认被写成短形式to_file()model_dump时传入context={'use_short_form': True}(见 dataset.py),NamedSpec序列化器据此输出IsInstance: str/EqualsExpected这种紧凑写法(见 pydantic_ai_slim/pydantic_ai/_spec.py);
  • YAML 写出使用yaml.dump(dumped_data, sort_keys=False, allow_unicode=True),因此不会打乱字段顺序,且保留 Unicode 原样——仓库测试 tests/evals/test_dataset.py 专门验证了Привет这类非 ASCII 文本在往返后不丢失。

供 IDE 使用的 JSON Schema

YAML 首行的yaml-language-server指令让编辑器将my_tests_schema.json作为该文件的 Schema,从而获得:

  • 自动补全(VS Code、PyCharm 等编辑器)
  • 编辑过程中的内联校验
  • 字段文档提示(tooltips)
  • 非法数据的高亮报错

支持该指令的编辑器包括:VS Code(需安装 YAML 扩展)、JetBrains 系列 IDE(PyCharm、IntelliJ 等),以及大多数支持 YAML Language Server 的编辑器。若希望获得完整补全体验,需确保编辑器已启用 YAML Language Server 的内联 Schema 支持。

从 YAML 加载

from pathlib import Path from typing import Any from pydantic_evals import Case, Dataset from pydantic_evals.evaluators import EqualsExpected, IsInstance # First create and save the dataset Path('my_tests.yaml').parent.mkdir(exist_ok=True) dataset = Datasetstr, str, Any], evaluators=[IsInstance(type_name='str'), EqualsExpected()], ) dataset.to_file('my_tests.yaml') # Load the dataset with type parameters dataset = Dataset[str, str, Any].from_file('my_tests.yaml') def my_task(text: str) -> str: return text.upper() # Run evaluation report = dataset.evaluate_sync(my_task)

加载链路为from_file()_infer_fmt()推断格式 → 读取文本 →from_text()/from_dict()_DatasetModel.model_validate()校验 → 从注册表还原 Evaluator 实例(见 dataset.py)。

值得注意的加载行为:

  • 若 YAML 中没有name字段from_file()会自动使用**文件名主干(stem)**作为数据集名称(通过default_name=path.stem传入),这一点由 test_deserializing_without_name 验证;
  • 泛型参数Dataset[str, str, Any]决定了inputsexpected_output等字段的校验类型,加载是类型安全的。

JSON 格式:程序化生成的首选

JSON 格式适合程序化生成数据集或需要严格结构的场景。

基础示例

from typing import Any from pydantic_evals import Case, Dataset from pydantic_evals.evaluators import EqualsExpected dataset = Datasetstr, str, Any, ], evaluators=[EqualsExpected()], ) # Save to JSON dataset.to_file('my_tests.json')

JSON 输出内容

{ "$schema": "my_tests_schema.json", "name": "my_tests", "cases": [ { "name": "test_1", "inputs": "hello", "expected_output": "HELLO" } ], "evaluators": [ "EqualsExpected" ] }

顶层的$schema键由Dataset@model_serializer(mode='wrap')钩子注入:序列化上下文携带$schema时,输出结果会被前置合并(见 dataset.py)。它和 YAML 首行注释一样,为 IDE 提供 Schema 关联;同时_DatasetModel.json_schema_path字段别名保证了加载时该键能被正确解析而不报错。

从 JSON 加载

from typing import Any from pydantic_evals import Case, Dataset from pydantic_evals.evaluators import EqualsExpected # First create and save the dataset dataset = Datasetstr, str, Any], evaluators=[EqualsExpected()], ) dataset.to_file('my_tests.json') # Load from JSON dataset = Dataset[str, str, Any].from_file('my_tests.json')

JSON 加载走model_validate_json()路径,同样受泛型参数的类型约束。仓库测试 test_serialization_to_json 验证了 JSON 往返后$schema指向的 Schema 文件确实存在于同一目录。

Schema 生成:自动、定制与手动

自动创建

默认情况下,to_file()会在数据集文件旁生成 JSON Schema 文件:

from typing import Any from pydantic_evals import Case, Dataset dataset = Datasetstr, str, Any]) # Creates both my_tests.yaml AND my_tests_schema.json dataset.to_file('my_tests.yaml')

默认模板常量DEFAULT_SCHEMA_PATH_TEMPLATE = './{stem}_schema.json'决定了文件名为<数据集文件名主干>_schema.json。若 Schema 文件内容与上次一致,_save_schema()会跳过重复写入(见 dataset.py)。

自定义 Schema 位置

from pathlib import Path from typing import Any from pydantic_evals import Case, Dataset dataset = Datasetstr, str, Any]) # Create directories Path('data').mkdir(exist_ok=True) # Custom schema filename (relative to dataset file location) dataset.to_file( 'data/my_tests.yaml', schema_path='my_schema.json', ) # No schema file dataset.to_file('my_tests.yaml', schema_path=None)

关键行为(源码依据见 dataset.py):

  • schema_path相对路径是相对于数据集文件所在目录解析的:data/my_tests.yaml+schema_path='my_schema.json'会把 Schema 写到data/my_schema.json
  • schema_path绝对路径且位于数据集文件目录内,写入时会用_get_relative_path_reference()递归计算相对引用,保证 YAML 首行/JSON$schema中记录的是可移植的相对路径(见 dataset.py);
  • 传入schema_path=None则完全不生成 Schema 文件,首行注释也不会写入。

Schema 路径模板

{stem}占位符会被替换为数据集文件名主干:

from typing import Any from pydantic_evals import Case, Dataset dataset = Datasetstr, str, Any]) # Creates: my_tests.yaml and my_tests.schema.json dataset.to_file( 'my_tests.yaml', schema_path='{stem}.schema.json', )

模板替换实现于to_file()内部:Path(schema_path.format(stem=path.stem))

手动生成 Schema

不保存数据集、直接拿到 Schema 字典:

import json from typing import Any from pydantic_evals import Dataset # Get schema as dictionary for a specific dataset type schema = Dataset[str, str, Any].model_json_schema_with_evaluators() # Save manually with open('custom_schema.json', 'w', encoding='utf-8') as f: json.dump(schema, f, indent=2)

model_json_schema_with_evaluators()(见 dataset.py)的工作方式:

  1. 依据_params()解析Dataset[...]的三个泛型参数,据此构造临时的Case/DatasetPydantic 模型;
  2. 将默认 Evaluator(DEFAULT_EVALUATORS)与传入的自定义 Evaluator 类合并为 Union 类型写入evaluators字段,使 Schema 能精确描述每一种 Evaluator 的短形式/长形式;
  3. 额外注册$schema: {type: string}属性,与序列化输出的$schema键对齐。

默认注册的 Evaluator 清单见 pydantic_evals/pydantic_evals/evaluators/common.py:EqualsEqualsExpectedContainsIsInstanceMaxDurationLLMJudgeHasMatchingSpanToolCorrectnessTrajectoryMatchArgumentCorrectnessMaxToolCallsMaxModelRequestsGEval

自定义 Evaluator:序列化与反序列化

自定义 Evaluator 需要在序列化 / 反序列化时做特殊处理。

三项硬性要求

  1. 必须用@dataclass装饰;
  2. 必须继承Evaluator(或其报告级变体ReportEvaluator);
  3. 必须同时传给to_file()from_file()

源码侧,注册表构建函数_get_evaluator_registry()会逐条校验(见 dataset.py):

  • 不是Evaluator子类会报ValueError
  • __dict__中缺少__dataclass_fields__(即未用@dataclass)会报ValueError: All custom evaluator classes must be decorated with @dataclass, but ... is not

完整示例

from dataclasses import dataclass from typing import Any from pydantic_evals import Case, Dataset from pydantic_evals.evaluators import Evaluator, EvaluatorContext @dataclass class CustomThreshold(Evaluator): """Check if output length exceeds a threshold.""" min_length: int max_length: int = 100 def evaluate(self, ctx: EvaluatorContext) -> bool: length = len(str(ctx.output)) return self.min_length <= length <= self.max_length # Create dataset with custom evaluator dataset = Datasetstr, str, Any, ], ), ], ) # Save with custom evaluator types dataset.to_file( 'dataset.yaml', custom_evaluator_types=[CustomThreshold], )

保存后的 YAML

# yaml-language-server: $schema=dataset_schema.json cases: - name: test_length inputs: example expected_output: long result evaluators: - CustomThreshold: min_length: 5 max_length: 20

序列化细节(依据 pydantic_evals/pydantic_evals/evaluators/_base.py):

  • BaseEvaluator.as_spec()基于build_serialization_arguments()收集 dataclass 字段值,默认值字段会被剔除(如max_length: int = 100若等于默认值就不会写入),因此CustomThreshold(min_length=5, max_length=20)的两个字段都会保留,而只填min_length时输出会更紧凑;
  • 若唯一非默认字段恰好是第一个 dataclass 字段,则走单参数紧凑形式;否则退化为键值对形式;
  • get_serialization_name()默认返回类名,CustomThreshold因而成为 YAML 中的键名。

加载自定义 Evaluator

from dataclasses import dataclass from typing import Any from pydantic_evals import Case, Dataset from pydantic_evals.evaluators import Evaluator, EvaluatorContext @dataclass class CustomThreshold(Evaluator): """Check if output length exceeds a threshold.""" min_length: int max_length: int = 100 def evaluate(self, ctx: EvaluatorContext) -> bool: length = len(str(ctx.output)) return self.min_length <= length <= self.max_length # First create and save the dataset dataset = Datasetstr, str, Any], ), ], ) dataset.to_file('dataset.yaml', custom_evaluator_types=[CustomThreshold]) # Load with custom evaluator registry dataset = Dataset[str, str, Any].from_file( 'dataset.yaml', custom_evaluator_types=[CustomThreshold], )

!!! warning "重要"必须custom_evaluator_types同时传给to_file()from_file()

- `to_file()`:把该 Evaluator 类型纳入生成的 JSON Schema(否则 IDE 无法补全其参数); - `from_file()`:把该类型注册进反序列化注册表(否则加载时抛 "Unknown evaluator name" 错误)。

底层还原逻辑:_from_dataset_model()build_registry()将"自定义类型 + 默认类型"合并为名称→类的注册表,再对每个EvaluatorSpec调用load_from_registry(),等价于cls(*args, **kwargs)实例化(见 pydantic_ai_slim/pydantic_ai/_spec.py)。若某个 Evaluator 解析失败,错误会收集为ExceptionGroup抛出(最多展示前 3 条),异常信息中包含当前注册表全部合法名称,便于快速定位拼写问题。

Evaluator 序列化的三种形式

EvaluatorSpec(即共享模块pydantic_ai._spec.NamedSpec,见 pydantic_evals/pydantic_evals/evaluators/spec.py)支持三种书写形式,分别对应无参、单参、多参:

1. 仅名称(无参数)

evaluators: - EqualsExpected - IsInstance: str # Using default parameter

2. 单参数(短形式)

evaluators: - IsInstance: str - Contains: "required text" - MaxDuration: 2.0

3. 多参数(字典形式)

evaluators: - CustomThreshold: min_length: 5 max_length: 20 - LLMJudge: rubric: "Response is accurate" model: "openai:gpt-5" include_input: true

这三种形式的解析逻辑由_SerializedNamedSpec实现(见 pydantic_ai_slim/pydantic_ai/_spec.py):字符串 → 无参数;{Name: value}且 value 为字典 → 关键字参数;{Name: value}且 value 非字典 → 单个位置参数。需要说明的是:LLMJudgemodel字段在序列化时会通过_serialize_model_as_string()Model实例改写为model_id字符串,从而保证规范可往返(见 pydantic_evals/pydantic_evals/evaluators/common.py);序列化端还会做反向保护——若单个位置参数本身是"全字符串键字典",会自动退化为长形式,避免反序列化时被误判为 kwargs(见 pydantic_ai_slim/pydantic_ai/_spec.py)。

格式对比与选型建议

FeatureYAMLJSON
人类可读✅ 优秀⚠️ 良好
注释✅ 支持❌ 不支持
紧凑✅ 紧凑⚠️ 冗长
机器解析✅ 良好✅ 优秀
IDE 支持✅ 支持✅ 支持
版本控制✅ 干净 diff⚠️ 嘈杂 diff

建议:绝大多数场景使用 YAML(可读、可注释、diff 友好);程序化生成数据集或对结构有严格约束时使用 JSON。

仓库中的真实案例可佐证这一选型:examples/pydantic_ai_examples/evals/example_01_generate_dataset.py用 LLM 生成数据集后以fmt='yaml'写入datasets/time_range_v1.yaml,其产物(见 examples/pydantic_ai_examples/evals/datasets/time_range_v1.yaml)展示了数据集级LLMJudge与用例级IsInstance: TimeRangeBuilderSuccess短形式混合书写的实际效果,并配套生成time_range_v1_schema.json

进阶:自定义序列化名称

当 Evaluator 类名很长时,可通过get_serialization_name()控制其在序列化文件中的名称:

from dataclasses import dataclass from pydantic_evals.evaluators import Evaluator, EvaluatorContext @dataclass class VeryLongDescriptiveEvaluatorName(Evaluator): @classmethod def get_serialization_name(cls) -> str: return 'ShortName' def evaluate(self, ctx: EvaluatorContext) -> bool: return True

在 YAML 中:

evaluators: - ShortName # Instead of VeryLongDescriptiveEvaluatorName

源码层面,BaseEvaluator.get_serialization<|begin▁of▁sentence|>#默认返回cls.__name__,子类覆写后即可改变注册表键名与文件中的写法;同时build_registry()支持类通过返回None主动退出序列化(此时会抛错提示)。

故障排查

1. IDE 中 Schema 未生效(无自动补全)

现象:YAML 文件没有自动补全。

排查步骤

  1. 检查 YAML 首行 Schema 路径是否正确:
    # yaml-language-server: $schema=correct_schema_name.json
  2. 确认同名 Schema 文件存在于同一目录(相对路径是相对于数据集文件目录解析的);
  3. 重启 IDE 的 YAML Language Server;
  4. 安装 YAML 扩展(VS Code 使用 Red Hat 出品的 "YAML" 扩展)。

2. 自定义 Evaluator 无法加载

现象ValueError: Unknown evaluator name: 'CustomEvaluator'

原因:反序列化注册表里没有这个名称。实际的报错信息是Evaluator 'xxx' is not in the provided custom_evaluator_types. Valid choices: [...](可在 tests/evals/test_dataset.py 的test_from_text_failure中看到完整错误样例)。

解决:加载时传入custom_evaluator_types

from dataclasses import dataclass from typing import Any from pydantic_evals import Case, Dataset from pydantic_evals.evaluators import Evaluator, EvaluatorContext @dataclass class CustomEvaluator(Evaluator): def evaluate(self, ctx: EvaluatorContext) -> bool: return True # First create and save with custom evaluator dataset = Datasetstr, str, Any])], ) dataset.to_file('tests.yaml', custom_evaluator_types=[CustomEvaluator]) # Load with custom evaluator types dataset = Dataset[str, str, Any].from_file( 'tests.yaml', custom_evaluator_types=[CustomEvaluator], # Required! )

3. 格式推断失败

现象ValueError: Cannot infer format from extension

原因_infer_fmt()只认.yaml/.yml/.json后缀(见 dataset.py),其它扩展名无法自动判断,且扩展名判断不区分大小写

解决:显式指定fmt

from typing import Any from pydantic_evals import Case, Dataset dataset = Datasetstr, str, Any]) # Explicit format for unusual extensions dataset.to_file('data.txt', fmt='yaml') dataset_loaded = Dataset[str, str, Any].from_file('data.txt', fmt='yaml')

4. Schema 生成失败

现象:自定义 Evaluator 导致model_json_schema_with_evaluators()报错。

原因:Schema 生成依赖build_schema_types()通过inspect.signature读取构造参数(见 pydantic_ai_slim/pydantic_ai/_spec.py),普通类没有 dataclass 元信息。

解决:确保 Evaluator 是规范的 dataclass:

from dataclasses import dataclass from pydantic_evals.evaluators import Evaluator, EvaluatorContext # ✅ Correct @dataclass class MyEvaluator(Evaluator): value: int def evaluate(self, ctx: EvaluatorContext) -> bool: return True # ❌ Wrong: Missing @dataclass class BadEvaluator(Evaluator): def __init__(self, value: int): self.value = value def evaluate(self, ctx: EvaluatorContext) -> bool: return True

下一步

  • Dataset Management —— 数据集的创建与组织
  • Custom Evaluators —— 编写自定义评估逻辑
  • Core Concepts —— 理解底层数据模型

【免费下载链接】pydantic-aiHow Python does AI. Agents, realtime voice, image generation, embeddings. Every model, every interface, typed end to end.项目地址: https://gitcode.com/GitHub_Trending/py/pydantic-ai

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/13 12:44:24

FlashMLA 注意力内核源码走读:656 字节 KV 缓存背后的完整链路

FlashMLA 注意力内核源码走读&#xff1a;656 字节 KV 缓存背后的完整链路 【免费下载链接】FlashMLA FlashMLA: Efficient Multi-head Latent Attention Kernels 项目地址: https://gitcode.com/GitHub_Trending/fl/FlashMLA FlashMLA 注意力内核库是 DeepSeek 面向多头…

作者头像 李华
网站建设 2026/9/13 12:43:42

Vivado HLS实战避坑指南:从环境配置到RTL生成

1. 这份“最全”不是噱头&#xff0c;而是按真实学习路径踩出来的资料地图Vivado HLS——这个缩写背后藏着多少人第一次打开时的茫然&#xff1f;不是代码写不出来&#xff0c;是根本不知道该从哪一行开始敲&#xff1b;不是不会仿真&#xff0c;是连仿真波形里哪个信号代表你写…

作者头像 李华
网站建设 2026/9/13 12:43:37

高拍仪集成与图像处理优化实践

1. 项目背景与核心价值高拍仪作为一种常见的文档采集设备&#xff0c;在办公自动化、档案数字化和教育信息化等领域有着广泛应用。但市面上的通用扫描软件往往无法满足专业场景下的定制化需求&#xff0c;比如特定行业的文档分类标准、批量处理的效率要求或特殊格式的输出规范。…

作者头像 李华
网站建设 2026/9/13 12:42:32

WSL中用OpenCode Web界面高效调试本地大模型

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/13 12:41:00

基于LSTM的日志异常检测:从日志解析到F1评估

简介&#xff1a;这套基于LSTM的日志异常检测系统资源包&#xff0c;适合计算机相关专业的学生用于课程设计、期末大作业&#xff0c;也适合需要完整项目练习的开发者参考&#xff0c;帮助理解并复现日志数据的异常检测流程。资源共115个文件&#xff0c;压缩包大小约82.22MB&a…

作者头像 李华