尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

【Agent 的多模型路由熔断:免费模型池怎么做到「永远有能用的」】

【Agent 的多模型路由熔断:免费模型池怎么做到「永远有能用的」】
📅 发布时间:2026/8/2 5:03:01

Agent 的多模型路由熔断:免费模型池怎么做到「永远有能用的」

系列专栏【打造你自己的 Agent】第 2 篇 · 代码全部来自真实项目 Rescene

先讲个真实事故。

我的项目 Rescene 主打「永久免费」,靠的是聚合各厂商的免费档模型。上线初期,路由层是「配了一个模型就用一个模型」——结果就是灾难:OpenRouter 的免费档全部 slug 限流 429(连 llama-3.3-70b 都 429),NVIDIA NIM 免费试用档限流严重到跑 Agent 频繁 429,我一度怀疑是不是自己代码写错了。

后来想明白了:免费模型的本质是「不稳定」。今天能用的,明天可能就 429;上午还快的,下午可能超时。所以多模型路由不是「选个好模型」的问题,而是**熔断(Circuit Breaking)**的问题——把「不稳定」当作默认前提来设计。

今天这篇讲清楚三件事:

  1. 路由链:怎么把多个模型源排成一条「备用链」
  2. failover 时机:什么时候该切、什么时候不该切(这个细节决定成败)
  3. 探测与淘汰:怎么让免费池「永远活着」

一、路由链:把模型源排成一条链

多模型路由的第一步,是把所有可用源排成一条有序链:先试谁、失败切谁、最后兜底谁。

Rescene 的路由链构建(真实代码,已精简):

funcresolveBackends(userKeystring,modelstring)[]RouterBackend{// 精确路由:前端明确选了某个模型 → 只走那一个ifmodel!=""&&model!="auto"{ifb:=resolveExact(userKey,model);b!=nil{return[]RouterBackend{*b}}}varuserChain,freeChain[]RouterBackend// 1. 用户自己配置的提供方(有 Key 的)—— 排最前for_,e:=rangeuserEntries{b:=RouterBackend{Name:e.Name,BaseURL:e.Endpoint,Model:e.DefaultModel,...}ife.IsDefault{userChain=append([]RouterBackend{b},userChain...)// 默认的提到链头}else{userChain=append(userChain,b)}}// 2. 免费池:Key 来源 = 用户保存的 > 环境变量;没 Key 的源直接不进链for_,f:=rangefreeModelCatalog{iff.Disabled{continue}// 探测失败的已被标记退役key:=lookupKey(f)// 用户保存 > env,都没有就跳过b:=RouterBackend{Name:f.Name,Model:f.Model,Timeout:45*time.Second,...}freeChain=append(freeChain,b)}// 免费池按参数规模降序:优先用「最能打」的免费模型sort.SliceStable(freeChain,func(i,jint)bool{returnfreeChain[i].ParamsB>freeChain[j].ParamsB})returnappend(userChain,freeChain...)// 用户链在前,免费链兜底}

设计要点:

  • 用户自己的 Key 永远排最前——稳定、快、可控,免费池只是兜底
  • 免费池按参数规模降序——能干活的大模型优先,实在不行才退到小模型
  • 拿不到 Key 的源根本不进链——与其连了再失败,不如一开始就排除

二、failover:切,但只在「对的时候」切

路由链有了,接下来是核心问题:什么时候切下一个?

非流式:失败就秒切

普通(非流式)请求最简单——失败就切下一个,全部失败才报错:

funcrouteChatOnce(ctx context.Context,backends[]RouterBackend,msgs,tools)(string,[]core.ToolCall,error){vartried[]stringfor_,b:=rangebackends{ifctx.Err()!=nil{return"",nil,ctx.Err()}// 用户取消了就别试了content,calls,err:=openAIChatOnce(ctx,b,msgs,tools)iferr!=nil{tried=append(tried,fmt.Sprintf("%s: %v",b.Name,err))continue// 失败,秒切下一个}returncontent,calls,nil}return"",nil,fmt.Errorf("所有模型源不可用:%s",strings.Join(tried,";"))}

注意两个细节:

  1. ctx.Err()检查——用户已经关掉页面/取消请求时,别还在那试下一家
  2. 失败原因全部收集——最后报错时把每家失败原因拼出来,用户一看就知道是谁挂了

流式:只在「首包之前」切

Agent 场景下 90% 的请求是流式的,这里有个反直觉的规则(Rescene 的真实注释):

// 沿路由链做流式调用。failover 只发生在拿到 200 响应之前 // (连接失败/非200 秒切下一个);流一旦开始就不再切换源。

为什么流开始后不能切?两个原因:

  1. 已吐的 token 全白费——用户已经看到了 200 字,切到下一个源重新生成,体验是「话说到一半突然重来」
  2. 会重复执行工具——如果流式响应里已经包含 tool_call 指令(比如"调用 write_file"),切源重发会导致同一个工具被执行两次

所以规则就是:连接失败 / 非 200 → 秒切;已经开始吐字 → 死磕到底。

流式超时:一个坑死无数人的细节

Go 的http.Client.Timeout会把「读取整个响应体」也计入超时窗口——免费档模型生成一次常常 >45 秒,流式响应会被Client.Timeout在读到一半时砍断。Rescene 的解法:

funcstreamHTTPClient()*http.Client{return&http.Client{Timeout:0,// 关键:整体超时归零,绝不能在这里设Transport:&http.Transport{DialContext:(&net.Dialer{Timeout:15*time.Second,KeepAlive:30*time.Second,}).DialContext,ResponseHeaderTimeout:30*time.Second,// 只卡「连接 + 首字节」...},}}

原则:「连接 + 首字节」要限时(30s),「流式生成过程」不限时。首字节 30 秒没到说明这源大概率挂了,直接 failover;一旦开始吐字,哪怕生成 2 分钟也随它去——取消权交给请求上下文(浏览器断开即取消)。


三、探测与淘汰:免费池怎么「永远活着」

路由链再聪明,也架不住链上的源全是死的。Rescene 的做法是每日探测 + 自动退役:

  • 每天自动探测各厂商免费档模型可用性(真实调用一次,看是否 429 / 超时 / 报错)
  • 跑不了的自动标记退役(f.Disabled = true,不再进链)
  • 能用的自动恢复(探测通过重新进链)

实测教训(都写在代码注释里了):

// —— OpenRouter 已整体移除:免费档全部 slug 限流 429(连 llama-3.3-70b/405b 都 429) // —— NVIDIA NIM 免费试用档已整体移除:2026-07-23 实测限流严重,跑 Agent 频繁 429

免费模型厂商今天送、明天限流是常态。把「探测 + 退役 + 恢复」做成自动化闭环,比任何手动维护都可靠——这也是「免费池永远是真能跑的」这句话的底气来源。


四、踩坑清单

  1. Client.Timeout会砍流式——它把读响应体也算进窗口,免费档 45s+ 的生成会被拦腰截断。流式 client 必须 Timeout=0,用ResponseHeaderTimeout只卡首字节。
  2. 流开始后绝不能切源——前面吐的字全白费,更危险的是 tool_call 指令会被重复执行(同一工具跑两遍)。
  3. max_tokens 太小会死循环——4k 上限会把稍长的 write_file 参数截断在 JSON 中间,工具报unexpected end of JSON input,模型收到错误后整份重写,又超 4k,又截断……死循环。Rescene 单轮给到 16384,靠工作流总预算兜底。
  4. reasoning_effort不能无脑塞给所有源——不支持该字段的上游大概率报错而不是安静忽略,只在b.Reasoning == true时才带。
  5. 空链要有明确的错误信息——一个 Key 都没配时链就是空的,报「所有模型源不可用:」加一片空白会让用户以为产品坏了。明确提示「请在设置面板填入至少一个 API Key」。
  6. 免费源 Key 的来源要分层——用户自己保存的 > 环境变量,都没有就跳过。免得环境变量里的旧 Key 让用户以为「我配了呀为什么不用」。

五、关于 Rescene

本文代码来自我手写的开源项目Rescene——专攻前端设计、浏览器自动化、Computer Use的二次元 Agent:

  • 🔋免费模型每日更新:每天自动探测各厂商免费档模型,免费池永远是真能跑的(本文的探测/退役/恢复闭环就是它在跑)
  • 🎨专攻前端设计:内置 54 个真实设计系统参考,Agent 写完直接真实渲染给你看
  • 🌐真实浏览器自动化:基于 Chromium + CDP,是真浏览器在跑你的页面
  • 🧠成长中的记忆:每次工作流完成后自动萃取经验,下次自动融入上下文
  • 🔧4+4+2 Agent 工作流:40% 计划 → 40% 验证 → 20% 编码

🔗 官网:https://rescene.shanca.me/ (全速下载最新发行版)
🐙 GitHub:https://github.com/Rescenix/ResceneAgent


下篇预告

第 3 篇:《省 Token 工程:动态工具发现和前缀匹配之外,还有 11 种核心技术》——注入优化、轨迹压缩、架构隔离、调用策略四大类,全部有真实代码和实测数据,最后引出 Agent 的「并行任务-后台任务-系统级审批自检框架」(Agent Harness)。明天见~

相关新闻

  • 农牧企业出海,如何用“数字化+AI”穿越周期?——ALCF2026(越南站)观察
  • PyTorch GPU部署全攻略:从安装到排错,彻底解决torch.cuda.is_available()返回False
  • 我常年切换 Linux 发行版,如果你中了以下7招,你也该换了

最新新闻

  • 利用虚拟系统搭建纯净TIA Portal开发环境,解决HMI下载与仿真难题
  • PCB包地设计实战指南:从原理到应用,解决高速信号干扰
  • 从语言模型到世界模型:AI如何突破科学发现的“玻璃天花板”?
  • 树莓派Pico驱动电子墨水屏与电容触摸:SPI/I2C接口实战与低功耗UI设计
  • 相机标定实战指南:从原理到OpenCV实现,解决视觉测量不准问题
  • 桁架建筑如何深化设计?

日新闻

  • 怀化母婴除甲醛公司测甲醛中心怎么选:康之居母婴除甲醛标准、流程、避坑指南 - 信誉隆金银铂奢回收
  • 三步打造你的终极音乐中心:foobox-cn网络电台功能完整指南
  • Lance湖仓格式:为多模态AI工作流设计的终极数据存储方案

周新闻

  • 怀化母婴除甲醛公司测甲醛中心怎么选:康之居母婴除甲醛标准、流程、避坑指南 - 信誉隆金银铂奢回收
  • 三步打造你的终极音乐中心:foobox-cn网络电台功能完整指南
  • Lance湖仓格式:为多模态AI工作流设计的终极数据存储方案

月新闻

  • ClickHouse版本管理深度实战:4步构建零风险升级与回滚体系
  • Java 23 种设计模式:从踩坑到精通 | 番外:责任链模式 —— 物流审批流程实战
  • 华硕笔记本性能解放指南:G-Helper轻量级控制工具全面解析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号