
1. 当你的代码里开始出现第三个模型调用我最早意识到中间层这件事躲不掉是在一个内部工具项目上。最开始只接了一家模型服务代码写得很直白一个函数拼好参数发请求拿结果解析返回。二十行搞定清爽得很。后来产品那边说能不能加一个备用模型万一主服务响应慢或者临时不可用能自动切过去。我想了想加了个 if-else判断一下返回状态不行就换一个函数再调一次。代码从二十行变成六十行还能忍。再后来需求变成了不同任务用不同模型。摘要类任务走便宜的模型推理类任务走能力强的模型代码生成类任务走专门调过的模型。同时还要记录每次调用的耗时、消耗的额度、成功率方便月底算账。这时候我打开那个文件发现它已经变成了四百多行里面塞满了各种判断、重试、日志、格式转换。每次加一个新模型我都要在五六个地方改代码改完还得小心翼翼地测生怕碰坏了别的分支。那一刻我明白了问题不在于模型多而在于我的应用代码和模型服务之间缺少一个稳定的中间层。这个中间层现在行业里通常叫它AI 网关或者模型网关。它不是某个具体产品的名字而是一类架构角色的统称。你可以把它理解成应用和模型之间的一个总机应用只管把请求交给它它负责决定发给谁、怎么发、发失败了怎么办、发完了怎么记账。应用不需要知道背后到底有几家模型、分别叫什么、接口长什么样。这篇文章想聊的就是这件事。如果你正在做 AI 相关的应用不管是大模型对话、图像生成、语音转写还是别的什么只要你开始接触第二家模型服务中间层的问题就会浮出水面。我会从为什么需要讲到怎么落地把选型逻辑、核心能力、实操步骤和踩过的坑都摊开说。适合已经跑通单模型 Demo、正准备往生产环境推进的开发者也适合想理解 AI 应用架构演进的产品和技术负责人。2. 没有中间层时应用代码会烂成什么样2.1 从一个函数到一团乱麻的典型路径我先还原一下大多数项目的真实演进过程你看看是不是眼熟。第一阶段单模型直连。代码里有一个call_model函数参数是 prompt 和几个配置项返回文本。简单、直接、好调试。这个阶段没有任何问题也不该提前引入中间层那是过度设计。第二阶段加备用模型。产品要求高可用主服务挂了要能切。于是call_model里出现了 try-catchcatch 里调用call_backup_model。两个函数的参数格式可能还不一样一个用messages数组一个用prompt字符串你得在中间做转换。这时候代码开始有分支了。第三阶段多模型按任务路由。摘要走 A推理走 B代码走 C。路由逻辑写在业务代码里每个调用点都要判断我现在这个任务该用哪个模型。业务逻辑和模型选择逻辑搅在一起改一个路由规则要翻遍整个项目。第四阶段加可观测性。老板要看每个模型花了多少钱、响应多快、失败率多少。你开始在每次调用前后打点记录时间戳、token 数、状态码。这些打点代码散落在几十个调用点里重复且容易漏。第五阶段加缓存和限流。相同的问题不要重复问模型浪费钱某个模型有 QPS 限制超了要排队或降级。缓存 key 怎么设计、限流策略怎么配又是一堆和业务无关的代码。走到这一步你会发现一个残酷的事实你的应用代码里真正属于业务逻辑的部分可能不到三成剩下七成都在处理怎么和模型打交道这件事。而这些代码高度重复、难以测试、每次换模型都要大改。2.2 耦合带来的三个具体代价我把这种耦合的代价归纳成三条都是实打实会咬人的。代价一换模型等于重写。不同模型服务的接口协议不一样。有的用 OpenAI 兼容格式有的用自己的一套有的鉴权走 Header有的走 Query 参数有的返回 JSON 里字段叫content有的叫text。你的业务代码如果直接依赖某家的返回结构换一家就得把所有解析逻辑重写一遍。我见过一个项目因为主模型涨价想换一家便宜的结果评估下来改动量要两周最后硬扛着没换白花了不少钱。代价二故障无法隔离。没有中间层某个模型的超时或报错会直接冒泡到业务层。用户看到的是一个莫名其妙的错误而不是稍后重试或者已自动切换。更麻烦的是如果多个业务共用同一个模型调用函数一个业务的异常流量可能拖垮整个模型配额连带影响其他业务。代价三成本是个黑盒。没有统一的调用入口就没有统一的记账点。每个调用点各自打日志格式还不一样月底想统计这个月推理任务花了多少基本靠人肉翻日志。我试过用 grep 统计结果因为日志格式不统一漏了一大半算出来的数字根本不能用。2.3 中间层到底中间了什么那中间层是怎么解决这些问题的核心思路就一句话把和模型打交道这件事从业务代码里抽出来收敛到一个独立的组件里。这个组件对外提供统一的接口业务代码只认这个接口不认具体模型。对内它管理着所有模型服务的连接、协议转换、路由规则、重试策略、限流配置、日志打点。业务说我要处理这个任务中间层负责用哪个模型、怎么调、失败了怎么办。打个比方。以前你是自己打电话找人办事得记住每个人的号码、脾气、办事流程。现在你打给一个总机说我要办某某事总机帮你转接、帮你跟进、办不成帮你换人。你不需要知道总机背后有多少人、怎么排班。这个总机带来的直接好处是业务代码变干净了模型相关的变更被隔离在中间层内部换模型、加模型、调策略都不用动业务。同时因为所有调用都经过中间层可观测性和成本统计变得自然而然。注意中间层不是越早引入越好。单模型、单业务、还没上生产的阶段直连完全够用提前引入只会增加复杂度。判断信号是当你开始接第二个模型或者开始有多个业务共用模型调用时就该考虑了。3. 一个合格的 AI 网关该具备哪些能力3.1 统一接口与协议适配这是中间层最基础也最重要的能力。它要对外暴露一套统一的接口不管背后接的是哪家模型业务看到的都是一样的入参和出参格式。统一接口的价值在于一次接入多处复用。业务代码写一次调用逻辑之后换模型、加模型都不用改。我建议统一接口尽量向主流格式靠拢比如对话类任务用messages数组这样迁移成本最低很多模型的官方 SDK 也直接兼容。协议适配则是另一面。中间层内部要为每个模型服务写一个适配器负责把统一格式翻译成该模型要求的格式再把返回结果翻译回统一格式。适配器是中间层里唯一需要懂具体模型的地方其他部分都不需要。这里有个实操经验适配器要处理的不只是字段名差异还有语义差异。比如有的模型不支持 system 角色你得把 system 内容合并进第一条 user 消息有的模型对空字符串敏感你得做兜底有的模型返回的结束原因字段含义不同你得映射成统一的状态。这些细节不处理统一接口就是假的业务还是会踩坑。3.2 路由、重试与降级路由解决的是这个请求该发给谁。最简单的路由是按任务类型静态配置比如摘要走 A、推理走 B。进阶一点可以按负载、按成本、按延迟动态选择。我建议从静态配置起步跑稳了再考虑动态。重试解决的是发失败了怎么办。这里要区分两类失败一类是可重试的比如网络超时、服务端 5xx一类是不可重试的比如参数错误、额度不足。中间层要能识别这两类只对可重试的做重试并且要设置重试次数上限和退避策略避免雪崩。降级解决的是重试也不行怎么办。可以降级到备用模型可以返回缓存结果也可以返回一个友好的错误提示。降级策略要可配置不同业务对失败的容忍度不一样。我踩过的一个坑早期重试逻辑写得太粗暴所有失败都重试三次结果遇到参数错误时白白重试了三次浪费了时间和额度。后来改成先判断错误类型只对超时和 5xx 重试问题就解决了。3.3 可观测性与成本核算这是中间层最容易被低估、但价值极高的能力。因为所有调用都经过中间层它天然就是一个完美的埋点位置。需要记录的核心指标包括每次调用的模型标识、任务类型、请求时间、响应时间、输入输出 token 数、状态码、是否重试、是否降级。这些数据汇总起来就能回答很多关键问题哪个模型最慢、哪个任务最贵、失败率最高的环节在哪、成本趋势如何。成本核算要特别注意 token 计数的准确性。不同模型的 token 计算方式不一样有的按字符估、有的用专门的分词器。中间层最好能拿到模型返回的官方 token 数拿不到再用估算。我见过因为估算偏差太大导致成本报表和实际账单对不上的情况很尴尬。3.4 缓存、限流与安全缓存解决的是相同请求不要重复花钱。对于确定性任务比如固定 prompt 的翻译、分类缓存命中能省下大量成本。缓存 key 的设计要考虑模型、参数、prompt 内容任何一个不同都应该视为不同请求。限流解决的是别把模型服务打挂。每个模型服务通常有 QPS 或并发限制中间层要能配置这些限制超限时排队或拒绝。限流粒度可以按模型、按业务、按用户看实际需要。安全方面中间层是统一管理密钥的好地方。业务代码不应该持有模型服务的密钥密钥统一放在中间层业务只和中间层通信。这样密钥轮换、权限控制都集中在一处泄露风险也小很多。能力解决的问题优先级统一接口与协议适配换模型不用改业务必须路由与重试降级高可用、按需分配必须可观测性与成本核算知道钱花在哪、问题出在哪强烈建议缓存省钱、提速建议限流保护模型服务建议密钥统一管理安全、便于轮换强烈建议4. 自建还是用现成方案怎么选4.1 三种落地形态的对比中间层落地有三种形态各有适用场景。第一种是纯自建。自己写一个服务实现统一接口、适配器、路由、重试这些逻辑。优点是完全可控想怎么改就怎么改没有额外依赖。缺点是工作量大尤其是可观测性、限流这些非核心但必要的功能自己写要花不少时间。适合团队有较强的后端能力、且对中间层有特殊定制需求的场景。第二种是用开源网关。社区里有一些专门做模型网关的开源项目提供了统一接口、多模型适配、路由、限流、可观测性等能力部署起来就能用。优点是省事功能相对完整社区还在持续维护。缺点是要接受它的设计约束深度定制可能受限。适合大多数中小团队能快速把中间层搭起来。第三种是用云厂商的托管服务。一些云平台提供了模型网关类的托管产品开箱即用不用自己运维。优点是省心缺点是绑定特定平台迁移成本高且费用可能不低。适合已经在某云平台深度使用、不想自己运维的团队。我的建议是先用开源方案快速搭起来跑通业务等真正遇到开源方案解决不了的问题时再考虑自建或改造。不要一上来就自建那是在重复造轮子而且大概率造得不如开源的好。4.2 选型时要盯住的几个硬指标不管选哪种形态有几个指标必须盯住。协议兼容性。它支持哪些模型的适配是否支持你正在用和计划用的模型适配器的质量如何有没有处理前面说的语义差异这个直接决定了你能接多少模型。扩展性。加一个新模型要改多少代码是写个配置文件就行还是要改核心逻辑好的设计应该是适配器插件化加模型不动核心。可观测性。它记录哪些指标能不能对接你现有的监控系统数据能不能导出做自定义分析这个决定了你后续能不能算清账、定位问题。性能开销。中间层本身会引入延迟这个延迟有多大在高并发下会不会成为瓶颈要实测不能只看文档。社区活跃度。如果是开源方案看最近的提交频率、issue 响应速度、文档更新情况。一个半年没更新的项目用起来风险很大。4.3 一个容易被忽略的决策点部署位置中间层部署在哪也是个要提前想清楚的问题。如果部署在应用内部比如作为一个库调用没有网络开销但升级要跟着应用一起发版多语言支持也麻烦。如果部署为独立服务升级独立、多语言友好但多了一跳网络开销还要考虑这个服务本身的高可用。我倾向于独立服务因为中间层的价值就在于独立于业务做成库就失去了这个独立性。网络开销在大多数场景下可以接受实在在意延迟的可以在应用侧加一层本地缓存。5. 从零搭一个最小可用中间层的实操路径5.1 先定义统一接口别急着写适配器很多人一上来就写适配器结果写到一半发现接口设计不合理返工。正确的顺序是先定接口。统一接口要定义清楚请求长什么样、响应长什么样、错误怎么表达。我建议请求包含任务类型、消息列表、模型偏好可选、超时时间等字段响应包含结果内容、使用的模型、token 消耗、耗时等字段错误用统一的结构包含错误码、错误类型可重试/不可重试、原始错误信息。接口定好后先写一个假适配器返回固定结果把整条链路跑通。这样能验证接口设计是否合理也能让业务侧提前对接。5.2 适配器的编写要点与常见陷阱适配器是中间层里最琐碎的部分但也是最需要细心的部分。每个适配器要做三件事请求转换、响应转换、错误映射。请求转换时注意参数名的映射、必填项的校验、默认值的填充。响应转换时注意提取统一的字段、处理缺失字段、归一化结束原因。错误映射时把各家不同的错误码映射成统一的错误类型这是重试逻辑能正确工作的前提。常见陷阱有几个。一是忽略了模型的上下文长度限制请求太长直接报错适配器应该提前校验并给出明确提示。二是没处理流式响应很多模型支持流式输出适配器要能正确处理分块数据。三是时区或时间格式不一致导致日志时间错乱。这些坑我都踩过写适配器时多留个心眼。5.3 路由与重试逻辑的配置化设计路由和重试逻辑一定要配置化不要硬编码。硬编码意味着每次调整都要改代码、重新部署很痛苦。配置化的做法是用一个配置文件或配置中心定义任务类型到模型的映射、每个模型的重试策略、降级链路。中间层启动时加载配置运行时按配置执行。改配置不用改代码热更新更好。配置示例伪代码仅示意结构routes: - task: summarization primary: model-a fallback: [model-b, model-c] retry: max_attempts: 2 backoff_ms: 500 retryable_errors: [timeout, server_error] - task: reasoning primary: model-d fallback: [model-a] retry: max_attempts: 1 backoff_ms: 1000 retryable_errors: [timeout]这样的配置改路由、调重试策略都是改几行 YAML 的事不用碰代码。5.4 可观测性埋点的最小集合可观测性不用一上来就做全套先埋最小集合每次调用的模型、任务类型、耗时、token 数、状态。这五个字段能回答大部分问题。埋点位置就在中间层处理请求的入口和出口天然统一。数据可以先写日志后续再接监控系统。日志格式要结构化方便后续解析。我建议给每次调用生成一个唯一的请求 ID贯穿整个调用链路。这样排查问题时能通过请求 ID 把中间层的日志、业务侧的日志、模型侧的日志串起来定位效率高很多。6. 上线后才会暴露的几个真实问题6.1 中间层自己挂了怎么办这是最讽刺的问题你为了高可用引入中间层结果中间层成了新的单点。解决办法有两个方向。一是中间层本身做多实例部署前面挂负载均衡一个实例挂了还有其他实例。二是应用侧做降级中间层不可用时能直接连主模型虽然会破坏统一接口但至少业务不中断。我倾向于第一个方向为主第二个方向作为极端情况的兜底。中间层多实例部署是标准做法成本也不高。6.2 流式响应经过中间层后的体验问题流式响应逐字输出经过中间层时如果中间层做了缓冲用户会感觉卡一下然后一大段出来体验很差。中间层必须支持流式透传不能缓冲整个响应再转发。实现上要注意中间层要能处理分块数据及时转发同时还要在流结束后统计 token 和耗时。这比处理非流式响应复杂但必须做对否则流式就白做了。6.3 成本统计和实际账单对不上前面提过 token 估算的问题这里展开说。如果中间层用的是估算的 token 数和模型服务实际计费的 token 数可能有偏差导致成本报表不准。解决办法是优先使用模型返回的官方 token 数。大多数模型在响应里会带上输入输出 token 数中间层直接取用即可。如果模型不返回再退而求其次用估算并在报表里标注估算值。另外要注意有些模型对缓存命中、批量调用有特殊计费规则成本统计逻辑要能覆盖这些情况否则算出来的数字会误导决策。6.4 多租户场景下的配额隔离如果中间层服务多个业务或团队配额隔离就很重要。一个业务把额度用光了不能影响其他业务。做法是在中间层按租户维度记录用量设置配额上限超限时拒绝或降级。租户标识可以从请求里带也可以按 API Key 区分。这个功能在业务少的时候可以不做但一旦业务多起来不做会出大问题。7. 我对中间层这件事的几点个人判断聊了这么多技术和实操最后说几点我自己的判断不一定对但都是从实际项目里磨出来的。第一中间层的价值随着模型数量增加而指数级上升。一个模型时它是负担两个模型时它是便利三个以上时它是刚需。所以别在只有一个模型时纠结要不要上等第二个模型来了再上完全来得及。第二统一接口的设计质量决定了中间层的天花板。接口设计得好后面加模型、加功能都顺设计得差每加一个模型都要打补丁。我建议接口设计多参考主流格式别自己发明一套。第三可观测性不是锦上添花是中间层的核心价值之一。很多人把中间层当成转发器只关注转发功能忽略了它作为统一埋点位置的价值。实际上能算清账、能定位问题往往比支持多模型更让老板满意。第四别追求一步到位。先搭最小可用版本统一接口加两三个适配器加基础重试跑起来。然后根据实际遇到的问题逐步加缓存、限流、可观测性。一上来就设计一个大而全的架构大概率会过度设计而且很多功能根本用不上。第五中间层是基础设施稳定性优先于功能丰富。它挂了所有依赖它的业务都挂。所以它的代码质量、测试覆盖、部署方式都要比普通业务代码更严格。宁可功能少一点也要稳。如果你现在正处在要不要上中间层的纠结期我的建议是先评估你接了几个模型、有几个业务在共用模型调用。如果答案是两个以上那就别犹豫了早点上早省心。如果还是一个那就先把业务跑通等信号出现再动手。技术选型这件事时机比方案更重要。