☰
AI智能体循环工程-第7章第7节-验证工程-分层模型策略强模型干活小模型验收
2026/10/13 3:13:00 网站建设 项目流程

第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-4o,Checker 用别家的小模型(异厂商+低档次双满足);预算紧、任务简单时,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 logger=get_logger("model_router")classModelTier(BaseModel):"""模型分层配置:改档位只改这里,循环代码零改动"""maker:str="gpt-4o"# 写代码:要生成质量checker:str="gpt-4o-mini"# 审查:要快和便宜judge:str="gpt-4o-mini"# 完成判定:是/否问题summarizer:str="gpt-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.tier=tier self.clients=clients# {"gpt-4o": client1, "gpt-4o-mini": client2}self.stats:list[CallStat]=[]defget_client(self,role:str)->tuple[OpenAI,str]:"""根据角色获取(client, 模型名)"""model_name=getattr(self.tier,role)returnself.clients[model_name],model_namedefcall(self,role:str,prompt:str)->str:"""按角色路由调用:baseurl/端口、key尾6位、长度、起止、耗时全记录"""client,model=self.get_client(role)baseurl=os.getenv("OPENAI_BASE_URL","https://api.openai.com:443")key=os.getenv("OPENAI_API_KEY","sk-none")t0,start=time.time(),datetime.now().strftime("%Y-%m-%d %H:%M:%S")resp=client.chat.completions.create(model=model,messages=[{"role":"user","content":prompt}])content=resp.choices[0].message.contentor""stat=CallStat(role=role,model=model,baseurl=baseurl,key_tail6=key[-6:],input_len=len(prompt),output_len=len(content),start=start,end=datetime.now().strftime("%Y-%m-%d %H:%M:%S"),cost_sec=round(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.role==role]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 logger=get_logger("judge")classJudgeVerdict(BaseModel):"""判定输出:结构化,不收自由文本"""done:boolreason:strdefjudge_completion(goal:str,code:str,result:str,router:ModelRouter)->bool:"""用小模型判定任务是否完成:结构化JSON输出,pydantic校验+文本兜底"""prompt=f"""任务目标:{goal}代码:{code[:500]}运行结果:{result[:500]}这个任务完成了吗?只输出JSON,不要任何其他内容: {{"done": true或false, "reason": "一句话理由"}}"""response=router.call("judge",prompt)# 第一层:JSON过pydantic校验;第二层:格式不守约时按文本前缀兜底try:verdict=JudgeVerdict.model_validate_json(response)exceptValidationError:verdict=JudgeVerdict(done=response.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调用 角色=judgebaseurl=https://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调用 角色=judgebaseurl=https://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.00
GPT-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 logger=get_logger("layered_loop")deflayered_loop(goal:str,router:ModelRouter,max_steps:int=15)->str|None:"""分层模型循环:maker每轮写,judge每轮判,小模型当门卫"""feedback=""try:forstepinrange(1,max_steps+1):# 强模型写代码(带上轮判定反馈)code=router.call("maker",f"任务:{goal}\n上一轮反馈:{feedbackor'无'}\n请给出完整实现。")# 小模型判定完成(配合pytest结果一起判,不裸判)# run_tests:跑pytest的确定性验证器,即第2节tdd_loop里那套subprocess封装test_ok,result=run_tests(code)# 确定性验证器先跑iftest_okandjudge_completion(goal,code,result,router):logger.info("✅ 小模型判定完成(%d轮)",step)returncode feedback=result[: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.03(maker)+ 100 × 5 × $0.002(judge) = $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节的调优效率直接翻倍。配置和逻辑分离这种老生常谈,在实验密集的场景里价值会放大十倍。

**坑2:judge 的提示词让模型"自由发挥"。**早期判定提示词写的是"请评估任务完成情况",小模型经常回一大段分析,还喜欢答"基本完成,但建议……"——是/否的判断被稀释成了议论文。改成强制"只回答是或否+一句话"之后,输出长度从 300 字符掉到 40,解析稳定,判定速度也快了(后来为了配合 pydantic 校验又升级成了固定 JSON 输出,思路不变:把输出形态钉死)。

**坑3:忘了小模型也记日志。**有阵子我以为判定环节"反正便宜"就没接全字段日志,结果想复核一次批量判定的准确率时无据可查,只能重跑。全字段日志的成本相对调用本身可以忽略,越便宜的环节越要记账,因为你对它的质量心里最没底。

坑4:一次性把所有环节都降档。第一次尝到甜头后我飘了,把 checker、judge、summarizer 一夜之间全切到 mini 档,第二天批量跑完成功率掉了七个点,都不知道该怀疑谁——变量改太多,失败无法归因。正确姿势是一次只降一个环节,跑一批基准再降下一个,虽然慢,但每次劣化都能立刻定位到具体环节。这跟第8节要讲的调优方法论是同一条:单变量原则。

**坑5:mini 模型的限流打爆了循环。**小模型便宜到让人忘了它也有速率限制,每轮判定加审查并发打过去,429 报错把循环卡死。解法是 router 里给小模型通道加个简单退避(指数等待重试三次),十几行代码的事,没有它批量任务跑不完一个通宵。


小结

角色模型成本节省
写代码强模型$3/M—
审查小模型$0.25/M92%
判定小模型$0.25/M93%
摘要小模型$0.1/M97%

核心要点:

  1. 不是所有任务需要最强模型:判定/分类/摘要用小模型,按认知负荷分配算力
  2. 模型路由:角色→模型的映射配置化(ModelTier),改档位零代码改动
  3. 成本节省:分层策略整体节省30-60%;判定环节单独省93%
  4. 质量不降的前提是兜底:pytest前置、黄金样例否决,小模型只做它够格的判断
  5. 算账基线要对:拿"同等功能全强模型"对照,别拿残缺方案对照
  6. 降档前先校准:50个标注样本的一致率≥80%才降,长上下文自动升档

延伸阅读与思考

  1. 实践:给你的循环加ModelTier配置,把判定环节切到mini档跑一周看账单
  2. 实验:抽50个任务做判定一致率校准,看你的小模型够不够格
  3. 思考:你的循环里还有哪些环节在"用总工程师看工牌"?

下节预告

第7章第8节《调优实录:一个修复循环从71%到90%+全过程》——完整复盘真实仓库修复循环的调优。


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


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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询