1. 企业大模型网关到底解决什么问题
1.1 从一个真实痛点说起
去年下半年,我帮一家做 SaaS 的中型团队做架构评审,他们的 CTO 抛出一个很典型的问题:公司内部已经有六七个业务线在调用大模型,每个团队各自申请 API Key、各自封装调用逻辑、各自处理重试和限流。结果就是账单分散在五六个账号里对不上,某个团队把 Key 硬编码到前端被刷爆了额度,还有人偷偷换了模型版本导致线上输出格式全乱。这不是技术问题,这是治理问题。
企业大模型网关(LLM Gateway)要解决的就是这件事。它本质上是一个位于业务应用和各家模型服务之间的中间层,所有请求先打到网关,由网关统一做鉴权、路由、限流、计费、日志、内容审计,再转发给后端真正的模型。你可以把它理解成微服务架构里的 API Gateway,只不过下游换成了 OpenAI、Claude、通义、文心这些模型服务。
为什么企业非要做这一层?因为当模型调用量从每天几百次涨到几十万次的时候,直连模式会暴露三个致命缺陷。第一是成本不可控,没有统一计量就没法做预算,也没法按业务线分摊。第二是安全不可控,Key 满天飞,谁泄露了都不知道。第三是稳定性不可控,某个模型服务抖动,业务侧没有降级能力,只能干等。
1.2 网关的核心能力清单
一个能上生产的大模型网关,至少要具备下面这些能力,我按优先级排一下:
| 能力模块 | 作用 | 优先级 |
|---|---|---|
| 统一鉴权 | 业务侧只拿网关颁发的虚拟 Key | P0 |
| 多模型路由 | 按模型名/成本/可用性转发到不同后端 | P0 |
| 限流熔断 | 防止单业务打爆额度,后端故障自动降级 | P0 |
| 用量计量 | 按 token 统计,支持按业务线出账单 | P0 |
| 日志审计 | 记录请求响应,满足合规要求 | P1 |
| 缓存 | 相同请求命中缓存,省成本 | P1 |
| 内容安全 | 输入输出过滤 | P1 |
| Prompt 模板管理 | 统一管理各业务 Prompt | P2 |
这里我要强调一点:不要一上来就追求大而全。我见过太多团队花三个月搭了个功能齐全的网关,结果业务侧根本不愿意接入,因为改造成本太高。正确的做法是先做 P0 的四项,用最小可用版本跑通一条业务线,拿到真实数据再迭代。
1.3 网关和 Agent 的关系
现在热词里 agent、agent 开发、agent 架构满天飞,很多人会问:有了 Agent 框架,还需要网关吗?答案是更需要了。Agent 的特点是一次任务会发起几十甚至上百次模型调用,它自己会规划、会反思、会调工具,调用量是普通对话的几十倍。如果没有网关做限流和计量,一个跑飞的 Agent 能在半小时内烧掉你一个月的预算。
而且 Agent 场景对网关提出了新要求:需要支持流式响应(Agent 要边想边做)、需要支持函数调用透传(工具调用参数不能被网关吃掉)、需要会话级别的追踪(把一次 Agent 任务的所有调用串起来看)。这些在传统 API 网关里是没有的,必须专门设计。
2. 网关架构设计与技术选型
2.1 整体架构分层
我推荐的分层是这样的,从上到下依次是接入层、治理层、适配层、后端层。
接入层负责协议转换,对外统一暴露 OpenAI 兼容的接口格式。为什么选 OpenAI 格式?因为现在几乎所有客户端、SDK、Agent 框架都默认支持 OpenAI 协议,你只要兼容它,业务侧改个 base_url 就能接入,改造成本几乎为零。这是降低接入阻力的关键决策。
治理层是网关的大脑,包含路由引擎、限流器、计量器、审计模块。路由引擎根据请求里的 model 字段和业务标识决定转发目标;限流器用令牌桶算法按业务维度限流;计量器解析响应里的 usage 字段累加 token;审计模块异步写日志。
适配层负责把统一的 OpenAI 格式翻译成各家后端的真实格式。比如转发给某国产模型时,字段名可能不一样,需要做映射。这一层要做成插件化的,新增一个模型厂商只需要加一个适配器。
后端层就是真正的模型服务,可以是公有云 API,也可以是企业私有部署的推理服务。
2.2 技术栈选型对比
网关这种 IO 密集型服务,语言选型很关键。我对比过三种方案:
| 方案 | 优势 | 劣势 | 适用场景 |
|---|---|---|---|
| Go + Gin | 并发强、部署简单、生态好 | 动态配置稍弱 | 大多数企业首选 |
| Rust + Axum | 性能极致、内存安全 | 开发速度慢、招人难 | 超大规模、极致性能 |
| Node.js + Fastify | 开发快、和前端同栈 | 高并发下 CPU 密集任务弱 | 小团队快速验证 |
我个人的建议是:如果没有特殊性能要求,直接上 Go。它的 goroutine 模型天然适合处理大量并发的流式请求,单机扛几千并发连接毫无压力,而且编译出来就是一个二进制,运维成本极低。Rust 方案我也试过,性能确实好,但一个流式转发的 bug 能调一整天,团队里没有 Rust 老手的话不建议碰。
存储选型上,计量数据用Redis + 时序数据库的组合。Redis 做实时计数和限流,时序库(比如 ClickHouse 或 TDengine)存历史明细用于出报表。配置数据放 etcd 或数据库都行,看团队习惯。
2.3 流式响应的处理难点
流式是网关最容易踩坑的地方。普通 HTTP 请求是收完再转发,流式是边收边转。这里有几个细节必须处理好。
第一是缓冲策略。如果你每收到一个 SSE 事件就立刻转发,网络抖动时会产生大量小包,效率低。正确做法是攒一小段再发,但攒太久又会影响首字延迟。我的经验是攒到 1KB 或者超过 50ms 就 flush,实测下来首字延迟和吞吐能兼顾。
第二是连接中断处理。客户端断开时,网关必须及时取消对后端的请求,否则后端还在傻傻地生成,白白烧钱。Go 里用 context 的 cancel 机制就能搞定,关键是别忘了在 defer 里调用。
第三是错误透传。流式响应一旦开始,HTTP 状态码已经发出去了,后面出错只能通过 SSE 的 error 事件传递。很多网关在这里处理不当,导致客户端收到半截响应还不知道出了错。
3. 自动化编程与 CLI 工具链实践
3.1 为什么 CLI 在 Agent 时代重新火了
这两年 codex cli、zcode cli、gitlab cli、trae cli 这类工具密集出现,背后有个清晰的逻辑:Agent 需要一个能执行真实操作的入口。大模型再聪明,它也没法直接操作你的文件系统、跑你的构建脚本、提交你的代码。CLI 就是那个把手,让 Agent 从"只会聊天"变成"能干活"。
我自己的日常开发流程已经变成这样:用 CLI 工具把代码库上下文喂给模型,让模型生成修改方案,再通过 CLI 执行实际的编辑和测试命令。整个过程人只做审核,重复劳动大幅减少。这就是自动化编程的核心价值——不是让 AI 替你写代码,而是让 AI 替你干那些机械的、有明确规则的活。
3.2 主流 CLI 工具的能力对比
我实际用过几款,简单说说感受:
Codex CLI是 OpenAI 官方出的,和它的模型配合最紧密。安装方式一般是 npm 全局装,但经常有人遇到missing optional dependency @openai/codex-win32-x64这种报错,本质是平台相关的二进制包没装上。解决办法是先清 npm 缓存再重装,或者直接用官方提供的独立安装包。它的常用命令里,/compact用来压缩上下文省 token,/model切换模型,/resume恢复上次会话,这几个是高频操作,建议记熟。
ZCode CLI主打的是把本地代码和云端模型打通,支持上传代码库做分析。有人问它能不能上传到 Git 仓库,这个要看具体配置,一般是通过本地 git 命令配合,而不是 CLI 自己直连。
GitLab CLI严格说不是 AI 工具,但它在自动化流程里很关键。Agent 生成的代码要提交 MR、要触发流水线,都靠它。装好之后配好 token,就能用命令行完成大部分 GitLab 操作。
这里有个经验:别指望一个 CLI 通吃所有场景。我的做法是 Codex CLI 负责代码生成和重构,GitLab CLI 负责流程操作,再写几个自己的小脚本做胶水,组合起来比单一工具强得多。
3.3 从 CLI 到 Agent 的编排
单个 CLI 是工具,把多个 CLI 编排起来就是 Agent。这里要区分两个概念:harness 和 agent。Harness 是执行框架,负责调度、状态管理、错误恢复;Agent 是决策逻辑,负责"下一步该干什么"。很多人把两者混为一谈,导致架构设计混乱。
一个典型的自动化编程 Agent 长这样:用户给出任务描述,Agent 先规划步骤,然后依次调用 CLI 工具执行,每步执行完把结果喂回模型判断是否继续。这里网关的作用就体现出来了——Agent 每步都要调模型,所有调用都走网关,才能统一管控成本和限流。
我踩过的一个坑是:Agent 循环没有终止条件。有次我写了个自动修 bug 的 Agent,结果它陷入"改代码-跑测试-失败-再改"的死循环,一晚上烧了不少钱。后来加了最大迭代次数和成本上限才解决。所以任何 Agent 上线前,一定要设硬性熔断。
4. 实操落地:从零搭一个最小网关
4.1 环境准备与依赖安装
先明确目标:我们要搭一个能转发 OpenAI 格式请求、支持多后端路由、带基础限流和计量的网关。技术栈用 Go + Gin + Redis。
第一步装环境。Go 版本建议 1.21 以上,Redis 用 7.x。依赖主要是这几个:
go mod init llm-gateway go get github.com/gin-gonic/gin go get github.com/redis/go-redis/v9 go get github.com/google/uuid配置我建议用 YAML,比环境变量清晰。一个最小配置长这样:
server: port: 8080 backends: - name: openai-gpt4 provider: openai base_url: https://api.openai.com/v1 api_key: ${OPENAI_KEY} models: [gpt-4, gpt-4-turbo] - name: local-qwen provider: openai-compatible base_url: http://localhost:8000/v1 api_key: dummy models: [qwen-max] rate_limit: default_qps: 10 per_business: team-a: 50 team-b: 20注意 api_key 用环境变量注入,千万别写死在配置文件里提交到仓库,这是血泪教训。
4.2 核心转发逻辑实现
转发逻辑的核心是:解析请求里的 model,找到对应后端,改写请求头,转发,再把响应流式回传。关键代码骨架如下:
func (g *Gateway) ChatCompletions(c *gin.Context) { var req ChatRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(400, gin.H{"error": "invalid request"}) return } backend := g.router.Match(req.Model) if backend == nil { c.JSON(404, gin.H{"error": "no backend for model"}) return } business := c.GetHeader("X-Business-Id") if !g.limiter.Allow(business) { c.JSON(429, gin.H{"error": "rate limit exceeded"}) return } g.proxy.Stream(c, backend, &req) }Stream方法里要做的事:构造到后端的请求,带上真实 Key,发起请求,然后逐块读取响应体,解析 SSE 事件,累加 usage,最后写回客户端。这里有个细节,usage 字段通常在流的最后一个 chunk 里,所以计量逻辑要放在流结束的时候,不能边读边算。
4.3 计量与限流的实现细节
限流用 Redis 的令牌桶,Lua 脚本保证原子性。核心思路是每个业务 ID 一个桶,桶容量和补充速率从配置读。为什么用 Lua?因为"读当前令牌数-判断-扣减"这三步必须原子,用普通命令会有并发问题。
计量我分两个维度:请求数和token 数。请求数用 Redis 的 INCR 按分钟聚合,token 数在流结束时累加。这里要注意,不同模型的 token 计价不一样,所以计量表里要存模型名,出账单时再乘以单价。
一个容易忽略的点:失败请求也要计量。有些请求后端返回了错误,但可能已经消耗了部分 token,或者至少占用了连接资源。我的做法是失败请求单独计数,不计入 token 但计入请求数,方便排查异常。
4.4 部署与灰度
网关是核心链路,部署必须谨慎。我的建议是双实例起步,前面挂负载均衡,任何一个挂了另一个顶上。配置更新用热加载,不要重启服务,否则正在跑的流式请求全断。
灰度策略上,新版本先接 5% 流量,观察错误率和延迟,没问题再逐步放量。网关这种组件,一次全量出问题就是全公司业务停摆,容不得侥幸。
5. 常见问题与排查实录
5.1 高频问题速查表
| 现象 | 可能原因 | 排查方向 |
|---|---|---|
| 流式响应卡住不动 | 缓冲策略太激进 | 检查 flush 阈值 |
| 429 频繁出现 | 限流配置过严 | 看 Redis 桶配置 |
| token 统计偏少 | usage 字段没解析到 | 抓包看最后 chunk |
| 后端 Key 报无效 | 环境变量没注入 | 检查部署配置 |
| 首字延迟高 | 网关到后端链路慢 | 测网络 RTT |
| 内存持续上涨 | 流式连接没释放 | 查 goroutine 泄漏 |
5.2 几个我踩过的坑
坑一:SSE 的换行符处理。SSE 协议规定事件之间用两个换行分隔,但有些后端返回的是\r\n,有些是\n。如果你的解析器只认一种,就会漏事件。正确做法是两种都兼容。
坑二:超时设置。普通 HTTP 请求超时设 30 秒没问题,但流式请求可能跑几分钟。如果网关的超时和客户端不一致,会出现客户端还在等、网关已经断开的诡异现象。我的经验是流式接口超时设长一点,比如 300 秒,同时用心跳事件保活。
坑三:并发下的计量丢失。早期我用普通 Redis 命令做计数,高并发下丢了不少数据。后来改成 Lua 脚本 + 批量写入才稳定。计量数据不准,账单就没法看,业务方会直接质疑你的网关。
坑四:Agent 场景的会话追踪。普通请求一个 request-id 就够了,但 Agent 一次任务几十个请求,必须有个 session-id 把它们串起来。我在网关里加了从请求头透传 session-id 的逻辑,出问题时能一键拉出整个任务的调用链,排查效率提升巨大。
5.3 安全方面的注意事项
网关是流量的必经之路,也是安全的关键点。几个必须做的:请求体大小限制,防止有人传超大 payload 打爆内存;敏感信息脱敏,日志里不能记录完整的 Key 和用户隐私数据;输入输出过滤,对接内容安全服务做审核;审计日志留存,满足合规要求。
还有一点,网关自身的管理接口要单独鉴权,不能和业务接口混在一起。我见过有人把配置管理接口暴露在公网,被人改了路由配置,所有流量被导到恶意后端,后果不堪设想。
6. 后续扩展方向
网关跑起来只是第一步,后面还有很多可以做的。比如智能路由,根据请求的复杂度自动选择便宜还是贵的模型,简单问题用便宜模型,复杂问题才上大模型,成本能降一大截。再比如语义缓存,把相似的问题命中缓存,重复问题直接返回,省下的 token 相当可观。
Agent 方向上的扩展空间更大。可以在网关层做工具调用的统一管理,把 Agent 能用的工具注册到网关,由网关做权限控制和调用审计。这样 Agent 的能力边界就清晰了,不会出现 Agent 乱调工具的情况。
我个人的体会是,网关这个东西越早做越好。等业务铺开了再补,改造成本会翻好几倍。而且它不是一个做完就完的项目,是要跟着业务一起演进的。先把核心链路跑通,再根据实际痛点加功能,比一开始就设计一个完美架构要靠谱得多。