news 2026/9/24 21:08:59

AI推理网关路由架构与策略实践:应对多模型调用混乱

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
AI推理网关路由架构与策略实践:应对多模型调用混乱

做AI推理网关这件事,说白了就是一句话:当你的大模型后端从一两个变成七八个,调用入口必须有一个统一的路由架构,把流量按策略分到最合适的推理服务上。这篇是“大模型推理优化系列”的第一篇,我会把AI推理网关的路由架构和策略实践拆开讲,包括怎么分层、怎么设计策略、怎么处理流式中断这些细节,适合正在做模型治理、推理成本控制,或者打算从单模型直连走向多模型统一入口的团队参考。

去年上半年我们线上模型只有两个,一个自建的vLLM服务,一个第三方的接口。业务代码直接调SDK,倒也不乱。到年底模型服务涨到八个,Ollama上还跑着qwen2.5-7b这类本地小模型,调用层就彻底失控了:有些请求明明该走便宜模型却走了贵的,有些模型升级了业务方还在用旧接口,有些后端无响应时所有调用一起超时。最终我决定在业务和推理服务之间加一层AI推理网关,把路由决策从业务代码里捞出来,统一管理。这篇文章记录的就是这个过程。

1. 推理网关要解决的真实问题:模型变多之后调用层先乱了

1.1 从“两个模型”到“八个模型”的失控过程

一开始真没想过要引入网关。一个vLLM集群管住了大部分线上对话,一个外部API负责高难度任务,业务方在自己的服务里写死两套调用逻辑,顶多封装一个公共类,看起来挺干净。

转折点出现在“性价比”三个字上。有人开始接轻量模型处理简单问答,有人把文本分类任务切到本地Ollama上的开源模型,再后来多模态、长上下文、代码生成各自找到了专属后端。线上模型服务从两个变成八个,每个服务的调用方式、鉴权方式、超时要求都不一样。团队里每个人都在自己的代码里维护一份模型清单,你去问某个模型现在能不能用,没有人能立刻答上来。

失控的典型表现是:A服务通过vLLM的Python SDK调用模型,B服务直接用Ollama的HTTP接口,C服务又接了一版厂商的官方SDK。三个服务各自封装,没有一个统一的地方能看清所有模型服务的健康状态和调用量。新模型上线、旧模型下线变成一次高危变更,因为影响面根本理不清。

1.2 散落在业务代码里的路由逻辑就是技术债

我见过最典型的“路由代码”长这样:

def chat(messages): last = messages[-1]["content"] if "复杂" in last or "推理" in last: return call_vllm(messages) elif "简单" in last or "闲聊" in last: return call_ollama(messages) else: return call_api(messages)

这段代码看着能跑,实际上问题很大。判断依据是拍脑袋写出来的关键词,模型稍微升级一下,这个规则就废了,但谁也不知道该什么时候改、改成什么。今天新接入一个模型,所有相关服务都要跟着改一遍,路由逻辑从一份复制成了三四份,改漏一个就出事。

更麻烦的是没法观测。每个模型每天处理多少请求、花了多少token、失败率多高,这些数据散落在各个服务的日志里,想统计得人肉捞日志。等到月底算推理成本的时候,财务拿着账单过来,我们连哪部分钱花在哪个模型上都说不清楚。

1.3 推理网关的定位:入口、策略执行点、观测点

给业务和推理服务之间加一层网关,核心不是“转发”这个动作,而是把三件事集中在一层解决:

  • 流量入口:所有模型调用统一走一个地址、一套协议,业务方不用关心后端是vLLM还是API。
  • 策略执行点:路由规则、限流、鉴权、熔断、降级全部下沉到网关,业务代码里不再有路由判断。
  • 观测点:每个请求经过网关时统一记录耗时、token数、成功失败状态,成本和性能数据一口出。

一个比较贴近的类比是:以前每个业务方自己开车去不同的车站,现在统一到一个枢纽站换乘。多跳一跳,但是每个站点的状态、每辆车的班次都被集中管理起来。引入网关初期会有少量转发开销,但从治理角度看,这笔开销非常值得。

2. 路由网关的分层架构设计:接入、策略、转发各管一段

2.1 接入层要做的协议归一化

网关接入层最核心的一件事是统一协议。目前几乎所有的推理服务都兼容OpenAI的Chat Completions格式,vLLM、Ollama、各类外部API全都能接收这种格式的请求,所以直接把OpenAI兼容格式作为网关的内部标准格式是成本最低的选择。

接入层要做的事情包括:

  • 鉴权:校验调用方的API Key或内部token,区分租户和业务线。
  • 参数校验:检查必填字段、约束temperature范围、确认max_tokens不超过上限。
  • 模型名映射:业务方传过来的model名字是逻辑名,网关映射成实际后端,业务方不需要知道后端地址。比如业务方传model="chat-smart",网关映射到vllm-main;传model="chat-cheap",映射到ollama-local。
  • 额外字段剥离:比如内部用的trace_id、租户id不能透传给外部API,需要在接入层剥掉。

2.2 策略层要组织的是“规则+候选集+打分”,不是if-else

很多人实现路由时,第一反应就是写if-else,这是最容易被代码腐化的做法。网关策略层应该围绕“规则、候选集、选择器”三个概念组织:

  • match:描述什么请求命中这条路由规则,比如路径前缀、模型名、请求头里的租户标识。
  • candidates:命中规则后,有哪些后端可以作为候选。
  • selector:从候选集中挑出最终目标的方式,可以是加权随机、最小负载、分数排序。

一套典型的YAML路由配置长这样:

routes: - name: chat_default match: path_prefix: /v1/chat/completions candidates: - upstream: vllm-main weight: 80 - upstream: public-api weight: 20 selector: weighted_random

这套配置的逻辑是:所有Chat Completion请求,80%流量走自建vLLM,20%流量走外部API。路由规则和代码分离,改权重不需要改代码,热更新配置就能生效。规则一多,还可以加优先级字段,从上到下匹配,第一条命中就先走第一条。

2.3 转发层与连接管理

转发层承担的是“把请求送到后端”的脏活,每个后端类型要有对应的适配器。vLLM适配器要处理好max_model_len和stream参数;Ollama适配器要注意本地接口和OpenAI兼容接口的差异;外部API适配器要关注上游限流头、账户余额这类因素。

连接管理是转发层比较容易忽略的点。如果每次请求都新建一条TCP连接,再做TLS握手,在高并发下延迟会明显抬高。网关内部一定要给每个后端维护连接池,复用HTTP长连接。实测下来连接复用能降低30%左右的请求延迟,同时对后端的连接数也更稳定,不容易把推理服务打挂。

2.4 一次请求在网关里的完整生命周期

阶段做什么关键点
1接收HTTP请求网关入口统一地址
2鉴权和限流校验调用方身份和配额
3参数校验和归一化模型名映射、字段规整
4路由匹配找到命中的路由规则
5候选打分选出最终目标后端
6转发上游通过适配器和连接池发送请求
7响应处理透传SSE流或聚合完整响应
8指标记录记录耗时、token、失败原因
9返回业务统一响应格式返回给调用方

整个过程对业务方是透明的,业务方只需要和一个域名交互。

3. 路由策略的核心维度:能力、成本、延迟、优先级怎么组合

3.1 能力约束先行

模型路由本质上是一个多目标决策。能力是硬约束,成本、延迟、负载是软目标。如果一个请求要求function calling,而候选模型根本不支持,那不管它多便宜、多快,都不能选。

给每个上游维护一份能力元数据,至少包含以下字段:

  • supports_tool_calling:是否支持函数调用
  • supports_vision:是否支持图片输入
  • max_context_length:最大上下文长度
  • output_limit:最大输出长度
  • quantization:量化精度,比如FP16、INT8、AWQ

路由开始之前,先根据元数据把不满足硬约束的候选过滤掉,再进入后续打分。这一步没做好的话,后面所有优化都是空中楼阁。

3.2 成本与负载怎么平衡

过滤完能力约束之后,如果候选集里还剩多个后端,就需要权衡成本和负载。

自建vLLM集群的边际成本很低,GPU已经买了,电费和运维都是固定的;外部API按token计费,每千token一个价,流量大了之后账单很吓人;本地Ollama模型几乎零边际成本,但能力弱一些,适合低优先级的简单任务。

动态权重是更细致的做法。网关不停统计每个后端的队列长度、每分钟token消耗,负载高的后端自动降低权重,负载低的后端多分流量。外部API还可以做配额管理,设置每分钟token上限和月度预算,超过限额之后直接把流量全切到内部模型,避免账单失控。

3.3 延迟敏感度决定路由偏好

同一个模型服务,交互式对话和离线批处理对延迟的要求完全不同。

  • 网页对话框:用户等着回复,要求首token延迟尽量低,TTFT(首token时间)最好控制在800ms以内。
  • 离线推理任务:凌晨跑一批文本分类,延迟不是首要指标,成本才是。
  • 数据标注:要求模型输出质量稳定,延迟可以放宽。

所以在路由策略里应该带上场景标签。偏重延迟的场景优先选快的后端,偏重成本或质量的场景优先选对应的后端。我们线上就把请求头里增加了一个X-Scene字段,网关根据场景做不同维度的打分,效果比单一策略好很多。

3.4 多维度打分路由的实现思路

多维度打分是目前比较通用的做法,核心思路是:先做硬性过滤,再做加权求和。一个简化的打分函数:

def score_candidate(candidate, ctx): # 硬性过滤 if ctx.need_tool_calling and not candidate.supports_tool_calling: return -1 if candidate.health == "down": return -1 if ctx.max_context_length > candidate.max_context_length: return -1 # 软目标打分 score = 0.0 score += candidate.quality_rank * 10 # 质量分越高越好 score += (100 - candidate.current_load) * 0.2 # 负载越低越好 if candidate.cost_class <= ctx.max_cost_class: score += 15 # 满足成本约束加分 if ctx.latency_sensitive and candidate.ttft_p95 > 1200: score -= 30 # 延迟超标扣分 return score

选出分数最高的候选作为转发目标。注意,分数本身没有绝对意义,只有排序价值。权重不要硬编码在代码里,应该做成配置项,最好还能通过AB实验来调,否则调参一次发版一次,维护成本也很高。

4. 流式输出与中断处理:SSE 和 abort 在网关层远比想象中麻烦

4.1 网关转发SSE的技术要点

大模型对话普遍用SSE(Server-Sent Events)做流式输出。一个典型的SSE事件长这样:

data: {"id":"chatcmpl-1","object":"chat.completion.chunk","choices":[{"delta":{"content":"你好"}}]} data: [DONE]

每个事件以data:开头,以空行结尾,最后发一个[DONE]表示结束。网关转发SSE时,必须做到边收边推,绝不能等上游全部生成完再一次性返回,否则用户感知的延迟会非常差。

还有几个细节容易踩坑:

  • 不要把整个SSE流缓存在内存里再转发,模型输出可能很长,内存会扛不住。
  • 有些后端会周期性发心跳注释行:keep-alive,网关要么透传,要么过滤,但不要影响事件解析。
  • 网关配置超时必须区分读超时和写超时,SSE场景下写超时要宽松很多,因为连接虽然空闲,但用户还在等数据。

4.2 客户端断开后必须终止上游请求

这是流式场景里代价最大的坑。用户在网页上点了一个“停止生成”,前端调用AbortController.abort(),连接断开了。但如果网关没有把断开信号传递给上游,上游大模型会继续生成token,GPU继续算,token继续烧,月底账单上的钱就是这么流走的。

网关在转发流式响应时,必须持续检测客户端连接状态,一旦发现客户端断开,要立刻向上游发起终止请求。用Python写转发逻辑时,需要在一个循环里不断检查request.is_disconnected()

@app.post("/v1/chat/completions") async def relay_chat_completion(request: Request): payload = await request.json() upstream = route_to_upstream(payload) async with httpx.AsyncClient(timeout=600) as client: async with client.stream("POST", upstream, json=payload) as resp: async for line in resp.aiter_lines(): if await request.is_disconnected(): await resp.aclose() break yield line + "\n\n"

注意不要在检测到客户端断开后直接什么都不做就break,最好先关闭上游响应流,让上游感知到连接终止。如果你的网关用的是其他语言,核心逻辑是一样的:收到abort信号时,向上游请求对象调用cancel或者close,而不是听之任之。

4.3 流式限流与重试边界

流式请求的限流策略和非流式不一样。流式请求一旦发出,用户已经看到前半段内容了,这时候重试会导致内容重复,体验极其糟糕。

所以网关对流式请求的重试必须严格限制在“首包之前”。也就是说,只有连接建立阶段失败、或者超过首包超时时间还没有收到第一个事件,才允许重试;一旦收到第一个数据块,后面任何失败都只能中断连接,不能重试。

限流维度上,除了常规的QPS,还需要关注并发连接数和token速率。大模型推理服务的并发能力非常有限,网关需要给每个后端设置并发上限,超出上限的请求排队或直接快速失败。流式连接断开的一瞬间,如果有大量客户端同时abort,还会形成一波“断开风暴”打到上游,网关最好加个保护,控制同一瞬间向上游发终止请求的数量。

5. 故障转移与熔断降级:后端集群不健康时的兜底方案

5.1 健康检查的层次

网关路由的前提是知道哪些后端是健康的。健康检查不能只停留在“端口通”这个层面。

层次检查方式能发现的问题不能发现的问题
L4 TCP端口连通性进程挂掉服务假死、死锁
L7 HTTPGET /health 返回200应用层是否存活模型是否能实际推理
业务级发一个极小推理请求模型是否可推理显存OOM、队列堆积

最怕的就是服务进程还在,健康检查也返回200,但实际推理时因为显存不足或者并发打满,每个请求都超时。这种情况下只做TCP检查基本等于没有防护。建议至少做到HTTP层,每个后端定期探活;关键后端可以加业务级探测,用一个极短的prompt周期性验证模型能正常返回。

5.2 熔断器三态及其参数设定

熔断器是故障转移里最经典的机制。它的状态有三个:

状态含义网关行为
closed正常状态请求正常转发
open熔断状态不再转发到该后端,直接走降级
half-open试探状态放少量请求进去验证后端是否恢复

参数上我一般先给一组推荐值,再根据实际压测调整:连续失败3次进入open状态,open状态持续30秒,30秒后进入half-open,half-open状态放2个试探请求,试探成功则回到closed,失败则重新进入open。这组参数不是银弹,如果你的后端启动恢复特别慢,open窗口要加长;如果失败信号非常可靠,阈值可以调小一些。

5.3 超时分级与降级策略

超时如果不分级,所有请求卡在同一个超时值上,网关很容易把自己拖死。推荐分成三档:

  • 连接超时:2-3秒,连不上的后端直接跳过。
  • 首包超时:5-10秒,大模型加载prompt可能比较慢,但首次数据迟迟不来多半有问题。
  • 整体空闲超时:流式过程中超过30-60秒没有新数据,主动断开,避免僵尸连接一直占着资源。

降级策略上,比较实用的是语义降级。主模型失败后,根据业务允许的程度,降级到本地小模型或者外部兜底API。降级不是返回一个错误,而是返回一个尽量合理的结果,同时在响应头里加一个X-Fallback: true标记,业务方可以根据这个标记判断本次回复质量。比如vLLM集群挂了,低优先级的闲聊请求直接切到Ollama本地模型,用户几乎无感知。

5.4 故障场景速查表

故障场景网关行为业务方感知
上游连接超时尝试下一个候选后端可能感觉稍慢
上游返回5xx熔断计数,切换备胎基本无感
客户端中途断开终止上游流式请求无感知
所有候选都不可用返回503并附带说明明确失败
外部API配额耗尽全部切到内部模型可能质量下降

这张表可以直接拿去做联调时的测试用例,让每个故障场景真的走一遍,比看文档有效得多。

6. 一次真实的路由切换落地:配置、指标与踩坑

6.1 初始架构和改造目标

我们当时的后端组合很有代表性:一个自建的vLLM集群作为主力,跑qwen2.5-7b;一个Ollama本地服务,跑轻量模型;一个外部API兜底处理复杂任务。改造前的痛点是,三个后端分别被不同服务用不同方式调用,没有一个统一入口。

改造目标定得很朴素:业务方只认网关一个地址;路由策略要可配置、可热更新;任何单一后端故障都要自动切走,不让业务方感知。

6.2 路由配置示例

这是当时简化后的核心配置:

upstreams: vllm-main: type: vllm endpoint: http://vllm-cluster:8000 capability: max_context: 32768 supports_tool_calling: true health: kind: http url: /health interval: 10s ollama-local: type: ollama endpoint: http://ollama:11434 capability: max_context: 8192 supports_tool_calling: false health: kind: http url: /health interval: 15s public-api: type: openai endpoint: https://api.example.com/v1 quota: tokens_per_minute: 100000 monthly_budget_usd: 2000 routes: - name: chat_main match: path_prefix: /v1/chat/completions candidates: - vllm-main - ollama-local - public-api selector: score_based fallback: public-api

这个配置表达的信息是:所有Chat Completion请求都进这条路由;候选集是三个后端;选择器用分数排序;如果所有候选都失败,最后兜底走到public-api。

6.3 灰度切流过程

网关上线不能一把梭,需要分步走。我们分了四步:

  1. 旁路模式:网关先开启,但不接管真实流量。复制线上请求打到网关,观察路由策略命中的结果是不是符合预期。这一步能发现大部分路由规则配错的问题。
  2. 小流量验证:把5%-10%的流量切到网关,重点观察流式转发、abort处理是否正常。
  3. 按业务线切换:优先切延迟不敏感的业务线,交互类业务放后面。每条业务线独立回滚。
  4. 全量切换:所有流量统一走网关,业务方的模型调用代码收敛为一个HTTP请求。

灰度过程中的监控面板必须包含三类指标:网关本身的错误率、各后端健康状况、token消耗成本趋势。没有这些指标,灰度就是在盲飞。

6.4 效果指标与经典踩坑记录

切换完成后的体感变化很明显:路由规则变更从几天一次发版变成秒级热更新,后端故障转移从人工介入的分钟级变成自动处理的秒级,成本也因为低优先级任务被分流到本地模型而明显下降。

踩过的坑也值得记一笔:

后果解决方案
网关超时设得比上游短上游还在算,网关先断了,返回空响应网关超时要大于上游最长输出时间
健康检查只做TCP后端假死,照样路由过去,请求全超时增加HTTP和业务级探测
流式请求做了重试用户看到两段重复内容只在首包前重试
客户端abort没有传给上游前端断开了,GPU还在烧token检测断开立即终止上游流

我自己的一个习惯是,网关上线后必须安排专人盯一周指标,重点看路由命中分布和失败原因。不要以为加上网关就万事大吉,路由策略要基于真实流量持续调,尤其是权重和熔断参数,前两周基本每天都要微调一次。

网关只是推理优化的第一站,后续还有prompt缓存、动态批处理、模型并行这些更深的优化可以做,但干净统一的路由架构是所有优化能够落地的地基。如果你们也正在做类似的改造,我建议先从小范围灰度开始,把策略维度想清楚再动手,不要一上来就堆一堆功能。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/24 21:08:57

Git Worktree + Skill:多任务并行开发的高效工作流实践

1. 多任务并行开发的真实痛点&#xff1a;从"切分支噩梦"说起1.1 一次紧急修复引发的分支切换事故先讲个真实经历。上个月某个周五下午&#xff0c;我正在一个功能分支上开发新模块&#xff0c;代码改到一半&#xff0c;涉及六个文件&#xff0c;逻辑已经在大脑里串起…

作者头像 李华
网站建设 2026/9/24 21:08:21

从提示词到AI Skill:5步工程化流程打造可复用技能

1. 为什么提示词学会就废&#xff1f;先搞懂 Skill 到底解决什么问题这两年我见了太多人&#xff0c;提示词收藏了上百条&#xff0c;真正能稳定复用的没几条。今天问 ChatGPT 要一个方案觉得不错&#xff0c;明天换个需求重新写一段提示词&#xff0c;效果又飘了。问题不在于你…

作者头像 李华
网站建设 2026/9/24 21:07:40

示波器与信号发生器联合使用:实验操作与排查技巧

这周刚把“电子测试平台与工具”系列的第一个实验做完&#xff0c;题目就是示波器加信号发生器的联合使用。说句实话&#xff0c;刚看到实验任务书的时候&#xff0c;我还觉得这种基础仪器有什么好折腾的&#xff0c;屏幕上出个波形、读几个数不就完事了。真正上手才发现&#…

作者头像 李华
网站建设 2026/9/24 21:07:36

基于溯源图与RGAT-GRU的APT攻击检测:HUST毕设源码实战解析

简介&#xff1a;这份资源是2023年华中科技大学计算机学院毕业设计项目&#xff0c;主题为基于溯源图的APT攻击检测方法优化&#xff0c;面向网络安全方向的学生与研究人员&#xff0c;适合具备一定机器学习与图神经网络基础、希望深入理解高级持续性威胁检测的读者。项目围绕溯…

作者头像 李华
网站建设 2026/9/24 21:07:17

RTX 5090笔记本功耗墙解锁实录:AI辅助调参从150W到250W

先说结论&#xff1a;我这台5090笔记本&#xff0c;到手时GPU功耗被锁在150W附近&#xff0c;3DMark Time Spy图形分稳定在2.2万左右。在不动一颗螺丝、不换硅脂、不加外部供电的前提下&#xff0c;我通过软件层面的功耗墙解锁&#xff0c;配合AI辅助动态调参&#xff0c;把功耗…

作者头像 李华
网站建设 2026/9/24 21:05:04

YOLO数据集实战:1531张罐头瓶子图像从检查到训练全流程

简介&#xff1a;这是一套面向YOLO系列算法的目标检测数据集&#xff0c;以罐头、鲜奶瓶等包装类物品为主要检测类别&#xff0c;适合需要训练检测模型的开发者、研究人员或相关专业学生使用。压缩包中提供2000个标签文件&#xff0c;包括1073个XML文件&#xff08;VOC标注格式…

作者头像 李华