基于 Agno ReliabilityEval 的单工具调用可靠性评估实战:从预期调用校验到参数级断言
【免费下载链接】agnoBuild, run, and manage agent platforms.项目地址: https://gitcode.com/GitHub_Trending/ag/agno
本文以 Agno 开源仓库中的 single_tool_calls 可靠性评估示例 为核心,讲解如何用ReliabilityEval对 Agent 的单次工具调用进行自动化评估:既验证"预期的工具是否被正确调用",也通过expected_tool_call_arguments进一步断言"调用参数是否完全正确"。读完本文,你将掌握 Agno 可靠性评估的完整用法、核心参数语义与底层执行匹配原理,并能在自己的 Agent 项目中直接落地一套可回归、可接入 CI 的工具调用质量门禁。
一、场景定位:为什么要做单工具调用可靠性评估
在 Agent 应用中,模型"调没调工具、调了哪个工具、参数传得对不对"直接决定了下游业务的正确性。比如计算器 Agent,如果模型在回答"10! 等于多少"时没有调用factorial,或者回答"10 * 5"时把参数传成了{"a": 10, "b": 3},即便最终文本看起来"像模像样",结果也是错的。这类缺陷无法靠传统答案匹配(accuracy)捕捉,必须针对工具调用行为本身做专项校验。
Agno 的ReliabilityEval正是为此设计:它接收一次 Agent 运行产生的RunOutput,与开发者声明的expected_tool_calls(预期调用列表)和expected_tool_call_arguments(预期参数)逐项比对,输出结构化ReliabilityResult,从而把"工具调用是否可靠"变成可断言、可回归、可入库的客观指标。本仓库的 single_tool_calls/calculator.py 用最精简的形态演示了这一能力,是理解可靠性评估的最佳起点。
二、运行环境与前置依赖
示例使用 OpenAI 模型(OpenAIChat),运行前需要:
- 安装 Agno 及 OpenAI 支持:
pip install "agno[openai]"; - 配置
OPENAI_API_KEY环境变量; - 安装
rich(ReliabilityResult.print_eval依赖它渲染结果表格)。
本仓库对应的实现位于 libs/agno/agno/eval/reliability.py,评估入口ReliabilityEval与结果对象ReliabilityResult均通过agno.eval.reliability模块导出,可直接from agno.eval.reliability import ReliabilityEval, ReliabilityResult导入。
三、完整示例代码:一次运行、两项评估
calculator.py 的核心逻辑只有两部分:一是构造带CalculatorTools的 Agent 并运行一次真实问答,二是将返回结果交给ReliabilityEval校验。完整代码如下:
from typing import Optional from agno.agent import Agent from agno.eval.reliability import ReliabilityEval, ReliabilityResult from agno.models.openai import OpenAIChat from agno.run.agent import RunOutput from agno.tools.calculator import CalculatorTools # --------------------------------------------------------------------------- # 评估一:校验"factorial 工具被调用"这一事实 # --------------------------------------------------------------------------- def factorial(): agent = Agent( model=OpenAIChat(id="gpt-5.2"), tools=[CalculatorTools()], ) response: RunOutput = agent.run("What is 10! (ten factorial)?") evaluation = ReliabilityEval( name="Tool Call Reliability", agent_response=response, expected_tool_calls=["factorial"], ) result: Optional[ReliabilityResult] = evaluation.run(print_results=True) if result: result.assert_passed() # --------------------------------------------------------------------------- # 评估二:校验"multiply 被调用,且参数必须是 a=10, b=5" # --------------------------------------------------------------------------- def multiply_with_argument_check(): """Verify that the tool was called with the correct arguments.""" agent = Agent( model=OpenAIChat(id="gpt-5.2"), tools=[CalculatorTools()], ) response: RunOutput = agent.run("What is 10 * 5?") evaluation = ReliabilityEval( name="Tool Call Argument Validation", agent_response=response, expected_tool_calls=["multiply"], expected_tool_call_arguments={ "multiply": {"a": 10, "b": 5}, }, ) result: Optional[ReliabilityResult] = evaluation.run(print_results=True) if result: result.assert_passed() if __name__ == "__main__": factorial() multiply_with_argument_check()运行方式为直接执行脚本(python calculator.py),也可在任意 Python 进程中导入factorial、multiply_with_argument_check后调用。示例所依赖的CalculatorTools定义在 libs/agno/agno/tools/calculator.py,它注册了 8 个函数工具:add、subtract、multiply、divide、exponentiate、factorial、is_prime、square_root,其中multiply(a, b)与factorial(n)正是本示例的评估对象。
代码模式可以归纳为固定的四步流程:
- 构造 Agent 并运行:
agent.run(...)得到RunOutput; - 声明评估器:传入
agent_response与期望的工具调用(及可选参数断言); - 执行评估:
evaluation.run(print_results=True)返回ReliabilityResult; - 断言通过:
result.assert_passed(),不通过时抛出AssertionError(错误信息携带ReliabilityResult全部字段,便于 CI 排障)。
四、ReliabilityEval 核心参数详解
ReliabilityEval是一个 dataclass(定义于 libs/agno/agno/eval/reliability.py#L79-L117),除示例用到的name、agent_response、expected_tool_calls、expected_tool_call_arguments外,还提供一批实用配置:
| 参数 | 类型 | 默认值 | 作用说明 |
|---|---|---|---|
name | Optional[str] | None | 评估名称,用于日志、结果文件占位符与入库标识 |
agent_response | Optional[RunOutput] | None | Agent 单次运行的输出,作为评估证据来源 |
team_response | Optional[TeamRunOutput] | None | 团队(Team)运行输出,与agent_response二选一 |
expected_tool_calls | Optional[List[str]] | None | 预期工具调用名列表,满足"无序集合匹配"语义 |
allow_additional_tool_calls | bool | False | 严格/宽松模式开关,见下文第五节 |
expected_tool_call_arguments | Optional[Dict[...]] | None | 预期参数映射,单个 dict 或多个 dict 列表均可 |
print_results | bool | False | 是否打印富文本结果表格(run()时也可临时传参) |
show_spinner | bool | True | 是否渲染进度 spinner;套件(suite)运行时建议关闭 |
file_path_to_save_results | Optional[str] | None | 结果落盘路径,支持{name}、{run_id}占位符 |
debug_mode | bool | 由AGNO_DEBUG环境变量决定 | 开启调试日志 |
db | Optional[BaseDb/AsyncBaseDb] | None | 配置数据库后将评估结果持久化入库(如 PostgreSQL 示例) |
telemetry | bool | True | 向 Agno 平台发送最小化遥测,帮助改进 Evals |
其中expected_tool_call_arguments支持两种声明形态(见 reliability.py#L95-L97):
- 单个调用校验:
{"multiply": {"a": 10, "b": 5}}—— 期望multiply至少有一次调用满足参数完全相等; - 多次调用校验:
{"add": [{"a": 2, "b": 2}, {"a": 3, "b": 3}]}—— 期望每次调用分别命中一个参数规格,各规格至少被一次干净执行满足(规格之间无序)。
五、严格模式与宽松模式:allow_additional_tool_calls
expected_tool_calls校验的是"预期调用是否都发生了",而allow_additional_tool_calls决定"预期之外的调用是否允许存在":
- 严格模式(默认
False):任何不在expected_tool_calls中的工具调用都会使评估 FAILED,并记录到failed_tool_calls。注意严格模式"管制的是尝试而非成败"——一个报错或被拒绝的额外调用同样失败,见 reliability.py#L172-L178。 - 宽松模式(
True):额外调用只计入additional_tool_calls(仅作观测,不影响通过与否),即"子集匹配"。
从单元测试 test_reliability_eval.py 可验证这两种语义:test_exact_match_fails_on_unexpected_tool断言出现额外工具时严格模式 FAILED;test_subset_matching相关用例则验证allow_additional_tool_calls=True时评估 PASSED 且额外调用被记录(test_reliability_eval.py#L340-L369)。
六、结果对象 ReliabilityResult:字段与断言
ReliabilityEval.run()返回ReliabilityResult(reliability.py#L26-L62),字段含义如下:
| 字段 | 含义 |
|---|---|
run_id | 本次评估生成的 UUID,用于结果文件命名与入库关联 |
eval_status | "PASSED"或"FAILED",总判定 |
passed_tool_calls | 干净执行且属于预期列表的工具名 |
failed_tool_calls | 严格模式下不在预期列表中的工具名 |
additional_tool_calls | 宽松模式下额外发生的调用 |
missing_tool_calls | 预期但从未被干净执行的工具名;若曾请求但被拒绝/报错,会附带注释(requested but refused/errored — execution matching, new in 2.8.0) |
passed_argument_checks | 参数断言通过的工具名 |
failed_argument_checks | 参数断言失败的工具名 |
assert_passed()的实现只有一行核心断言(reliability.py#L58-L62):
assert self.eval_status == "PASSED", f"ReliabilityEval failed: {self}"即任何failed_tool_calls、missing_tool_calls、failed_argument_checks非空都会抛错,适合直接放进测试用例或 CI 门禁。
七、底层原理:基于"执行匹配"的证据采集(2.8.0 起)
可靠性评估的关键在于"拿什么当证据"。从 reliability.py#L119-L128 的注释与实现可以看出,自 2.8.0 起,Agno 改为只从工具执行记录(ToolExecution,即RunOutput.tools)取证,而不再采纳 message 侧的请求(request)记录:
- 干净执行(clean execution):
tool_name非空、tool_call_error不为True、且is_paused为假,才可满足一个预期(reliability.py#L162)。从存储恢复的运行中tool_call_error为None,同样视为成功; - message 侧请求仅作失败注释:一个被
tool_call_limit拒绝的调用不会产生执行记录,此时只能靠请求侧证明"模型确实尝试过",最终以带注释的missing_tool_calls呈现,保证 CI 变红时可一眼分辨"是评估配错还是 Agent 行为错"; - 历史消息排除:
from_history=True的旧轮次工具调用会被跳过,避免"昨天的调用导致今天的评估失败"(reliability.py#L146-L148)。
ToolExecution本身定义于 libs/agno/agno/models/response.py#L27-L66,其中tool_args是已解析的参数字典(None归一化为{}),这正是参数断言的数据来源。
参数断言的匹配规则
expected_tool_call_arguments的校验逻辑(reliability.py#L219-L248)遵循四条明确规则:
- 只匹配干净执行:报错执行即使参数正确也不计入;单个规格只需"至少一次调用命中"(
test_argument_validation_multiple_calls_any_match验证了多次调用中任一命中即通过); - 子集匹配:规格中声明的键必须在实际参数中存在且值相等,实际调用多出的额外键不影响通过(
test_argument_validation_partial_match中{"a":10,"b":5,"c":99}对规格{"a":10}仍 PASSED); - 多规格全部满足:列表形式的多个规格需逐一被满足,任一缺失则整个参数检查 FAILED;
- 未调用即失败:工具从未被调用时,该工具直接进入
failed_argument_checks,不会"静默跳过"。
以上语义均有对应的单元测试固化在 test_reliability_eval.py#L371-L497。
八、扩展用法:团队评估、异步执行与结果持久化
single_tool_calls示例展示的是单 Agent、同步的最简形态,同一评估器还支持更复杂场景:
- 团队(Team)评估:传入
team_response替代agent_response,评估器会递归遍历所有成员响应,合并每一层级的工具执行证据(包括"团队成员本身是团队"的嵌套情形),见 reliability.py#L65-L76 的_collect_member_evidence; - 异步评估:调用
await evaluation.arun()即可,逻辑与run()对齐(reliability.py#L351-L426); - 结果持久化:通过
db传入数据库实例(如SqliteDb、InMemoryDb),评估记录将以EvalType.RELIABILITY类型写入;通过file_path_to_save_results可将结果保存为文件,路径支持{name}与{run_id}占位符; - 套件化:
show_spinner=False专为agno.eval.suite等嵌入运行器设计,避免污染控制台输出。
九、运行验证与回归实践
本仓库的 TEST_LOG.md 记录了示例的实际验证结果:calculator.py状态PASS,两项评估(factorial 工具调用检查、multiply 参数校验)均通过。这也印证了该示例可作为稳定性回归基线:当模型版本、Prompt 或工具定义发生变化时,重跑同一脚本即可快速确认工具调用行为未被破坏。
十、小结与最佳实践
ReliabilityEval用极小的 API 表面解决了 Agent 评估中最容易被忽视的一环——工具调用本身。基于本仓库示例与源码,可以沉淀以下实践建议:
- 按"单预期调用"拆分用例:每个
ReliabilityEval只校验一个核心工具调用,失败定位更清晰,正如single_tool_calls目录所示; - 参数断言优先于名称断言:名称只证明"调了",
expected_tool_call_arguments才能证明"调对了",计算、检索、SQL 生成等场景务必加上; - 默认使用严格模式:除非确有额外调用的合理场景,否则保持
allow_additional_tool_calls=False,防止模型"偷换工具"; - 将
assert_passed()接入 CI:作为回归门禁,配合db入库沉淀历史趋势。
从 示例代码、评估器实现 到 单元测试,本仓库提供了从用法到原理的完整闭环,可以直接作为团队内部工具调用可靠性测试的落地模板。
【免费下载链接】agnoBuild, run, and manage agent platforms.项目地址: https://gitcode.com/GitHub_Trending/ag/agno
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考