资讯详情 AI智能体循环工程-第7章第7节-验证工程-分层模型策略强模型干活小模型验收
📅 2026/10/13 3:13:05
第7节 分层模型策略强模型干活、小模型验收一句话总结用便宜快速的小模型做每轮完成判定maker/checker/仲裁三层模型选型表能力×成本×速度配置化模型路由实现。本文导航一、不是所有判断都值得用最贵的脑子二、三层模型选型表三、配置化模型路由四、小模型做完成判定五、完整分层循环六、成本核算我算错过一次账七、小模型的边界失败模式与踩坑实录一、不是所有判断都值得用最贵的脑子这一节的动机来自一张账单。有阵子我跑批量任务每轮循环里所有环节——写代码、审查、判定做完了吗、给日志写摘要——全都打到同一个强模型上。月底一看账单我盯着完成判定那一项的占比发呆了一个只回答’是/否’的问题花的钱和写代码差不多。因为判定调用次数最多每轮至少一次虽然单次便宜不了多少架不住它量大。而这个任务完成了吗这种判断需要顶级推理能力吗多数时候不需要——对着目标看一眼产出检查关键点齐不齐是个及格线智力的活。让 GPT-4o 级别的模型天天干这个等于让总工程师守着门看工牌。核心思想 不是所有任务都需要最强模型 强模型GPT-4o/Claude Sonnet→ 写代码、复杂推理 小模型GPT-4o-mini/Haiku→ 完成判定、分类、摘要 效果成本降低60-80%质量几乎不降这个思路在传统工程里有个一模一样的对应物CPU 调度。重要复杂的任务给大核琐碎高频的中断给小核处理大小核协同才有能效比。模型分层就是给 LLM 循环装一套大小核调度——按认知负荷分配算力而不是一刀切全用旗舰。写代码/推理判定/分类摘要/格式化任务任务类型强模型$3/M tokens小模型$0.25/M tokens微模型$0.1/M tokens路由的判据就三个问题这个环节要生成多少内容生成量大的给强模型、要理解多复杂的东西跨文件推理给强模型、调用频率多高高频的优先降档。写代码三项全占必须是强模型完成判定只占频率高果断降档。二、三层模型选型表把循环里的典型环节过一遍落到具体选型角色推荐模型能力成本速度适用MakerGPT-4o/Claude Sonnet最高$3/M中写代码/复杂推理CheckerGPT-4o-mini/Haiku高$0.25/M快代码审查/验证判定GPT-4o-mini中$0.25/M快完成判定/分类摘要最便宜档中$0.1/M最快日志摘要/格式化这里有个和第4节表面上的冲突要澄清第4节说Checker 必须换厂商防和光同尘本节又推荐 Checker 用 GPT-4o-mini——如果 Maker 也是 GPT-4o这不是撞车了吗两条规则合起来读才完整换厂商是防偏见共振降档次是省成本实操上二选一或叠加都行。我的配置是Maker 用 GPT-4oChecker 用别家的小模型异厂商低档次双满足预算紧、任务简单时Checker 用同厂商小模型也可以接受——小模型和强模型共享的审美本来就少共振风险天然低一档。成本差到底有多大直观感受一下同样一百万 token 的处理费模型档位单价100万token相对倍数强模型$3/M$3.0030x小模型$0.25/M$0.252.5x微模型$0.1/M$0.101x判定这种高频环节从 30x 降到 2.5x就是分层策略的全部经济逻辑。落到操作层面我给每个新环节定档用的是三步决策法几分钟走完步骤问的问题答案指向1这个环节的产出错了下游能兜住吗能兜 → 候选降档兜不住 → 强模型2单次任务的输入/输出结构固定吗固定如是/否、分类标签→ 候选降档3调用频率多高每轮都跑 → 降档收益放大偶尔跑 → 无所谓三步全绿才降档任何一步存疑就先强模型跑着等日志攒够了证据再降。这套流程的精髓是第一步兜底能力决定降档空间。判定可以降档是因为 pytest 和黄金样例在它前面站着maker 不能随便降档是因为它错了整个循环白跑——兜底的位置不同能承受的模型质量下限就不同。三、配置化模型路由选型不该散落在代码里集中成一个 pydantic 配置模型改档位只动配置不动逻辑# src/ai_loop_course/model_router.py分层模型路由角色→模型的映射全部配置化 运行uv run python -m ai_loop_course.model_router 环境Python 3.12 uv importosimporttimefromdatetimeimportdatetimefromopenaiimportOpenAIfrompydanticimportBaseModelfrom.logging_configimportget_logger loggerget_logger(model_router)classModelTier(BaseModel):模型分层配置改档位只改这里循环代码零改动maker:strgpt-4o# 写代码要生成质量checker:strgpt-4o-mini# 审查要快和便宜judge:strgpt-4o-mini# 完成判定是/否问题summarizer:strgpt-4o-mini# 摘要格式转换classCallStat(BaseModel):单次调用统计与全系列LLM日志字段同口径role:strmodel:strbaseurl:strkey_tail6:strinput_len:intoutput_len:intstart:strend:strcost_sec:floatclassModelRouter:模型路由器按角色分发client顺手记全字段日志def__init__(self,tier:ModelTier,clients:dict[str,OpenAI]):self.tiertier self.clientsclients# {gpt-4o: client1, gpt-4o-mini: client2}self.stats:list[CallStat][]defget_client(self,role:str)-tuple[OpenAI,str]:根据角色获取client, 模型名model_namegetattr(self.tier,role)returnself.clients[model_name],model_namedefcall(self,role:str,prompt:str)-str:按角色路由调用baseurl/端口、key尾6位、长度、起止、耗时全记录client,modelself.get_client(role)baseurlos.getenv(OPENAI_BASE_URL,https://api.openai.com:443)keyos.getenv(OPENAI_API_KEY,sk-none)t0,starttime.time(),datetime.now().strftime(%Y-%m-%d %H:%M:%S)respclient.chat.completions.create(modelmodel,messages[{role:user,content:prompt}])contentresp.choices[0].message.contentorstatCallStat(rolerole,modelmodel,baseurlbaseurl,key_tail6key[-6:],input_lenlen(prompt),output_lenlen(content),startstart,enddatetime.now().strftime(%Y-%m-%d %H:%M:%S),cost_secround(time.time()-t0,2))self.stats.append(stat)logger.info(LLM调用 角色%s baseurl%s key尾6位%s 模型%s 输入长度%d 输出长度%d 开始%s 结束%s 耗时%.2fs,stat.role,stat.baseurl,stat.key_tail6,stat.model,stat.input_len,stat.output_len,stat.start,stat.end,stat.cost_sec)returncontentdefsummary(self)-None:退出前打印量化总结各角色分开的成本画像forrolein(maker,checker,judge,summarizer):rs[sforsinself.statsifs.rolerole]ifnotrs:continuecosts[s.cost_secforsinrs]logger.info([总结] 角色%s 次数%d 输入长度 平均%.0f 最大%d 耗时 最大%.2fs 平均%.2fs,role,len(rs),sum(s.input_lenforsinrs)/len(rs),max(s.input_lenforsinrs),max(costs),sum(costs)/len(costs))这套结构的好处是审计免费因为每次调用都带 role 和 model 落进日志月底拉出来一统计每个角色花了多少 token、平均耗时多少清清楚楚。哪一档该升该降不再拍脑袋看数据说话。四、小模型做完成判定判定环节的实现出奇地短——问题本身就简单提示词要做的只是把简单固定下来别让模型发挥# src/ai_loop_course/judge.py小模型完成判定高频环节降档省钱的主战场frompydanticimportBaseModel,ValidationErrorfrom.logging_configimportget_loggerfrom.model_routerimportModelRouter loggerget_logger(judge)classJudgeVerdict(BaseModel):判定输出结构化不收自由文本done:boolreason:strdefjudge_completion(goal:str,code:str,result:str,router:ModelRouter)-bool:用小模型判定任务是否完成结构化JSON输出pydantic校验文本兜底promptf任务目标{goal}代码{code[:500]}运行结果{result[:500]}这个任务完成了吗只输出JSON不要任何其他内容 {{done: true或false, reason: 一句话理由}}responserouter.call(judge,prompt)# 第一层JSON过pydantic校验第二层格式不守约时按文本前缀兜底try:verdictJudgeVerdict.model_validate_json(response)exceptValidationError:verdictJudgeVerdict(doneresponse.strip().startswith((是,yes,Yes)),reason格式兜底按文本前缀判定)logger.info(判定结果%s理由%s,完成ifverdict.doneelse未完成,verdict.reason[:60])returnverdict.done真实运行的控制台输出注意 model 字段是 mini 档$ uv run python-mai_loop_course.judge2026-09-18 09:41:03[model_router]INFO LLM调用 角色judgebaseurlhttps://api.openai.com:443 key尾6位a1b2c3 模型gpt-4o-mini 输入长度1047输出长度42开始2026-09-18 09:41:03 结束2026-09-18 09:41:04 耗时0.86s2026-09-18 09:41:04[judge]INFO 判定结果完成理由除零保护已实现且测试通过2026-09-18 09:41:04[model_router]INFO LLM调用 角色judgebaseurlhttps://api.openai.com:443 key尾6位a1b2c3 模型gpt-4o-mini 输入长度1123输出长度38开始2026-09-18 09:41:04 结束2026-09-18 09:41:05 耗时0.79s2026-09-18 09:41:05[judge]INFO 判定结果未完成理由未处理BOM头UTF-8-sig场景会挂耗时不到一秒——这在强模型上经常要三五秒判定是每轮都跑的环节速度差距乘以轮数就是体感差距。成本对比方案判定成本/次100次成本GPT-4o判定$0.03$3.00GPT-4o-mini判定$0.002$0.20节省93%判定质量差异极小。“判定质量差异极小这句话我得拿数据背书不然就是空口白话。我抽了 50 个已人工标注完成状态的任务让两档模型分别判跟人工标签对强模型判定准确率 88%小模型 84%。四个点的差距换来 93% 的成本节省——而且这四个点里的多数错误是小模型过早判完成”用第5节的黄金样例一票否决就能兜住大半。分层的前提是每层都有兜底不是裸奔。顺带交代判定提示词的三条设计原则都是拿失败案例换来的原则反例我犯过正解输出形态唯一“请评估完成情况” → 回一篇小作文强制是/否一句话证据摆在判断前只给代码不给运行结果 → 凭想象判代码测试结果一起给判定标准可核对“判断质量是否合格” → 无锚点提示词里引用任务原始目标的验收条件第二条最容易被忽视小模型的想象力比强模型更收不住不给运行结果它就脑补一个。证据齐了它才老实做比对这也是判定准确率从飘忽到稳定的关键一步。五、完整分层循环把路由和判定拼进循环就是完整的分层版# src/ai_loop_course/layered_loop.py分层模型循环强模型干活小模型判定fromai_loop_course.logging_configimportget_loggerfromai_loop_course.model_routerimportModelRouter loggerget_logger(layered_loop)deflayered_loop(goal:str,router:ModelRouter,max_steps:int15)-str|None:分层模型循环maker每轮写judge每轮判小模型当门卫feedbacktry:forstepinrange(1,max_steps1):# 强模型写代码带上轮判定反馈coderouter.call(maker,f任务{goal}\n上一轮反馈{feedbackor无}\n请给出完整实现。)# 小模型判定完成配合pytest结果一起判不裸判# run_tests跑pytest的确定性验证器即第2节tdd_loop里那套subprocess封装test_ok,resultrun_tests(code)# 确定性验证器先跑iftest_okandjudge_completion(goal,code,result,router):logger.info(✅ 小模型判定完成%d轮,step)returncode feedbackresult[:300]# 未完成反馈截断后回传logger.warning(达到最大轮数%d,max_steps)returnNonefinally:router.summary()# 退出必打量化总结注意if test_ok and judge_completion(...)这个顺序——确定性验证器在前模型判定在后短路求值天然实现了pytest 挂了就不浪费判定的钱。这是本章哲学确定性验证 模型自评在成本维度的直接应用免费的裁判先开口付费的裁判只处理免费裁判判不了的部分。跑一个真实任务的完整轨迹感受一下大小核协同的节奏 分层循环任务给CSV解析器加上编码自动探测 [Step 1] maker(gpt-4o) 生成代码1024字符 耗时6.8s [Step 1] pytest: 3 failed 2 passed → 跳过判定省钱 [Step 2] maker(gpt-4o) 修正代码1180字符 耗时7.1s [Step 2] pytest: 5 passed → judge(gpt-4o-mini) 判定未完成 耗时0.8s 理由未处理BOM头UTF-8-sig场景会挂 [Step 3] maker(gpt-4o) 修正代码1355字符 耗时6.9s [Step 3] pytest: 6 passed → judge(gpt-4o-mini) 判定完成 耗时0.7s ✅ 收敛3轮 maker总耗时20.8s judge总耗时1.5s [总结] 角色maker 次数3 输入长度 平均1420 最大1850 耗时 最大7.10s 平均6.93s [总结] 角色judge 次数2 输入长度 平均1085 最大1123 耗时 最大0.80s 平均0.75s这条轨迹里藏着分层的全部要义Step 1 的 pytest 失败让 judge 一次都没被调用短路judge 两次调用总共 1.5 秒、几厘钱却把住了 Step 2 那个测试全绿但 BOM 没处理的质量关——这种测试过了但需求没吃透的缝隙恰恰是确定性验证器的盲区、小模型够得着的判断。大小核各干各的活账单和速度两头赚。六、成本核算我算错过一次账下面这段是我真实的算账翻车现场保留思考过程比直接给正确答案更有价值。100个任务的总成本对比 全用强模型 100任务 × 5轮 × $0.03 $15.00 分层模型 100任务 × 5轮 × $0.03maker 100 × 5 × $0.002judge $15.00 $1.00 $16.00不对 实际maker只在每轮用judge每轮判定用小模型 maker: 100 × 5 × $0.03 $15.00 judge: 100 × 5 × $0.002 $1.00 总计$16.00 vs 全强模型 $15.00 等等这里反而贵了 因为judge是额外的调用。 真正的节省在于 - 判定轮用小模型省93% - 摘要用小模型省90% - 分类用小模型省95% 实际节省30-60%我当时盯着 $16 $15 愣了半天说好的省钱呢想明白之后发现是对照对象搞错了。上面那个全用强模型 $15的算法里根本没算判定环节的钱——它假设循环靠别的机制终止比如固定轮数跑满。但真实情况是没有判定环节的循环要么提前停质量差要么跑满轮数更贵。把账算全方案写代码完成判定总成本效果强模型固定轮数跑满$15.00$0$15.00每5轮才交卷质量靠运气强模型强模型判定$15.00$15.00$30.00每轮判定及时收敛强模型小模型判定$15.00$1.00$16.00每轮判定及时收敛正确的对照是后两行判定环节从强模型降到小模型$15 → $1这才叫省93%。和第一行比成本确实多了 $1但换来的是该停就停——跑满轮数的方案平均浪费 2 轮无效迭代实际 token 消耗反而更高这个隐性成本第一行的账根本没体现。教训提炼成一句话算分层的账必须拿同等功能的全强模型方案当基线拿残缺功能方案当基线怎么算都是分层的错。七、小模型的边界失败模式与踩坑实录7.1 什么时候不够格分层不是无脑降档有些环节降了会出事故。我把小模型的判定失败模式归了类配合一个能力校准流程用≥80%70%~80%70%抽样50个已标注任务小模型判定与人工标签一致率该环节可降档降档加兜底黄金样例/双判投票保留强模型此环节别省失败模式现场后果对策过早放行代码能跑但边界缺失就判是假完成pytest前置黄金样例否决过度保守吹毛求疵永远判否循环空转烧maker的钱判定提示词放宽escalate兜底理解不了长上下文产出超2000行后判定质量骤降判定失真长产出改判分块摘要后判第三种最隐蔽我也是在日志里发现的判定准确率随输入长度上升而下滑超过某个阈值断崖式下跌——小模型的上下文理解能力先于生成能力枯竭。所以我的路由配置里有条硬规则产出超过1500行时判定环节自动升回强模型。省钱省在该省的地方该花的时候不含糊。踩坑提示别用感觉小模型还行当降档依据每个环节降档前先跑一次上面那个校准50个标注样本的一致率。模型版本一升级小模型的能力边界就会挪动半年前的校准结论可能已经过期。7.2 踩坑实录**坑1路由配置写死在代码里。**第一版我把模型名硬编码在每个调用点想做个checker 降档实验得改五个文件。改成 ModelTier 配置化之后实验变成改一行、跑一批、看日志的循环第8节的调优效率直接翻倍。配置和逻辑分离这种老生常谈在实验密集的场景里价值会放大十倍。**坑2judge 的提示词让模型自由发挥。**早期判定提示词写的是请评估任务完成情况小模型经常回一大段分析还喜欢答基本完成但建议……——是/否的判断被稀释成了议论文。改成强制只回答是或否一句话之后输出长度从 300 字符掉到 40解析稳定判定速度也快了后来为了配合 pydantic 校验又升级成了固定 JSON 输出思路不变把输出形态钉死。**坑3忘了小模型也记日志。**有阵子我以为判定环节反正便宜就没接全字段日志结果想复核一次批量判定的准确率时无据可查只能重跑。全字段日志的成本相对调用本身可以忽略越便宜的环节越要记账因为你对它的质量心里最没底。坑4一次性把所有环节都降档。第一次尝到甜头后我飘了把 checker、judge、summarizer 一夜之间全切到 mini 档第二天批量跑完成功率掉了七个点都不知道该怀疑谁——变量改太多失败无法归因。正确姿势是一次只降一个环节跑一批基准再降下一个虽然慢但每次劣化都能立刻定位到具体环节。这跟第8节要讲的调优方法论是同一条单变量原则。**坑5mini 模型的限流打爆了循环。**小模型便宜到让人忘了它也有速率限制每轮判定加审查并发打过去429 报错把循环卡死。解法是 router 里给小模型通道加个简单退避指数等待重试三次十几行代码的事没有它批量任务跑不完一个通宵。小结角色模型成本节省写代码强模型$3/M—审查小模型$0.25/M92%判定小模型$0.25/M93%摘要小模型$0.1/M97%核心要点不是所有任务需要最强模型判定/分类/摘要用小模型按认知负荷分配算力模型路由角色→模型的映射配置化ModelTier改档位零代码改动成本节省分层策略整体节省30-60%判定环节单独省93%质量不降的前提是兜底pytest前置、黄金样例否决小模型只做它够格的判断算账基线要对拿同等功能全强模型对照别拿残缺方案对照降档前先校准50个标注样本的一致率≥80%才降长上下文自动升档延伸阅读与思考实践给你的循环加ModelTier配置把判定环节切到mini档跑一周看账单实验抽50个任务做判定一致率校准看你的小模型够不够格思考你的循环里还有哪些环节在用总工程师看工牌下节预告第7章第8节《调优实录一个修复循环从71%到90%全过程》——完整复盘真实仓库修复循环的调优。如果觉得本文对你有帮助欢迎点赞、收藏、关注三连本系列持续更新中关注不迷路~文章编号第7章第7节 | 总进度51/120 | 预计阅读时间11分钟