第6章 工具增强型Agent的评测体系
本章你将学到:
- 为什么"最终输出正确"不等于"过程正确"
- 在评测体系中新增工具调用审计维度
- 构建包含工具调用链的黄金标准测试集
- 用Trae生成扩展后的评测Agent和批量评测脚本
- 跑通完整评测流程,基于数据做一次迭代优化
本章你将产出 :一套能同时评估"输出质量"和"工具使用质量"的评测系统,以及一份包含工具调用审计的评测报告
全部章节 :收录在专栏《AI应用工程化实战教程》之【智能体工具使用实战】
6.1 评测的新挑战
在第一部【AI智能体工程化实战】中,评测的逻辑很清晰:Agent输出一个JSON,你把它的 valid 字段和黄金标准的 gt_label 做对比。一致就对了,不一致就错了。评测Agent只看两样东西------业务Agent的输出和GT------然后给出 match 或 不match。
现在情况变了。
你的数据分析助手Agent在一次任务中可能会经历这样的过程:
用户提问 → Agent调用 read_file → Agent调用 execute_python → Agent调用 write_file → Agent输出总结
最终它生成了一份 analysis_report.md,也输出了总结文字。你打开报告一看------数据正确,格式清晰,结论合理。看起来不错。
但你有想过这些问题吗:
- 它有没有在应该用
execute_python的时候,选择了自己"心算"? - 它有没有把
read_file的参数写错,导致读了错误的文件,只是恰好那个文件也有类似数据? - 它有没有调了三次工具但其实一次就够了(浪费API调用)?
- 它有没有在某个工具调用失败后,假装什么都没发生,继续编造结果?
最终输出正确,不代表过程正确。 如果Agent第一次就读错了文件但恰好数据相似,如果它跳过了计算步骤直接编造了数字,你只看最终输出是发现不了的。
这就是工具增强型Agent评测面临的新挑战:你不仅要评"说了什么",还要评"做了什么"。
6.2 工具调用审计维度
参照第一部L1/L2/L3三层指标体系的设计思路,我们在第二部的评测体系中新增一个工具调用审计模块。它在L2业务指标层面增加了一个全新的评估维度。
6.2.1 新增错误类型代码
在评测Agent的判定逻辑中,原有的错误类型(FP、FN、RE)仍然有效------它们评的是最终输出。但我们需要新增一组专门针对工具调用的错误代码:
| 错误代码 | 全称 | 含义 | 示例 |
|---|---|---|---|
| TW | Tool Wrong | 工具选择错误------应该用A工具却用了B,或者应该用工具却直接回答 | 需要计算平均值,Agent没有调 execute_python,而是自己"估算"了一个数字 |
| PA | Parameter Error | 参数错误------工具选对了,但参数传错了 | 调用了 read_file,但 path 写成了 "score.csv" 而不是 "scores.csv" |
| EX | Execution Error | 工具执行异常------工具选对了,参数也对了,但执行失败,Agent没有正确处理 | 沙盒返回了超时错误,Agent却假装成功,继续编造后续结果 |
| TC | Tool Call Missing | 缺少必要的工具调用------GT预期应该调用的工具,Agent没有调用 | GT预期需要调 execute_python 进行计算,但Agent跳过了这一步 |
这四个代码和原有的OK、FP、FN、RE一起,构成了扩展后的错误分类体系。
6.2.2 审计的逻辑
工具调用审计的核心逻辑是:把Agent实际调用的工具序列,和GT中标注的"预期工具调用序列"做比对。
比对什么?
- 工具名称:Agent调用的工具是否在预期列表中?
- 调用顺序:工具的调用顺序是否合理?(这一步不强制完全一致,但如果顺序明显错误------比如先写文件再读文件------应该标记)
- 关键参数:对于决定性参数(如文件路径),是否与GT一致?
- 缺失调用:GT预期必须发生的工具调用,Agent是否跳过了?
- 多余调用:Agent是否调用了不需要的工具(浪费)?
一个完整的工具调用审计结果包含:
json
{
"tool_audit": {
"overall": "OK" / "TW" / "PA" / "EX" / "TC",
"expected_tools": ["read_file", "execute_python"],
"actual_tools": ["read_file", "execute_python", "write_file"],
"match_details": [
{"step": 1, "expected": "read_file", "actual": "read_file", "match": true, "param_match": true},
{"step": 2, "expected": "execute_python", "actual": "execute_python", "match": true, "param_match": true}
],
"issues": []
}
}
6.3 构建含工具调用链的GT测试集
6.3.1 扩展GT结构
第一部的GT是一条简单的标签:"有效" 或 "无效"。
第二部的GT需要扩展为包含两部分:
- final_output:预期的最终输出(与第一部相同,用于评估输出质量)
- expected_tool_calls:预期的工具调用序列,每个调用包含工具名、关键参数、是否必须
一个完整的GT条目结构如下:
json
{
"test_id": "T001",
"user_request": "请读取 scores.csv,计算高数平均分,保存报告为 高数分析.md",
"setup": {
"files": {"scores.csv": "已存在于项目根目录"}
},
"expected_tool_calls": [
{
"tool_name": "read_file",
"params": {"path": "scores.csv"},
"required": true,
"note": "读取成绩数据"
},
{
"tool_name": "execute_python",
"params_check": "code 参数中应包含 df['高数'].mean() 或类似的计算逻辑",
"required": true,
"note": "计算高数平均分"
},
{
"tool_name": "write_file",
"params": {"path": "高数分析.md"},
"required": true,
"note": "保存分析报告"
}
],
"expected_final_output": {
"should_contain": ["高数平均分", "高数分析.md"],
"should_not_contain": ["无法读取", "没有数据"],
"format": "自然语言总结"
}
}
6.3.2 用Trae生成测试集
在Trae对话面板中输入:
在项目 data-analyst-agent 中创建 data/ 目录,然后生成一份测试数据集 data/test_cases.json。
## 要求
1. 包含8-10个测试用例,覆盖以下场景:
- 正常场景:读数据→计算→写报告(2-3个)
- 需要多步计算的场景:先求平均,再找最高最低,再排名(2个)
- 文件不存在场景:Agent应该优雅处理错误(1-2个)
- 需要多文件读取的场景:读两个CSV,做对比分析(1个)
- 不需要计算只读文件的场景:只需要描述数据(1个)
- 边界场景:空文件、编码错误的文件(1-2个)
2. 每个测试用例包含:
- test_id: "T001" 到 "T010"
- description: 测试场景描述
- user_request: 模拟用户请求
- setup: 测试前需要准备的文件
- expected_tool_calls: 预期的工具调用序列
- expected_final_output: 预期输出的约束(should_contain/should_not_contain)
3. JSON数组格式
4. 注意 expected_tool_calls 要合理------不是每个请求都必须调用全部三个工具,取决于具体任务
6.3.3 审查并校准GT
生成测试集后,打开 data/test_cases.json,你需要人工审查至少3条关键的GT条目:
- 审查T001(正常场景):预期的工具调用顺序是否合理?有没有遗漏必要的调用?
- 审查文件不存在场景:Agent的预期行为是"处理错误,不崩溃"。GT中是否体现了这一点?
- 审查多文件读取场景:工具调用顺序是否可能有两种合理路径?如果是,GT不应该定死唯一路径。
一个重要的原则 :GT中 expected_tool_calls 的 required: true/false 标记要慎重。如果某个工具调用确实不可或缺,标 true;如果只是推荐但不是必须的,标 false。过于严格的GT会导致评测失真------Agent用了另一种同样合理的方式却被扣分。
6.4 用Trae生成扩展版评测Agent
现在我们需要一个能同时评"输出"和"工具"的评测Agent。
6.4.1 评测Agent的系统提示词
在Trae对话面板中输入:
在项目 data-analyst-agent 中创建 evaluator.py,包含一个扩展版的评测Agent。
## 评测Agent的系统提示词(写在代码中或保存为 eval_prompt.txt)
角色:你是一个AI系统评测审计员。你的任务是评估一个"数据分析助手Agent"的执行质量。你会收到以下信息:
1. 用户的原始请求
2. Agent的完整执行日志(包含每一步工具调用的名称、参数、返回结果)
3. Agent的最终输出
4. 黄金标准(GT):包含预期的工具调用序列和预期输出约束
## 评测维度
### 维度1:最终输出质量(50%)
- 是否包含了 should_contain 中的所有关键信息?
- 是否出现了 should_not_contain 中的不应该出现的内容?
- 格式是否符合预期?
### 维度2:工具调用审计(50%)
- 工具选择是否正确(是否该用工具时用了工具,不该用时没用)?
- 参数是否正确(尤其是关键参数如文件路径)?
- 是否有遗漏的必要工具调用(GT中 required: true 的调用是否都发生了)?
- 是否有多余的工具调用(浪费)?
- 工具执行异常时Agent是否正确处理了(而不是假装成功)?
错误类型代码:OK, FP, FN, TW, PA, EX, TC
## 输出格式(严格JSON)
{
"test_id": "T001",
"overall_score": 0-100,
"output_quality": {
"score": 0-100,
"match_type": "OK"/"FP"/"FN",
"issues": []
},
"tool_audit": {
"score": 0-100,
"error_type": "OK"/"TW"/"PA"/"EX"/"TC",
"expected_tools": ["..."],
"actual_tools": ["..."],
"match_details": [
{"step": 1, "expected": "...", "actual": "...", "match": true/false, "note": ""}
],
"issues": ["具体问题描述"]
},
"analysis": "综合评价,不超过150字"
}
## 核心函数
def evaluate(test_case: dict, agent_log: str, agent_output: str) -> dict
参数:test_case是GT条目,agent_log是执行日志,agent_output是最终输出。
返回上述JSON格式的字典。
## 模型配置
使用 openai 库调用 DeepSeek API,从 .env 读取密钥。
6.4.2 生成批量评测脚本
在Trae对话面板中继续输入:
在项目 data-analyst-agent 中创建 run_evaluation.py。
功能:
1. 读取 data/test_cases.json 中的所有测试用例
2. 对每个测试用例:
a. 根据 setup 准备测试环境(创建必要的文件)
b. 调用 agent.run_agent(user_request) 执行Agent(需要修改 agent.py 使其返回执行日志和最终输出)
c. 调用 evaluator.evaluate(test_case, agent_log, agent_output) 进行评测
d. 收集评测结果
3. 计算汇总指标:
- 平均总分
- 输出质量平均分
- 工具审计平均分
- 各错误类型分布
4. 将详细结果和汇总指标保存为 evaluation_report.json
5. 终端打印汇总概览
## 注意事项
- agent.py 的 run_agent 函数需要修改,使其返回 (agent_output, agent_log) 元组
- agent_log 是字符串,记录每一步工具调用的名称、参数和返回结果
- 每个测试用例执行前需要恢复测试环境(删除之前测试可能遗留的文件)
6.4.3 修改 agent.py 使其返回执行日志
评测需要Agent的执行日志。我们需要修改 agent.py 中的 run_agent 函数,让它在返回最终回答的同时,也返回工具调用的完整记录。
在Trae对话面板中输入:
请修改 agent.py 中的 run_agent 函数。
## 修改要求
1. 函数签名改为 run_agent(user_query: str) -> tuple[str, str]
返回:(最终回答文本, 执行日志字符串)
2. 在函数内部维护一个 log_lines 列表,记录每一步:
- 用户请求
- 每一轮API调用:模型是否返回了 tool_calls
- 每个工具调用:工具名、参数(JSON格式)、返回结果(前200字符)
- 最终回答
3. 日志格式示例:
[用户请求] 请读取 scores.csv...
[第1轮] 模型调用工具: read_file({"path": "scores.csv"})
[工具返回] 学号,姓名,高数...
[第2轮] 模型调用工具: execute_python({"code": "import pandas..."})
[工具返回] 72.3
[第3轮] 模型最终回答: 高数平均分为72.3分...
4. 将 log_lines 用换行符拼接为字符串,与最终回答一起返回
5. 同时更新脚本底部的测试入口,适配新的返回值格式
6.5 运行评测与分析
所有组件就绪。现在跑通完整的评测流程。
6.5.1 运行评测
在Trae终端中执行:
bash
python run_evaluation.py
你会看到终端里逐个显示每个测试用例的执行过程和评测结果。运行完成后,打开 evaluation_report.json。
6.5.2 解读评测报告
评测报告的 summary 部分类似这样:
json
{
"summary": {
"total": 8,
"avg_overall_score": 78.5,
"avg_output_score": 85.0,
"avg_tool_score": 72.0,
"error_distribution": {
"OK": 5,
"TW": 1,
"PA": 1,
"EX": 1,
"TC": 0
}
}
}
重点关注 avg_tool_score ------它往往低于 avg_output_score。这说明Agent的最终输出看起来还行,但工具使用过程存在问题。这正是只看最终输出发现不了的"水下冰山"。
查看 error_distribution:
- TW(工具选择错误)最多:Agent在判断"该用什么工具"时有问题。需要优化系统提示词中工具使用的指引,或者优化工具描述让它更容易被正确选择。
- PA(参数错误)最多 :Agent选了正确的工具,但参数传错了。检查工具描述中的
parameters定义------参数名是否清晰?description是否说明了参数格式? - EX(执行异常)最多:工具执行失败但Agent没正确处理。可能是沙盒拦截了某些操作,但Agent没有读取错误信息并调整。
- TC(缺少工具调用)最多:Agent跳过了必要的工具调用。可能是在系统提示词中没有强调"必须先读文件再分析"。
打开 details 列表,找到得分最低的那个测试用例 。仔细阅读它的 tool_audit.match_details 和 issues,理解Agent到底在哪个步骤出了问题。
6.6 基于评测数据做一次迭代优化
假设你的评测报告显示:T003(多文件对比分析场景)的 tool_audit.error_type 是 PA,原因是Agent在读取第二个文件时,路径写成了 "scores2.csv" 但实际文件是 "scores_2.csv"。
这个问题的根因可能是:Agent在第一次 read_file 拿到第一个文件内容后,没有仔细核对第二个文件的路径,而是凭"感觉"写了一个类似的文件名。
修改方向:在系统提示词中增加对文件路径的强调。
在Trae对话面板中输入:
请修改 agent.py 的系统提示词,在工具使用指引中增加一条:
"在使用 read_file 工具时,务必使用用户提供的精确文件路径。如果需要读取多个文件,每读取一个文件前都要确认文件名。如果文件不存在,仔细检查路径拼写,修正后重试。"
修改完成后,重新运行评测:
bash
python run_evaluation.py
对比两次 evaluation_report.json 中 PA 错误的数量变化。
用Git记录这次迭代:
bash
git add agent.py evaluator.py run_evaluation.py data/ evaluation_report.json
git commit -m "v1.1: 扩展评测体系,增加工具调用审计。修改系统提示词修复PA问题,工具审计分从72提升至XX"
6.7 本章小结
- "最终输出正确"不等于"过程正确"。工具增强型Agent的评测必须同时评估输出质量和工具使用质量。
- 新增四种工具调用错误代码:TW(工具选择错误)、PA(参数错误)、EX(执行异常处理不当)、TC(缺少必要工具调用)。
- GT结构扩展 :从单一标签变为
expected_tool_calls+expected_final_output。工具调用序列包含工具名、参数、是否必须。 - 评测Agent也升级了:从"比对最终输出"升级为"审计完整执行日志"。执行日志需要Agent主动记录每一步工具调用。
- 迭代优化依然是闭环:评测→归因→修改→再评测。与第一部的工程化方法论完全一致。
下一章是第二部能力跃迁的顶点------你将让Agent自己给自己造工具。当它发现现有工具箱缺少某个功能时,自动生成Python代码、保存为工具文件、注册到ToolManager、然后调用。
课后练习
- 打开
evaluation_report.json,找出tool_audit.error_type不是OK的测试用例。阅读它的issues和match_details,分析Agent到底在哪个环节出了问题。 - 如果你发现某个工具调用错误反复出现(比如总是PA),尝试定位根因------是工具描述不够清晰,还是系统提示词中的使用指引有歧义?修改后重新评测,观察指标变化。
- (进阶)在评测体系中增加一个效率指标:Agent实际使用的工具调用次数与GT中
required调用次数的比值。如果Agent调了5次工具但GT只需要3次,这个指标会反映"过度调用"问题。修改evaluator.py实现这个指标。 - (进阶思考)如果一个任务存在多种同样合理的工具调用路径(比如可以先计算再筛选,也可以先筛选再计算),评测体系应该如何避免"路径唯一性偏见"?写下你的设计思路。