adk-python 评估数据文件解析:`.test.json` 与 `.evalset.json` 的同源异用
2026/9/13 11:19:27 网站建设 项目流程

adk-python 评估数据文件解析:.test.json.evalset.json的同源异用

【免费下载链接】adk-pythonAn open-source, code-first Python toolkit for building, evaluating, and deploying sophisticated AI agents with flexibility and control.项目地址: https://gitcode.com/GitHub_Trending/ad/adk-python

导读

在 Google ADK(Agent Development Kit)的 Python 实现(adk-python)中,.test.json.evalset.json其实承载的是同一套EvalSetPydantic 数据模型,二者由同一个加载函数按 Schema 校验、由同一条adk eval命令驱动。本文以仓库中 test_file_vs_evalset 示例为骨架,结合src/google/adk/evaluation下的源码实现,讲清两种文件后缀的命名约定、文件结构、命令行运行方式、评估指标配置(IN_ORDER轨迹匹配与 ROUGE-1 文本匹配),以及如何让同一份数据同时被pytest复用。读完本文,你将能够为任意 Agent 编写、配置并运行单元级与集成级的离线评估。

同一个 EvalSet:扩展名只是命名约定

一切始于EvalSetSchema

无论是.test.json还是.evalset.json,文件内容都会被解析为同一个EvalSet模型。该模型定义在 src/google/adk/evaluation/eval_set.py,核心字段包括:

字段类型含义
eval_set_idstr(必填)评估集的唯一标识
nameOptional[str]数据集名称
descriptionOptional[str]数据集描述
eval_caseslist[EvalCase]评估用例列表,每个用例代表一次待评估的交互
creation_timestampfloat(默认0.0数据集创建时间

EvalCase模型位于 src/google/adk/evaluation/eval_case.py,每个用例包含eval_idconversation(静态多轮对话)或conversation_scenario(交给 UserSimulator 动态生成,二者二选一)、session_input(会话初始化输入,如app_nameuser_id、初始state)等字段。

conversation中的每一轮是一个Invocation(见 eval_case.py),由以下部分组成:

  • invocation_id:本轮调用的唯一标识;
  • user_content:用户输入内容;
  • final_response:期望的 Agent 最终回复(ground truth);
  • intermediate_data:期望的中间过程数据,其中tool_uses记录了按时间顺序排列的期望工具调用轨迹(工具名 + 参数)。

load_eval_set_from_file:按 Schema 校验,而非按扩展名

adk eval之所以能用同一条命令加载两种后缀的文件,关键在于加载函数不关心文件扩展名。在 src/google/adk/evaluation/local_eval_sets_manager.py 中:

def load_eval_set_from_file(eval_set_file_path: str, eval_set_id: str) -> EvalSet: with open(eval_set_file_path, "r", encoding="utf-8") as f: content = f.read() try: return EvalSet.model_validate_json(content) except ValidationError: # 若新 Schema 校验失败,则假设数据为旧格式并尝试转换 return convert_eval_set_to_pydantic_schema(eval_set_id, json.loads(content))

函数首先用EvalSet.model_validate_json直接按 Pydantic Schema 校验 JSON;只有当新格式校验失败时,才会回退到旧版 eval 数据格式的转换逻辑(convert_eval_set_to_pydantic_schema,同样位于该文件中,负责把query/reference/expected_tool_use等旧字段映射到新的Invocation结构)。因此,扩展名对加载结果没有任何影响——foo.test.jsonfoo.evalset.json甚至foo.json都能被正常加载,只要内容满足 Schema。

两种后缀的定位:单元测试 vs 集成测试

既然 Schema 相同,两种扩展名便只是一种命名约定,用于表达数据的规模与意图:

  • .test.json—— 单元测试约定:包含一个简单、聚焦的 session,保持小而精,对应单个单元测试;
  • .evalset.json—— 集成测试约定:将多个更长、更多轮次的 session 组织在一起,对应集成测试。

这种约定的价值在于团队协作与 CI 语义:扫描代码库时,.test.json可以让开发者一眼识别出轻量级回归用例,.evalset.json则标识出覆盖完整业务链路的场景集。

样例资产剖析:一个 Agent,两份评估数据

本示例针对共享的智能家居 Agent(home_automation_agent)各提供一份数据文件。该 Agent 由内存字典模拟设备状态,所有工具(get_device_infoset_device_infoget_temperatureset_temperaturelist_devices)都是确定性的,保证评估轨迹可复现;模块内的reset_data()会被adk eval在每个 eval case 之间调用,重置状态,避免用例间相互污染。

single_turn.test.json:单轮单元测试

文件位于 contributing/samples/evaluation/test_file_vs_evalset/single_turn.test.json,仅包含 1 个 session、1 轮对话:

  • 用户提问:What's the temperature in the Kitchen?
  • 期望最终回复:The temperature in the Kitchen is 24 degrees Celsius.
  • 期望工具调用:get_temperature,参数{"location": "Kitchen"}

对应eval_idkitchen_temperaturesession_inputapp_namehome_automation_agentuser_iduserstate为空。这就是典型的"一个断言点"的单元级用例:验证 Agent 收到特定问题后会调用正确的工具并给出正确的回复。

multi_session.evalset.json:多会话集成测试

文件位于 contributing/samples/evaluation/test_file_vs_evalset/multi_session.evalset.json,包含 2 个 session:

  1. list_then_turn_off(两轮)

    • 第 1 轮:Which devices are on?→ 期望调用list_devices,参数{"status": "ON"},期望回复列出开启的设备;
    • 第 2 轮:Turn that one off.→ 期望调用set_device_info,参数{"device_id": "device_1", "status": "OFF"},期望回复确认已关闭。
  2. set_bedroom_temperature(单轮)Set the Bedroom to 21 degrees.→ 期望调用set_temperature,参数{"location": "Bedroom", "temperature": 21}

这个文件示范了集成测试的两个关键特征:多会话(两个独立场景)与多轮上下文依赖(第二轮"Turn that one off."依赖第一轮返回的设备信息,验证 Agent 的上下文记忆与指代消解能力)。

运行评估:同一条adk eval命令

命令与参数

两种文件使用完全相同的adk eval命令,唯一变化的是评估数据路径。在仓库根目录下执行:

运行.test.json

adk eval contributing/samples/evaluation/home_automation_agent \ contributing/samples/evaluation/test_file_vs_evalset/single_turn.test.json \ --config_file_path contributing/samples/evaluation/test_file_vs_evalset/eval_config.json \ --print_detailed_results

运行.evalset.json

adk eval contributing/samples/evaluation/home_automation_agent \ contributing/samples/evaluation/test_file_vs_evalset/multi_session.evalset.json \ --config_file_path contributing/samples/evaluation/test_file_vs_evalset/eval_config.json \ --print_detailed_results

两个位置参数分别为:

  • agent_module_file_path:Agent 模块所在目录(必须是包含agent.py的目录,且模块中定义root_agentget_agent_async);
  • eval_set_file_path_or_id:可接受一个或多个评估数据文件路径,或已注册的 eval set id(文件路径与 id 不可混用)。

--print_detailed_results会在控制台打印Actual-vs-Expected 对照表,逐轮对比 Agent 真实的工具调用与回复和文件中的期望值。从 src/google/adk/cli/cli_eval.py 的pretty_print_eval_result实现可以看到,该表格以 DataFrame 形式输出,每行包含promptexpected_responseactual_responseexpected_tool_callsactual_tool_calls,并为每个指标附加StatusScore列。

命令的底层调用链

从 src/google/adk/cli/cli_tools_click.py 可以看到cli_eval的完整流程:

  1. 解析参数后,parse_and_get_evals_to_run将位置参数解析为{文件路径或 eval set id: [要运行的 eval_id 列表]}的映射;
  2. 若第一个参数是已存在的文件,则切换到InMemoryEvalSetsManager,对每个文件调用load_eval_set_from_file加载为EvalSet,再逐个把eval_cases灌入内存管理器;
  3. 通过get_evaluation_criteria_or_default读取--config_file_path指定的评估配置(见下文);
  4. 构建InferenceRequest交给LocalEvalService执行推理与指标计算。

顺带一提,如果省略--config_file_path且只传入单个eval 文件,CLI 会自动回退到test_config.json作为配置(见 cli_tools_click.py 的_resolve_eval_config_file_path)——这与AgentEvaluator的约定保持一致。

只运行部分用例

如果只想运行某个文件中的特定 eval case,可以在文件路径后追加:和逗号分隔的 eval id,例如:

adk eval contributing/samples/evaluation/home_automation_agent \ contributing/samples/evaluation/test_file_vs_evalset/multi_session.evalset.json:list_then_turn_off \ --config_file_path contributing/samples/evaluation/test_file_vs_evalset/eval_config.json \ --print_detailed_results

评估配置深入:eval_config.json

示例的评估配置位于 contributing/samples/evaluation/test_file_vs_evalset/eval_config.json:

{ "criteria": { "tool_trajectory_avg_score": {"threshold": 1.0, "match_type": "IN_ORDER"}, "response_match_score": 0.6 } }

配置的加载与解析在 src/google/adk/evaluation/eval_config.py 中完成:若指定路径存在则以EvalConfig.model_validate_json解析;否则回退到内置默认配置——tool_trajectory_avg_score: 1.0response_match_score: 0.8(见 eval_config.py)。也就是说,省略--config_file_path时评估仍可运行,只是文本匹配阈值会更严格(0.8)

tool_trajectory_avg_score:工具轨迹匹配

该指标比较 Agent 实际工具调用轨迹与文件中期望的工具调用轨迹(工具名 + 参数)。MatchType枚举定义在 src/google/adk/evaluation/eval_metrics.py,共有三种取值:

  • EXACT(默认):要求实际工具调用与期望完全一致;
  • IN_ORDER:期望的工具调用必须按给定顺序出现,允许中间穿插额外工具调用
  • ANY_ORDER:期望的工具调用只需全部出现,不关心顺序(适合"发起多次同类搜索"等场景)。

示例配置选择了IN_ORDER:期望调用必须按序出现,但实际执行中插入的额外调用会被容忍。例如期望[T1, T2, T3],实际为[T1, T1.1, T2, T2.1, T2.2, T3, T3.1]即满足条件;而一旦某个期望调用缺失(如期望 4 个只出现 3 个),则该指标失败。threshold设为1.0,意味着每条期望调用(名称 + 参数)都必须命中一次真实调用。

此外,ToolTrajectoryCriterion还提供ignore_args字段(默认False):设为True时只比较工具名、忽略参数(见 eval_metrics.py),适用于参数值本身不重要、只关心是否调用了正确工具的断言。

response_match_score:ROUGE-1 文本匹配

response_match_score使用ROUGE-1 unigram 重叠分数来比较 Agent 的真实回复与期望回复。之所以使用模糊匹配而非精确匹配,是为了容忍真实推理(live inference)带来的措辞变化——只要关键词重叠度足够,语义一致的回复即可通过。

实现位于 src/google/adk/evaluation/final_response_match_v1.py 的RougeEvaluator,判分规则为score >= thresholdPASSED(见 final_response_match_v1.py)。值得注意的是,该项目使用自定义的_UnicodeAwareTokenizer(见 final_response_match_v1.py):默认的rouge_score分词器会丢弃非[a-z0-9]字符,导致中文、泰文等非拉丁文字文本永远得 0 分;该分词器通过 NFKC 归一化、按字符切分 CJK、按字素簇聚合泰文等逻辑,使 ROUGE-1 在非拉丁语言上依然有效。

示例将response_match_score阈值设为0.6:期望回复与真实回复的 ROUGE-1 分数达到 0.6 即视为通过。

pytest复用:AgentEvaluator.evaluate自动发现.test.json

除了adk evalCLI,同一份.test.json文件还可以直接由 Python 测试驱动。AgentEvaluator定义在 src/google/adk/evaluation/agent_evaluator.py,其静态方法evaluate有以下行为:

  • 传入目录时,会递归扫描该目录下所有以.test.json结尾的文件并逐一评估(见 agent_evaluator.py);
  • 传入单个文件时,直接评估该文件;
  • 配置文件的自动查找遵循约定:在 test 文件同目录下寻找test_config.jsonfind_config_for_test_file,见 agent_evaluator.py);
  • 评估结束时,若有失败会抛出断言(assert not failures),天然适配 pytest 的失败语义。

因此,一个典型的 pytest 测试可以这样写(示例本身只用adk eval,此用法来自源码契约):

from google.adk.evaluation.agent_evaluator import AgentEvaluator def test_home_automation_agent(): AgentEvaluator.evaluate( agent_module="contributing/samples/evaluation/home_automation_agent", eval_dataset_file_path_or_dir="contributing/samples/evaluation/test_file_vs_evalset/single_turn.test.json", print_detailed_results=True, )

这种设计意味着你只需维护一份评估数据,既能在 CI 中通过adk eval批量跑,也能嵌入 pytest 做针对性回归。

实践建议:如何选择两种约定

  • 首选.test.json:当验证目标是"给定输入,Agent 是否调用正确工具、给出正确回复"这样的单一、原子化行为时。文件保持小而聚焦,配合AgentEvaluator.evaluate的目录扫描能力,可以按目录组织成一组"单元测试套件"。
  • 首选.evalset.json:当验证目标是覆盖完整业务链路(如多轮指代消解、跨轮状态保持、多场景组合)时。把多个相关场景聚合到一个文件中,运行一次命令即可获得整体通过率。
  • 评估配置始终独立:无论数据文件用哪种后缀,评估标准(轨迹匹配方式、文本匹配阈值)都由eval_config.json(或默认配置)统一控制,做到"数据与断言分离"。

总结

.test.json.evalset.json是 adk-python 评估体系中一对"形异而神同"的文件约定:底层共享同一套EvalSet/EvalCasePydantic Schema,由load_eval_set_from_file按 Schema(而非扩展名)加载,由同一条adk eval命令执行,并可被AgentEvaluator.evaluate无缝接入 pytest。区别仅在于数据规模与语义定位——前者是单元测试,后者是集成测试。理解这一约定,再配合IN_ORDER/ANY_ORDER轨迹匹配与 ROUGE-1 文本匹配的灵活配置,你就能为 Agent 构建一套既能快速回归、又能深度验证的离线评估体系。

【免费下载链接】adk-pythonAn open-source, code-first Python toolkit for building, evaluating, and deploying sophisticated AI agents with flexibility and control.项目地址: https://gitcode.com/GitHub_Trending/ad/adk-python

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

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

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

立即咨询