尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

多模型时代为什么需要AI网关?核心功能与落地实践

多模型时代为什么需要AI网关?核心功能与落地实践 你有没有遇到过这种情况项目里已经接了三四个不同的模型服务每个接口的请求格式都不一样鉴权方式不同超时设置也不一样想在两个模型之间做切换结果要改动的地方散落在一堆文件里。我去年做一个 AI 应用时就被这种问题折磨过后来引入了一个专门负责收口的中间层也就是现在很多人说的 AI 网关情况才彻底改观。这篇文章就围绕多模型时代为什么需要这样一个中间层展开讲讲它到底解决什么问题、核心功能怎么设计、最小可用版本怎么落地。如果你正在做 AI 应用开发或者准备接入 AI 能力但被供应商选择搞得头疼这篇文章应该能帮你少走不少弯路。我会把这套东西讲得尽量接地气不扯大而全的架构框架把注意力放在真正要理解的那几个关键点上。1. 为什么多模型时代你的应用比以往更需要一个中间层1.1 模型越来越多接入却越来越乱两年前做 AI 应用选项基本就是那一两个模型。现在再看GPT、Claude、Gemini国内的通义千问、文心一言、DeepSeek、Kimi还有一堆开源模型通过托管平台提供商业 API。每个模型各有擅长领域有的适合长文本处理有的在代码生成上更强有的多模态能力突出能直接看图理解图像。你不可能只押注一家因为模型迭代太快今天的最强模型三个月后可能就被另一家超越。问题在于每家模型提供商的接口规范都不一样。OpenAI 风格的接口用messages数组传对话历史Anthropic 风格的接口把system单独拿出来Gemini 用的是contents结构不同平台的鉴权方式也各有不同。如果你的业务代码直接调用这些接口你就得为每一家适配一套请求构造逻辑、错误解析逻辑、限流重试逻辑。这还没算上流式输出也就是逐字返回的 SSE 流不同平台的流格式切分方式甚至都不同。我见过一个项目只接了两家模型光是为了统一超时和错误处理就写了 600 多行重复代码。这不是代码能力问题而是架构上缺少了一个统一收口的地方。1.2 每一次切换模型都是一场冒险没有中间层的时候想从模型 A 切到模型 B表面看只是换个 URL 和 key实际要面对的问题一大堆。最直接的是请求格式不兼容你的代码可能到处都写死了modelgpt-4o这样的参数切换时要全局替换。更隐蔽的是两家供应商对错误响应的定义不同A 家在error.type里返回rate_limitB 家用 HTTP 状态码 429 表示限流你的重试逻辑可能只识别其中一种结果就是切过去之后某些错误场景下失效。供应商不稳定也是一个常被忽略的变量。某家模型服务在高峰期可能延迟翻倍或者短时间内频繁报错你如果直接在业务代码里调用它服务抖动就只能硬扛。有中间层之后你可以实现故障转移A 供应商超时后自动把请求转发到同级别的 B 供应商业务侧完全无感。我曾经把一个推理服务的成功率从 97% 提到 99.9% 以上靠的就是网关层的自动切换而不是让上游模型供应商承诺什么 SLA。1.3 没有中间层的账算下来比想象中贵最后一点成本问题。各家模型的定价差异非常大同一类任务贵的和便宜的能差 5 到 10 倍。没有统一计量和路由层你很难精细控制每个月花了多少钱也很难做简单任务走便宜模型、复杂任务走贵模型这种策略。有了中间层之后你可以记录每一次请求的模型、token 用量、耗时、费用再根据自己的预算做分配。这不是锦上添花而是多模型时代的基本能力因为当你的应用有一定用户量之后模型成本不是一个可以忽略的数字。2. 中间层到底该做什么不该做什么2.1 统一 API 格式把差异留在网关里AI 网关最核心的职责就是格式适配。对外它向业务代码提供一个稳定统一的接口通常做法是把 OpenAI 的 chat completions 格式当成基线因为这套格式市面上大多数模型都已经适配过。对内它针对不同供应商实现各自的适配器把上游模型的请求格式、响应格式、错误格式都转换成统一的形态。这样业务代码只依赖这一套格式模型供应商怎么变都影响不到上层。举个例子你的应用要支持多模态输入用户上传一张图片附带一句描述一下这张图。网关收到请求后需要根据目标模型的能力做不同的格式转换有的模型支持 base64 编码图片放进消息里有的模型接受图片 URL还有的模型要求图片与文字分开传输。这些差异全部在网关内部消化上层只需要传一个 OpenAI 风格的image_url就行。2.2 智能路由把请求分发给最合适的模型统一格式只是第一步路由才是体现网关价值的地方。你可以基于任务类型定义路由规则摘要任务走性价比高的模型代码生成走代码能力强的模型复杂推理走顶级旗舰模型。也可以按用户级别区分免费用户用基础模型付费用户用旗舰模型。还有一种场景是灰度发布新模型先分配 5% 的流量试跑对比效果稳定后再逐步放大比例。路由策略的实现在工程上并不复杂核心就是一个规则引擎加随机权重分配。比如某条路由规则配置成modelfast时70% 流量分给模型 A30% 分给模型 B。网关每次收到请求根据请求里的路由标签、用户 ID、请求内容特征算出该走哪条路然后交给对应的适配器处理。配置化之后调整分流比例只需要改配置和热加载不需要重新发版。2.3 容错重试与故障转移最容易被忽略的救命功能调用模型 API 不是每次都能成功。上游服务可能 429 限流、500 内部错误、504 网关超时或者连接在网络传输中被切断。没有网关时这些错误会直接抛给业务层处理有网关时你可以把重试和降级策略集中管理。这里要特别注意重试不是简单的失败就再来一次。要考虑几点一是退避策略连续重试要有指数退避比如第一次失败后等 300ms第二次等 600ms第三次等 1200ms避免加重上游压力二是重试次数上限一般 2 到 3 次就够了多了没有意义三是幂等性如果一个请求已经在上游执行了但响应超时重试可能导致用户被重复扣费所以网关设计里最好支持幂等键或者对非幂等场景只做故障转移而不做盲目重试。故障转移则是把请求发给同类的备用模型前提是两边能力对齐不能从文本模型转到一个不支持文本输入的模型。2.4 密钥管理、计量计费与可观测性这些隐形功能同样关键很多团队一开始只把网关当格式转换工具用着用着才发现密钥管理和计量也必须在网关层做。如果密钥直接下发到客户端或者散落在各个业务服务里一旦泄露就要挨个换而且不知道哪里在用。统一收到网关之后模型供应商的密钥只存在于网关这一个地方业务服务只需要使用网关颁发的应用密钥泄露了也可以单独吊销不影响其他业务。计量计费牵涉到每一路请求的 token 消耗。文本模型的 token 计数相对明确多模态请求则没那么简单图片的 token 消耗由图片尺寸和细节级别决定音频要按时长换算。网关层记录这些数据长期留存后面做成本分析、异常检测、按用户计费才有所依据。可观测性则是把每一次请求的模型、耗时、状态码、token 数、错误原因全部落日志通过监控面板展示出来。反过来说网关不该做什么不该承担业务逻辑。很多人用着用着就想把 prompt 拼接、RAG 检索、工具调用逻辑都塞进网关里这会让网关变得又重又不可维护。中间层只负责连接、转换、分发、保护业务逻辑应该留在你的应用层。3. 核心设计思路从零设计一个 AI 网关的骨架3.1 先想清楚数据模型与目录结构真正动手写网关之前先把数据模型定义好。我习惯用一张providers表存储供应商信息包括名称、类型、Base URL、鉴权密钥、支持的模型列表另一张models表存储模型实例关联到某个 provider标注能力标签文本、视觉、语音、上下文窗口、计费单价再有一张routes表定义路由策略包括匹配条件、目标池、权重分配、熔断阈值。日志表也要从一开始就设计request_logs至少要记录这些字段请求 ID、时间戳、路由命中的规则、上游模型、请求消耗的输入/输出 token、总耗时、状态码、错误信息。有了这些原始数据后面做成本分析、限流阈值调整、路由策略优化才有依据。不要等到上线了再补日志设计晚一天就少一天的观测数据。3.2 一次请求在网关里的完整流转过程我画过很多次这个流程核心路径其实只有一条。第一步接收业务侧统一格式的请求第二步根据路由规则解析决定这次请求走哪个模型第三步检查限流超过配额直接返回 429第四步调用适配器把统一格式转换为目标模型的格式并发送第五步收到响应后把响应转换回统一格式记录日志返回给业务侧。多模态请求在这个流程里多了一道工序因为输入内容可能很大。比如用户传了一张 5MB 的图片网关需要判断是否要压缩、是否要转成 base64 还是传 URL、目标模型的最大输入限制是多少。有时候还要做内容裁剪把多张图按 token 预算选出最重要的几张。这些处理放在网关卡层比放在业务侧更合适因为同样的图片对不同模型需要的预处理方式可能不一样。3.3 流式响应是这个环节里最容易翻车的地方流式响应也就是打字机效果是 AI 应用体验的核心。网关在做流式转发时必须保持流本身的完整性不能等全部内容收到再一次性返回那样用户体验就毁了。具体实现要点是上游返回 SSE 流时网关逐块读取逐块转发同时对块内容做格式转换。这里有个细节不同模型供应商流的切分方式不同有的把整个 JSON 对象一次性输出有的切成十几个小块分次输出。网关要把这些块映射到统一协议的流格式上保证下游接收到的数据形状与直接调用 OpenAI 时一致。另外流传输过程中连接可能中断网关要能识别中断位置并把未完成的流状态记录下来。我在实操中见过最多的坑是网关把上游的流缓冲之后再转发导致中文字符被截断或者忘记设置合适的 TCP 缓冲区和心跳参数流在长时间空转时被中间网络设备断开。这些问题排查起来非常隐蔽只能靠详尽的日志来定位。3.4 技术选型语言和框架怎么定网关的技术选型我建议优先考虑生态成熟、异步处理能力强的语言。Python 有 FastAPI 和 httpx写适配层非常方便团队熟悉成本低Node.js 的流式处理天然适配 SSE如果你们团队前端背景强用它写转发逻辑也很顺手Go 的并发和部署优势明显适合大规模、高吞吐的场景。但实际选型时最关键的考量不是性能极限而是团队维护能力和迭代速度。我个人的做法是中小规模项目用 Python 起步因为最大的瓶颈通常不是并发而是各家模型适配器要写的代码量很大Python 的灵活性在这里最能发挥。等到请求量上来了再把网关中真正热路径的部分比如路由与限流用更高效的方案优化。要注意的是网关本质上是网络 IO 密集而非 CPU 密集性能的关键在于异步模型和连接池配置而不是语言本身的运算速度。4. 落地实操最小可用 AI 网关搭建全过程4.1 第一步写一个最基本的统一接入层我们假设技术栈是 Python FastAPI。先定义一个统一的请求模型尽量兼容 OpenAI 风格model、messages、temperature、stream、max_tokens再加一个扩展字段x-model-tags用来做自定义路由标签。核心代码逻辑如下from fastapi import FastAPI, Request from pydantic import BaseModel from typing import List, Optional app FastAPI() class ChatMessage(BaseModel): role: str content: Any # 接受字符串或多模态内容列表 class ChatRequest(BaseModel): model: str messages: List[ChatMessage] temperature: Optional[float] 0.7 max_tokens: Optional[int] 2000 stream: Optional[bool] False extra: Optional[dict] {}这个模型故意设计得比较宽松content用Any因为多模态消息的内容可能是字符串也可能是包含image_url、audio的列表。接下来注册一个统一入口/v1/chat/completions所有业务请求都打到这里app.post(/v1/chat/completions) async def chat_completions(req: ChatRequest): route route_manager.resolve(req.model, req.extra) await rate_limiter.check(route) provider_adapter adapter_factory.get(route.provider) response await provider_adapter.chat_completions(req, route) await usage_logger.record(req, route, response) return response这段代码看起来简单但实际上已经把前面提到的路由、限流、适配、日志都串联进来了。每一层各自独立后续要替换或升级某个环节不会影响其他部分。4.2 第二步实现一个供应商适配器适配器是网关里代码量最大、最容易错的部分。每个适配器负责两件事把统一请求格式翻译成目标模型的请求把目标模型的响应翻译回统一格式。举一个简化的例子假设某个供应商要求消息体格式是contents而不是messagesclass GeminiAdapter: def convert_request(self, req: ChatRequest) - dict: contents [] system_prompt None for msg in req.messages: if msg.role system: system_prompt msg.content else: contents.append({role: msg.role, parts: [{text: msg.content}]}) payload { contents: contents, generationConfig: { temperature: req.temperature, maxOutputTokens: req.max_tokens, }, } if system_prompt: payload[systemInstruction] {parts: [{text: system_prompt}]} return payload def convert_response(self, resp_data: dict) - dict: candidate resp_data[candidates][0] return { id: resp_data.get(requestId), model: resp_data.get(model), choices: [{ index: 0, message: { role: assistant, content: candidate[content][parts][0][text], }, finish_reason: candidate.get(finishReason, stop), }], usage: { prompt_tokens: resp_data.get(usageMetadata, {}).get(promptTokenCount, 0), completion_tokens: resp_data.get(usageMetadata, {}).get(candidatesTokenCount, 0), total_tokens: resp_data.get(usageMetadata, {}).get(totalTokenCount, 0), }, }实际写适配器的时候比这个要复杂得多因为要处理错误映射、流式返回、重试判定、超时字段差异。但核心思想是一样的每个供应商差异都封装成独立的转换器不要写进路由逻辑里。这样以后接入新模型工作量就是新增一个适配器而不是改动已有的任何代码。4.3 第三步路由配置与动态权重路由配置建议用 YAML 文件管理理由是可读性好适合团队评审和审计。一个典型的路由配置长这样routes: - name: default match: model: fast providers: - provider: aliyun_qwen weight: 70 - provider: openai_gpt4o_mini weight: 30 fallback: - provider: deepseek_chat这个配置表达的意思是业务侧请求modelfast时默认 70% 走国内通义千问30% 走 GPT-4o mini如果这两条路都失败降级到 DeepSeek。权重分配不要求两边模型完全一样但业务上要保证能力等级接近不能让用户明显感觉到随机换了一个能力差很多的模型。动态权重更新的实现就是定时加载配置文件根据provider和weight构建一个加权随机选择器。比例调整不用发版改配置后触发热重载即可。我在生产环境里的策略是新模型先给 5% 流量观察一周延迟和错误率没问题再逐步加到 30%稳定之后放量到 70%。4.4 从零迁移存量代码怎么平滑切到网关上存量代码迁移这件事很多人一开始就搞错了方向。正确的做法是不要在业务代码里把openai OpenAI()全部替换成custom OpenAI(base_urlhttp://gateway)就算完而是要回到两个层面处理。第一个层面是接口兼容。OpenAI 官方 SDK 支持传入自定义base_url你的业务代码原本怎么用 SDK 的现在只需要把 base URL 指向网关地址模型名改成你在网关定义的路由名称。如果原本用的是原始 HTTP 请求改造方式也类似把 URL 前缀换掉再加一个网关应用密钥的请求头。这个层面的改动很小通常一个配置文件就能解决。第二个层面是策略对齐。存量代码里如果有自己实现的重试、超时、熔断逻辑建议逐步移除收敛到网关统一管理。最省事的做法是先是网关层和业务层各留一套但把业务层的阈值调低比如重试次数改成 0等运行稳定后代码再删掉。不要想着一步到位逐步收敛的风险最小。4.5 上线前必须检查的清单我每次部署网关前都会过一遍这几个检查项其中大部分是踩过坑之后总结出来的流式接口是否真的逐 token 转发前端的打字机效果有没有变卡顿对上游 5xx 错误的处理是否走了故障转移而不是直接抛给用户超时配置是否覆盖了连接超时、读取超时、总超时三个维度请求 body 大小限制是否考虑过多模态场景图片上传是否会被网关层误拦截日志系统是否记录了请求和响应的完整上下文尤其是错误场景限流策略是否分桶设置普通用户、高频用户、管理员配额是否各不相同。这一份清单看起来琐碎但每一个都曾经在真实项目中引起过问题。别偷懒跳过。5. 常见问题与排查技巧实录5.1 问题速查表先看看有没有你遇到过的症状可能原因排查与解决流式响应断断续续前端打印字速度极慢网关没有透传流数据而是先缓存再整包返回检查流式分支是否使用了StreamingResponse确认上游数据块确实即时转发同一请求不同模型token 计量差异巨大各供应商 tokenizer 不同多模态输入换算规则不同不要跨模型精确对比 token网关记录各供应商原始用量即可成本核算以各供应商账单为准切到新模型后频繁超时新模型响应速度慢但网关沿用旧模型的超时阈值不同模型分别配置超时参数长文本模型可以设到 120s上游 429 太频繁限流重试反把连接打满退避时间太短或重试次数设置过多指数退避起始 300ms最大重试不超过 3 次重试请求也必须走限流统计网关部署后业务接口延迟增加网关与业务服务不在同一区域网络往返增加网关与模型服务的网络链路优先就近原则避免跨地域调度图片上传请求经常报 413网关默认请求体限制小多模态文件被拒针对/v1/chat/completions单独放宽请求体限制但也要设上限防止滥用5.2 流式响应断流的排查思路这是我在实践中遇到最多的问题也是用户感知最明显的故障。流式响应断流的典型表现是内容输出到一半连接没有报错但前端不再收到新的数据过几十秒才超时。这种问题非常隐蔽因为错误日志里没有任何异常只有日志记录显示某次请求的finish_reason为空。排查步骤一般是这样先看网关到上游的原始流是否完整用 curl 直接请求上游模型接口检查是否能完整输出如果上游完整那问题大概率出在网关的反向转码环节检查流响应是否因为某种原因被节点代理缓存或者是网关内部读取循环中遇到了空块就提前退出。还要检查 Nginx 或负载均衡器的proxy_buffering配置如果开启缓冲SSE 流会被攒成多个大块再下发前端看到的效果就是一顿一顿的严重时直接表现为断流。另外网关自身和中间网络设备的中转超时也值得看长连接空闲时间过长容易被网络设备断开需要设置心跳或关闭空转清理。5.3 配置化的边界别把网关做成一个万能平台网关本身的复杂度在快速增加这是一个隐患。很多团队做着做着就想把网关做成一个AI 中台在里面加 prompt 管理、知识库、向量检索、可视化编排最后得到一个又慢又难维护的巨无霸。我个人的体会是网关的功能边界要克制只做与模型连接和流量治理强相关的事业务能力往下放。你在设计路由策略和适配层的时候要把可替换作为核心原则。今天用了一个开源网关组件半年后流量翻倍需要换实现语言或者换部署架构你的配置数据和日志格式是否还迁移得过去好的设计应该让迁移成本集中在适配器本身而不是把所有业务逻辑都耦合在网关里。从长远看先小后大、边用边补比一开始就规划一个全能平台要稳妥得多。6. 最后再分享一点个人的实践体会这套 AI 网关方案我从零搭到上线前后用了大约两周时间真正帮我解决问题的是第一批日志上线后的数据。刚开始我以为业务里大部分请求都走的是 GPT 系列结果日志显示有一半流量被路由到了性价比更高的模型上月成本直接降了四成。这件事给我的启发是中间层不是凭空增加一层复杂度而是把你之前分散在业务代码里的隐性复杂度集中到一个可以观察、可以控制的地方。如果你现在的 AI 应用还只在接一个模型我也不会劝你立刻上网关。等到你开始纠结这个任务用哪家模型合适怎么在不大改代码的前提下换个模型试试的时候再动手做也不迟。但有一点可以现在就做把你业务代码里所有调用模型接口的入口收敛到一个独立模块哪怕是同一个文件里的一个函数未来你接入网关时就会发现这个习惯有多重要。
返回列表