ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

手搓生产级 AI Agent 系统(13):别让所有任务都上旗舰模型——路由、预算和降级

手搓生产级 AI Agent 系统(13):别让所有任务都上旗舰模型——路由、预算和降级 文章摘要前十二篇把生产级 Agent 的执行链路基本补齐了任务理解、Tool Calling、状态、Memory、Planner-Executor-Reviewer、Checkpoint、人工审批、多 Agent、安全沙箱和发布评测。系统走到这里会出现一个非常现实的问题它能跑了但可能太贵。一个 Agent 请求背后可能包含规划、检索、三四次工具调用、子 Agent、Reviewer 和最终生成。如果这些节点全部默认使用最强模型成本会很快变成产品上线后的主要约束反过来如果为了省钱全部切到低价模型复杂任务成功率和异常恢复能力又会下降。这一篇不再讨论“哪个模型最好”而是实现一套模型调度层根据任务类型、风险、上下文、失败历史和预算选择模型为每个 Run 预留成本允许低价模型先跑、失败再升级高风险任务直接锁定高能力模型Provider 故障时按照能力而不是名字降级所有路由结果进入评测闭环最终用真实 Task Success 和 Cost per Successful Task 调整策略。先看一个很容易烧钱的 Agent用户请求读取三份合同比较差异 查询供应商历史履约数据 生成采购建议并发给负责人。一个未经成本治理的流程可能是旗舰模型理解任务 旗舰模型生成计划 旗舰模型合同A 旗舰模型合同B 旗舰模型合同C 旗舰模型供应商分析 旗舰模型Reviewer 旗舰模型最终邮件8 次模型调用。如果中间两个步骤失败重试10—12 次再加长合同 Context单请求成本非常容易失控。而实际上里面很多任务不需要同一档模型。更合理的拆法任务分类低成本模型 合同结构抽取低成本/中档模型 复杂差异判断高能力模型 供应商数据整理低成本模型 最终风险判断高能力模型 邮件润色中档模型这就是模型调度层存在的意义。一、不要让业务代码直接写模型名最糟糕的写法chatClient.model(gpt-5.6-sol).prompt(...)然后散落在几十个 Service 里。模型升级、价格变化、Provider 故障时全项目改。业务代码应该请求“能力”publicrecordModelRequirement(TaskTypetaskType,CapabilityLevelcapability,RiskLevelrisk,intcontextTokens,booleantoolsRequired,booleanvisionRequired,DurationlatencyTarget,BigDecimalmaximumCost){}路由层再决定具体模型。二、模型注册表publicrecordModelProfile(StringprofileId,Stringprovider,Stringmodel,CapabilityLevelcapability,longcontextWindow,booleantoolCalling,booleanvision,BigDecimalinputPricePerMillion,BigDecimaloutputPricePerMillion,SetTaskTypepreferredTasks,RiskLevelmaximumAutonomousRisk,ModelHealthhealth){}配置示例models:-profile-id:fast-agentprovider:googlemodel:gemini-3.7-flashcapability:STANDARDinput-price-per-million:0.75output-price-per-million:3.75-profile-id:balancedprovider:openaimodel:gpt-5.6-terracapability:ADVANCEDinput-price-per-million:2.50output-price-per-million:15.00-profile-id:frontier-openaiprovider:openaimodel:gpt-5.6-solcapability:FRONTIERinput-price-per-million:5.00output-price-per-million:30.00-profile-id:frontier-anthropicprovider:anthropicmodel:claude-opus-5capability:FRONTIERinput-price-per-million:5.00output-price-per-million:25.00价格必须版本化。Gemini 3.7 Flash 当前促销价格到 2026 年底不能把这套价格永久写死。三、先做“硬过滤”再做评分Router 第一步不是让 LLM 选模型。先用确定性条件淘汰不合格候选publicListModelProfileeligible(ModelRequirementreq,ListModelProfileprofiles){returnprofiles.stream().filter(p-p.health().isAvailable()).filter(p-p.contextWindow()req.contextTokens()).filter(p-!req.toolsRequired()||p.toolCalling()).filter(p-!req.visionRequired()||p.vision()).filter(p-p.capability().atLeast(req.capability())).toList();}这样可以防止模型自己“推荐”一个根本不支持需求的候选。四、任务难度不要只让 LLM 自评任务难度可以来自一组信号输入长度 文档数量 工具数量 历史失败率 是否需要多步计划 是否开放式判断 是否高风险 是否需要代码执行 是否存在冲突证据publicrecordTaskComplexity(intscore,intdocumentCount,intexpectedToolCalls,booleanmultiStep,booleanambiguous,booleanconflictingEvidence,RiskLevelrisk){}模型可以参与分类但最终阈值由程序控制。五、默认模型不应该是最强模型我更推荐先选满足能力要求的最低成本模型然后允许升级。示例publicModelProfileselectInitial(ModelRequirementreq,ListModelProfilecandidates){returncandidates.stream().filter(p-p.capability().atLeast(req.capability())).min(Comparator.comparing(this::estimatedUnitCost)).orElseThrow();}但“最低成本”不是单纯按输入价排序还要结合历史成功率。六、真正应该优化的是 Cost per Successful Task定义CPS 总成本 / 成功任务数假设Model A 单次 $0.02 成功率 60% Model B 单次 $0.04 成功率 95%如果失败要重试A 未必便宜。所以 Registry 里还要维护线上统计publicrecordModelTaskStats(StringprofileId,TaskTypetaskType,longsampleCount,doubletaskSuccessRate,doubleretryRate,doublep95LatencyMs,doubleaverageInputTokens,doubleaverageOutputTokens,BigDecimalcostPerSuccessfulTask){}七、升级式路由第一次使用低成本模型。出现以下条件升级结构化输出连续失败 Reviewer阻断 Tool循环 证据冲突 低置信 任务超出能力 用户要求更深入分析publicEscalationDecisionevaluate(AgentStepResultresult,AgentTracetrace){if(trace.retryCount()2){returnEscalationDecision.yes(RETRY_LIMIT);}if(trace.toolCallCount()8){returnEscalationDecision.yes(TOOL_LOOP_RISK);}if(result.hasBlockingConflict()){returnEscalationDecision.yes(EVIDENCE_CONFLICT);}returnEscalationDecision.no();}八、升级不是重新跑整个 Agent这是很重要的一点。错误第5步失败 →换旗舰模型 →从第1步重跑会浪费已完成检索已生成 Artifact已执行 ToolToken时间。正确Checkpoint Artifact Step Ledger只升级当前失败节点。九、Run BudgetpublicrecordAgentRunBudget(BigDecimalmaximumCost,longmaximumInputTokens,longmaximumOutputTokens,intmaximumModelCalls,intmaximumEscalations,Instantdeadline){}每个步骤执行前先预估并预留。十、预算预留publicrecordModelCallReservation(StringreservationId,StringrunId,StringstepId,StringmodelProfile,BigDecimalreservedCost,longreservedTokens,InstantexpiresAt){}执行前Estimate →Reserve →Call →Settle避免并行 Sub-Agent 同时把剩余预算花掉。十一、如何估成本publicBigDecimalestimateCost(ModelProfilemodel,longestimatedInput,longestimatedOutput){BigDecimalinputBigDecimal.valueOf(estimatedInput).divide(BigDecimal.valueOf(1_000_000)).multiply(model.inputPricePerMillion());BigDecimaloutputBigDecimal.valueOf(estimatedOutput).divide(BigDecimal.valueOf(1_000_000)).multiply(model.outputPricePerMillion());returninput.add(output);}生产代码要处理精度和舍入这里只展示思路。十二、预算不足时怎么降级顺序我会这样设计1. 取消可选 Sub-Agent 2. 减少 Reviewer 轮次 3. 压缩 Context 4. 使用缓存 Artifact 5. 切低成本模型 6. 缩短输出 7. 返回部分结果并说明缺失不能降级的权限 高风险审批 幂等 安全规则 租户隔离十三、Provider 故障的 Fallback 不应只按名字不要GPT失败 → Claude Claude失败 → Gemini应该按 Capability Contract 找替代publicListModelProfilefallbacks(ModelProfilefailed,ModelRequirementrequirement){returnregistry.available().stream().filter(p-!p.profileId().equals(failed.profileId())).filter(p-p.capability().atLeast(requirement.capability())).filter(p-p.contextWindow()requirement.contextTokens()).sorted(Comparator.comparing(this::historicalSuccess).reversed()).toList();}十四、Fallback 还要考虑行为差异不同 Provider 的Tool SchemaReasoningPrompt CacheStructured Output多模态拒绝策略都可能不同。所以要有 Provider AdapterpublicinterfaceModelGateway{ModelCallResultcall(ModelProfilemodel,NormalizedModelRequestrequest);}业务层只看到统一请求。十五、模型切换时Prompt 也可能需要 Profile不要假设一份 Prompt 在所有模型上表现相同。publicrecordPromptProfile(StringpromptId,StringbaseVersion,MapString,StringmodelOverrides){}但 Override 不要无限分叉否则无法维护。优先让 Prompt 保持通用只对明显差异做少量适配。十六、健康检查publicenumModelHealth{HEALTHY,DEGRADED,RATE_LIMITED,UNAVAILABLE}健康度来自错误率 429 P95 超时 Provider状态 连续失败处于 DEGRADED 的模型可以降低权重不一定完全摘除。十七、熔断短时间连续超时 →打开熔断 →新请求路由其他模型 →半开探测 →恢复不要让每个 Agent Step 自己独立疯狂重试。十八、重试和 Fallback 是两回事Retry同一个模型再试 Fallback换另一个模型错误分类可重试网络抖动 临时 5xx 少量 429应 Fallback模型不可用 持续限流 Context不支持 能力不匹配不应自动重试权限失败 策略拒绝 错误业务参数 真实副作用结果未知十九、路由决策必须记录publicrecordModelRoutingDecision(StringdecisionId,StringrunId,StringstepId,StringselectedProfile,ListStringcandidateProfiles,StringreasonCode,BigDecimalestimatedCost,StringpolicyVersion,InstantcreatedAt){}否则一个月后成本上涨你不知道为什么全走了旗舰模型。二十、核心指标agent_model_call_total{ profile, task_type, result } agent_model_escalation_total{ from, to, reason } agent_model_fallback_total{ reason } agent_task_success_rate{ profile, task_type } agent_cost_per_success{ profile, task_type } agent_budget_exceeded_total二十一、最容易被忽略的指标Escalation Rate如果某类任务80% 都从 Flash 升级到 Sol说明默认路由就错了。如果只有 5% 升级 且最终成功率接近旗舰模型这才是高性价比路由。二十二、用离线评测初始化路由上一篇的评测平台可以直接给 Router 提供先验Task Type × Model → Success Rate → Cost → P95新模型上线时先影子测试不直接替换默认模型。二十三、再用线上数据持续校准路由策略每周或每月更新离线 Benchmark 线上 Task Success 成本 人工修改 失败分类不要实时让算法自动改路由权重尤其高风险系统。先生成候选策略再经过 Canary。二十四、多 Agent 的预算要从父 Run 分配Run Budget $2 ├─Planner $0.15 ├─Research $0.50 ├─Finance $0.40 ├─Legal $0.40 ├─Reviewer $0.30 └─Reserve $0.25子 Agent 不能认为自己有完整 $2。否则并发一开总预算马上穿透。二十五、一定要留安全预算我会提前保留Reviewer 异常恢复 最终输出的预算。不要前面研究 Agent 把钱花光最后连 Review 都跑不起。二十六、一个实用的初始策略如果没有任何历史数据可以先用低风险高频低成本模型 普通企业任务中档模型 高风险 / 模糊 / 长任务旗舰模型再通过实际数据优化。别一开始就做一个复杂的 ML Router。规则路由更容易解释和调试。二十七、上线检查□ 业务代码不直接写死模型名 □ Model Registry包含能力、价格、健康度 □ 路由先做硬能力过滤 □ 默认不是最高价模型 □ 失败支持当前Step升级 □ 升级不重跑已完成副作用 □ Run有总预算 □ 并行调用前原子预留预算 □ Fallback按能力契约选择 □ Retry与Fallback错误分类明确 □ Provider差异由Adapter处理 □ 路由决策可审计 □ 统计Cost per Successful Task □ 监控Escalation Rate □ 新模型先评测和影子再进主路由写在最后2026 年的模型竞争越来越激烈更强的模型不断出现Flash 类模型又不断把价格往下压开放权重模型同时在扩大部署选择。这意味着企业架构不应该继续问“我们到底选哪一个模型”更好的问题是“哪一类任务在什么风险和预算下应该由哪个模型处理”模型调度层的价值就是把这个问题从人工争论变成数据和策略。一个成熟的 Agent 系统不会把最贵模型当默认答案也不会为了省钱把所有任务都塞给便宜模型。它会知道什么时候该省什么时候该升级什么时候该降级什么时候应该直接停下来交给人。这才是生产环境里的“模型智能”。
返回列表