1. 企业大模型网关到底解决什么问题
很多团队在2024年前后开始把大模型接进业务系统,最初的做法往往很直接:业务代码里硬编码一个API地址,把密钥写在配置文件里,谁要用就自己调。这种模式在只有一两个应用、三五个开发的时候还能凑合,一旦接入的应用超过十个,问题就会集中爆发。我自己经历过的一个项目,光是密钥就散落在七个不同的仓库里,某次一个开发离职,密钥轮换花了整整两天,期间还有两个服务因为漏改配置直接挂了。
大模型网关本质上就是在业务应用和大模型服务之间加一层统一入口。所有请求先打到网关,由网关负责鉴权、路由、限流、计费、日志、缓存这些横切关注点,业务侧只需要关心"我要调用哪个模型能力"。这个思路和传统API网关(比如Kong、APISIX)是一脉相承的,只不过大模型场景有它自己的特殊性。
1.1 和传统API网关的关键差异
传统网关主要处理HTTP请求的转发和治理,请求体通常很小,响应也是结构化的JSON。大模型网关面对的是完全不同的负载特征:
- 请求体可能很大:多轮对话场景下,上下文动辄几万token,请求体轻松超过100KB
- 响应是流式的:SSE(Server-Sent Events)流式输出是标配,网关必须支持流式透传,不能把整个响应缓冲完再返回
- 计费维度复杂:按输入token、输出token分别计价,不同模型单价差异巨大
- 超时时间很长:一次复杂推理可能跑几十秒甚至几分钟,传统网关默认的30秒超时完全不够用
我见过有团队直接拿Nginx做转发,结果流式响应被缓冲,用户端要等模型全部生成完才看到第一个字,体验直接崩掉。所以选型时一定要确认网关是否原生支持流式透传。
1.2 企业为什么非得上网关
从成本角度看,网关带来的收益非常直接。某中型SaaS团队接入网关后,通过统一的语义缓存(对相似问题命中缓存),把重复请求拦截掉了约35%,月度API账单直接降了三成。从安全角度看,密钥集中管理在网关侧,业务侧拿到的只是网关签发的内部token,即使业务代码泄露也不会暴露真实的模型密钥。
从可观测性角度看,网关是所有调用的必经之路,天然适合做全链路追踪。哪个业务线用了多少token、哪个模型响应最慢、哪类请求失败率最高,这些数据在网关层一目了然。没有网关的时候,这些指标要靠各个业务自己埋点上报,口径不统一,数据根本没法汇总。
提示:网关不是银弹。如果团队只有一两个应用、调用量很小,直接调API反而更简单。网关的价值在规模上来之后才显现,过早引入会增加不必要的运维负担。
2. 网关核心模块的设计与选型
网关要落地,核心是把几个模块设计清楚。我按重要性排序,逐个拆解。
2.1 鉴权与密钥管理
企业场景下,鉴权至少要分两层:业务侧到网关的鉴权和网关到模型服务的鉴权。前者用内部签发的API Key或JWT,后者用真实的模型服务密钥。
密钥管理我强烈建议用专门的密钥管理服务,而不是放在数据库明文或环境变量里。环境变量在容器编排环境下容易通过docker inspect或进程信息泄露,数据库明文存储更是大忌。如果团队规模不大,至少要做到密钥加密存储,解密密钥通过独立的配置中心下发。
网关侧给业务签发的Key建议带上元信息:所属业务线、配额、可访问的模型列表。这样鉴权的时候可以一次性完成权限校验,不用再查额外的表。我通常会用类似这样的结构:
{ "key_id": "biz-payment-001", "business_unit": "payment", "allowed_models": ["gpt-4o-mini", "claude-3-5-sonnet"], "quota": { "daily_tokens": 5000000, "rpm": 600 }, "expires_at": "2025-12-31T23:59:59Z" }2.2 路由与模型映射
业务侧不应该关心具体调用哪个模型,而是表达"我要一个便宜的快速模型"或"我要一个强推理模型"。网关负责把这种逻辑模型名映射到物理模型。
这样做的好处是模型切换对业务透明。某天OpenAI涨价了,或者某个模型下线了,只需要在网关改映射规则,业务代码一行不用动。我一般会维护一张映射表:
| 逻辑模型名 | 物理模型 | 适用场景 | 单价(每百万token) |
|---|---|---|---|
| fast-chat | gpt-4o-mini | 客服问答、分类 | 输入0.15美元/输出0.6美元 |
| strong-reason | gpt-4o | 复杂分析、代码生成 | 输入2.5美元/输出10美元 |
| cheap-bulk | 国产某模型 | 批量文本处理 | 输入0.5元/输出1元 |
路由还可以做更细的灰度:按业务线、按用户ID哈希、按请求特征分流。比如新模型上线时先给5%的流量试跑,观察一周再全量。
2.3 限流与配额
限流要分多个维度,单一维度很容易被绕过:
- 按Key限流:防止单个业务打爆整个网关
- 按模型限流:某些模型有上游并发限制,必须保护
- 按全局限流:兜底,防止整体流量超过预算
算法上,令牌桶适合处理突发流量,漏桶适合平滑输出。大模型场景我一般用令牌桶,因为业务请求本身就有波峰波谷。RPM(每分钟请求数)和TPM(每分钟token数)要同时限制,只限RPM的话,有人发超长上下文一样能把配额吃光。
配额管理要支持日、月两个周期,并且要能实时扣减。这里有个坑:流式响应下,输出token是逐步产生的,配额扣减必须等流结束才能拿到准确数字。我的做法是先按预估上限预扣,流结束后再按实际用量修正,避免超用。
2.4 缓存策略
大模型调用贵,缓存能省的钱非常可观。但缓存不是简单地对请求体做哈希,要考虑语义相似度。
精确缓存好做,请求体规范化(去掉无关字段、统一温度参数)后哈希即可。语义缓存需要引入embedding模型,把请求转成向量,在向量库里找相似度超过阈值的缓存结果。阈值一般设在0.92到0.95之间,太低会返回不相关的答案,太高命中率又上不去。
注意:语义缓存只适合问答类、知识类场景。代码生成、创意写作这类需要多样性的场景不要开缓存,否则用户会发现每次问同样的问题答案都一样,体验很怪。
2.5 可观测性
网关要记录的核心指标:请求量、token消耗、延迟分布(P50/P95/P99)、错误率、缓存命中率。日志要能关联到具体的业务请求ID,方便排查。
延迟这块要特别关注首token延迟(TTFT)和总生成时间两个指标。用户对TTFT最敏感,超过2秒就会觉得卡。总生成时间则影响并发容量规划。
3. 自动化编程Agent的落地路径
网关解决的是"调用"的问题,自动化编程Agent解决的是"生产"的问题。这两件事在企业里往往是配套推进的:网关让模型调用可控,Agent让开发效率提升。
3.1 Agent和普通脚本的本质区别
很多人把Agent理解成"会调用工具的脚本",这个理解不准确。普通脚本的执行路径是写死的:先A再B再C。Agent的核心是由模型决定下一步做什么,它有一个循环:观察当前状态、思考、选择动作、执行、再观察。
这个循环带来的最大变化是不确定性。脚本跑一百次结果一样,Agent跑一百次可能走一百条不同的路径。这对工程化提出了新要求:必须有清晰的终止条件、必须有失败重试、必须有执行轨迹记录。
我见过有团队把Agent用在生产环境的数据库操作上,结果模型某次"灵机一动"执行了删除语句。所以Agent的能力边界一定要卡死,危险操作必须走人工确认。
3.2 CLI形态为什么适合企业落地
Agent的交互形态有好几种:Web界面、IDE插件、CLI工具。企业场景下我更推荐CLI,原因有几个。
CLI天然适合集成到现有工作流。CI/CD流水线、Git钩子、定时任务,这些地方调用CLI比调用Web接口方便得多。CLI的输出是纯文本,容易做管道处理,也容易写进日志。
CLI的资源占用小。IDE插件要跑在开发者的机器上,Web界面要维护前端,CLI只需要一个终端。对于需要批量执行的任务,CLI可以轻松并行。
CLI的权限模型清晰。它跑在哪个用户下、能访问哪些文件、能执行哪些命令,都是操作系统层面管着的,不用自己再造一套权限系统。
3.3 一个典型的编程Agent工作流
以"根据需求文档生成代码并提交PR"为例,完整流程大概是这样:
- 读取需求:Agent读取指定的需求文档或issue
- 理解代码库:扫描项目结构,识别技术栈、代码规范、相关模块
- 制定计划:拆解任务,列出要改哪些文件、新增哪些文件
- 执行修改:逐个文件生成或修改代码
- 自检:运行lint、类型检查、单元测试
- 修复:根据自检结果迭代修改
- 提交:创建分支、提交、推送、开PR
这个流程里,第5步和第6步是关键。没有自检的Agent就是"生成完就跑",质量完全靠运气。有了自检循环,Agent能自己发现问题并修复,成功率会高很多。
3.4 工具调用的设计要点
Agent的能力上限取决于它能调用哪些工具。工具设计有几个原则:
工具粒度要适中。太细的话,Agent要调用很多次才能完成一件事,token消耗大、出错概率高。太粗的话,Agent没法灵活组合。我一般按"一个完整的业务动作"来切分,比如"读取文件"是一个工具,"运行测试"是一个工具,"提交代码"是一个工具。
工具描述要精确。模型是根据工具描述来决定调不调的,描述模糊会导致误用。参数说明要写清楚类型、是否必填、取值范围。我习惯在描述里加一两个使用示例,效果比纯文字说明好很多。
工具要有幂等性。Agent可能会重试,如果工具不幂等,重试就会产生副作用。读操作天然幂等,写操作要设计成"重复执行结果一致",比如用版本号或内容哈希做去重。
4. 从零搭建的实操步骤
前面讲的是设计,这一节讲具体怎么落地。我按搭建顺序来。
4.1 环境准备与依赖安装
网关侧我一般用Go或Node.js,Go的并发性能好,Node.js生态丰富。Agent侧Python和Node.js都行,Python的AI生态更成熟,Node.js和前端工具链集成更顺。
以Node.js为例,基础依赖包括:
# 网关核心依赖 npm install fastify undici ioredis # Agent侧依赖 npm install @modelcontextprotocol/sdk zod这里要提醒一点:安装Agent相关CLI工具时网络可能很慢。国内环境下npm源建议换成国内镜像,否则装一个包等十分钟是常事。如果遇到类似missing optional dependency的报错,通常是平台相关的二进制包没装上,重新安装对应平台的包即可。
4.2 网关最小可用版本
先做一个能跑通的最小版本,不要一上来就追求功能齐全。最小版本只需要:接收请求、鉴权、转发到模型、流式返回。
import Fastify from 'fastify'; import { request } from 'undici'; const app = Fastify({ logger: true }); app.post('/v1/chat/completions', async (req, reply) => { // 1. 鉴权 const apiKey = req.headers['x-api-key']; const biz = await validateKey(apiKey); if (!biz) return reply.code(401).send({ error: 'invalid key' }); // 2. 路由:逻辑模型名映射到物理模型 const logicalModel = req.body.model; const physicalModel = routeModel(logicalModel, biz); if (!physicalModel) return reply.code(400).send({ error: 'model not allowed' }); // 3. 转发,保持流式 const upstream = await request('https://api.openai.com/v1/chat/completions', { method: 'POST', headers: { 'content-type': 'application/json', 'authorization': `Bearer ${process.env.UPSTREAM_KEY}` }, body: JSON.stringify({ ...req.body, model: physicalModel }) }); reply.code(upstream.statusCode); reply.header('content-type', upstream.headers['content-type']); return reply.send(upstream.body); }); app.listen({ port: 8080 });这个版本能跑,但缺限流、缓存、日志。建议先跑通再逐步加,一次性写全容易出bug还不好排查。
4.3 流式透传的关键细节
流式透传最容易踩的坑是缓冲。很多HTTP客户端默认会把响应读完再返回,必须显式关闭缓冲。
用undici的时候,upstream.body是一个可读流,直接reply.send就能透传。但如果中间加了任何处理逻辑(比如统计token数),要确保不破坏流式特性。统计token数可以在流上挂一个transform,边转发边计数,不要等流结束。
还有一个坑是超时。Node.js的HTTP服务器默认超时是2分钟,长推理会被切断。要显式设置:
const app = Fastify({ logger: true, serverFactory: (handler) => { const server = http.createServer(handler); server.requestTimeout = 0; // 不限制请求超时 server.headersTimeout = 60000; return server; } });4.4 Agent的骨架实现
Agent的核心是一个循环,伪代码大概是这样:
def run_agent(task, max_steps=20): messages = [{"role": "system", "content": SYSTEM_PROMPT}, {"role": "user", "content": task}] for step in range(max_steps): # 1. 让模型决定下一步 response = call_model(messages, tools=TOOLS) # 2. 如果没有工具调用,说明任务完成 if not response.tool_calls: return response.content # 3. 执行工具 for tool_call in response.tool_calls: result = execute_tool(tool_call.name, tool_call.arguments) messages.append({"role": "tool", "content": result}) return "达到最大步数限制,任务未完成"这个骨架有几个关键点:max_steps必须有,防止无限循环;工具执行要有超时,防止某个工具卡死;每步都要记录,方便事后分析。
4.5 上下文管理
Agent跑多轮之后,上下文会越来越长,token消耗直线上升。必须做上下文压缩。
常见做法是保留最近N轮完整对话,更早的对话做摘要。摘要用便宜的小模型生成,成本可以忽略。摘要的prompt要强调"保留关键决策和结论,丢弃中间过程"。
另一个技巧是工具结果截断。有些工具返回的内容很长(比如读取一个大文件),全部塞进上下文很浪费。可以只保留前若干行加后若干行,中间用省略号代替,并注明"完整内容已省略"。
5. 常见问题与排查实录
这一节是我踩过的坑和帮别人排查过的问题,整理成速查表。
5.1 网关侧问题速查
| 现象 | 可能原因 | 排查方法 | 解决 |
|---|---|---|---|
| 流式响应变成一次性返回 | 中间有缓冲层 | 抓包看首字节时间 | 关闭Nginx缓冲,检查客户端是否流式读取 |
| 长请求被切断 | 超时设置过短 | 看日志里的超时错误 | 调大requestTimeout和上游超时 |
| 配额扣减不准 | 流式下token统计时机不对 | 对比网关统计和上游账单 | 流结束后修正,或按预估预扣 |
| 密钥泄露 | 环境变量或日志打印 | 搜索代码库和日志 | 用密钥管理服务,日志脱敏 |
| 缓存命中率低 | 阈值设太高或请求未规范化 | 看缓存key的分布 | 调低阈值,规范化请求体 |
5.2 Agent侧问题速查
| 现象 | 可能原因 | 排查方法 | 解决 |
|---|---|---|---|
| Agent陷入死循环 | 没有终止条件或工具反复失败 | 看执行轨迹 | 加max_steps,工具失败N次后终止 |
| 工具调用参数错误 | 工具描述不清晰 | 看模型生成的参数 | 完善工具描述,加示例 |
| 上下文超限 | 没有压缩机制 | 看每轮的token数 | 加摘要和截断 |
| 执行结果不稳定 | 温度参数过高 | 对比多次执行 | 编程任务温度设0或接近0 |
| 危险操作被执行 | 权限没卡死 | 看执行日志 | 危险操作走人工确认 |
5.3 几个容易忽视的坑
坑一:模型名大小写敏感。有些上游对模型名大小写敏感,网关做映射时要注意统一。我遇到过业务传GPT-4o,上游只认gpt-4o,直接报模型不存在。
坑二:SSE的心跳。长推理过程中,如果上游很久不返回数据,中间的代理可能会断开连接。要在网关侧定期发送SSE注释行(以:开头)做心跳,保持连接活跃。
坑三:Agent的文件路径。Agent操作文件时,路径要限制在工作目录内,防止../../etc/passwd这种路径穿越。所有路径都要做规范化后再校验前缀。
坑四:并发写冲突。多个Agent同时改同一个文件会冲突。要么加文件锁,要么让每个Agent在独立的分支或工作区操作。
提示:排查Agent问题时,执行轨迹是最重要的线索。建议把每一步的输入、输出、耗时都记下来,出问题时能完整回放。没有轨迹的Agent调试起来就是盲人摸象。
6. 成本控制与性能优化
网关和Agent都跑起来之后,成本会成为关注重点。这一节讲几个实用的优化手段。
6.1 模型分级路由
不是所有请求都需要最强模型。分类、抽取、简单问答用便宜模型完全够用,只有复杂推理才需要上强模型。网关可以根据请求特征自动分级:请求短、任务简单的走便宜模型,请求长、任务复杂的走强模型。
分级规则可以基于关键词、请求长度、历史成功率动态调整。我做过一个项目,通过分级路由把整体成本降了约60%,而用户感知的质量几乎没有下降。
6.2 Prompt缓存
很多模型服务支持Prompt缓存,相同的系统提示词部分可以复用,按更低的单价计费。对于系统提示词很长的场景(比如带大量few-shot示例),这个优化能省不少钱。
要利用Prompt缓存,关键是把不变的部分放在前面,变化的部分放在后面。系统提示词、工具定义这些固定内容放最前面,用户输入放最后。这样缓存命中率最高。
6.3 批处理
非实时任务尽量批处理。批处理接口的单价通常比实时接口低不少,而且可以攒一批一起发,减少请求次数。适合批处理的场景:数据标注、内容审核、批量摘要、离线分析。
批处理的代价是延迟高,所以只适合对时效性不敏感的任务。要区分清楚哪些任务必须实时,哪些可以等。
6.4 并发控制
上游模型服务通常有并发限制,超过会被限流。网关侧要做并发控制,把并发数控制在限制以内。用信号量或令牌桶都行,关键是不要把所有请求同时发出去。
我一般会设置一个并发上限,超出的请求排队等待。排队时间要设上限,超过就返回繁忙,让客户端重试。这样比直接打爆上游要好,至少不会触发上游的封禁。
7. 安全边界与合规要点
企业场景下,安全是不能妥协的。这一节讲几个必须守住的边界。
7.1 数据不出域
如果企业对数据出境有要求,网关必须能识别敏感数据并拦截。常见的做法是在网关侧加一层内容检测,命中敏感规则的请求直接拒绝或脱敏后再转发。
脱敏要小心,不能破坏语义。比如把身份证号替换成占位符,模型可能就理解不了上下文了。更好的做法是本地做脱敏、本地做还原,模型只看到脱敏后的内容。
7.2 提示注入防护
用户输入里可能藏有恶意指令,试图让模型忽略系统提示词。网关侧要做输入检测,识别常见的注入模式。但完全防住很难,所以更重要的防线是限制模型的能力:即使被注入,模型也做不了危险操作。
Agent场景下,工具权限要最小化。Agent只能访问它完成任务必需的文件和命令,其他一律拒绝。这样即使提示注入成功,损失也可控。
7.3 审计日志
所有模型调用和Agent操作都要留审计日志。日志要包含:谁调的、什么时候调的、调了什么、结果是什么。日志要防篡改,最好写到独立的存储,和业务数据隔离。
审计日志的保留期限根据合规要求定,一般至少保留半年。日志里的敏感信息要脱敏,但脱敏规则要记录清楚,方便需要时还原。
8. 团队协作与推广落地
技术方案再好,推不动也没用。这一节讲怎么让团队接受并用起来。
8.1 从痛点切入
不要一上来就讲"我们要建网关",而是从具体痛点切入。比如"现在密钥管理太乱,轮换一次要两天",或者"上个月API账单超预算了,但不知道是谁用的"。用真实痛点说服人,比讲架构图有效得多。
先解决一个最痛的点,做出效果,再逐步扩展。我一般建议先做密钥集中管理和用量统计,这两个见效快、阻力小。
8.2 提供好用的SDK
业务侧接入网关,如果SDK不好用,大家会绕过网关直接调API。SDK要做到:一行代码初始化、调用方式和原来一致、错误信息清晰。
最好提供多语言SDK,至少覆盖团队主要用的语言。SDK里把重试、超时、流式处理这些细节都封装好,业务侧不用关心。
8.3 建立反馈闭环
网关和Agent都是要持续迭代的。要建立反馈渠道,让业务侧能方便地提问题和建议。定期看用量数据,发现异常及时沟通。
我习惯每月出一份用量报告,发给各业务线,让大家看到自己的消耗和优化空间。有了数据,优化就有方向,不用靠感觉。
9. 后续扩展方向
网关和Agent跑顺之后,可以往几个方向扩展。
多模态支持。现在很多模型支持图片、音频输入,网关要能处理这些非文本内容。多模态的计费方式和文本不同,要单独设计。
Agent编排。单个Agent能力有限,复杂任务需要多个Agent协作。可以做一个编排层,让不同专长的Agent分工合作。编排的难点是任务分解和结果汇总,需要仔细设计。
本地模型接入。出于成本或数据安全考虑,部分场景可以用本地部署的模型。网关要能同时路由到云端和本地,根据任务特征选择。
效果评估体系。Agent生成的内容质量怎么衡量?需要建立评估体系,用人工标注加自动指标结合的方式,持续监控质量变化。
这些方向不用一次全做,根据业务需要逐步推进。我的经验是,每加一个能力,都要先想清楚它解决什么具体问题,不要为了技术而技术。
我个人在实际操作中的体会是,网关和Agent这类基础设施,最难的不是技术实现,而是找到合适的落地节奏。做早了没人用,做晚了业务已经各自为政、难以收拢。比较好的时机是业务侧开始出现明显的重复建设或成本失控苗头的时候,这时候推,既有痛点支撑,又有实际收益可以展示。另外,任何基础设施都要有明确的负责人和迭代计划,否则上线即巅峰,后面没人维护,很快就会变成技术债。