大语言模型应用的测试与质量保障,一直是开发者社区持续关注的难点。传统的测试方法在确定性系统中表现良好,但当面对大模型输出结果的不确定性时,许多技术团队往往陷入“测了不放心、不测更担忧”的两难局面。此前我们曾深入分析过 DeepEval 评估框架的解决方案,虽然功能全面,但较高的上手门槛让不少开发者望而却步。
近期在研读 Google ADK 框架的技术文档时,意外发现其内置的评估模块设计得相当人性化——配置流程简洁明了,几乎可以实现开箱即用。简单来说,即使是初学者也能快速上手编写 Agent 自动化评估脚本。接下来,我们将基于 ADK 框架,完整展示一套 Agent 自动化评估脚本的具体实现流程。
一、为什么需要为 Agent 构建自动化评估体系
在生成式大模型兴起之前,软件测试的逻辑非常直观:预期输出与实际结果精确匹配,这种确定性断言机制贯穿了整个测试流程。然而,当系统中引入 LLM 之后,传统测试思路的有效性开始显著下降。由于模型输出具有天然的随机性和多样性,我们无法再简单地用“通过/失败”来判定结果质量,而是需要引入更加灵活的评估指标体系,从输出内容质量、工具调用路径等多个维度进行综合衡量。
Google 在 ADK 官方文档中的一段论述精准地概括了这一问题:
In traditional software development, unit tests and integration tests provide confidence that code functions as expected and remains stable through changes. These tests provide a clear "pass/fail" signal, guiding further development. However, LLM agents introduce a level of variability that makes traditional testing approaches insufficient.Due to the probabilistic nature of models, deterministic "pass/fail" assertions are often unsuitable for evaluating agent performance. Instead, we need qualitative evaluations of both the final output and the agent's trajectory - the sequence of steps taken to reach the solution. This involves assessing the quality of the agent's decisions, its reasoning process, and the final result.
二、评估的核心维度是什么
一个 Agent 的完整执行流程,本质上可以拆解为两个关键环节:
- 工具的选择与调用链路
- 最终的输出结果
ADK 框架的评估设计也恰好聚焦于这两个方面:
- Evaluating Trajectory and Tool Use:评估工具调用路径的准确性以及执行顺序的合理性。
- Evaluating Final Response:评估最终输出是否准确命中用户的核心需求。
有一个设计细节值得特别关注:工具调用轨迹默认采用严格匹配机制(通过分数为 1.0,即要求 100% 正确);而最终输出的判定则允许一定范围内的偏差(默认通过分数为 0.8,即达到 80% 即可)。这种差异化的评分策略,实际上反映了业界对 Agent 评估的普遍共识——工具调用路径的正确性,往往比最终输出的文字完美度更为关键。
三、如何实施评估
在 ADK 框架中,评估脚本的开发主要包含两大任务:编写验证 JSON 文件与编写调用代码。
1. 编写验证 JSON 文件
这个验证文件相当于评估的“执行剧本”,采用 JSON 格式描述一次完整的交互过程,包含四个核心组成部分:
1)User Content:用户输入的原始问题。
"user_content": {
"parts": [
{
"text": "显示orders表结构"
}
],
"role": "user"
}
2)Expected Intermediate Tool Use Trajectory:预期的工具调用路径,包括选择了哪些工具以及调用的先后顺序。
"expected_tool_use": [
{"name": "transfer_to_agent", "args": {"agent_name": "table_info"}},
{"name": "get_table_info", "args": {"table_name": "orders"}}
],
"intermediate_data": {
"tool_uses": [
{
"name": "transfer_to_agent",
"args": {
"agent_name": "table_info"
}
},
{
"name": "get_table_info",
"args": {
"table_name": "orders"
}
}
],
"intermediate_responses": []
}
3)Expected Intermediate Agent Responses:各阶段 Agent(主 Agent 或子 Agent)的中间响应内容。
"intermediate_data": {
...
"intermediate_responses": [] # Any intermediate sub-agent responses.
}
4)Final Response:最终返回给用户的输出结果。
"final_response": {
"parts": [
{
"text": "## 表结构:ordersnn**基本信息:**n- 表名:ordersn- 记录数:900 行n- 列数:19 列nn**字段详情:**n| 字段名 | 数据类型 | 说明 |n|-------------------------|--------------------|--------------------------------|n| id | INTEGER | 订单唯一标识 |n| coupon_code | varchar(255) | 使用的优惠券代码 |n| create_time | timestamp | 订单创建时间 |n| logistics_company | varchar(255) | 物流公司名称 |n| note | varchar(255) | 订单备注 |n| pay_time | timestamp | 支付时间 |n| payment_method | varchar(255) | 支付方式 |n| payment_transaction_id | varchar(255) | 支付交易ID |n| product_id | bigint | 关联的产品ID |n| quantity | INTEGER | 商品数量 |n| receiver_address | varchar(255) | 收货地址 |n| receiver_name | varchar(255) | 收货人姓名 |n| receiver_phone | varchar(255) | 收货人电话 |n| specification | varchar(255) | 商品规格 |n| status | varchar(255) | 订单状态 |n| total_amount | numeric(38,2) | 订单总金额 |n| tracking_number | varchar(255) | 物流跟踪号 |n| update_time | timestamp | 订单更新时间 |n| user_id | bigint | 关联的用户ID |nn**设计特点:**n1. **主键**:`id` 字段作为订单的唯一标识。n2. **时间字段**:`create_time` 和 `update_time` 分别记录订单的创建和更新时间。n3. **关联字段**:`product_id` 和 `user_id` 可能与其他表(如 `products` 和 `users`)关联。n4. **状态管理**:`status` 字段用于跟踪订单状态。n5. **金额字段**:`total_amount` 使用 `numeric(38,2)` 类型,适合存储精确的金额数据。nn**建议后续操作:**n- 查看样本数据:使用 `sample_data_agent`。n- 执行查询:使用 `query_execution_agent`."
}
],
"role": "model"
}
以我们之前实现的 Agent 为例,当用户输入“显示 orders 表结构”时,系统内部依次触发了三个关键步骤:LLM 首先识别用户意图并调用 transfer_to_agent 将请求路由到对应的子 Agent;子 Agent 接着执行 get_table_info 获取表结构信息;最后生成自然语言回复返回给用户。整个执行链路如下图所示,这些信息都可以完整地写入验证 JSON 文件中。
完整的验证 JSON 文件 table_schema_analysis.test.json 内容如下:
{
"eval_set_id": "table-schema-analysis-sqlite",
"name": "Table Schema Analysis SQLite",
"description": "测试表结构探索和schema分析能力",
"eval_cases": [
{
"eval_id": "table-schema-orders-structure",
"query": "显示orders表结构",
"expected_tool_use": [
{"name": "transfer_to_agent", "args": {"agent_name": "table_info"}},
{"name": "get_table_info", "args": {"table_name": "orders"}}
],
"conversation": [
{
"invocation_id": "inv-schema-1",
"user_content": {
"parts": [
{
"text": "显示orders表结构"
}
],
"role": "user"
},
"final_response": {
"parts": [
{
"text": "## 表结构:ordersnn**基本信息:**n- 表名:ordersn- 记录数:900 行n- 列数:19 列nn**字段详情:**n| 字段名 | 数据类型 | 说明 |n|-------------------------|--------------------|--------------------------------|n| id | INTEGER | 订单唯一标识 |n| coupon_code | varchar(255) | 使用的优惠券代码 |n| create_time | timestamp | 订单创建时间 |n| logistics_company | varchar(255) | 物流公司名称 |n| note | varchar(255) | 订单备注 |n| pay_time | timestamp | 支付时间 |n| payment_method | varchar(255) | 支付方式 |n| payment_transaction_id | varchar(255) | 支付交易ID |n| product_id | bigint | 关联的产品ID |n| quantity | INTEGER | 商品数量 |n| receiver_address | varchar(255) | 收货地址 |n| receiver_name | varchar(255) | 收货人姓名 |n| receiver_phone | varchar(255) | 收货人电话 |n| specification | varchar(255) | 商品规格 |n| status | varchar(255) | 订单状态 |n| total_amount | numeric(38,2) | 订单总金额 |n| tracking_number | varchar(255) | 物流跟踪号 |n| update_time | timestamp | 订单更新时间 |n| user_id | bigint | 关联的用户ID |nn**设计特点:**n1. **主键**:`id` 字段作为订单的唯一标识。n2. **时间字段**:`create_time` 和 `update_time` 分别记录订单的创建和更新时间。n3. **关联字段**:`product_id` 和 `user_id` 可能与其他表(如 `products` 和 `users`)关联。n4. **状态管理**:`status` 字段用于跟踪订单状态。n5. **金额字段**:`total_amount` 使用 `numeric(38,2)` 类型,适合存储精确的金额数据。nn**建议后续操作:**n- 查看样本数据:使用 `sample_data_agent`。n- 执行查询:使用 `query_execution_agent`."
}
],
"role": "model"
},
"intermediate_data": {
"tool_uses": [
{
"name": "transfer_to_agent",
"args": {
"agent_name": "table_info"
}
},
{
"name": "get_table_info",
"args": {
"table_name": "orders"
}
}
],
"intermediate_responses": []
}
}
]
}
]
}
2. 编写调用代码
评估脚本本身极为精简,核心代码仅需 2 行即可完成:
@pytest.mark.asyncio
async def test_table_schema_analysis():
"""Test table structure exploration and schema analysis capabilities."""
await AgentEvaluator.evaluate(
"sqlite_agent",
str(pathlib.Path(__file__).parent / "data/table_schema_analysis.test.json"),
num_runs=1
)
第 6 行用于指定验证 JSON 文件的路径,第 7 行用于设定评估运行的次数。代码编写工作到这里就结束了,整个过程简洁到让人有些意外。
四、如何运行评估
ADK 框架提供了三种灵活的运行方式,开发者可以根据实际场景自由选择:
方式一:使用 ADK 内置命令行工具,直接在终端中执行,干脆利落:
adk eval sqlite_agent eval/data/data_analysis.test.json
方式二:通过 pytest 集成运行,非常适合纳入 CI/CD 持续集成流程:
pytest eval/test_eval.py::test_table_schema_analysis
方式三:使用 Python 脚本直接调用:
python -m pytest eval/test_eval.py::test_table_schema_analysis -v
五、如何快速编写验证 JSON 文件
看到 JSON 文件中包含那么多字段,可能有人会担心需要手动抓包、手动拼装,开发成本太高。事实上,ADK 提供了非常友好的辅助工具,操作步骤也非常清晰:
第一步:启动 ADK 的 Web UI
adk web
在浏览器中访问 http://127.0.0.1:8000/dev-ui/
第二步:输入问题进行一次完整的对话交互,利用 Web UI 的输入框模拟用户的真实查询场景。
第三步:查看交互日志并导出数据。在 Event 面板中可以清晰查看每一轮请求与响应的详细内容,点击“下载”按钮即可将整个对话保存为 JSON 文件。开发者既可以从中手动提取关键字段,也可以借助 AI 辅助编码工具自动生成验证脚本模板。
第四步:直接使用 Web UI 自带的 Eval 在线评估功能。Web UI 甚至内置了在线评估模块,在 Eval 菜单中新建一个评估任务,选定某次对话 session,即可对 Agent 的行为表现进行快速验证与评分。
六、总结与展望
与 DeepEval 相比,ADK 在 Agent 自动化评估方面提供了更贴近 LLM 运行机制的原生支持。验证 JSON 的结构设计紧密贴合了 Agent 执行的真实流程,从文件编写、脚本运行到结果调试,整个流程始终围绕“降低开发者心智负担”这一核心理念来构建。对于正在搭建或优化大模型应用测试体系的团队而言,ADK 无疑是一个值得认真评估的技术选型方向。
