news 2026/9/10 1:36:13

agno Environments 实战:用 TEST_LOG 验证约束式代码修复决策环境,并理解任务难度校准

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
agno Environments 实战:用 TEST_LOG 验证约束式代码修复决策环境,并理解任务难度校准

agno Environments 实战:用 TEST_LOG 验证约束式代码修复决策环境,并理解任务难度校准

【免费下载链接】agnoBuild, run, and manage agent platforms.项目地址: https://gitcode.com/GitHub_Trending/ag/agno

本文以 agno 仓库中cookbook/environments/_23_code_fixes/示例目录的测试日志为核心素材,完整还原三个"代码修复决策"环境示例的验证过程、逐任务通过率数据与难度校准思路。读完本文,你将理解如何用"封闭选择题 + 确定性打分"的方式验证 Agent 的代码修复判断力,看懂 5/6、6/8 这类通过率背后的 learning zone 含义,并能在自己的验证环境中复现同样的校准流程。

测试范围与验证环境

_23_code_fixes是 agno Environments(Agent 验证与数据集生成)cookbook 中第 23 个主题目录,其定位在 README.md 中给出:

Verify repair decisions against constrained, reviewable choices. These examples score patch and regression-test ids instead of executing model-written code.

也就是说,这组示例不执行模型写出的代码,而是把"代码修复"问题转写成有界决策题:给出一组候选补丁(A/B/C/D)与候选回归测试(T1/T2/…),让模型选出正确的补丁 id 与最小回归测试集,再用确定性 Python 函数打分。这样既验证了模型的修复判断力,又保证验证过程完全可复现、可审计——未信任的生成代码不会进入 cookbook 流程,真正需要执行代码的场景应放在单独设计的沙箱中。

该目录包含三个单文件可运行示例:

  • basic.py —— 为微妙的生产级 bug 选择"最小安全补丁";
  • patch_selection.py —— 在多个都能修复可见症状的补丁之间,依据并发/协议/兼容性不变量做出区分;
  • regression_tests.py —— 选出锁定该修复所需的最小精确测试集。

TEST_LOG.md 记录的验证基线是:

Tested 2026-07-20 against `gpt-5.5` through `OpenAIResponses`, Agno 2.7.4.

即 2026-07-20 使用OpenAIResponses驱动的gpt-5.5模型、在 Agno 2.7.4 版本下完成测试。三个示例文件的状态均为PASS。运行前提与命令(继承自 README 的 Run 章节):

python cookbook/environments/_23_code_fixes/basic.py python cookbook/environments/_23_code_fixes/patch_selection.py python cookbook/environments/_23_code_fixes/regression_tests.py

所有示例都需要设置OPENAI_API_KEY,每次模型调用均使用OpenAIResponsesgpt-5.5

三个示例的公共结构:Agent + Task + CodeScorer

TEST_LOG 中的每项 PASS 结论,背后都是同一套可验证的实现结构。以 basic.py 为例,核心组件有四部分:

1)带结构化输出的 Agent。模型输出被约束到一个 pydantic 模型上,保证打分时拿到的是类型化字段而非自由文本:

class FixChoice(BaseModel): patch_id: str = Field(description="The single selected patch id") test_ids: list[str] = Field( description="Minimal regression-test ids in lexical order" ) reason: str = Field(description="Why it satisfies every stated constraint") agent = Agent( model=OpenAIResponses(id="gpt-5.5", reasoning_effort="low"), output_schema=FixChoice, instructions=( "Act as a Python maintainer. Select exactly one listed patch. Prefer the " "smallest patch that satisfies every stated invariant. Also select the " "smallest listed regression-test set covering every named invariant. Return " "test ids in lexical order; do not invent code or tests." ), )

注意两条指令设计:reasoning_effort="low"说明这些验证题不依赖高推理档位,降低验证成本;instructions 中"不发明代码或测试"(do not invent code or tests)则把模型限定在"从给定选项中做判断",这是整个 constrained 验证模式成立的前提。

2)任务行 Task。每个Taskidinput(问题描述,内嵌候选补丁与候选测试)和expected(期望答案)组成。basic.py 定义了 5 个任务,覆盖五个真实而有微妙性的 Python 工程问题:

任务 id考察点期望答案
shared-task-cancellation共享asyncio.Task缓存中,单等待者取消不得取消共享加载;失败/取消需移除以便重试patchC(done 回调仅在 cancelled/exception 时 pop,再await shield(task)),测试T1, T2, T5
race-safe-path-openLinux 下带符号链接竞态的安全路径打开patchCopenat2+RESOLVE_BENEATH\|RESOLVE_NO_MAGICLINKS相对 root_fd),测试T1–T4全要
dst-fold-timeline跨越 DST 回拨重复小时的 elapsed-time 计算patchBfromtimestamp(timestamp + 3600)),测试T1
canonical-caseless-key大小写无关的用户 id 归一化(含多字符 case fold)patchCNFC(NFD(s).casefold())),测试T1, T2, T3
weakref-finalizer-captureweakref.finalize回调强引用资源导致永不触发patchC(只捕获整数 fd,显式 close 触发一次 finalizer),测试T1, T2, T3

这些任务的input本身就是一个精心设计的"评审材料":候选项之间只差关键的语义细节(如shield是否配合finallypop、RESOLVE_BENEATHRESOLVE_NO_SYMLINKS的差异、NFKCNFD+casefold的等价性差异),错误选项往往"看起来也能用"。这正是 TEST_LOG 中反复讨论"难度校准"的素材来源。

3)CodeScorer 确定性打分。每个示例用一个纯函数比对类型化输出与期望值,再包成CodeScorer

def patch_matches(run, expected) -> bool: return ( isinstance(run.content, FixChoice) and run.content.patch_id == expected["patch_id"] and run.content.test_ids == expected["test_ids"] ) environment = Environment( name="safe-code-fix-selection", agent=agent, tasks=(...), scorer=CodeScorer(patch_matches), )

CodeScorer的实现见 libs/agno/agno/scorer/code.py:它接受任意(run, expected) -> bool | float | Score的可调用对象,bool会被转换为Score(1.0, True)/Score(0.0, False)(布尔值绕过pass_threshold判断),float则以value >= pass_threshold(默认 0.5)判定通过。源码注释还特别说明:run.content在启用output_schema时是 pydantic 模型,"与期望值比对类型化字段是推荐形态"——这正是三个 code_fixes 示例采用patch_matches/tests_match这种字段级精确比对的原因。另外,CodeScorer.digest()会对打分函数源码与pass_threshold计算 sha256,作为环境指纹的一部分,保证"同一函数不同阈值"不会生成相同的指纹,这也是测试结果可跨版本比较的底层保障。

4)run_rollouts 批量执行。三个脚本的入口段统一是:

results = run_rollouts(environment, k=6, concurrency=6) # regression_tests.py 使用 k=8 print(results) for task_result in results.task_results: print(f"{task_result.task.id}: {task_result.n_passed}/{task_result.n_scored}")

run_rollouts是 libs/agno/agno/environments/runner.py 中的同步入口(默认k=8concurrency=4),内部通过asyncio.run委托给arun_rollouts,对同一Environment的每个任务独立发起 K 次 rollout,完成后再统一打分。结果对象中每个TaskResult暴露n_passed/n_scored,即日志中6/65/6这类数据的直接来源;未打分的尝试(如超时)不计入统计,pass_rate = n_passed / n_scored

TEST_LOG 逐文件结果解读

下面按 TEST_LOG 的结构逐节解读,并对照当前仓库中的最终脚本版本说明"日志 → 代码"的演化关系。

basic.py:K=6 的约束式安全补丁选择

Status:PASSDescription:Constrained safe patch selection at K=6.

TEST_LOG 记录的最终结果:

shared-task-cancellation 6/6 race-safe-path-open 5/6 dst-fold-timeline 6/6 canonical-caseless-key 6/6 weakref-finalizer-capture 6/6

日志原文揭示了两个校准动作:

  1. 补齐缺失的回归案例。"Final live run after adding the previously missing shared-task-cancellation regression case"——shared-task-cancellation任务最初缺少一个覆盖"取消共享任务本身"的回归测试项,补上 T5 之后该任务在 K=6 下全部通过。这对应当前 basic.py 中expected={"patch_id": "C", "test_ids": ["T1", "T2", "T5"]}的 T5("取消共享任务本身,验证所有等待者观察到取消,且下次调用会重试")。
  2. 从"只选补丁"升级为"补丁 + 最小回归测试集"。"Earlier three-choice and five-choice versions saturated at 6/6 on every row. Requiring the exact minimal regression-test ids as well as the patch id exposed the final 5/6 middle band." 即早期的三选项/五选项版本在每一行都 6/6 饱和;要求模型同时给出精确的最小回归测试 id 列表(字段级全等比对,多选、少选、乱序都算错)之后,race-safe-path-open才落到了 5/6 的中间地带。

对照 basic.py 中race-safe-path-open的期望["T1", "T2", "T3", "T4"]:它要求模型同时守住"竞态逃逸不可发生""安全内部相对符号链接仍可打开""procfs 魔法链接必须被拒绝""普通文件正常打开"四条不变量,且patch_matches要求列表严格全等——这正是该行最容易出波动的原因。5/6 并非缺陷,而是 agno Environments 期望的"learning zone"信号(见下文)。

patch_selection.py:K=6 的竞争性补丁选择

Status:PASSDescription:Competing patches with concurrency and protocol invariants at K=6.

测试结果:

single-flight-cache 6/6 stream-timeout 5/6 conditional-update 6/6 nested-exception-group 6/6 float-cache-key 6/6

日志的校准叙述:"Patch-id-only grids saturated even after two harder cases were added. Scoring the plausible runner-up as evidence exposed the 5/6 learning-zone row." 含义是:当模型只需要给出补丁 id 时,即使加了两道更难的题,整个网格依然全绿;最终手段是在输出 schema 中把"最接近的被拒选项"作为证据一并打分。对照 patch_selection.py 的 schema 与打分函数:

class PatchChoice(BaseModel): patch_id: str = Field(description="The selected patch id") runner_up_id: str = Field(description="The closest rejected patch id") rejected_risk: str = Field(description="The key risk in the closest alternative") def patch_matches(run, expected) -> bool: return ( isinstance(run.content, PatchChoice) and run.content.patch_id == expected["patch_id"] and run.content.runner_up_id == expected["runner_up_id"] )

"closest" 的判定规则被明确写进 instructions:违反的陈述不变量最少的选项即为最接近者,平局时取更早的补丁 id——这种可操作的定义让runner_up_id本身也变成可以确定性验证的字段。5 个任务各自考察一类工程判断:

任务 id陷阱期望 (patch / runner-up)
single-flight-cachesetdefault重复计算;递归请求不同 key;失败不缓存且锁状态不能永久残留C(短锁内装 Future 占位、锁外计算、失败后移除占位)/ B
stream-timeoutwait_for超时会取消共享迭代器的__anext__,破坏共享状态B(保留一个 pending Task,wait_for(shield(task)),超时后保留、关闭时取消)/ A
conditional-updateIf-Match 缺失 = 无条件更新;存在但空/畸形 = 失败;*匹配任意存在记录A(is not None判断 + 处理*+ 精确比较)/ B
nested-exception-group嵌套ExceptionGroup中的 Transient 叶子要逐个入队重试,fatal 叶子须保持子组形状与 tracebackB(except* TransientError递归入队 +except* BaseException: raise兜底)/ A
float-cache-key保留所有有限浮点区分(含+0.0vs-0.0),但所有 NaN 载荷归一到同一个 keyC(NaN 用固定哨兵 key,其余按x.hex())/ D

stream-timeout落在 5/6:该题的 A 选项(每次调用内联shield(anext))与 B 选项在文字上非常接近,模型偶尔会把 runner-up 判成 A 之外的选项,导致runner_up_id全等比对失败。

regression_tests.py:K=8 的精确最小回归测试选择

Status:PASSDescription:Exact minimal regression-test selection at K=8.

测试结果(日志中的任务名对应当前脚本中的完整 id):

cursor-boundary(-tests) 8/8 single-flight(-tests) 8/8 atomic-write-tests 8/8 overlapping-coverage 6/8

日志披露的校准过程最值得参考:

The initial three tasks, a 10-invariant cover, and a 16-invariant cover all saturated. A 20-invariant, 25-test unique minimum-cover task produced the observed partial row at K=8. A mutation-budget candidate also saturated and was removed rather than presented as useful evidence.

对照 regression_tests.py,前三个任务(cursor-boundary-testssingle-flight-testsatomic-write-tests)都是"从 4–5 个候选测试里选出精确最小集"的小规模题(期望分别为["T1","T2"]["T1","T2","T3"]["T1","T2","T5"]),在 K=8 下全部 8/8 饱和;早期甚至尝试过 10 不变量、16 不变量的覆盖题,同样饱和。最终留在脚本里的那道overlapping-coverage题把规模推到 20 个必须覆盖的不变量(I1–I20)× 25 个候选测试(T01–T25),且唯一最小覆盖["T02","T05","T07","T10","T18"]——任何多一个测试都会被 review 判负("extra tests fail review"),打分函数也只做列表全等比对:

def tests_match(run, expected) -> bool: return isinstance(run.content, TestSelection) and run.content.test_ids == expected

这道题在 K=8 下得到 6/8,成为该示例中的 learning zone 行。日志还特别记录:一个"mutation-budget"候选题也被尝试过,但它同样饱和了,因此被移除而不是作为证据保留——这体现了一条严格的验证文化:饱和(100% 通过)的题目对评估没有区分度,宁可删掉也不留"看起来有用"的噪声。注意该脚本调用run_rollouts(environment, k=8, concurrency=6),k 值与日志中 "at K=8" 一致。

如何解读这些通过率:learning zone 与难度校准

_23_code_fixes不是孤立存在的:它位于 agno Environments cookbook 的渐进式主题序列中(cookbook/environments/README.md 说明了整体设计),前承 SQL 验证示例_22_sql_generation/,后接_24_structured_extraction/。整个 Environments 体系的核心信号在 README 中定义得很明确:

The central signal is the learning zone: tasks with0 < pass_rate < 1. Tasks that always pass are already saturated, while tasks that always fail provide no successful trajectory to export.

这与 _06_learning_zone 的表述一致:对布尔打分器而言,learning zone 恰好就是0 < pass_rate < 1——"既没有全部通过(已饱和、无区分度),也没有全部失败(无法导出成功轨迹)"。据此回看 TEST_LOG 的三个网格,可以看到一份完整的校准记录:

  1. 全绿(6/6、8/8)的行不是"成功演示",而是提示信号。environments README 直言 "an all-full grid is a prompt to make the task harder"。dst-fold-timelinefloat-cache-keyatomic-write-tests等饱和行说明:以gpt-5.5+reasoning_effort="low"为基线,这些题目前处于"已掌握"区间,适合作为回归哨兵(防止退化),但不适合作为区分度评估项。
  2. 5/6、6/8 的行才是这套题集想保留的行。race-safe-path-open(5/6)、stream-timeout(5/6)、overlapping-coverage(6/8)落在中间地带,表明策略"有能力但不稳定",正是 learning zone 的目标区间。
  3. 校准手段是可枚举、可回溯的。从日志能还原出实际使用的杠杆:把选项数从 3/5 调整到 4、在输出 schema 中追加"必须精确命中的证据字段"(test_idsrunner_up_id)、放大集合覆盖问题的规模(10 → 16 → 20 不变量)、删除饱和的候选题。每一招都直接体现在当前仓库的三个脚本中,可以逐行复核。

需要强调的适用前提:这些通过率数字仅对"2026-07-20 / gpt-5.5 via OpenAIResponses / Agno 2.7.4 / reasoning_effort=low"这一组合成立。换一个模型、推理档位或 Agno 版本,网格几乎必然变化;这不影响方法论,但引用数字时必须带上基线条件。另外 environments README 也声明了本版本的边界:当前 release 执行的是"独立 rollout + 事后打分",不是实时 RL reward 循环,导出 JSONL 也不等于训练模型。

复现验证的最小步骤

若要基于当前仓库复现 TEST_LOG 中记录的验证,只需:

  1. OPENAI_API_KEY已配置的环境中运行三条命令(见文首 Run 段落),命令与 README.md 完全一致;
  2. 观察每个脚本打印的task_id: n_passed/n_scored行——例如race-safe-path-open: 5/6表示 6 次尝试中 5 次同时命中patch_idtest_ids两个字段;
  3. 对照 TEST_LOG.md 的基线网格:出现整行饱和(全过)时,按本文所述的校准手段加难度;出现整行全挂时,检查任务描述是否对目标模型不可解,或先调低期望(如放宽为"选对补丁即得分")再逐步收紧字段级要求。

打分与执行链路的实现细节可在 libs/agno/agno/scorer/code.py(CodeScorer的 bool/float/Score 归一化与指纹摘要)和 libs/agno/agno/environments/runner.py(run_rollouts同步入口、TaskResultn_passed/n_scored/pass_rate统计语义)中进一步查证;TaskEnvironment的数据结构定义在 libs/agno/agno/environments/environment.py,其中Environment是 frozen dataclass,保证"结果确实来自这份任务集 + 打分器 + 策略对象",任务 id 在构造时即做唯一性校验。

小结

_23_code_fixes的 TEST_LOG 不是一份普通的"跑通了没"清单,它同时记录了验证对象的最终形态(三个约束式修复决策环境:补丁选择、竞争性补丁判别、最小回归测试选择)与到达该形态的校准轨迹(补齐回归案例、追加证据字段、放大集合覆盖规模、移除饱和题)。这套做法的通用价值在于:当你想评估 LLM 的"代码修复判断力"而不想在验证流程里执行不可信代码时,可以把问题压缩为"封闭选项 + 精确最小集 + 字段级全等打分",用 K 次独立 rollout 的通过率网格区分"已饱和 / learning zone / 不可解"三态,并持续把饱和题升级为更有区分度的版本。仓库中三个脚本与 TEST_LOG 的组合,恰好提供了每个环节可直接对照的样板。

【免费下载链接】agnoBuild, run, and manage agent platforms.项目地址: https://gitcode.com/GitHub_Trending/ag/agno

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 1:31:34

VL53L0X激光测距模块与STM32实战:从ToF原理到I2C调试全解析

简介&#xff1a;VL53L0X与STM32激光测距开发包&#xff0c;将ST的飞行时间激光测距传感器与意法半导体Cortex-M3内核的STM32F103VET6结合&#xff0c;为需要非接触式精确测距的嵌入式项目提供可复用工程&#xff0c;适合熟悉I2C外设与GPIO配置的开发者参考。包内共239个文件&a…

作者头像 李华
网站建设 2026/9/10 1:30:49

WPF自学手册:从源代码到MVVM的完整学习路线

简介&#xff1a;这份源代码是《葵花宝典 WPF自学手册》随书光盘的完整内容&#xff0c;适合刚开始接触WPF或希望系统梳理桌面开发知识的开发者。包内共有1713个文件&#xff0c;包含655个C#源码、385个XAML界面布局、109个工程文件和105个解决方案&#xff0c;并附带可直接运行…

作者头像 李华
网站建设 2026/9/10 1:30:42

WPF中流畅显示OpenCV图像:高级显示控件2.0实现解析

做机器视觉和工控上位机的朋友&#xff0c;一定对这个问题不陌生&#xff1a;OpenCV把图像处理好了&#xff0c;怎么流畅地显示到WPF界面里&#xff1f;直接用PictureBox塞进WindowsFormsHost&#xff0c;缩放交互又难受&#xff0c;WPF的透明和叠加层还会被“吃掉”&#xff1…

作者头像 李华
网站建设 2026/9/10 1:30:25

基于背对背MMC的电力质量调节系统Simulink仿真建模与控制策略

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华