先讲个真实场景。我的项目大概跑到400多用户的时候,每天调用量和模型账单同时起飞,各种模型报错开始零星出现。那时团队还在快乐地直连各家API,每个程序员手里握着三四个key,代码里散落着不同厂商的SDK。直到某天凌晨,某家模型服务商突然限流,用户反馈刷屏,我们才知道什么叫“为直连买单”。
后来我把AI Gateway这个基础设施角色单独拎出来,才算是把工程效率从泥潭里拖了出来。这篇文章就聊聊,为什么直连模式会在用户量上来后出现系统性问题,以及AI Gateway到底解决了我这边的哪些具体麻烦。内容偏实操,适合已经跑通demo、正在往生产环境迈进的开发者和技术负责人。
1. 直连模式的隐形债务:为什么500用户成了分水岭
1.1 表面是调用量问题,实际是治理问题
很多人觉得,AI Gateway不就是个反向代理吗,把请求转发给各家模型服务商,再加个API key管理,能有多深的水?
我的早期判断也是如此。可当用户量过了某个临界点之后,你会发现直连模式在工程层面积累的债务,是几何级数爆发的。500用户其实不是我瞎说的心理线,而是我在日志里观察到的拐点。小于这个规模,你手工处理key轮换、写死模型名、在业务代码里堆try-catch,都还能扛住。但一旦业务方开始频繁调整模型参数、需求方开始对比不同模型效果、运营开始要求统计每次调用的成本,这套脆弱的直连架构就彻底露馅了。
直连模式最大的问题不是性能,而是决策点分散。每个业务模块自行决定调用哪家模型、哪个版本、什么参数,相当于每个团队各自维护一套模型路由策略。当模型服务商调整价格、下线旧版本、改动接口协议时,你得追着所有代码去改。这种隐形成本消耗的不是服务器资源,而是开发人员的精力和团队的迭代速度。
1.2 成本失控的三个典型信号
我复盘了自己项目的日志数据,直连模式下成本失控通常会经历三个阶段。
第一阶段是账单焦虑。某个月模型调用费用突然翻倍,排查后发现是有个定时任务把循环条件写错了,24小时跑了几十万次接口。直接连模型服务商让你根本来不及做实时监控,月底看账单才知道爆了。
第二阶段是key泄露风险。前端的key只要被有心人抓包,就可能在外部被恶意刷量。我见过不止一个团队的前端代码里硬编码了API key,等到账单爆炸才发现早被薅了几万块钱。直连模式下,你甚至没有一个统一的地方去吊销、轮换、设置调用来源限制。
第三阶段是模型切换成本。今天这个模型效果差,明天那个模型降价了,需求方永远在不断尝试。但你发现每切换一次模型,就要改代码、改配置、重启服务。如果哪天想搞A/B对比,把同样的问题发给两个模型,看哪个效果好,工程实现的上手成本依旧不小。这三个信号出现之后,就是该把AI Gateway放进架构的时候了。
2. AI Gateway的核心价值拆解:路由、成本、可观测性
2.1 路由层:让模型调用变成配置而不是代码改动
AI Gateway解决的第一件事,就是把“调用哪个模型”这个决策从代码层剥离出来。
我在项目里用的是开源方案,核心思路很简单:业务代码只发一个请求给Gateway,Gateway根据内部配置规则决定转发给到底层的哪个模型服务商。这个规则可以是简单的model名映射,也可以是基于用户等级、请求类型、当前价格、限流配额的动态路由。
举一个我实际在跑的例子。我的业务里有文本生成、图像描述、代码分析三种场景,分别对接了三个不同厂商的模型。直连模式下,业务代码里就得写三套SDK、三套错误处理、三套计费逻辑。引入Gateway之后,业务代码统一变成类似这样的一次HTTP调用,我在Gateway的配置文件里维护了这样一张路由表:
| 场景 | 模型标识 | 实际后端 | 备注 |
|---|---|---|---|
| 文本生成 | gpt-4o-mini | OpenAI | 默认 |
| 文本生成 | kimi-fast | Moonshot | 降级备用 |
| 图像描述 | qwen-vl-plus | 阿里云 | 国内直连延迟低 |
| 代码分析 | deepseek-coder | DeepSeek | 性价比优先 |
这张表的意义在于,业务方只需要知道“我调的是gpt-4o-mini”,至于这个标识背后指向谁、被换成谁、是否加了缓存、是否被限流,业务方完全不用关心。今天ModelA涨价了,我在Gateway配置里把gpt-4o-mini的实际后端改成ModelB,业务代码一行不动。
这个能力在模型服务商频繁调整策略的当下,几乎成了刚需。我用一个形象的比喻:以前是每家餐厅你都要单独办一张会员卡,堡垒机、账单、投诉电话全得分别处理;现在你找了一个代订平台,点餐时只说自己想吃川菜,具体去哪家店,由平台帮你定。
2.2 成本治理:预算、限流、缓存三板斧
AI Gateway在成本控制方面天然具备集中管理的优势,因为所有请求都过它这一层。我实践中用得最频繁的是三个功能。
第一是预算控制。可以按项目、按用户、按请求维度设置上限。比如免费用户每天最多调100次,超出后直接返回429,提醒用户充值或次日再来。这个配置在Gateway里只是写几行规则,但在直连模式下就得在各业务代码里加判断逻辑,还容易漏。
第二是限流熔断。当某个模型服务商响应变慢、大量报错时,Gateway可以自动切换到备用模型,或者直接拒绝新的请求避免雪崩。我实际遇到过OpenAI那边某个模型的p95延迟从3秒飙到15秒的情况,如果直连,我的服务可能整片超时,但有了Gateway之后限流策略在毫秒级生效,用户体验基本不受影响。
第三是缓存。对于高频且答案可以复用的请求,比如产品介绍、固定问答、FAQ,直接在Gateway层做语义或精确匹配缓存。准确来说,我这边用的是精确匹配缓存,同一个用户短时间内的重复请求直接命中缓存,不再打到模型服务商。粗略估算,这一项就能省三到四成的token费用。不过要注意,缓存设置必须考虑语义覆盖、过期策略和用户隐私,不是所有业务都适合开缓存。
2.3 可观测性:从“猜”到“看”
第五个让我感受最深的能力是可观测性。直连模式下,模型调用日志散落在各个服务里,你想统计某个模型每天消耗多少token、接口耗时分布、错误率变化,基本靠猜。而AI Gateway天然是一个汇聚点,所有调用都经过它,所以可以做统一的日志、指标、链路追踪。
我搭的Gateway暴露了几个核心指标:每分钟请求量、分钟级token消耗、按模型分组的延迟分位数、按服务商分组的错误率。这些指标接到Prometheus以后,我可以在Grafana上拖一个仪表盘,实时看模型调用状态。再配合告警规则,比如“当某个模型的5分钟错误率超过5%就发告警”,就能在用户感知到问题之前介入处理。
这块的价值没法用具体的金钱衡量,但它能让团队对模型的线上表现有清晰感知。说句实在话,以前我总觉得模型调用的黑盒是不可控的,但有了Gateway之后,至少这条链路上能见度提升了非常多。
3. 部署与配置:从零搭建自己的AI Gateway
3.1 工具选型:开源方案怎么挑
现在市面上的AI Gateway方案并不少,开源的、商业的、托管的都有。我的经验是:别急着选最重的,也别选最轻的,要选能在你的部署环境里稳定跑起来的。
梳理下来,开源方案里比较有名的有LiteLLM、Higress AI Gateway、Portkey Gateway等。选型时我主要看三个维度。
一是协议兼容性。当前业务代码调用的是OpenAI格式的接口,那我希望Gateway最好能直接兼容这个协议,这样业务代码几乎不用改,只要把base url指向Gateway就行。LiteLLM和Higress在这点上做得都不错,而Higress网关则适配了包括OpenAI、Anthropic等主流供应商在内的协议格式,内置了多模型的路由逻辑。
二是部署方式是否契合自己团队的技术栈。我这边本身就有Kubernetes集群,所以倾向于选择能够以标准方式部署的方案。LiteLLM提供Docker镜像,也能轻松部署到K8s;Higress则是基于Istio和Envoy的云原生网关,天然适配K8s生态,如果你已经在用Istio那接入成本几乎为零。
三是路由策略的灵活度。我需要支持按模型标识映射到不同后端,也希望能扩展自定义逻辑。这个点需要实际去读文档、跑demo验证。最终我选了LiteLLM,理由是它配置简单、路由能力足够、社区活跃。如果你的团队已经有较强的网关治理经验,并且想直接在网关层集成更多流量管理能力,那Higress AI Gateway可能更合适。
3.2 配置示例:路由、限流、预算落地的样子
下面给一个我实际在用的配置片段,基于LiteLLM,用docker-compose启动。
# docker-compose.yml version: "3.8" services: litellm: image: ghcr.io/berriai/litellm:main-latest ports: - "4000:4000" volumes: - ./config.yaml:/app/config.yaml command: ["--config", "/app/config.yaml", "--port", "4000", "--num_workers", "4"]对应的config.yaml核心配置如下:
model_list: - model_name: gpt-4o-mini litellm_params: model: openai/gpt-4o-mini api_key: os.environ/OPENAI_API_KEY - model_name: kimi-fast litellm_params: model: moonshot/moonshot-v1-8k api_key: os.environ/MOONSHOT_API_KEY rpm: 60 - model_name: local-llama litellm_params: model: ollama/llama3:8b api_base: http://localhost:11434 rpm: 120 router_settings: routing_strategy: usage-based-routing num_retries: 2 request_timeout: 30 allowed_fails: 3 cooldown_time: 30 litellm_settings: drop_params: true set_verbose: false这里解释几个关键配置的意思。
routing_strategy: usage-based-routing表示按照模型权重和健康状态做路由,如果主模型挂了,会自动把流量转发到备用模型。num_retries: 2表示当主模型失败时,Gateway会尝试重试2次。注意,这里的重试并不是原样重发,而是可以根据配置的路由策略,把请求转发给备用的模型。
rpm限制的是每分钟请求数,可以防止某个模型被某个调用方打爆。有一点需要提醒:如果配置了多个模型共享同一个后端,rpm的配额是基于整个Gateway实例的,而不是每个模型单独计算。我曾经在这里栽过跟头,以为给模型A配了60的rpm、模型B也配了60的rpm,各自能跑满,结果两者共用同一个上游Key,整体还是被上游限到60,升级了认知之后才好很多。
allowed_fails: 3和cooldown_time: 30的含义是,如果某个模型连续失败3次,Gateway会把它临时标记为不健康,冷却30秒后再尝试恢复。这个机制非常实用,避免了一个故障模型反复被打导致大面积超时。
启动之后,业务代码里的base url从直连的https://api.openai.com换成http://<gateway-host>:4000,就能无缝接入。如果用的是OpenAI的Python SDK,代码改动量就很小了。
from openai import OpenAI client = OpenAI( base_url="http://localhost:4000", api_key="sk-litellm-master-key", ) resp = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": "你好"}] ) print(resp.choices[0].message.content)这个过程中我踩过最大的坑是环境变量注入。LiteLLM在容器启动时读取的api_key是容器内的环境变量,而不是宿主机上的,所以在docker-compose里得用environment字段,或者用env_file,不然启动后你会发现模型列表全加载了,但实际调用时一直报401。排查了半小时,最后发现是环境变量没传进去。
3.3 本地模型接入:GGUF与Ollama配合
除了云上模型,我也在Gateway后面接了自己的本地模型。原因很简单:一些数据处理任务涉及用户隐私,不方便把内容发送到第三方API,必须要本地推理。选型上我用了Ollama作为本地推理服务,然后在LiteLLM里配置model为ollama/<model-name>。
本地模型文件用的是GGUF格式,这个格式主要是为了解决大模型在CPU上的高效推理问题。你从Hugging Face下载好的GGUF文件,通过Ollama的模型管理机制导入后,就能用一行命令启动本地推理服务:
ollama serve ollama run llama3:8b然后在LiteLLM配置里加一条这样的路由:
model_list: - model_name: local-llama litellm_params: model: ollama/llama3:8b api_base: http://localhost:11434 rpm: 120这样业务代码调local-llama,Gateway实际转发到本地Ollama。这里有个容易踩的坑:Ollama的并发能力非常有限,默认情况下只能同时处理一个请求,后面的请求会排队。所以如果你在Gateway层配置了很高的rpm,但本地Ollama只有一个模型实例在跑,同样会造成请求堆积。我的解决方式是给Ollama配置多实例,同时通过LiteLLM的权重轮询把请求分散到不同的本地端口上。
另一个坑是显存限制。本地模型的推理速度完全取决于GPU显存和算力。我用8GB显存的显卡跑Llama3 8B的量化版,单次请求大约需要4到8秒,这个速度没法支撑线上高并发场景。所以我的线上策略是:核心高频请求走云上模型,隐私任务和离线任务走本地模型,各取所长。
4. 模型熔断与参数优化:网关层才能做的精细活
4.1 动态模型切换:业务零感知
有了Gateway之后,模型切换变得非常轻量。我们有个功能原本用的模型A,某天突然开始输出质量变差,看起来是模型服务商进行了调整。之前直连时,面对这种情况只能全量回滚代码,重新部署。但在Gateway架构下,我只需要新增一个同名的模型条目,把请求转发到另外一个模型服务商,然后等流量逐步切过去就好。
实际操作中我做了个更稳的灰度策略。比如原本gpt-4o-mini后端是OpenAI,我想把流量切到某国产模型上,但又担心效果差异。我在配置里同时保留两个条目,给它们分配不同的权重,再配合前面的usage-based-routing,让Gateway按权重把请求分发到两个后端。先切5%流量跑几天,对比效果和延迟,逐步调整权重,直到全量切换。整个过程业务代码零改动,用户也基本感知不到。
这里补充一个关于模型融合的说明。很多人在热词里看到模型融合,以为是在网关层把多个模型的输出做个加权平均再返回。实际上,AI Gateway层的所谓模型融合,更多指的是多模型路由、多模型灰度、多模型容灾的组合,而不是文本层面的融合。如果真的要做“多个模型结果拼接/投票/交叉验证”,那是应用层该做的事,Gateway可以提供原始响应,但不会帮你做语义级融合。理清这个边界很重要,不然你会对Gateway的能力产生误判。
4.2 参数透传与上下文管理
模型调用时经常需要调整temperature、max_tokens、top_p这些参数。直连模式下,每次修改参数就要改代码,非常繁琐。而LiteLLM支持在请求参数里直接传这些参数,并原样透传给后端模型。这意味着你在业务代码里可以灵活控制生成风格,不需要去碰Gateway配置。
从上下文管理角度看,大多数模型API是无状态的,每一轮对话都要传完整的对话记录。这个数据积累多了以后,会吃掉大量token。我在Gateway这一层做了一些上下文裁剪的尝试:对超过一定长度的对话记录,自动做截断或摘要,再发给模型。目前的实现做得还比较粗,但对降低token消耗确实有用。
在你打算这么做之前有个提醒:修改上下文前一定要想清楚业务语义。比如客服场景,用户可能中途修改了地址,摘要如果没把新地址带进去,模型就会基于错误信息回答。我早期做简单截断时,就出过一次客服回答错误的事故。现在我的策略是:截断只针对比较早期的历史,最近N轮对话内容保持原样,且关键实体信息单独提取保存,拼接到系统提示词里,能显著降低信息丢失的概率。
4.3 模型检查器:在网关层做输入输出校验
关键词里有个“模型检查器”,这也是我最近在一个AI网关实例里开始用的功能。它的主要用途是在请求进入和响应返回时,做规则校验。比如:
- 输入侧:检查文本长度是否超限、是否包含非法关键词、是否带敏感信息,提前拦截,避免浪费模型调用。
- 输出侧:检查模型返回的JSON结构是否完整(如果让模型产出结构化数据)、是否有缺失字段、是否符合预期格式。不合法就自动重试一次,或者走降级方案。
这个能力放在Gateway里做,比在业务代码里做更合理,因为它是横切关注点,所有模型调用共用,而且还能和告警系统联动。我现在的网关配置里,就接了一个自定义的简单检查逻辑,用Python写了一段校验函数,在收到模型响应后先验证一段,再返回给上游业务。因为Gateway本身是异步框架,校验的延迟控制在几毫秒级别,业务方感受不到额外开销。
4.4 token耗尽与“回答被截断”的应对
做模型应用时间长了,一定会遇到“已达到输出token上限,回答被截断”的情况。这不是模型Bug,而是你在请求时设置了max_tokens,模型生成了这么多token之后被硬性截断。现象是用户看到回答说到一半就没有了,特别影响体验。
处理这个问题的三板斧是:
第一,合理设置max_tokens。根据业务类型评估回答的长度上限。比如分类任务的答案很短,200token就够;但文章生成任务,最好500token以上。别省这点头寸,被截断后用户重试一次的成本,远高于多花的那点token钱。
第二,在请求参数里加stream选项。流式输出的好处是用户可以实时看到内容生成过程,就算被截断,前面的内容也已经展示完毕,用户知道是模型停下来了,而不是系统故障了。配合流式输出,还能在前端做一个“继续生成”按钮,调用续写接口,把打断的部分补完。
第三,利用前文的信息。如果业务允许,把当前已生成的内容拼到下一次请求的上下文里,让模型接着写。这个在实现时需要小心上下文长度膨胀,通常配合token计数,动态决定携带多少历史内容。
5. 常见问题与排查技巧实录
5.1 网关报了401,但ApiKey明明是对的
这是个典型的部署问题。排查顺序建议是:先确认容器里环境变量有没有正确注入,再确认config.yaml里的引用格式是否正确。LiteLLM的api_key引用必须是os.environ/VAR_NAME,而不是直接写密钥明文。我遇到过把密钥明文写在yaml里,结果启动正常但一调用就报403权限错误的情况。换成环境变量引用后瞬间好了。
另一个是多Key轮换问题。有些厂商允许一个主账号创建多个API Key,Gateway可以把这些Key配成数组,按轮询或随机方式使用。这个设计能缓解单Key限流,但如果某个Key触发了厂商的风控,整个轮询池都可能被冻,现象是间歇性报错。遇到这种情况,直接去厂商后台看Key的使用记录,把异常Key换掉即可。
5.2 模型响应超时,但上游服务明明是正常的
这个问题很多时候出在DNS解析或网络路径上。我遇到过一种诡异场景:直接curl上游API只要1秒,但从Gateway所在服务器访问居然5秒才返回。用traceroute一查,发现网络出口绕到了很远的一个节点。解决办法是给Gateway所在节点设置更优的DNS或路由策略,或者内网部署一个API代理。
另外,如果你在配置里设了num_retries: 2,超时后会重试2次,这会让最终响应时间变成正常耗时的3倍,对一些要求低延迟的场景是不可接受的。所以重试策略不要只想着提升可用性,还得和业务超时时间对齐。我的经验是:普通场景重试1次就够了,幂等请求可以适当多试,但非幂等请求一律不要自动重试,否则可能重复扣费或产生重复订单。
5.3 本地模型推理慢,怎么定位瓶颈
在Gateway后面接本地模型时,最常见的求助就是“太慢了”。定位方法分两步。
第一步,确认瓶颈在网关还是推理服务。直接在服务器上curl本地Ollama的接口,测一下裸推理耗时。如果裸推理就很慢,那瓶颈就是GPU或模型本身;如果裸推理很快,但通过Gateway就慢,那瓶颈在网关的转发或频控上。
第二步,确认有没有排队。Ollama默认一次只能处理一个任务,如果同时有多个请求,会被排队处理。你可以通过Ollama的日志看到排队情况。解决方式是开多实例,或者换用支持并发推理的推理服务,比如vLLM或TensorRT-LLM。
实测下来,用vLLM部署量化模型并发能力比Ollama强不少。但vLLM对GPU显存的要求更高,且配置更繁琐。如果你只是为了给内部工具提供推理服务,Ollama的简单易用还是更合适;如果是为了生产环境的高并发,建议一步到位上vLLM。
5.4 成本报表和Token统计对不上
这种情况我碰过好几次。原因是统计维度不一致。上游API账单统计的是计费token,包含了输入输出,可能还有缓存命中折扣;而Gateway里统计的是请求日志里的token字段。两者本身就不是同一个口径。要看真实成本,建议以网关日志为准,通过对每次请求记录prompt_tokens和completion_tokens,再乘上对应模型的单价,自己算一笔账。把计算的单价单独做成一份配置表,这样还能顺便做各模型的成本对比,方便后续选型时做数据支撑。
另外,如果你开了缓存,Gateway统计到的token使用的是处理请求时的真实调用,而缓存命中的请求不再消耗token。这部分在这里会体现为成本下降,但在上游账单里却看不到,因为根本不会产生计费。如果你没留意这个问题,看到账单和统计的差异,很容易误判。
6. 3000字之外的几句实在话
这篇文章写到这里,基本上把我在500用户之后做Gateway改造的思考和实践都讲到了。回过头去看,AI Gateway这个组件解决的最核心问题不是“把请求转发到哪”,而是“让模型调用这个动作变得可治理、可观测、可编排”。在模型服务商百花齐放、模型能力快速迭代的这个时间段,这个治理层的价值只增不减。
我个人的体会是,不要等到模型调用量爆炸才考虑上Gateway,也不要因为系统还小就轻视这条链路的治理能力。从第一天起就把模型调用收口到一个统一的出入口,成本极低,收益却是指数级的。哪怕你只有100个用户,现在花一小时搭好的Gateway,在将来模型服务商更换API或价格调整的时候,能帮你省下至少一天的加班时间。
如果你目前也在纠结要不要上AI Gateway,我的建议是先跑一个最小闭环:把业务代码里直连模型API的调用收口到网关,配置两三个模型,观察几天。等你习惯了“换模型不改代码”的工作方式,就再也回不去直连的野蛮时代了。