2026/9/10 21:18:49

Agno Environments 任务集(Task Sets)实战:用稳定 ID、JSONL 与元数据组织可复现评测

Agno Environments 任务集(Task Sets)实战:用稳定 ID、JSONL 与元数据组织可复现评测 Agno Environments 任务集Task Sets实战用稳定 ID、JSONL 与元数据组织可复现评测【免费下载链接】agnoBuild, run, and manage agent platforms.项目地址: https://gitcode.com/GitHub_Trending/ag/agno导读本文围绕 Agno 开源仓库中 Environments 模块的Task Sets任务集展开讲解如何把可重复的输入、期望值、ID 与元数据组织成一个环境Environment所验证的任务集合让任务 ID 标注评测网格、对齐后续的差异对比diff。你将掌握三种任务组织方式——内联Task对象、JSONL 数据文件加载、按元数据筛选校准切片——并理解其底层run_rollouts执行与评分机制最终能依据测试日志中的通过率分布定位模型的学习区。一、Task Sets 是什么为什么任务需要集合化单次成功的 Agent 运行不足以证明可靠性。Agno 的 Environments 模块把一次运行升级为对一个任务集合的 K 次重复采样每次尝试独立评分最终形成一个通过/失败网格——全满的行表示已被掌握的任务全空的行表示需要换一种干预手段而部分通过的行就是值得深入研究的学习区learning zone。任务集正是这个网格的输入骨架它把四类信息收集在一起交给Environment统一验证信息字段作用输入input提交给 Agent 的原始提示词必填且必须是字符串期望值expectedAgent 应该产出的结果可选稳定 IDid标注网格行、作为后续 diff 的 join 键可选元数据metadata划分 split / difficulty 等切片可选不会自动进入提示词对应地cookbook/environments/_02_task_sets/ 目录提供了三个从易到难的示例以及一份本地可审查的 JSONL 任务夹具basic.py—— 以内联元组声明少量带稳定 ID 的任务from_jsonl.py—— 从版本控制的 JSONL 文件加载同构任务集with_metadata.py—— 通过任务元数据挑选校准行后再运行data/chained_arithmetic.jsonl—— 本地、可审查的任务数据夹具。二、三种任务组织方式实战2.1 内联 Task紧凑示例的首选basic.py 展示了最小可运行的任务集。核心是Task数据类每个任务三要素齐全——输入、期望值、稳定 IDfrom agno.agent import Agent from agno.environments import Environment, Task, run_rollouts from agno.models.openai import OpenAIResponses from agno.scorer import CodeScorer from pydantic import BaseModel class Answer(BaseModel): value: int def answer_matches(run, expected): return run.content.value expected TASKS ( Task(inputWhat is 43 multiplied by 47?, expected2021, ideasy-product), Task( input( Compute 2718281828459045 multiplied by 1618033988749895. Add the products decimal digits, multiply the sum by 131071, subtract the product remainder modulo 65521, and return the result. ), expected20944939, idchained-product-a, ), Task( input( Compute two chained values, then add them. First: multiply 2718281828459045 by 1618033988749895, ... Second: multiply 3141592653589793 by 1414213562373095, ... Return the sum of the two chained values. ), expected37676112, iddual-chain-sum, ), )任务之后是 Agent、Environment 与运行入口agent Agent( modelOpenAIResponses(idgpt-5.5, reasoning_effortlow), output_schemaAnswer, instructionsReturn only the requested final integer in the typed field., ) environment Environment( nametask-set-basics, agentagent, tasksTASKS, scorerCodeScorer(answer_matches), ) if __name__ __main__: results run_rollouts(environment, k4) print(results) print() for task_result in results.task_results: print(f{task_result.task.id}: {task_result.n_passed}/{task_result.n_scored})从 environment.py 的源码可以看到Task的完整字段定义input必填字符串、expected可选任意值、id可选字符串、metadata默认空映射。其中id用于展示与选择未声明时会在运行开始时按位置自动解析为t1、t2……tN。2.2 JSONL 加载任务集作为数据来治理当任务集合由数据团队拥有、需要评审与版本控制时内联代码就不再合适。from_jsonl.py 展示了Task.from_jsonl的用法from pathlib import Path from agno.agent import Agent from agno.environments import Environment, Task, run_rollouts from agno.models.openai import OpenAIResponses from agno.scorer import CodeScorer from pydantic import BaseModel TASKS_PATH Path(__file__).parent / data / chained_arithmetic.jsonl agent Agent( modelOpenAIResponses(idgpt-5.5, reasoning_effortlow), output_schemaAnswer, instructionsReturn only the requested final integer in the typed field., ) environment Environment( namejsonl-task-set, agentagent, tasksTask.from_jsonl(TASKS_PATH), scorerCodeScorer(answer_matches), ) if __name__ __main__: results run_rollouts(environment, k4) print(results) print(floaded {len(environment.tasks)} tasks from {TASKS_PATH})对应数据文件 chained_arithmetic.jsonl 每一行是一个 JSON 对象同时携带id、input、expected与metadata{id:easy-product,input:What is 43 multiplied by 47?,expected:2021,metadata:{split:smoke,difficulty:anchor}} {id:chained-product-a,input:Compute 2718281828459045 multiplied by 1618033988749895. Add the products decimal digits, multiply the sum by 131071, subtract the product remainder modulo 65521, and return the result.,expected:20944939,metadata:{split:validation,difficulty:calibration}} {id:dual-chain-sum,input:Compute two chained values, then add them. First: multiply 2718281828459045 by 1618033988749895, ... Return the sum of the two chained values.,expected:37676112,metadata:{split:validation,difficulty:calibration}}需要特别说明的是Task.from_jsonl的严格校验语义见 environment.py每行必须是一个对象顶层只允许input、expected、id、metadata四个键任何未知键都会抛出ValueError并指出具体行号与键名。这样设计是为了防止拼错列名例如把expected_output误写成expected导致每个任务都拿到expectedNone从而在宽松评分器下全绿的静默事故。此外input必须为字符串、metadata必须为对象、id若存在必须是字符串行内 JSON 语法错误也会被精确到行号地拒绝。2.3 按元数据挑选任务校准切片的正确姿势with_metadata.py 演示了元数据的核心价值——为任务打上split与difficulty标签然后在运行前从原任务对象中挑选子集TASKS ( Task( input(Compute 2718281828459045 multiplied by 1618033988749895. ...), expected20944939, idchained-product-a, metadata{split: validation, difficulty: calibration}, ), Task( input(Compute 3141592653589793 multiplied by 1414213562373095. ...), expected16731173, idchained-product-b, metadata{split: validation, difficulty: calibration}, ), Task( inputWhat is 43 multiplied by 47?, expected2021, ideasy-product, metadata{split: smoke, difficulty: anchor}, ), ) environment Environment( namemetadata-task-set, agentagent, tasksTASKS, scorerCodeScorer(answer_matches), ) if __name__ __main__: calibration_tasks tuple( task for task in environment.tasks if task.metadata[difficulty] calibration ) results run_rollouts(environment, taskscalibration_tasks, k4) print(results) print() for task_result in results.task_results: split task_result.task.metadata[split] difficulty task_result.task.metadata[difficulty] print( f{task_result.task.id}: split{split}, difficulty{difficulty}, fpass_rate{task_result.pass_rate} )这里有三个容易踩坑的细节值得展开元数据只用于组织不污染提示词文档明确说明它不自动加入 Agent 提示词评分器也不会因此改变——split/difficulty 纯粹是数据集治理层面的切片标签。子集选择必须按身份identity进行从 runner.py 的实现看run_rollouts的tasks参数要求从env.tasks中选出的原始对象例如[t for t in env.tasks if ...]重建出的新Task会因身份不匹配而抛出ValueError——因为选择必须保持环境身份才能保证指纹与 diff 语义成立。重复 ID 会被拦截Environment.__post_init__会对显式声明的 ID 做唯一性校验见 environment.py因为重复 ID 会让diff()静默地把行配错到错误的基线任务上。三、运行方式与环境要求三个示例的运行命令来自 README.mdpython cookbook/environments/_02_task_sets/basic.py python cookbook/environments/_02_task_sets/from_jsonl.py python cookbook/environments/_02_task_sets/with_metadata.py运行前提需要OPENAI_API_KEY环境变量所有模型调用都通过OpenAIResponses模型为gpt-5.5示例中以reasoning_effortlow降低成本三个脚本内部都以k4运行每个任务重复采样 4 次。学习路径上建议先阅读 _01_first_environment/ 的 README 掌握最小运行器再继续到 _03_code_scorer/ 的 README 了解期望值如何转化为分数。四、源码级原理run_rollouts 如何执行与评分任务集只是输入真正干活的是run_rollouts/arun_rollouts见 runner.py。其完整签名与默认值async def arun_rollouts( env: Environment, *, k: int 8, # 每个任务重复采样次数 tasks: Optional[Sequence[Task]] None, # 任务子集按身份从 env.tasks 挑选 model: Optional[Model] None, # 模型覆盖必须是 Model 实例不接受字符串 concurrency: int 4, # 并发尝试数 ) - EnvironmentRunResult:几个值得注意的执行语义每次尝试完全隔离每个 attempt 运行在新鲜的内存 DB 新的 rollout 用户 ID 关闭响应缓存的 Agent 副本上记忆、会话、用户态互不污染同时切断写路径记忆捕获、知识/学习写入、会话摘要写入、save_response_to_file但保留知识检索等只读路径。这保证了统计数据回答的是这个 Agent 本身行不行而不是被上一次尝试污染的 Agent 行不行。指纹fingerprint运行开始时对环境指纹任务、评分器、工具、提示词字符串与标志等版本前缀envfp2和策略指纹模型类、ID、provider、base_url 与请求参数分别做 sha256并盖在结果上。env_matches()保证只有同环境的结果才能diff()None指纹永不匹配。错误风暴error-storm早停单任务运行且k超过证据窗口时若开头一批尝试全部以同类型错误失败则中止整个运行并标记stopped_earlyerror-storm避免坏配置白白烧掉所有采样。同步入口的限制run_rollouts不能从正在运行的事件循环中调用应改用arun_rollouts同步评分器运行在线程中超时无法中断其执行体这是文档明示的残余语义。评分端使用CodeScorer详见 _03_code_scorer/ 的 README它接受一个命名函数返回布尔值、0~1 的分数或完整的Score。示例中的answer_matches(run, expected)即把结构化输出run.content.value与期望值做精确相等比较。五、结果对象与学习区判读run_rollouts返回EnvironmentRunResult其关键派生指标见 runner.py指标含义n_scored/n_unscored被评分的尝试数 / 未评分尝试数超时不算错误答案不计入统计n_passed/pass_rate通过数 / 通过率n_passed / n_scoredin_learning_zone0 n_passed n_scored——有失败也有通过每次失败都有可对照的通过样本summary()CI 契约返回固定键的摘要字典diff(baseline)与基线结果做逐任务 delta 对比输出 improved / regressedsave()/load()结果 JSON 落盘与回读供改完重跑看变化网格的可视化判读逻辑很直白全满行 已被掌握全空行 需要换干预手段部分行 学习区。示例打印的n_passed/n_scored正是把每个任务的网格行压缩成一行文本。六、测试日志解读校准过程与通过率分布TEST_LOG.md 记录了 2026-07-20 在 Agno 2.7.4 上针对gpt-5.5经OpenAIResponses的真实回归结果。这份日志同时是任务集校准calibration的方法论示范6.1 basic.pyPASS12/12 次尝试全部被评分0 次未评分观察到的通过率easy-product4/41.00、chained-product-a1/40.25、dual-chain-sum0/40.00校准过程第一版校准在易行和两个独立链式行上都饱和到 4/4于是用复合双链求和任务替换掉一个独立链式行后重跑——这正是任务集作为可靠性评测工具的核心用法一个全满的网格不是有用的可靠性示例必须让难度梯度产生分歧disagreement。6.2 from_jsonl.pyPASS从 JSONL 严格加载全部 3 行任务12/12 次尝试被评分0 次未评分观察到的通过率easy-product4/41.00、chained-product-a3/40.75、dual-chain-sum0/40.00日志特别指出独立链式行提供了真实的部分通过带——即中间难度的任务恰好落进学习区0 pass_rate 1这是评测数据最有价值的部分。6.3 with_metadata.pyPASS通过元数据挑选两个原始任务对象作为校准行8/8 次尝试被评分0 次未评分观察到的通过率chained-product-a3/40.75、chained-product-b3/40.75校准过程第一版元数据切片只产生 4/4 和 0/4 两行要么全过要么全挂因此被重新校准为包含两个独立的中间难度候选使两个任务都落入学习区。综合来看这份日志验证了任务集的三大能力id让每个网格行可命名、可对齐JSONL 让任务集可像数据一样被版本控制与评审metadata让校准切片成为可重复的筛选操作。而校准到分歧的过程本身就是评测可靠性的最佳实践——当任务难度梯度设计得当时通过率矩阵会清晰暴露模型能力的分界线。七、小结与延伸本文围绕 Agno Environments 的 Task Sets 主题完整覆盖了内联Task、严格 JSONL 加载、元数据选择三种任务组织方式并从源码层面解释了run_rollouts的隔离执行、指纹机制、结果指标与学习区判读逻辑。你可以沿着以下路径继续深入_01_first_environment/最小的完整环境typed output tasks CodeScorer K 次网格_03_code_scorer/把任务期望值转化为分数的三种评分方式_04_judge_scorer/当质量无法归结为确定性检查时改用裁判模型评分核心实现environment.pyTask/Environment/指纹、runner.pyrun_rollouts 与结果类型。记住任务集的设计哲学给每个任务一个持久 ID 和一个期望值ID 标注网格期望值驱动评分元数据组织切片而部分通过的行才是你真正要盯住的战场。【免费下载链接】agnoBuild, run, and manage agent platforms.项目地址: https://gitcode.com/GitHub_Trending/ag/agno创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考