1. 从"能跑通"到"敢上生产":LLM 网关到底卡在哪
大模型应用从 Demo 走向生产环境,最先撞上的往往不是模型效果问题,而是工程治理问题。我见过太多团队,Demo 阶段直接在前端代码里硬编码一个 API Key,调一个模型,跑得挺欢;一旦要接入多个业务线、多个模型供应商、多个租户,整个系统就开始失控——账单对不上、某个供应商限流导致全线雪崩、敏感数据被无意间发到了外部接口、想换个模型发现业务代码里到处是耦合。
这些问题的根源在于:缺少一个统一的 LLM 网关层。所谓 LLM 网关,你可以把它理解成所有大模型调用的"总闸口"——所有业务方不直接调用模型 API,而是先经过网关,由网关统一完成鉴权、计价、路由、限流、缓存、审计、降级等一系列治理动作。它和传统 API Gateway(比如 Nginx、Kong)的思路一脉相承,但针对 LLM 场景有大量特殊设计:Token 计量、流式响应、多模型协议适配、语义缓存、成本归因等等。
这篇文章要聊的,是我在实际项目中总结出的一套LLM 网关五级演进路径。之所以叫"五级演进",是因为我踩过一个很深的坑:一开始就想做一个"大而全"的网关,结果三个月没上线,业务方等不及全都绕过网关直连了。后来我换了个思路——按能力成熟度分五级逐步演进,每一级都能独立交付价值,每一级都为下一级打基础。这套路径后来在三个不同规模的项目里验证过,从日调用量几千次到几百万次都能适配。
如果你正在做 AI 平台、AI 中台、或者任何一个需要统一管理多个大模型调用的系统,这篇文章应该能帮你少走不少弯路。我会把每一级的设计动机、核心实现、踩坑经验都讲清楚,尤其是那些文档里不会写的细节。
2. 第一级:统一接入层——先把"入口"收拢
2.1 为什么第一级不是计价而是接入
很多人一上来就想做计价系统,觉得"钱"最重要。但我的经验是:没有统一入口,计价根本无从谈起。你连调用都散落在各个业务代码里,怎么统计?怎么归因?所以第一级的目标非常朴素——把所有模型调用收拢到一个统一的 HTTP 入口。
这一级要解决的核心问题是协议适配。市面上的大模型 API 协议并不统一:有的用 OpenAI 兼容格式,有的用自家私有格式,请求体字段名、响应结构、流式返回的 SSE 格式都有差异。网关的第一件事就是把这些差异屏蔽掉,对上暴露一套统一的接口。
我通常的做法是定义一个内部标准协议(直接对齐 OpenAI 的 Chat Completions 格式,因为生态最成熟),然后为每个供应商写一个 Adapter:
class BaseAdapter: def to_standard_request(self, raw_request: dict) -> dict: raise NotImplementedError def from_standard_response(self, raw_response: dict) -> dict: raise NotImplementedError def stream_transform(self, chunk: bytes) -> bytes: raise NotImplementedErrorstream_transform这个方法特别关键,也是新手最容易忽略的地方。流式响应不是简单地把字节转发出去,不同供应商的 SSE 事件格式、结束标记、错误帧都不一样。我踩过的坑是:某供应商在流式返回中途遇到限流,会发一个特殊的 error 事件而不是直接断开连接,如果不处理,前端会一直挂着等,用户体验极差。
2.2 接入层最容易被忽略的三个细节
第一个细节是超时与重试的边界。LLM 请求动辄几十秒,传统的 3 秒超时完全不够用。但超时设太长,连接池会被占满。我的经验是分两段设置:连接超时 5 秒,读取超时按模型类型区分——普通对话 60 秒,长文本生成 180 秒,推理类模型可以到 300 秒。重试要特别小心,流式请求一旦开始返回内容就绝对不能重试,否则用户会看到重复内容。
第二个细节是请求体的规范化。不同业务方传上来的参数五花八门,有的用max_tokens,有的用max_new_tokens,有的 temperature 传字符串。网关必须做一层参数校验和归一化,把非法参数挡在门外。我见过因为 temperature 传了"0.7"字符串导致某供应商直接 500 的案例。
第三个细节是连接池的隔离。不同供应商的连接池必须隔离,否则一个供应商响应慢会把整个网关的连接池拖垮。这个在压测时特别明显,我建议每个供应商独立配置连接池大小,并且设置独立的熔断阈值。
提示:第一级不要追求功能完备,能跑通"统一入口 + 协议适配 + 基础校验"就够了。我见过太多团队卡在第一级,非要一次做完美,结果业务方等不了直接绕过。
3. 第二级:计量与计价——把每一分钱算清楚
3.1 Token 计量的三种口径与选择
统一入口建好之后,第二级就是算账。LLM 的计价核心是 Token 计量,但这里有个容易被忽略的问题:Token 到底按谁的数算?我总结有三种口径:
| 口径 | 说明 | 优点 | 缺点 |
|---|---|---|---|
| 供应商返回 | 直接用 API 响应里的 usage 字段 | 准确、省算力 | 部分供应商流式不返回、有延迟 |
| 网关本地计算 | 用 tokenizer 在网关侧计算 | 实时、可控 | 与供应商口径可能有偏差 |
| 混合模式 | 优先用供应商返回,缺失时本地补算 | 兼顾准确与实时 | 实现复杂度高 |
我的建议是混合模式。具体做法是:非流式请求直接用供应商返回的 usage;流式请求因为很多供应商在最后一个 chunk 才返回 usage,网关需要本地用 tokenizer 预估,等最终 usage 到了再校正。这里要注意,不同模型的 tokenizer 不一样,本地计算必须按模型加载对应的 tokenizer,不能图省事全用一套。
本地计算 tokenizer 有个性能坑:每次请求都加载 tokenizer 会很慢。我的做法是用 LRU 缓存把 tokenizer 实例缓存起来,并且对超长文本做分片计算。实测下来,一个 8K 上下文的请求,本地计算耗时在 5ms 以内,对整体延迟影响可以忽略。
3.2 计价模型的设计:不只是"单价乘 Token"
计价听起来简单——单价乘 Token 数嘛。但实际做起来,你会发现要考虑的维度非常多:
- 输入输出分开计价:几乎所有供应商的输入和输出单价都不同,输出通常贵 2-4 倍。
- 缓存命中折扣:很多供应商对命中 prompt cache 的部分有折扣,计价时必须区分。
- 阶梯定价:部分供应商按调用量阶梯降价,需要累计统计。
- 模型版本差异:同一个模型名,不同版本单价可能不同。
- 汇率与结算周期:海外供应商涉及汇率,且结算周期不同。
我设计计价模块时,核心是一张价格配置表,把上述维度都参数化:
PRICING = { "gpt-4o": { "input": 0.005, # 每 1K token "output": 0.015, "cached_input": 0.0025, "currency": "USD", "effective_from": "2024-05-13", }, # ... }价格配置一定要支持版本化和生效时间,因为供应商调价很频繁。我踩过的坑是:某次供应商半夜调价,第二天对账发现所有账单都算错了,因为没有价格版本管理,历史账单无法复现。
3.3 成本归因:让每个业务方知道自己的账
计价做完,下一个问题是这笔钱算谁的。成本归因的核心是给每个请求打上足够的标签:租户 ID、业务线、应用 ID、用户 ID、甚至具体的功能模块。这些标签从请求头里透传进来,网关负责采集和聚合。
我建议在网关层强制要求请求头里带X-Tenant-Id和X-App-Id,没有的直接拒绝。一开始业务方会抱怨麻烦,但一旦他们看到自己的成本报表,就会主动配合了。归因数据我通常按小时聚合写入时序数据库,按天生成账单快照,这样既能实时看趋势,又能保证账单的稳定性。
注意:成本归因的粒度不是越细越好。我试过细到单个用户级别,结果数据量爆炸,查询慢得没法用。后来改成"租户 + 应用 + 模型"三个维度,既够用又高效。
4. 第三级:智能路由——在成本、质量、可用性之间找平衡
4.1 路由策略的四种典型场景
计量计价解决的是"算清楚",路由解决的是"算得省、算得稳"。LLM 网关的路由策略,我归纳为四类典型场景:
场景一:成本优先路由。同一个任务,优先选便宜的模型。比如简单的意图识别、文本分类,用小模型就够了,没必要上大模型。我通常配置一个"任务类型到模型"的映射表,业务方声明任务类型,网关自动选模型。
场景二:质量优先路由。复杂推理、代码生成这类任务,必须用能力强的模型。这时候路由策略是"指定模型 + 备用模型",主模型不可用时自动切换。
场景三:可用性优先路由。同一个模型有多个供应商提供(比如多个云厂商都部署了开源模型),网关做负载均衡和故障转移。这里要注意健康检查,不能只看 HTTP 状态码,还要看响应质量——我见过供应商返回 200 但内容是空的情况。
场景四:灰度路由。新模型上线时,按比例把流量切过去,观察效果再逐步放量。灰度路由的关键是流量标记的一致性,同一个用户应该稳定命中同一个版本,否则体验会割裂。
4.2 路由决策的实现:规则引擎还是策略链
路由决策的实现方式,我试过两种:规则引擎和策略链。规则引擎(比如用 JSON 配置规则)灵活但性能差,每次请求都要解析规则;策略链(代码里定义一系列策略,按顺序执行)性能好但改策略要发版。
最后我的选择是混合方案:核心路由逻辑用策略链实现,保证性能;策略的参数(比如权重、阈值、映射表)用配置中心下发,保证灵活。这样既不用频繁发版,又能保证性能。
路由决策里有个容易被忽略的点:决策要可观测。每次路由为什么选了这个模型,必须记录下来。我见过线上出问题排查半天,最后发现是路由配置写错了,但没有任何日志能证明。所以我在路由层强制记录决策链路:命中了哪条规则、候选模型有哪些、最终为什么选它。
4.3 故障转移的"坑":不是所有失败都该重试
故障转移听起来简单——主模型失败就切备用。但实际做起来,判断"失败"本身就是个难题。我总结了几类情况:
- 明确失败:HTTP 5xx、连接超时,这类直接重试没问题。
- 限流失败:429 状态码,这类要区分是短时限流还是长时限流,短时可以用指数退避重试,长时应该直接切备用。
- 内容失败:返回 200 但内容是空的、或者触发了内容安全拦截,这类不应该重试,因为重试大概率还是同样结果,应该直接返回错误。
- 流式中断:流式响应中途断开,这类要特别小心,如果已经返回了部分内容,重试会导致内容重复,我的做法是直接终止并返回错误,让业务方决定是否重新发起。
这里有个血泪教训:早期我没区分这些情况,所有失败都无脑重试三次,结果遇到内容安全拦截时,同一个违规请求被重试了三次,触发了供应商的风控,整个账号被限流了。后来加了失败分类,才解决这个问题。
5. 第四级:治理能力——限流、缓存、审计一个都不能少
5.1 限流:保护自己,也保护供应商
限流是网关的"保险丝"。LLM 网关的限流比传统 API 限流复杂,因为要限的维度多:
- 按租户限流:防止单个租户把配额用光,影响其他人。
- 按模型限流:不同模型的配额不同,要分别限制。
- 按供应商限流:供应商有 QPS 和 TPM(每分钟 Token 数)双重限制,都要遵守。
- 全局限流:兜底保护,防止整体过载。
实现上我推荐令牌桶 + 滑动窗口组合。令牌桶控制瞬时突发,滑动窗口控制长期速率。Token 维度的限流要特别注意,因为 Token 数是请求完成后才知道的,所以需要"预扣 + 校正"机制:请求进来时按预估 Token 预扣配额,请求完成后按实际 Token 校正。
限流触发后的处理也很讲究。直接返回 429 是最简单的,但体验差。我的做法是分级降级:先尝试排队等待(短时),排队超时再降级到备用模型,备用模型也不可用才返回 429。这样大部分限流场景用户是无感知的。
5.2 缓存:省钱的利器,但别乱用
LLM 网关的缓存分两种:精确缓存和语义缓存。
精确缓存就是请求体完全一致时直接返回缓存结果。这个实现简单,命中率取决于业务场景。对于"同一个问题反复问"的场景(比如 FAQ),命中率能到 30% 以上。
语义缓存是通过向量相似度判断"意思相近"的请求,返回缓存结果。这个省钱效果更明显,但风险也更大——语义相似不等于答案相同。我踩过的坑是:用户问"北京今天天气"和"北京明天天气",语义相似度很高,但答案完全不同,如果命中缓存就出大错了。
所以语义缓存必须设置高相似度阈值(我一般设 0.95 以上),并且对时间敏感、个性化强的场景直接禁用。缓存还要设置合理的 TTL,LLM 的回答有时效性,缓存太久会返回过时信息。
5.3 审计与合规:日志里不能有的东西
审计是很多团队最后才做、但出事时最需要的功能。LLM 网关的审计日志要记录:谁、什么时候、调用了什么模型、输入输出是什么、花了多少 Token、结果如何。
但这里有个大坑:审计日志里绝对不能明文存储用户的输入输出,因为可能包含敏感信息。我的做法是脱敏 + 加密双管齐下:对明显的敏感字段(手机号、身份证、邮箱)做脱敏,整体内容加密存储,只有授权人员才能解密查看。同时要设置日志保留期限,到期自动清理。
审计日志的另一个用途是问题回溯。线上出现"模型回答错误"的投诉时,能快速定位到具体的请求和响应。我建议给每个请求生成一个全局唯一的 trace ID,贯穿整个调用链路,排查问题时一查到底。
6. 第五级:平台化与生态——从工具到基础设施
6.1 自助化:让业务方自己管自己
前四级做完,网关已经能用了。但第五级的目标是让网关成为基础设施,业务方不需要找平台团队,自己就能完成大部分操作。这需要几个能力:
- 自助接入:业务方自己注册应用、申请配额、获取 API Key。
- 自助配置:路由策略、限流阈值、缓存规则都能自己配。
- 自助看板:实时看到自己的调用量、成本、错误率。
- 自助告警:配额快用完、错误率飙升时自动通知。
自助化的核心是权限隔离。每个业务方只能看到和操作自己的资源,不能越权。这个用 RBAC(基于角色的访问控制)实现,角色分平台管理员、租户管理员、普通用户三级。
6.2 可观测性:从"能看"到"能预测"
可观测性不只是看板,而是能提前发现问题。我在网关里做了几个预测性告警:
- 配额预测:根据当前消耗速率,预测配额什么时候用完,提前通知。
- 成本预测:预测本月成本,超出预算时告警。
- 异常检测:调用量、错误率、延迟出现异常波动时告警。
这些预测基于历史数据做简单的时间序列分析就够了,不需要复杂的机器学习。关键是告警要准,宁可少报也不要误报,否则业务方会直接屏蔽告警。
6.3 生态扩展:网关不只是"转发"
到了第五级,网关的角色从"转发器"变成"能力平台"。可以在网关上扩展很多能力:
- Prompt 管理:集中管理 Prompt 模板,支持版本化和 A/B 测试。
- 评测集成:新模型上线前自动跑评测集,达标才放量。
- Agent 编排:把多个模型调用编排成一个 Agent 流程。
- 内容安全:统一的内容审核,输入输出都过一遍。
这些扩展能力让网关的价值从"省钱"升级到"提效",业务方会越来越依赖网关,网关也就真正成为了基础设施。
7. 五级演进中的几个关键决策点
7.1 什么时候该演进到下一级
五级演进不是必须按顺序走完,而是按需演进。我的判断标准是:
| 信号 | 说明该做哪一级 |
|---|---|
| 调用散落各处,无法统计 | 第一级:统一接入 |
| 账单对不上,成本失控 | 第二级:计量计价 |
| 单一模型不够用,或成本太高 | 第三级:智能路由 |
| 出现限流、雪崩、合规问题 | 第四级:治理能力 |
| 业务方需求多样,平台团队疲于奔命 | 第五级:平台化 |
不要为了"技术先进"而提前做高级能力。我见过团队在第一级还没做稳的情况下就去做语义缓存,结果缓存命中率低、维护成本高,最后不了了之。
7.2 自研还是用开源
LLM 网关有开源方案(比如 One API、LiteLLM 等),也有商业方案。我的建议是:
- 第一、二级:直接用开源方案,快速上线。
- 第三、四级:开源方案 + 自研扩展,因为路由和治理策略往往和业务强相关。
- 第五级:基本要自研,因为平台化涉及大量业务定制。
自研的核心不是重复造轮子,而是在开源基础上做业务适配。我通常的做法是:用开源方案做协议适配和基础转发,自研的部分集中在路由决策、计价模型、治理策略这些业务强相关的模块。
7.3 性能与成本的平衡
网关本身也会消耗资源,尤其是做 Token 本地计算、语义缓存、内容审核时。我的经验是:
- Token 计算:用轻量 tokenizer,必要时异步计算。
- 语义缓存:向量检索用专门的向量库,不要用关系数据库硬扛。
- 内容审核:能用规则就不用模型,能用小模型就不用大模型。
网关的延迟增加要控制在50ms 以内,超过这个数业务方就会有感知。压测时重点关注 P99 延迟,而不是平均值。
8. 我在实际项目里踩过的几个坑
第一个坑是过度设计。早期我想做一个"万能网关",支持所有供应商、所有模型、所有场景,结果配置复杂到没人会用。后来砍掉一半功能,只保留核心场景,反而用起来了。教训是:网关的价值在于"统一",不在于"全能"。
第二个坑是忽视流式场景。我一开始按非流式请求设计,上线后才发现 80% 的请求是流式的,很多设计要推倒重来。流式场景对超时、重试、缓存、计量的要求都和非流式不同,必须从一开始就考虑。
第三个坑是计价精度。早期我用浮点数算钱,结果累计误差导致账单对不上。后来改成用整数(以最小货币单位计算),才解决这个问题。涉及钱的代码,永远不要用浮点数。
第四个坑是配置热更新。路由策略、限流阈值这些配置,早期改一次要发一次版,运维苦不堪言。后来接入配置中心,支持热更新,效率提升明显。但热更新要小心配置一致性,多个网关实例的配置必须同步,否则会出现路由不一致的问题。
第五个坑是监控盲区。早期只监控了网关自身的指标,没监控供应商的健康度。结果某供应商悄悄降级了服务质量,我们过了两天才发现。后来加了供应商健康度监控,包括响应时间、错误率、内容质量抽检,才做到提前发现。
9. 给正在做 LLM 网关的同行几句实在话
做 LLM 网关这两年,我最大的体会是:技术难度不在网关本身,而在"平衡"。平衡成本和体验、平衡灵活和稳定、平衡自研和开源、平衡当前需求和未来扩展。每一个决策都没有标准答案,只有适合当前阶段的答案。
如果你刚开始做,我的建议是从第一级老老实实做起,别想着一步到位。统一接入做扎实了,后面的计价、路由、治理都是水到渠成的事。如果你已经做到第三、四级,那重点应该放在可观测性和自助化上,让网关从"平台团队的负担"变成"业务方的工具"。
最后分享一个我一直在用的小技巧:给网关的每个能力都设计一个"开关"。新能力上线时默认关闭,业务方按需开启。这样既能快速迭代,又不会影响存量业务。这个开关机制帮我避免了好几次线上事故——新功能出问题时,一键关闭就能恢复,不用紧急发版。
网关这个事,做深了是无底洞,做浅了又不够用。找到自己团队的"甜点区",比追求技术完美重要得多。