尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

多AI Provider架构实践:从单模型接入到智能路由与故障转移

发布时间:2026/9/29 2:43:22

资讯中心
01
ARTICLE

多AI Provider架构实践:从单模型接入到智能路由与故障转移

多AI Provider架构实践:从单模型接入到智能路由与故障转移
最早给 HagiCode 接 AI Provider 的时候我的想法朴素得不能再朴素找个用得最多的模型把 key 配好写个 service 层一封装完事。上线第一个月确实挺香直到用户开始问能不能切到别的模型XX 模型便宜一半能不能用以及某个深夜第三方模型服务挂掉之后我们才意识到多 AI Provider 架构不是功能是刚需。这篇文章想跟你聊的是 HagiCode 平台从单模型接入走向多 AI Provider 架构的完整实践过程。它适合正在做 AI 应用、智能体平台或者想把多个大模型接进自己系统的技术同学。不管你是刚起步还是在优化上一版里面关于抽象层、路由、降级、成本治理的思路应该都能直接用上。1. 从单点接入到多 AI Provider 网关HagiCode 的架构演进动因1.1 最开始那版能用但不好用的接入方式最早版本HagiCode 的服务端只接了一个供应商的接口业务代码里直接调用对方的 HTTP API。OpenAI 兼容格式大家都熟prompt 拼好temperature 塞进去一键请求。这个阶段确实很顺利麻烦出现在三个节点上。第一个节点是模型价格。某个客户的对话场景需要高频调用用主力大模型一个月跑下来账单很可观。他问能不能用小一点的模型我们当时只能老实回答接不了。第二个节点是稳定性。那天下午第三方服务波动我们这边所有依赖它能力的功能全部跟着抖动。一个供应商挂了平台什么也干不了这个局面很尴尬。第三个节点是能力差异。语音输入、视觉理解、长上下文、工具调用不同厂商各有所长。用户的需求永远是用最合适的模型干最合适的活单 Provider 在选型上没有任何腾挪空间。这就是我们开始做多 AI Provider 架构的动因不是追新是被实际用户需求推着走。从架构角度看本质是要把模型能力变成一种可路由、可切换、可编排的资源而不是写死在代码里的一个远程函数。1.2 和传统微服务架构里的多数据源是同一个思路有一个类比我觉得特别能说明问题传统后端里你一般不会只连一个数据库生产库、从库、测试库、Redis、ES不同存储承担不同职责。多 AI Provider 在平台里的角色就相当于模型层的数据源。既然数据源可以做读路由、故障转移、超时控制、成本治理那多 Provider 当然也应该有这整套机制。这个定位想清楚之后我们的架构目标就很明确新增一个厂商时只写一个适配器不改业务代码上游某个供应商异常时服务自动切换调用量增长时可以按租户、按场景做配额和路由策略。这套目标直接决定了后面抽象层接口长什么样。2. 抽象层与适配器把所有模型统一成一套 ChatRequest 协议2.1 统一协议为什么不能用厂商各自的 SDK先说一个我们踩过的坑一开始图省事接入第二家模型时直接用了官方 SDK业务代码里同时存在两套 SDK 的调用方式、两种异常类型、两种超时配置。两个月后这个方案崩了因为每接一个新厂商业务层就要跟着改一遍。历史教训告诉我们多 Provider 接入必须在业务代码和厂商 SDK 之间加一层自己的协议。我们把这层叫 Provider 抽象层。统一的协议需要覆盖四个方面请求参数messages、model、temperature、max_tokens、top_p、stop、tools全部归一化成平台自己的结构。响应结构content、tool_calls、usage、finish_reason统一成一套。错误语义限流、超时、鉴权失败、模型不存在归一到平台错误码。流式事件统一成文本增量、工具调用增量、结束信号三类事件。这四件事缺一不可。如果我们只统一请求而不统一错误那下游代码里还是会散落着对不同厂商错误码的判断异常处理依然是一团浆糊。2.2 适配器模式每个厂商一个薄壳我们参考了六边形架构的思路把厂商 SDK看作外部系统的适配端口核心业务不依赖任何厂商类型。用 TypeScript 伪代码展示一下网关层的核心接口设计export interface ChatProvider { chat(req: ChatRequest, ctx: RequestContext): PromiseChatResponse; stream(req: ChatRequest, ctx: RequestContext): AsyncIterableStreamEvent; } export interface ChatRequest { model: string; // HagiCode 内部模型名 messages: ChatMessage[]; temperature?: number; maxTokens?: number; tools?: ToolDef[]; } export interface ChatResponse { content: string; toolCalls: ToolCall[]; usage: TokenUsage; finishReason: string; } export interface StreamEvent { type: text-delta | tool-call-delta | done; data: string | ToolCall | StreamResult; }每个厂商实现一个ChatProvider例如 OpenAIProvider、AnthropicProvider、GeminiProvider。适配器里做三件事内部模型名映射到厂商具体模型 ID参数从平台协议翻译到厂商参数厂商响应解析成平台协议。模型名映射在代码里看起来很简单const MODEL_MAP: Recordstring, string { hagi-sonnet: claude-sonnet-4-20250514, hagi-gpt-4o: gpt-4o, hagi-gemini-pro: gemini-2.0-pro, };这里有个容易被忽视的细节model 名不要直接把厂商版本日期塞给业务层。因为厂商会滚动更新模型版本同一个别名在不同时间是不同上游。平台侧维护别名厂商更新时我们只需要改适配器里的映射用户无感。这个设计在半年后证明是最高频使用的一个机制。2.3 HTTP 直连还是官方 SDK我们的选择接第三个厂商前我们做了一个重要决定不再用官方 SDK。所有 Provider 适配器统一走 HTTP 层对接。原因不复杂官方 SDK 更新节奏不可控有些 SDK 内部会自己重试、自己改超时、自己打日志出问题的时候你根本不知道是哪一层在搞鬼。自己写 HTTP 适配重试、熔断、日志全部统一掌控。代价是要处理厂商的协议细节比如有些厂商接口不是 OpenAI 兼容格式字段命名都不一样。但放在适配器里这些复杂度是被隔离的。新增厂商的边界很清晰看协议文档、写适配器、跑测试用例。这个选择还保证了性能层可控我们可以给不同厂商配置独立的连接池限制最大并发数避免某个厂商能力较弱时拖垮整个网关的资源。3. 动态路由与故障转移真正让多 Provider 发挥价值的核心机制3.1 从配死一家到按策略打分选路有多个 Provider 之后第一个问题不是用哪家而是这次请求应该用哪家。我们最开始用最简单的方式每天在配置中心手动改默认 Provider这能解决供应商挂了的情况但很被动——凌晨出问题你不可能爬起来改配置。于是我们做了一个路由打分器。每次请求进来网关根据以下因子给每个 Provider 打分健康度最近 5 分钟请求失败率、超时率。延迟最近 10 分钟的平均 P50/P95 延迟。成本权重每个 Provider 的单次调用预估成本。业务优先级比如某些租户指定了优先用 X、降级用 Y。场景匹配长上下文任务优先算力强的模型简单任务优先便宜的模型。每个因子有独立权重配置在后台。路由结果不是纯随机而是先过滤不可用 Provider再按权重做加权选择。伪代码如下func route(providers []Provider, req Request, weights RouteWeights) string { candidates : filterHealthy(providers, req) scores : make(map[string]float64) for _, p : range candidates { score : weights.health*p.healthScore (1-weights.health)*p.latencyScore weights.cost*(1-p.costScore) scores[p.ID] score } return pickByWeightedRandom(scores) }加权随机比单纯选最高分更稳。原因是多 Provider 场景下如果所有请求都打到最高分那家压力集中很容易把最优的那家也打挂。加权可以让请求稍微分散同时整体上还是偏向健康且便宜的选择。3.2 故障转移与重试的边界路由解决了平时怎么选故障转移解决选完发现不行怎么办。我们的策略分成三层同步重试如果请求返回 429、503、连接超时这类可重试错误且已经发送的请求不会产生副作用网关自动换一个 Provider 重试最多两次。熔断降级每个 Provider 维护一个滑动窗口统计最近 N 次请求的失败率。失败率超过阈值熔断器打开该 Provider 直接从候选列表剔除冷却 30 秒后进入半开状态试一个请求。舱壁隔离每个 Provider 有独立的连接池和信号量限制A 厂商响应变慢不会把网关的线程全部占满其他请求还能走别的 Provider。这三层机制里有几个细节容易做错我单独记一下重试一定要换 Provider不要对同一个 Provider 疯狂重试。同一个 Provider 限流了你再打还是限流。换一家才是多 Provider 架构的核心价值。不是所有错误都可重试。鉴权失败、参数 400、上下文超长这类错误重试只会浪费时间。流式请求的重试比较麻烦。如果响应已经吐了一半字节给客户端服务器再换 Provider 重组流客户端那边会出现内容断裂。提示流式请求一旦已经向客户端输出过半包内容就不要再做 Provider 级重试了。网关层的自动切换只适合首字节前失败中途断流应该交给业务层按状态机重置会话。3.3 健康检查不能只靠失败率单纯统计失败率有个盲区某 Provider 响应速度特别慢但没失败请求超时被算成失败才会触发熔断但用户已经卡了很久。我们的做法是增加三种探活方式真实请求的延迟采样取 P95 延迟连续超过阈值自动降权重。主动心跳低频30 秒一次发一个最小 tokens 的探活请求只判断连通性和首字节时间。罢工检测如果某个 Provider 持续 5 分钟没有处理量且候选列表里还有别的 Provider会在下一次路由时把权重拉低。主动心跳要额外花钱但量很小换来的稳定性是值得的。我们在生产环境是每 30 秒探一次一个月探活成本大约占总模型调用成本 0.2% 左右完全可以接受。这一条经常被忽略但它恰恰是多 Provider 架构和单 Provider 架构在运维习惯上最大的差异。4. 成本、限流与可观测性多 Provider 架构的运营侧挑战4.1 多 Provider 的成本治理别等账单出了再哭接多家模型之后最直观的问题就是账单变成好几份不同厂商计费单位还不一样。有的按 token 计有的按图像分辨率计有的按上下文缓存态计。发布会前我们最怕的就是成本失控。两个抓手帮我们稳住了账本。第一个是预算门禁。平台上每个租户可以设置每日/每月的模型调用预算网关在路由前先查当前已用量预算耗尽直接拒绝调用或者降级到更便宜的 Provider。这个功能听着简单但先查再路由这个顺序很重要先看预算再看路由因为路由一旦定了钱就花出去了。第二个是模型分级。把模型分成高成本主力和低成本备胎两类默认走主力失败或预算不足时走备胎。简单对话场景默认用小模型复杂推理场景才允许路由到大模型。模型分级放在配置中心可以按租户覆盖运营同学自己就能改。成本统计也需要统一口径我们给每条请求都打了三个 tag租户 ID、场景 ID、Provider ID。汇总表结构大概是字段说明request_id平台内部请求 IDprovider_id实际履行请求的模型厂商model_alias平台内部模型名prompt_tokens输入 token 数completion_tokens输出 token 数estimated_cost估算成本按发布价格折算route_reason本次路由落点原因有了这张表每周可以拉出哪个场景烧钱最多哪个 Provider 性价比最差这些数据直接反馈给路由权重和模型分级。4.2 限流不能只限总量要按租户、场景、Provider 多维限流单 Provider 架构里限流很简单总量限制。多 Provider 之后需要防的是四类情况单个租户恶意或意外循环调用刷爆预算。某个场景优先级极高被低优先级任务挤占。上游 Provider 对账号/API key 有频率限制平台并发触顶。熔断恢复瞬间大量积压请求同时冲向同一个 Provider。我们用的限流器是分布式的令牌桶实现Redis 存当前桶的令牌数。每个租户一个桶每个场景也可以再挂一个桶场景桶只在配置里开启时才使用避免每个请求多查一次 Redis 的开销。Provider 层面额外加一个 semaphore 限制最大并发防止网关配置 200 并发但厂商只能扛 50 的问题。需要特别注意不同厂商的限流阈值不同而且这件事是动态的厂商升级服务时可能变化。我们的应对是给每个 Provider 配置独立的 rate limit 参数监控里一旦出现 429除了触发重试还会自动把路由权重压低一点让流量逐渐远离被限流的厂商。按我们的实测这个反馈闭环比手动调权重及时得多。4.3 可观测性多 Provider 必须做全链路追踪多 Provider 架构里同一个用户请求可能会经过客户端 → 网关 → Provider A →失败→ Provider B → 回调业务服务 → 存储。没有全链路追踪出问题的时候你连到底哪一段慢了都说不清。我们的做法比较标准基于 OpenTelemetry 的 trace每次请求生成一个 trace ID网关把 Provider 调用作为子 span 埋进去。关键 span 包含provider_id、model_alias、输入 token 数、是否重试、重试次数、路由打分结果、熔断状态。为什么路由打分结果也要进 span因为多 Provider 架构里这次为什么走到了 B本身就是一个问题。如果你发现某个用户经常被路由到低成本模型但他的诉求是高精度推理这个 span 会直接告诉你原因。有数据支撑才能调权重否则都靠猜。日志和指标也不省每条 Provider 调用的耗时、成功/失败、错误类型全部打指标。告警规则针对每 Provider 失败率环比上升 50%这类做而不是笼统的整个平台错误率上升——后者问题出现时你已经不知道该优先救哪家。5. 模型差异实测同一条 Prompt 在不同 Provider 上的表现裂痕5.1 参数兼容性是第一个坑我们内部有一套 Prompt 兼容性测试集跑一把下来统计差异。结论是这样的temperature、top_p 这类采样参数各家行为不完全一致但大体可用。max_tokens 的命名差异很大有的叫 max_output_tokens有的叫 max_completion_tokens有的默认值还很保守。stop 参数的支持粒度不同有的支持多组 stop 序列有的只支持一组。system prompt 的支持差异最明显新出的模型普遍支持良好个别旧版模型没有独立 system 位。所以适配器里最重要的工作其实是参数归一化而不是简单发个 HTTP 请求。我们把配置层做成了平台参数 → 厂商标参数的翻译表每接一个新厂商时先把这张表补全然后跑一遍测试集验证。这步偷懒的话后面线上出问题排查成本会更高。5.2 输出质量差异不容忽视同一个任务不同 Provider 的表现差距比我们预期大得多。最明显的是 JSON 结构化输出。有的厂商原生支持 response_format 强制 JSON非常稳有的厂商只能靠 prompt 约束偶尔会多输出一句解释。我们针对必须结构化输出的场景做了一层后校验拿到 content 后先尝试 JSON.parse解析失败就自动重试一次换 Provider 或换参数再失败就返回明确错误码。这层结果质量门控是用失败率换稳定性的典型做法也是把多 Provider从降低成本工具变成质量保障工具的关键。另一个容易被忽略的点是内容安全策略。不同厂商对同一类输入的拦截尺度不同有的宽松有的严格。做多 Provider 架构时如果你的平台有 To B 场景网关层最好再叠加一层自己的内容合规校验不要完全依赖上游。合规策略统一放在平台侧比跟着厂商尺度走更可控。5.3 流式输出体验差异流式场景是适配器最容易出 bug 的地方。OpenAI 兼容格式是data: {json}Anthropic 用的是事件流Google 的 SDK 又有一套自己的消息结构。这些差异都要在适配器层拉齐。我们的 StreamEvent 统一成三个事件text-delta增量文本、tool-call-delta工具调用参数增量、done结束。下游只依赖这三个事件类型无论上游是什么格式。做流式适配时有一个实测经验首字节时间差异极大。有些 Provider 会对完整上下文做一次预处理首字节可能要 3-5 秒另一些模型会更快返回。多 Provider 架构下如果要做交互体验优化流式的 TTFB首字节时间应该单独打指标因为它是用户感受最明显的性能指标比总耗时更值得盯。5.4 工具调用Tool Calls的兼容性HagiCode 的机器人功能依赖工具调用这块坑最深。各家对 function calling 的定义虽然接近但字段结构各不一样有的返回 function name arguments 字符串有的直接返回结构化 JSON有的把 tool_calls 嵌在 message 里。适配器里要单独做一层解析。我们踩过的最大的坑是某个厂商模型不声明支持 tools 也会默默忽略 tools 参数然后直接输出自然语言导致平台下游解析失败。这之后我们在适配器层面加了 capability 声明每个 Provider 适配器必须声明是否支持 tools、是否支持 JSON mode、是否支持视觉输入。路由打分时如果场景需要工具调用不支持 tools 的 Provider 直接从候选列表剔除。这个机制让路由和场景匹配真正绑定在一起。6. 半年复盘新 Provider 接入从两周到两天的经验沉淀6.1 抽象层别过度设计第二个 Provider 才是适配器诞生的契机多 Provider 架构最容易被过度设计。如果平台刚起步主要流量只在一个模型上不建议第一天就做完美抽象层。我们的路径是先支持一个 Provider把链路调通稳定跑两周再支持第二个 Provider第二个开始引入适配器模式。设计抽象层时可以在代码里预留接口但别为还没出现的厂商写任何代码。过早抽象出来的东西大概率和你真实遇到的需求不匹配最后还得返工。6.2 故障转移的优先级高于成本优化对大部分业务来说多 Provider 带来的第一收益不是用哪个模型效果好而是别因为我一家挂了就全站瘫痪。所以我的建议是第一梯队先把故障转移、熔断、超时控制做好这个投入产出比最高。路由做简单权重就可以了复杂的成本优化可以晚点再上。我们早期把精力花在成本路由上结果一次供应商故障暴露出故障转移配置根本没生效优先级的排序确实是踩过坑才懂的。6.3 模型别名是长期维护里最容易被低估的机制厂商隔几个月会下线旧版本、发布新版本如果业务层直接写了厂商版本 ID升级就是要改代码。我们内部维护了一个模型别名表平台给上层只有一个不可变的别名别名背后指向哪个厂商版本由运维配置控制。这个习惯看起来很小但在长期维护里省了特别多事。现在厂商换版本我们只需要改配置、跑一遍回归测试不需要动业务代码。6.4 故障演练必须纳入常规迭代节奏我们会定期在测试环境做 Provider 故障注入演练随机让一个 Provider 返回 500、返回 429、延迟 10 秒。验证目的只有一个平台能不能在用户无感的情况下自动切走。没有演练过的故障转移代码等于没有这话可能有点绝对但我在 HagiCode 上见过太多逻辑看着对一压测就崩的配置。演练不需要频繁一个月一次就够但每次切换 Provider 版本或调整路由权重后一定要重新跑一遍。目前这套架构已经稳定跑了半年多新增一个 Provider 的时间从最初的两周缩到了两天左右账单和稳定性都控制在了预期内。说实话多 AI Provider 架构能不能跑好七成功夫不在代码而在运维意识和流程路由要敢碰配置模型要敢做别名映射故障要敢做真实演练。接下来我们打算把路由引擎拆成独立的调度服务让它能基于更长时间窗口的流量数据自动调节 Provider 权重再配合成本预算自动降级。这个方向还在打磨等有结论了再来分享。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。