Agno Eval Suite 实战指南:基于 Case 声明式评测、CLI 与 CI 集成的完整测试记录解读 Agno Eval Suite 实战指南基于 Case 声明式评测、CLI 与 CI 集成的完整测试记录解读【免费下载链接】agnoBuild, run, and manage agent platforms.项目地址: https://gitcode.com/GitHub_Trending/ag/agno本篇技术指南以cookbook/09_evals/suite/TEST_LOG.md的测试记录为核心围绕 Agno 内置的 Eval Suite 能力展开如何用Case声明式定义评测用例Agent 与 Team 两种被测对象通过内置cli()完成用例列表、标签筛选、JSON 报告输出与 CI 退出码控制并深度解读JudgeMode.NUMERIC数值评分、expected_tool_calls可靠性检查在 Team 场景下的真实行为。读完本文你将掌握如何把多个评测用例打包成一套可被 CI 消费的 eval suite并理解其底层实现libs/agno/agno/eval/suite.py的关键调用链与数据契约。一、Eval Suite 是什么从测试日志看功能全景cookbook/09_evals/suite/TEST_LOG.md记录了 suite 目录下两个评测示例脚本的完整测试结论是理解 Agno Eval Suite 最直接的入口。该日志覆盖了两个层面suite_basic.py面向单个 Agent 的基础评测套件测试了--list列表、--json-output全量运行、未知--tag选择器三种 CLI 场景以及python -m agno.eval模块入口被移除这一行为变更。suite_team_scoring.py面向 Team团队的数值评分评测leader 将算术任务委派给 calculator 成员、将写作任务委派给 writer 成员验证了run_cases/arun_cases程序化入口与 CLI 双路径。日志呈现的核心事实包括观测点结果suite_basic.py全量运行2/2 用例通过退出码 0JSON 载荷含预期 summary/cases 结构未知--tag选择器退出码 2并列出可用用例名可靠性检查expected_tool_calls()在构造期即被拒绝falsy 守卫python -m agno.eval模块入口已移除CLI 统一走脚本内cli(CASES)Team 用例载荷携带team_id: assistant-team、agent_id: nullTeam 成员真实工具经team_response可见tools_called: [delegate_task_to_member, multiply]数值评分两例均报告judge_score: 10这些结论均有对应源码佐证下文将逐一展开。二、快速上手声明一个最小 Eval Suite测试日志提到的suite_basic.py源码见 suite_basic.py是理解整个机制的最佳起点。一个 suite 由三部分组成被测 Agent/Team、一组Case、以及入口cli(CASES)。import sys from agno.agent import Agent from agno.eval import Case, cli from agno.models.openai import OpenAIResponses from agno.tools.calculator import CalculatorTools # 1. 创建被测 Agent agent Agent( idmath-tutor, modelOpenAIResponses(idgpt-5.5), tools[CalculatorTools()], instructionsUse the calculator tools for any arithmetic., ) # 2. 声明评测用例 CASES ( Case( namefactorial_uses_calculator, agentagent, inputWhat is 10! (ten factorial)?, tags(smoke,), criteriaStates that 10! equals 3628800., expected_tool_calls(factorial,), ), Case( nameexplains_compound_interest, agentagent, inputExplain compound interest in one short paragraph., criteriaExplains that interest is earned on both the principal and previously earned interest., ), ) # 3. 以 CLI 方式运行 if __name__ __main__: sys.exit(cli(CASES))运行方式由脚本内置 CLI 提供详见 suite/README.mdpython cookbook/09_evals/suite/suite_basic.py # 运行全部用例 python cookbook/09_evals/suite/suite_basic.py --list # 仅列出用例不运行 python cookbook/09_evals/suite/suite_basic.py --tag smoke # 只运行带 smoke 标签的子集 python cookbook/09_evals/suite/suite_basic.py --name factorial_uses_calculator # 按名称筛选 python cookbook/09_evals/suite/suite_basic.py --json-output tmp/evals.json # 输出机器可读 JSON python cookbook/09_evals/suite/suite_basic.py -v # 每个用例渲染完整运行面板这段示例覆盖了日志中的两个核心场景第一个用例同时启用 Agent-as-Judge 检查criteria与可靠性检查expected_tool_calls第二个用例仅启用 judge 检查。值得注意的是Case的name、input为必填agent/team必须二选一且至少配置一种检查criteria、expected_tool_calls 或 scorer 之一否则在构造期直接抛出ValueError见 suite.py 第 110-126 行的__post_init__校验。三、Case 数据结构详解配置参数与取值边界Case是 suite 的原子单元定义于 suite.py。其字段分为四组理解每一组才能在声明用例时不踩坑。3.1 基础字段谁被测试、如何筛选字段类型默认值说明namestr必填用例名也是--name筛选与 JSON 载荷中的标识inputstr必填送入 Agent/Team 的输入文本agentOptional[Agent]None被测 Agent与team二选一teamOptional[Team]None被测 Team与agent二选一tagsTuple[str, ...]()标签元组供--tag子集筛选timeout_secondsOptional[int]None单用例超时秒缺省回落到 runner 的default_timeoutagent与team分离为两个字段是有意设计源码注释明确说明这是为了镜像AccuracyEval的约定避免 Team 被塞进名为agent的参数里造成语义混淆。构造时若两者均为空或同时非空会分别抛出 provide one of agent or team 与 provide only one of agent or team 的错误。3.2 Judge 检查criteria、judge_mode、judge_threshold字段类型默认值说明criteriaOptional[str]None判分标准描述设置后启用 AgentAsJudgeEvaljudge_modelOptional[Model]None单用例判官模型覆盖缺省回落到 runner 的judge_modeljudge_modeJudgeModeJudgeMode.BINARY二值通过/失败或 1-10 数值评分judge_thresholdint7数值模式的及格线1-10仅 NUMERIC 模式生效JudgeMode是定义在 suite.py 的str枚举BINARY binary二值判定与NUMERIC numeric1-10 打分达到judge_threshold即通过。其字符串值直接对应对应AgentAsJudgeEval.scoring_strategy的取值见 agent_as_judge.py因此向judge_mode传入等值字符串numeric同样被接受。值得注意的构造期校验judge_threshold必须落在 1-10 区间否则抛出 judge_threshold must be 1-10 的ValueError。数值模式的底层行为是score threshold判定通过agent_as_judge.py而NumericJudgeResponse的 schema 用ge1, le10约束了评分范围agent_as_judge.py。3.3 可靠性检查expected_tool_calls 与 allow_additional_tool_calls字段类型默认值说明expected_tool_callsOptional[Tuple[str, ...]]None期望触发的工具名序列设置后启用 ReliabilityEvalallow_additional_tool_callsboolTrue为 True 时允许出现期望之外的额外工具调用子集匹配日志特别记录了 2026-07-05 外部评审修复轮之后的行为expected_tool_calls()会在构造期被 falsy 守卫拒绝。其逻辑位于 suite.py校验采用 truthiness 而非is None因为criteria或expected_tool_calls()会构造出一个检查真空通过的用例——即一个什么也没验证的绿色 CI 门禁。同理scorer is None用is None判断因为 scorer 实例恒为真值。3.4 生命周期与扩展setup/teardown、scorer/expectedCase还提供两组高阶能力setup/teardown钩子setup在运行前执行不计入超时其返回值作为 context 传给teardownteardown只要 setup 已完成就必然执行无论 pass/fail/error/timeout接收(context, result)以便检查result.error/result.timed_out。同步可调用对象经asyncio.to_thread执行异步可调用对象被 await。scorer/expected字段设置scorer后在进程内评分agno.scorer协议即任何拥有async ascore(run, expected)的对象运行于用例超时窗口内接收(result.response, case.expected)Team 用例的 response 是TeamRunOutput。源码注释特别强调字段顺序是承重设计这些 dataclass 并非kw_only新字段必须追加在末尾否则会静默重排位置参数调用者。四、CLI 参数全解析退出码、JSON 载荷与筛选逻辑cli()定义于 suite.py是对公共 runner API 的纯消费者其参数解析逻辑在acli()中suite.py参数说明--name只运行指定名称的用例--tag只运行带指定标签的用例--timeout默认单用例超时秒缺省 120--json-output将机器可读 JSON 结果写入指定路径--list仅列出被选中的用例而不运行-v/--verbose每个用例后渲染完整运行面板Message、Tool Calls、Response4.1 退出码契约0所有被选中的用例全部通过1任一失败含--json-output写入失败2没有用例匹配选择器如日志中测试的未知--tag。acli()在无匹配时会打印no cases selected与可用用例名列表并返回 2suite.py这与日志记录的行为一致。4.2 JSON 载荷结构CI 消费方的稳定契约SuiteResult.to_dict()suite.py生成的载荷是日志反复验证的核心对象其结构如下{ summary: { total: 2, passed: 2, failed: 0, status: PASS }, cases: [ { name: factorial_uses_calculator, agent_id: math-tutor, team_id: null, tags: [smoke], session_id: eval-factorial_uses_calculator-1a2b3c4d, duration_seconds: 12.345, judge_passed: true, judge_reason: ..., judge_score: null, reliability_passed: true, output: ..., tools_called: [factorial], timed_out: false, skipped: false, passed: true, error: null, score_value: null, score_passed: null, score_reason: null } ] }几个关键设计点空 suite 的 status 为FAILSuiteResult.status在results为空时直接返回FAILsuite.py。源码注释点明原因——CI 门禁比较 PASS拼错标签绝不能什么都没运行却绿灯放行发布。judge_score仅在数值模式非空二值模式下为None数值模式下保存 1-10 分数以便载荷跟踪质量漂移而非只有通过/失败。tools_called为运行期间按序触发的工具名。对 Team 用例_tool_names()suite.py会下沉一层收集member_responses中的工具调用——这正是日志中 Team 用例显示tools_called: [delegate_task_to_member, multiply]的原因如果不收集成员层只能看到 leader 的委派调用看不到成员的真实工具。score_*三字段为 2.8.0 起追加suite.py 注释未配置 scorer 时全部为null对 CI 消费者纯增补、向后兼容。五、程序化调用run_cases 与 arun_cases无控制台 I/O测试日志明确记录了run_cases与arun_cases两个程序化入口均被覆盖测试。它们适合 CI 工作流或嵌入式场景因为runner 本身不做任何控制台 I/O——所有呈现都通过on_case_start/on_run_event/on_case_end三个钩子流出suite.py。import asyncio from agno.eval import Case, run_cases, arun_cases # 同步入口整个 suite 运行在单一事件循环上 suite_result run_cases( CASES, tagsmoke, # 可选标签筛选 nameNone, # 可选名称筛选 default_timeout120, # 单用例默认超时 judge_modelNone, # suite 级判官模型默认值 # dbmy_db, # 传入则评测结果写入存储 ) # 异步入口在已有事件循环内使用 async def main(): suite_result await arun_cases(CASES) payload suite_result.to_dict() # 稳定契约供 CI 消费 print(suite_result.passed, /, suite_result.total)run_cases是arun_cases的同步包装asyncio.run两者共享全部参数。钩子的设计约束值得注意呈现钩子仅支持同步可调用对象且直接在事件循环上执行应保持轻量异步钩子会被_call_presentation_hook显式拒绝并抛出TypeErrorpresentation hooks must be sync callables避免异步钩子返回协程后从未执行的静默失败。钩子抛异常或异步钩子被拒会被记录到该用例的error字段前缀hook: ...不会中止整个 suite。取消行为某用例以cancelled状态结束时服务端cancel_run或 agno 将 KeyboardInterrupt 转换成的取消suite 中止并将未运行的剩余用例记为skippedTrue、errorskipped: suite aborted after cancelled run保证载荷与on_case_end钩子看到的用例数一致。六、Team 数值评分评测suite_team_scoring 深度解读日志第二个测试对象是 suite_team_scoring.py将一个含 calculator 与 writer 两个成员的 Team 作为被测对象leader 委派任务且每个答案都用 1-10 数值判官评分。import sys from agno.agent import Agent from agno.eval import Case, JudgeMode, cli from agno.models.openai import OpenAIResponses from agno.team.team import Team from agno.tools.calculator import CalculatorTools calculator Agent( idcalculator, modelOpenAIResponses(idgpt-5.5), tools[CalculatorTools()], instructionsUse the calculator tools for every arithmetic operation. Never compute arithmetic yourself., ) writer Agent( idwriter, modelOpenAIResponses(idgpt-5.5), instructionsAnswer in one clear paragraph., ) assistant_team Team( idassistant-team, modelOpenAIResponses(idgpt-5.5), members[calculator, writer], instructionsDelegate arithmetic to the calculator member and writing to the writer member, then report the members result., ) CASES ( Case( nameteam_uses_calculator, teamassistant_team, inputWhat is 4891 multiplied by 7238?, tags(smoke,), criteriaStates that the product is 35,401,058., judge_modeJudgeMode.NUMERIC, judge_threshold7, expected_tool_calls(multiply,), ), Case( nameteam_explains_clearly, teamassistant_team, inputExplain compound interest in one paragraph., criteriaExplains that interest is earned on both the principal and previously earned interest., judge_modeJudgeMode.NUMERIC, judge_threshold7, ), ) if __name__ __main__: sys.exit(cli(CASES))日志记录的两项 Team 关键事实在源码中均有对应实现载荷中team_id: assistant-team、agent_id: nullCaseResult仿照AccuracyEval拆分Agent 用例填agent_idTeam 用例填team_id另一侧保持Nonesuite.py由_component_id()统一取值team.id or case.name。tools_called: [delegate_task_to_member, multiply]如 4.2 节所述_tool_names()递归收集member_responses中的工具调用。同时可靠性检查通过team_response注入suite.pyReliabilityEval收到agent_response还是team_response取决于响应类型——agent 的RunOutput走前者Team 的TeamRunOutput走后者reliability.py中的_collect_member_evidence()reliability.py会递归收集每层成员响应的工具执行与消息因此 leader 的delegate_task_to_member与成员的真实multiply都能被统计。数值评分模式下的judge_score: 10来自AgentAsJudgeEval的NumericJudgeResponse结构化输出{score: int 1-10, reason: str}scoring_strategynumeric时以score threshold判过agent_as_judge.py且 suite 层的JudgeMode.NUMERIC字符串值与之一致judge_threshold7直接透传为判官的threshold。七、底层原理一次 Case 的完整执行流程把日志中的行为映射到源码一次 Case 的运行流程如下对应_arun_case与_run_case_bodysuite.py生成独立会话每个用例分配session_idfeval-{case.name}-{uuid4().hex[:8]}保证评测流量不污染 Agent/Team 的历史记录db设置时该会话会关联存储的 trace。执行 setup 钩子在超时窗口之外运行失败则跳过运行阶段。流式运行被测对象以arun(input..., streamTrue, stream_eventsTrue, yield_run_outputTrue)迭代事件。RunOutput/TeamRunOutput在流中到达时立即提交响应与证据字段——即使后续流停滞如持久化挂起触发超时已产出的结果也不会丢失。错误事件捕获Agent、Team、Workflow 三种错误事件类_RUN_ERROR_EVENTS元组都会被识别并记录为agent:/team:前缀的错误而非静默失败。可评分性判定仅当无错误、存在响应且status RunStatus.completed时才进入评分。paused/cancelled 等状态携带占位内容如 HITL 样板文本不视为真实答案未完成状态按_STATUS_ERRORS映射为可读错误。Judge 检查配置了criteria则构造AgentAsJudgeEval沿用judge_mode字符串值作为scoring_strategyjudge_threshold作为threshold关闭 spinner 与遥测对(input, output)评分。可靠性检查配置了expected_tool_calls则构造ReliabilityEvalAgent 走agent_response、Team 走team_response。Scorer 检查配置了scorer则调用ascore(response, expected)。teardown 钩子在finally中确保即使超时/错误也会执行失败以cleanup:前缀记录。汇总duration_seconds精确到毫秒结果汇入SuiteResult。这一流程解释了日志中所有行为断言为何未知 tag 退出码为 2、为何空 suite 为 FAIL、为何 Team 的成员工具可见、为何expected_tool_calls()在构造期就被拦截。八、在 CI 中落地 Eval Suite综合测试日志与 README 的指引将 suite 接入 CI 的标准模式是以 JSON 载荷为门禁输入运行python cookbook/09_evals/suite/suite_basic.py --json-output tmp/evals.json退出码 0 即通过同时解析summary.status PASS作为双保险。分层筛选日常开发用--tag smoke跑冒烟子集发版前跑全量。程序化集成CI 编排器中直接run_cases(CASES, tag...)并读取to_dict()runner 无控制台 I/O 的特性保证输出纯净。关注载荷中的证据字段tools_called、judge_reason、duration_seconds足够在单条 JSON 里定位失败根因日志中 enough evidence to debug a failure from the payload alone 正是CaseResult的设计目标。九、与既有评测体系的关系Eval Suite 并非孤立功能而是 Agno 评测体系libs/agno/agno/eval/包的上层编排器。其内部复用了两个成熟组件AgentAsJudgeEvalagent_as_judge.py以 LLM 为判官BinaryJudgeResponse/NumericJudgeResponse两种结构化输出 schema 支撑二值/数值评分prompt 通过fence_untrusted对被评测输出做不可信数据隔离。ReliabilityEvalreliability.py核对实际工具调用与期望集ReliabilityResult区分 failed/passed/additional/missing 工具调用与参数检查结果。agno.eval包通过懒加载__getattr__导出init.py规避与Agent的循环导入。同目录的 accuracy.py、performance.py 则提供准确率与性能评测suite 负责将它们组合成可调度的用例集。结语cookbook/09_evals/suite/TEST_LOG.md不仅是一份测试记录更是一份浓缩的 Eval Suite 行为规范。透过它可以看到声明式的Case、稳定可消费的to_dict()契约、严谨的空检查守卫、Team 场景下对成员工具调用的深度可见性以及runner 与呈现解耦的架构取舍。按本文的 CLI 参数与程序化入口你可以立即将评测套件接入自己的 CI 门禁让每次 Agent/Team 变更都有可追踪、可断言的自动化质量反馈。【免费下载链接】agnoBuild, run, and manage agent platforms.项目地址: https://gitcode.com/GitHub_Trending/ag/agno创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考