Pydantic Evals 第三方评测框架集成:把 Ragas 与 DeepEval 指标封装为自定义 Evaluator
2026/9/13 19:44:28 网站建设 项目流程

Pydantic Evals 第三方评测框架集成:把 Ragas 与 DeepEval 指标封装为自定义 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-ai 仓库的 第三方集成文档 与 Pydantic Evals 评测器源码,讲解如何在不引入硬依赖的前提下,把 Ragas、DeepEval 等上游评测框架的具体指标封装为 Pydantic Evals 的自定义Evaluator,并复用数据集、报告与结果管线。读完本文,你可以自行实现与上游框架"逐分对齐"的评测适配器,并理解框架底层如何执行、校验和记录这些评测结果。

为什么需要第三方框架集成

Pydantic Evals 不对任何特定指标框架做硬依赖。当团队已经在用 Ragas、DeepEval 或其他评分库时,Evaluator基类提供了一个轻量入口:把上游指标包一层适配器,就能在任何 Pydantic Evals 数据集里运行。这一点在 pyproject.toml 中可以直接验证:pydantic-evals的动态依赖只有richlogfire-apipydanticpydantic-ai-slimanyiopyyamlragasdeepeval既不在核心依赖里,也不在任何可选依赖组中(可选组仅logfire)。

但原文档给出了一条重要决策建议,写适配器之前应先确认:

优先使用原生评测器。如果基于评分细则(rubric)的LLMJudge(见标准质量指标页面提供的现成 rubric)或自定义评测器已能覆盖你的场景,那通常更简单——零额外依赖,分数还能干净地落入报告。只有当你确实需要上游实现的精确行为时才使用下述集成:与已发表基准可复现、与现有评测套件保持口径一致、或上游框架有原生侧未暴露的能力。同一数据集内可以混合外部评测器和原生评测器。

集成模式:三步封装上游指标

每个框架集成遵循同一模式:

  1. 继承Evaluator基类;
  2. ctx.inputsctx.outputctx.expected_output和元数据适配成上游指标所需的输入结构;
  3. 返回float分数、bool断言、EvaluationReason,或这些类型的dict

官方给出的适配器示例刻意保持紧凑——你可以按需扩展模型选择、阈值、按 case 开关等团队所需的任何配置。

Evaluator 基类与返回值类型

封装的契约来自 evaluator.py 中的Evaluator

  • 子类必须实现evaluate,可以用def(同步)也可以用async def(异步),框架自动兼容两种形式;
  • 允许的返回类型由EvaluatorOutput定义(evaluator.py#L49):EvaluationScalar | EvaluationReason | Mapping[str, ...]
  • EvaluationScalar = bool | int | Annotated[float, Field(allow_inf_nan=False)] | str(evaluator.py#L27):int和有限float视为分数,str视为标签,bool视为断言。注意float被显式约束为有限值——返回NaN±inf会在执行期触发EvaluatorFailure而不是静默记录;
  • EvaluationReason(evaluator.py#L34-L46)携带标量value和可选的reason解释,是封装上游指标最推荐的返回形式,因为上游框架的打分理由可以被保留到报告中。

另外,Evaluator继承的BaseEvaluator(_base.py)带有一个严格的元类_StrictABCMeta:如果你的子类没有实现evaluate,会在类定义时就抛TypeError,而不是等到实例化才报错。BaseEvaluator.as_spec()还会把评测器序列化为EvaluatorSpec(spec.py),默认值字段会被自动剔除,这让你的适配器能像内置评测器一样被写入 YAML/JSON 数据集定义。

EvaluatorContext:适配器能拿到的数据

适配层的全部输入来自 EvaluatorContext,字段包括:

  • ctx.name:case 名称;
  • ctx.inputs/ctx.output:任务输入与实际输出;
  • ctx.metadata:case 元数据(可为None,适配器里通常要先判空);
  • ctx.expected_output:期望输出(可为None);
  • ctx.durationctx.metricsctx.attributesctx.span_tree:执行耗时、自定义指标/属性、OpenTelemetry span 树等,可用于行为类检查,但对纯文本评分指标一般用不到。

Ragas:封装 Faithfulness 指标

ragas需单独安装(pip install ragas,不包含在pydantic-evals中)。下面的适配器把ragas.metrics.Faithfulness包装成单轮(single-turn)评分。每个 case 需要在 inputs 或 metadata 中提供检索到的上下文:

from dataclasses import dataclass from ragas.dataset_schema import SingleTurnSample from ragas.metrics import Faithfulness from pydantic_evals.evaluators import EvaluationReason, Evaluator, EvaluatorContext @dataclass class RagasFaithfulness(Evaluator): """Wrap `ragas.metrics.Faithfulness` as a Pydantic Evals evaluator.""" context_field: str = 'context' async def evaluate(self, ctx: EvaluatorContext) -> EvaluationReason: metadata = ctx.metadata or {} retrieved_contexts = metadata.get(self.context_field, []) if isinstance(retrieved_contexts, str): retrieved_contexts = [retrieved_contexts] sample = SingleTurnSample( user_input=str(ctx.inputs), response=str(ctx.output), retrieved_contexts=retrieved_contexts, ) metric = Faithfulness() score = await metric.single_turn_ascore(sample) return EvaluationReason(value=float(score), reason=f'ragas.Faithfulness = {score:.3f}')

设计要点:

  • context_field是 dataclass 字段(默认'context'),让上下文的存放键名可配置;
  • ctx.metadata or {}防御metadataNone的情况(EvaluatorContext.metadata的类型注解允许None);
  • 兼容上下文是单个字符串的写法,统一转成列表;
  • 因为 Ragas 提供异步评分入口single_turn_ascoreevaluateasync def实现,避免阻塞事件循环;
  • 返回EvaluationReason并把分数写进reason,让报告里既有序数分也有解释文本。

使用方式与内置评测器完全一致:

from pydantic_evals import Case, Dataset dataset = Dataset( name='rag_eval', cases=[ Case( inputs='What is the capital of France?', metadata={'context': ['Paris is the capital of France.']}, ), ], evaluators=[RagasFaithfulness()], )

同一模式适用于ragas.metrics.answer_relevancycontext_precision等其他评分指标:换成对应的 metric 类,并按需要替换SingleTurnSample的字段即可。

DeepEval:封装 GEval 指标

deepeval同样需单独安装(pip install deepeval,不包含在pydantic-evals中)。这个适配器把 DeepEval 的GEval指标封装为按准则(criteria)对LLMTestCase打分。由于 DeepEval 的measure是同步的,评测器也用同步实现:

from dataclasses import dataclass from deepeval.metrics import GEval from deepeval.test_case import LLMTestCase, LLMTestCaseParams from pydantic_evals.evaluators import EvaluationReason, Evaluator, EvaluatorContext @dataclass class DeepEvalGEval(Evaluator): """Wrap `deepeval.metrics.GEval` as a Pydantic Evals evaluator.""" metric_name: str criteria: str threshold: float = 0.5 def evaluate(self, ctx: EvaluatorContext) -> dict[str, float | bool | EvaluationReason]: test_case = LLMTestCase( input=str(ctx.inputs), actual_output=str(ctx.output), expected_output=None if ctx.expected_output is None else str(ctx.expected_output), ) metric = GEval( name=self.metric_name, criteria=self.criteria, evaluation_params=[LLMTestCaseParams.INPUT, LLMTestCaseParams.ACTUAL_OUTPUT], threshold=self.threshold, ) metric.measure(test_case) return { f'{self.metric_name}_score': EvaluationReason(value=float(metric.score), reason=metric.reason or ''), f'{self.metric_name}_pass': bool(metric.success), }

设计要点:

  • metric_name同时作为 DeepEval 指标名和输出前缀,一个适配器实例产出一对结果:{name}_score(带理由的分数)和{name}_pass(是否过阈值);
  • threshold(默认0.5)对应 DeepEval 的通过线,metric.success被映射为布尔断言列;
  • expected_output is None时显式传NoneLLMTestCase,避免把 Python 的None变成字符串"None"

同一个包装器可以复用给 DeepEval 的FaithfulnessMetricAnswerRelevancyMetricHallucinationMetric等:换成对应的 metric 类,并填充相关的LLMTestCase字段(例如 faithfulness 需要retrieval_context)。

框架如何执行你的适配器:源码视角

理解执行管线能帮你确认适配器"够用"的边界。run_evaluator 是入口:

  1. 调用evaluator.evaluate_async(ctx)——同步实现会原样返回结果,异步实现被await(见 Evaluator.evaluate_async),所以 Ragas 的async def和 DeepEval 的def写法各自天然合适;
  2. 传入可选的RetryConfig时,会用 tenacity 重试包裹evaluate_async,网络类指标(两个框架都要自调 LLM)可因此获得重试能力;
  3. 原始返回值经过 PydanticTypeAdapterrevalidate_instances='always'重新校验(_run_evaluator.py#L110-L114),返回非法类型会转为ValueError
  4. 标量结果会被包进映射,键名取get_default_evaluation_name()(默认即类名,如RagasFaithfulness);返回dict时则以 dict 的键作为各结果名(如DeepEvalGEval{name}_score/{name}_pass);
  5. 任何异常都被捕获为EvaluatorFailure(含error_messageerror_stacktraceerror_type),记为该 case 的评测器失败,而不是让整个数据集跑挂;
  6. 每个评测器调用都被包在名为evaluator: {evaluator_name}的 logfire span 里,方便在 Logfire 中按评测器查询。

此外,Evaluator还提供get_evaluator_version()(默认返回None):当你的适配器行为变化(换了上游指标版本、改了阈值语义)时,可以覆写它打一个版本标签(如'v2'),在线评测看板可以据此过滤掉旧版本的结果而无需删除历史数据。

依赖与运行成本注意事项

原文档的"依赖说明"部分要点如下,可结合仓库证据确认:

  • ragasdeepeval都是可选依赖——不随pydantic-evals安装,也不属于任何依赖组。只在真正使用这些集成的项目中安装,避免污染所有评测环境的依赖树(pyproject.toml 中optional-dependencies仅有logfire);
  • 这两个库会发起自己的 LLM 调用,与你的数据集任务执行相互独立。运行包含这些评测器的数据集时,要预留出额外的 API 用量和时长预算;
  • 适配器本身是普通 dataclass,@dataclass装饰器是必需的——_StrictABCMeta要求在类定义时完成evaluate的实现检查。

选型速查

需求建议
评分细则能覆盖、零额外依赖原生LLMJudgerubric 或GEval(标准质量指标)
需要与上游框架精确对齐(可复现、口径一致)本文的 Ragas / DeepEval 适配器模式
领域特化逻辑、外部 API 校验等自定义评测器

同一数据集可以混合以上评测器:原生评测器负责常规模块,第三方适配器负责必须逐分对齐的指标,结果统一进入同一份报告与同一套失败/版本标记体系。

【免费下载链接】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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询