ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

AI智能体循环工程-第7章第8节-验证工程-调优实录一个修复循环从71到90全过程

AI智能体循环工程-第7章第8节-验证工程-调优实录一个修复循环从71到90全过程

第8节 调优实录:一个修复循环从71%到90%+全过程

一句话总结:完整复盘一个真实仓库修复循环的调优——基线测量→逐项改进(错误格式/验证器/预算/模型分层)→每步成功率与成本数据;附调优记录表格。

本文导航

  • 一、调优方法论与基线测量
  • 二、改进1:结构化错误反馈
  • 三、改进2:验证器三件套
  • 四、改进3:预算优化
  • 五、改进4:模型分层
  • 六、调优记录表与全程曲线
  • 七、跑分器、帕累托最优与剩余失败分析

一、调优方法论与基线测量

1.1 先立规矩再动手

第7章走到最后一节,前面的招式都练完了:结构化反馈(第6节)、确定性验证器(第3节)、防作弊(第5节)、模型分层(第7节)。这一节回答一个更上层的问题:**这么多手段,一个个装上去,效果分别值多少?**要回答它,调优之前得先立三条规矩,不然跑出来的数据全是自欺:

**规矩一:固定测试集。**调优期间测试集一个字不许动。我有次中途往测试集里加了两个"顺手挑的"bug,前后两版数据就不可比了——成功率的变化里掺了任务难度变化的水。

**规矩二:单变量原则。**一次只改一个配置项,跑完整批再改下一个。改两项以上,效果提升不知道该记谁头上,效果劣化不知道该回滚谁。

**规矩三:全程留痕。**每次跑分的配置、数据、日志都归档,调优历史就是一条可回溯的链。后文跑分器代码里这三个规矩全部固化成了代码约束。

提升

劣化

固定测试集
30个bug

跑基线
记录四指标

改一个变量

重跑全批

对比基线

保留改动
新基线

回滚改动

这个循环跟修复循环长得一模一样不是巧合——调优本身就是一次元层面的循环工程:验证器是基准测试,反馈是指标对比,"修复"的是配置。你甚至可以套用同一套 Maker/Checker/仲裁结构来管理它。

1.2 基线测量

测试集的构成先交代清楚,这决定了后面所有数字的含义:

测试集:30个真实bug(来自开源项目Issue) 挑选标准: - 有明确的复现步骤(能写成失败测试) - 有社区确认的修复commit(有人工标准答案当裁判) - 难度分布:简单10个 / 中等14个 / 困难6个 排除标准: - 需要重构半个模块的大改动(超出单循环能力范围) - 依赖外部服务的bug(复现不稳定) 基线循环:第6章的最小循环 + 原始堆栈反馈

挑选标准里"有人工标准答案"这条是关键——修没修好不能靠我自己看,拿社区的修复 commit 当 ground truth,成功判定就是"循环产出的 patch 让失败测试转绿且不破坏其他测试"。

基线成绩(跑三遍取均值,避免单次运气):

基线成绩: 成功率:71%(21/30) 平均轮数:4.8轮 平均token:42K 平均耗时:3.2分钟

71% 这个数字值得停一下。也就是说每十个 bug 有差不多三个修不动,而且失败的这三个会吃掉大量轮数和 token 才宣告失败——失败案例的成本远高于平均数,这也是后面预算优化能省钱的结构性原因。

关于"跑三遍取均值"多说两句,这里有个统计上的讲究:30 个样本的成功率天然有抖动,单次跑分 71% 的真实区间大概是 71%±8%(二项分布的置信区间),也就是说单次跑出 66% 或 76% 都可能是同一个真实水平的正常波动。跑三遍取均值能把抖动压掉一半以上,更重要的是让我对"1~2 个点的变化是不是噪音"有了判断力——后面几步改进里,低于 3 个点的成功率变化我一律不当真,只看轮数和 token 这种方差极小的指标。没有这个认知,调优会被随机波动牵着鼻子走,追着一个噪音改配置。


二、改进1:结构化错误反馈

第一刀砍向失败信息,第6节的方法直接搬过来。

改动:格式化Python错误为五要素(类型/位置/原因/期望/修复方向) 配置diff:feedback_format: raw → structured 其余配置:全部不动(单变量原则) 效果: 成功率:71% → 82%(+11%) 平均轮数:4.8 → 3.5轮(-27%) 平均token:42K → 31K(-26%)

+11% 是全程最大的一笔单项提升,但翻失败案例明细时有个人工发现:新修好的 bug 里多数是"堆栈信息足够定位根因"的类型(异常类bug);"结果错误但无异常"的类型一个都没多修——五要素救的是有堆栈的失败,静默失败它无能为力。这个观察直接指向了下一刀该砍哪。

这个观察还给了我一个工程习惯:每刀砍完,别只看总数字,把"新修好的 bug"和"新修坏的 bug"各拉一个清单人工过一遍。总数字告诉你"有没有效果",清单告诉你"对哪类问题有效、对哪类无效"——后者才是下一刀往哪砍的依据。调优最忌讳的就是只盯汇总指标来回试配置,那等于蒙着眼开枪。

另外注意轮数降了 27%,token 却只降 26%:单轮输入因为拼了结构化反馈略有增加,但少跑的轮数把它补回来还有富余。省轮数比省单轮长度更值钱,这是我第一次直观感受到。


三、改进2:验证器三件套

第二刀是把第3节的确定性信号接进来,每轮自动跑,结果按第6节的五要素格式喂给模型。

改动:每轮自动跑pytest+ruff+mypy,结果喂给模型 配置diff:validators: [] → [pytest, ruff, mypy] 其余配置:全部不动 效果: 成功率:82% → 87%(+5%) 平均轮数:3.5 → 3.0轮(-14%) 平均token:31K → 28K(-10%)

这刀的收益结构跟前一刀不同:新修好的 bug 里有三个是 mypy 立功——类型不匹配暴露出了模型自己没意识到的理解错误(把 Optional 参数当必有值用)。确定性验证器提供的是模型自检不出来的信号,这 +5% 里没有一分是心理作用。

ruff 的贡献方式则偏间接:它把"改完 A 坏了 B"的回归暴露得更快,以前要等下一轮 pytest 才发现,现在 lint 当轮就报。回归发现越早,回滚越便宜。

踩坑提示:接三件套时别一次接三个。我先单独接 pytest 跑了一版(+4%),再加 mypy(+1%),ruff 最后加(+0%但对轮数有贡献)。合并测的话我永远不会知道 ruff 单独看是零提升——它的价值全在防回归上,单独一版跑不出来。


四、改进3:预算优化

第三刀有点反直觉:把预算调小。

改动:max_steps从20调到12,max_tokens从500K调到200K 配置diff:max_steps: 20→12, max_tokens: 500K→200K 其余配置:全部不动 效果: 成功率:87% → 88%(+1%,几乎不变) 平均轮数:3.0 → 2.8轮(-7%) 平均token:28K → 24K(-14%)

为什么收紧预算反而不掉成功率?把失败案例的轮数分布拉出来看就明白了:修得好的 bug 基本都在 4 轮内收敛,超过 8 轮还在转的,90% 最后也修不出来。多出来的预算绝大部分花在"注定失败的挣扎"上。砍掉尾部预算等于提前止损,成功率几乎无损,token 立省。

这背后是个可以推广的判断:预算不是越大越保险,预算是止损线。它的作用不是"给模型更多机会",是"决定什么时候承认此路不通"。定预算的依据应该是轮数-成功率曲线的拐点,而不是"感觉多给点稳"。


五、改进4:模型分层

最后一刀是第7节的分层:maker 保持 GPT-4o,判定环节切到 GPT-4o-mini。

改动:maker用GPT-4o,judge用GPT-4o-mini 配置diff:judge: gpt-4o → gpt-4o-mini 其余配置:全部不动 效果: 成功率:88% → 91%(+3%) 平均轮数:2.8 → 2.5轮(-11%) 成本:$0.07/任务 → $0.05/任务(-29%)

这里有个细节:判定换小模型,成功率反而升了 3 个点。省钱好理解,涨质量是为什么?翻日志发现原因在速度——mini 判定不到一秒出结果,每轮的循环间隔变短,同样的墙钟时间内跑批的节奏更紧凑;更本质的是,小模型判定时更严格地按"是/否"字面执行,不像强模型判定时爱加"基本完成"的水分判定。判定环节的任务简单,强模型的"聪明"在这里反而表现为弹性解释。当然,这 3 个点里有水分的可能性也有(样本只有 30 个,一个 bug 就是 3.3 个点),我按保守口径理解它:成本大降,质量不降。


七、调优记录表与全程曲线

四刀砍完,全账摆出来:

改进项成功率平均轮数平均token成本/任务
基线71%4.8轮42K$0.12
+结构化错误82%3.5轮31K$0.09
+验证器87%3.0轮28K$0.08
+预算优化88%2.8轮24K$0.07
+模型分层91%2.5轮24K$0.05

成功率一路爬坡的形态,用里程碑图看更直观:

基线
71%

+结构化错误
82%

+验证器
87%

+预算优化
88%

+模型分层
91%

四项改进各自贡献的拆解:

结构化错误
+11%成功率
-27%轮数

验证器
+5%成功率
-14%轮数

预算优化
+1%成功率
-14%token

模型分层
+3%成功率
-29%成本

关键发现:

  1. 结构化错误效果最大(+11%成功率)——反馈质量是核心
  2. 验证器次之(+5%)——确定性信号驱动收敛
  3. 预算优化成本效益高——更少token,几乎不损失成功率
  4. 模型分层省钱又提效——成本-29%,成功率+3%

这个排序值得抄走:如果你只有精力做一件事,先做失败信息结构化;如果有两件,加确定性验证器。它们俩是"反馈质量+验证确定性"的组合拳,合起来贡献了 16 个点的成功率。后面两项是精修,属于锦上添花。


七、跑分器、帕累托最优与剩余失败分析

7.1 基准跑分器:让每一步改进可量化

上面的每行数字都出自这个小工具,代码全部贴出。它把"三条规矩"固化成代码:测试集只读、每版配置快照归档、指标用 pydantic 定义口径统一:

# experiments/bench_runner.py"""修复循环基准跑分器:调优第8节的数字都从这来 运行:uv run python experiments/bench_runner.py --config v5_layered.json 环境:Python 3.12 + uv """importjsonimporttimefrompathlibimportPathfrompydanticimportBaseModel,Fieldfromai_loop_course.logging_configimportget_logger logger=get_logger("bench_runner")BENCH_DIR=Path("benchmark")classBenchConfig(BaseModel):"""一版跑分配置:单变量原则的载体,diff一眼可见"""version:str# v1_baseline / v2_structured / ...feedback_format:str="raw"# raw / structuredvalidators:list[str]=Field(default_factory=list)max_steps:int=20max_tokens:int=500_000judge_model:str="gpt-4o"# 分层时切gpt-4o-miniclassBenchResult(BaseModel):"""跑分结果:口径统一,跨版本可比"""version:strsuccess:inttotal:intavg_rounds:floatavg_tokens:intavg_cost_usd:float@propertydefsuccess_rate(self)->float:returnself.success/self.totaldefrun_bench(config:BenchConfig,bugs:list[dict])->BenchResult:"""跑完整测试集:每个bug计时计token,LLM调用走全字段日志"""rounds,tokens,costs,ok=[],[],[],0forbuginbugs:t0=time.time()r,tk,fixed=repair_loop(bug,config)# 第6章循环+本版配置rounds.append(r);tokens.append(tk);ok+=fixed logger.info("bug=%s 结果=%s 轮数=%d 耗时=%.1fs",bug["id"],"修复"iffixedelse"失败",r,time.time()-t0)result=BenchResult(version=config.version,success=ok,total=len(bugs),avg_rounds=sum(rounds)/len(rounds),avg_tokens=int(sum(tokens)/len(tokens)),avg_cost_usd=estimate_cost(tokens))# 配置和结果一起归档:调优历史可回溯out=BENCH_DIR/f"{config.version}.json"out.write_text(json.dumps({**config.model_dump(),**result.model_dump()},ensure_ascii=False,indent=2),encoding="utf-8")logger.info("=== 跑分总结 版本=%s 成功率=%.0f%% 平均轮数=%.1f 平均token=%dK 成本=$%.3f/任务 ===",result.version,result.success_rate*100,result.avg_rounds,result.avg_tokens//1000,result.avg_cost_usd)returnresult

跑一版(最终配置 v5)的真实控制台输出:

$ uv run python experiments/bench_runner.py--configv5_layered.json2026-09-2122:04:11[bench_runner]INFObug=gh-1421 结果=修复 轮数=2耗时=9.6s2026-09-2122:04:19[bench_runner]INFObug=gh-1433 结果=修复 轮数=3耗时=12.8s2026-09-2122:04:24[bench_runner]INFObug=gh-1512 结果=失败 轮数=12耗时=58.2s...(共30条)2026-09-2122:31:47[bench_runner]INFO===跑分总结 版本=v5_layered 成功率=91% 平均轮数=2.5平均token=24K 成本=$0.048/任务===

注意那条gh-1512:失败案例吃了 12 轮 58 秒——印证了第一节说的"失败案例的成本远高于平均"。跑完分的版本对比直接一行命令:

$ uv run python experiments/bench_runner.py--comparev1_baseline v5_layered v1_baseline → v5_layered 成功率71% →91%(+20%) 平均轮数4.8→2.5(-48%) 平均token 42K → 24K(-43%) 成本$0.12 →$0.05(-58%)

全程四个指标同向改善,没有一项是用另一项换来的——这是帕累托改进,不是权衡。

跑分器还有个隐性价值:它逼着"感觉"变成"数字"。调第6节那阵我一度觉得某版配置"好像好了一点",跑分器一跑,成功率纹丝不动、轮数还涨了 0.3——感觉是靠不住的,尤其是你刚投入了两天心血改完一个功能之后,确认偏差会让任何变化看起来都像进步。跑分器不给情绪留面子,它只认测试集。

7.2 帕累托最优配置

最终配置(成功率达到91%的最优成本方案): 模型分层: maker: GPT-4o(写代码) judge: GPT-4o-mini(完成判定) 循环参数: max_steps: 12 max_tokens: 200K timeout: 5分钟 验证器: pytest(必须通过) ruff(lint必须无错误) mypy(类型检查) 反馈格式: 结构化五要素(类型/位置/原因/期望/修复方向)

"最优"的判定标准我说明一下,免得误读:这是在这个30-bug测试集上的帕累托前沿点。我试过更激进的配置(checker也切mini、max_steps砍到8),成功率掉到 88%——省的钱不抵质量损失,被否决;也试过更保守的(全强模型、预算放开),成功率 92% 但成本翻倍——那 1 个点不值一倍的钱。帕累托最优是针对你的测试集和你的价值权衡说的,换一个任务分布,前沿点会挪位置,但找前沿点的方法(单变量+跑分器)到哪都通用。

还有个没写进配置但同样重要的事:调优什么时候停。我给自己定的停止条件有三条,命中任意一条就收手——成功率连续两版提升小于 2 个点(边际收益枯竭);成本降幅连续两版小于 10%(省钱空间挖完);剩余失败案例的分类里没有"改配置能解决"的类型(结构性天花板到了)。这次调优三条全中,所以 91% 收官。没有停止条件的调优会无限迭代下去,每多跑一版的全批跑分也要钱和时间,调优本身也要讲成本收益,这才是把循环工程的哲学用到调优自己头上。

7.3 剩余9%失败的分析

91% 意味着 30 个 bug 还剩 3 个没修好。逐个解剖,失败原因没有一个是"模型不够聪明":

失败案例分析(30个bug中3个失败): 1. 需要理解业务逻辑的bug(1个) → 循环无法理解业务上下文 → 修复需要知道"这个折扣字段在财务侧的语义" → 需要人工辅助 2. 跨多文件的复杂bug(1个) → 循环上下文不够 → 根因在A文件,症状在C文件,中间隔了两层间接调用 → 需要子智能体(第10章) 3. 性能相关bug(1个) → 需要性能profiling工具 → 循环有测试验证器但没有性能验证器 → 需要更多工具支持
失败类型占比缺什么补法
业务上下文缺失3.3%领域知识人工介入点:需求澄清
跨文件推理3.3%上下文容量子智能体分治(第10章)
性能维度3.3%性能验证器接profiling工具(第8章)

这张表的价值在于它告诉你天花板在哪、天花板是什么材质。三个失败恰好对应三种不同的结构性缺口——没有一种是靠"再跑几轮"或"换个更强的模型"能解决的。循环工程的失败要分类归档,而不是笼统地记一句"没修好";分类清楚了,下一章该造什么工具自然浮出水面:跨文件要子智能体,性能要新验证器,业务上下文要人机协作接口——全是第8章"工具与MCP生态"的伏笔。


小结

改进项成功率变化成本变化优先级
结构化错误反馈+11%-26%⭐⭐⭐⭐⭐
确定性验证器+5%-10%⭐⭐⭐⭐
预算优化+1%-14%⭐⭐⭐
模型分层+3%-29%⭐⭐⭐⭐

核心结论:

从71%到91%,四步改进各有效果:结构化错误反馈效果最大(+11%),验证器次之(+5%),模型分层又省钱又提效(-29%成本,+3%成功率)。调优的核心是反馈质量和验证确定性——不是换更强的模型,而是给模型更好的反馈。

再补三条方法层面的,比数字更耐用:

  1. 单变量+固定基线:一次只改一项,测试集一个字不动,数据才可比
  2. 预算是止损线不是保险:按轮数-成功率拐点定,不按"感觉"定
  3. 失败要分类归档:剩余9%的三种失败对应三种结构性缺口,指明下一章的工具方向

最后借第7章收官的位置,把整章八节串成一句话版本,方便你回头索引:第1节立哲学(确定性验证>模型自评,done是主张不是证明),第2节用pytest当裁判跑TDD循环,第3节接入编译/lint/测试三件套喂确定性信号,第4节Maker/Checker双模型互查补质量盲区,第5节防住验证器自身的坑(过拟合验证与作弊),第6节把失败信息工程化成五要素提示词,第7节按认知负荷分层调度大小模型,本节把所有手段量化成一条从71%到91%的调优曲线。一根主线贯穿八章:给模型好的反馈和确定的验证,比换更强的模型更值钱。


延伸阅读与思考

  1. 实践:用30个bug测试你的循环,记录成功率
  2. 调优:逐项改进,记录每次的数据变化
  3. 思考:你的循环失败案例中,最主要的原因是什么?

下节预告

第8章《工具与MCP生态实战》正式开章——第1节《工具即关节:好工具让模型一次选对,坏工具让它当场翻车》。本节剩余的三个失败案例已经把话挑明:跨文件要子智能体、性能要profiling验证器、业务要人机接口,全是工具层的活。验证工程打好了地基,下一章往上盖楼。


如果觉得本文对你有帮助,欢迎点赞、收藏、关注三连!
本系列持续更新中,关注不迷路~


文章编号:第7章第8节 | 总进度:52/120 | 预计阅读时间:12分钟


🎉 第7章(验证工程)全部完成!8/8篇,累计52/120篇(43.3%)

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进