跳到主要内容

大模型网关的设计与落地

实战项目推荐

网关层解决模型调用的统一入口、限流、观测和治理问题。超级 AI 智能体在会话链路里保留模型调用、路由判定、检索和执行阶段的关键记录,适合用来理解网关能力如何服务上层 AI 应用。

项目详细介绍:什么是超级 AI 智能体?

看到此章节你可能有这个疑问:我已经有 Nginx 了,有 Spring Cloud Gateway 了,为什么还需要单独搞一个"大模型网关"?

这个问题问得好,因为它恰好暴露了一个常见的误区——把 LLM 网关和传统 API 网关混为一谈。

传统网关管不了的事

传统 API 网关擅长的是:负载均衡、路由转发、限流熔断、协议转换。这些能力对普通的微服务接口调用来说完全够了。但大模型 API 有几个特殊性,让传统网关力不从心:

计费单位不同——传统 API 按请求次数计费或者干脆不计费,大模型按 token 数量计费。一个请求可能消耗 100 token,也可能消耗 10000 token。Nginx 不认识 token 这个概念,它没法做基于 token 的配额管理。

响应模式不同——大模型的流式输出(SSE)是一个长连接持续推送 token,传统网关的超时策略和负载均衡算法都不太适用。一个 SSE 连接可能持续 30 秒,你用轮询策略做负载均衡,某个节点可能因为几个"大请求"就被打满。

模型之间不是简单的"上下游"关系——你不是在多个相同服务实例之间做负载均衡,而是在能力不同、价格不同、速度不同的多个模型之间做智能路由。GPT-4o 贵但强,GPT-4o-mini 便宜但够用——什么时候该用哪个,这是传统网关做不到的决策。

需要语义级别的处理——Prompt 注入检测、敏感信息脱敏、语义缓存,这些都需要"理解"请求内容,不是简单的字节流转发。

网关放在哪里

流程图
流程图

所有业务服务不直接调模型 API,而是统一通过 LLM Gateway。Gateway 对外暴露一个标准化的接口(通常兼容 OpenAI 的格式),业务层调用的时候就像在调 OpenAI 一样,完全感知不到底层是哪个模型在工作。

核心功能模块拆解

一个生产可用的 LLM 网关,核心功能可以拆成这几块:

统一接口适配

不同模型供应商的 API 格式不一样:OpenAI 用 messages 数组,有些供应商用 prompt 字符串;参数命名也不统一,有的叫 max_tokens,有的叫 max_output_tokens

网关对外统一暴露一套接口(业界惯例是兼容 OpenAI Chat Completions 格式),在内部做协议转换:

public interface ModelAdapter {

/**
* 将统一格式的请求转换为供应商特定格式
*/
ProviderRequest convertRequest(UnifiedChatRequest request);

/**
* 将供应商返回的响应转换为统一格式
*/
UnifiedChatResponse convertResponse(ProviderResponse response);

/**
* 处理流式输出的每一个 chunk
*/
UnifiedStreamChunk convertStreamChunk(ProviderStreamChunk chunk);
}

@Component
public class DeepseekAdapter implements ModelAdapter {

@Override
public ProviderRequest convertRequest(UnifiedChatRequest request) {
// Deepseek 的 API 跟 OpenAI 基本兼容,但有些参数不同
return ProviderRequest.builder()
.url("https://api.deepseek.com/chat/completions")
.body(Map.of(
"model", mapModelName(request.getModel()),
"messages", request.getMessages(),
"temperature", request.getTemperature(),
"stream", request.isStream()
))
.build();
}
}

这样做的好处是:业务代码只需要记住一个接口格式,换模型只改网关的路由配置,不需要改调用方的任何代码。

智能路由:不只是负载均衡

传统负载均衡是"把请求平均分到多个相同实例上"。LLM 网关的路由更复杂——你需要根据请求的特征,决定用哪个模型。

几种常见的路由策略:

按任务类型路由——简单的意图分类用便宜的小模型,复杂的长文写作用贵的大模型。网关可以先对请求做一个轻量级的分类(甚至用正则匹配),然后决定路由方向。

按成本优先级路由——工作日白天用户量大的时候优先走本地部署的模型(边际成本低),本地模型排队长了再溢出到云端 API。

按模型能力路由——需要代码生成的走擅长代码的模型,需要中文创作的走中文能力强的模型。

@Component
public class SmartRouter {

private final List<RoutingRule> rules;

public ModelEndpoint route(UnifiedChatRequest request) {
for (RoutingRule rule : rules) {
if (rule.matches(request)) {
return rule.getTargetEndpoint();
}
}
return getDefaultEndpoint();
}
}

// 路由规则示例:token 预估超过 2000 的用 GPT-4o,否则用 mini
@Component
public class TokenBasedRule implements RoutingRule {

@Override
public boolean matches(UnifiedChatRequest request) {
int estimatedTokens = tokenEstimator.estimate(request.getMessages());
return estimatedTokens > 2000;
}

@Override
public ModelEndpoint getTargetEndpoint() {
return endpointRegistry.get("gpt-4o");
}
}

Fallback 和容灾

模型 API 不是 100% 可靠。OpenAI 偶尔 503,某些地区的接入点延迟突然飙高,高峰期触发供应商的限流。

网关需要做的是:当主路由失败(连续 N 次报错、延迟超阈值、返回 429),自动把流量切到备用路由。

@Component
public class FallbackHandler {

private final CircuitBreaker circuitBreaker;

public UnifiedChatResponse callWithFallback(UnifiedChatRequest request,
List<ModelEndpoint> endpoints) {
for (ModelEndpoint endpoint : endpoints) {
if (circuitBreaker.isOpen(endpoint.getId())) {
continue; // 这个节点正在熔断中,跳过
}
try {
UnifiedChatResponse response = doCall(endpoint, request);
circuitBreaker.recordSuccess(endpoint.getId());
return response;
} catch (Exception e) {
circuitBreaker.recordFailure(endpoint.getId());
log.warn("模型调用失败,尝试下一个: endpoint={}, error={}",
endpoint.getId(), e.getMessage());
}
}
throw new AllEndpointsFailedException("所有模型端点均不可用");
}
}
实际案例

一种常见的配置是:主路由 OpenAI GPT-4o → 备用路由 Azure OpenAI GPT-4o(不同区域)→ 兜底 Anthropic Claude Sonnet。这样即使某一家供应商出问题,用户也不会感知到服务中断。

限流与配额管理

这是 LLM 网关区别于传统网关的核心差异点之一。传统限流按"请求数"限——比如每秒最多 100 个请求。LLM 的限流需要按 token 来,因为一个消耗 50 token 的请求和一个消耗 5000 token 的请求,对成本的影响差了 100 倍。

网关通过给每个团队/服务分配独立的虚拟 Key 来实现配额隔离:

@Component
public class QuotaManager {

private final RedisTemplate<String, Long> redis;

/**
* 检查并扣减配额
* @return true 表示配额充足,已扣减;false 表示超出配额
*/
public boolean tryConsume(String virtualKeyId, int tokenCount) {
String dailyKey = "quota:" + virtualKeyId + ":" + LocalDate.now();
Long remaining = redis.opsForValue().get(dailyKey);

if (remaining == null) {
// 首次使用,初始化今日配额
long dailyLimit = getDailyLimit(virtualKeyId);
redis.opsForValue().set(dailyKey, dailyLimit, Duration.ofDays(2));
remaining = dailyLimit;
}

if (remaining < tokenCount) {
return false;
}

redis.opsForValue().decrement(dailyKey, tokenCount);
return true;
}
}

这样做的效果是:研发团队拿的 Key 每天限 100 万 token,产品线 A 的 Key 每天限 50 万 token。某个团队用超了只影响自己,不会殃及池鱼。月底算账也很清楚——每个 Key 的消耗量就是那个团队的成本。

成本追踪和可观测

网关天然是所有模型调用的"咽喉",在这里做数据采集最方便也最完整。每次调用都记录下来:哪个服务调的、用的哪个模型、输入多少 token、输出多少 token、花了多少钱、耗时多久。

这些数据汇总起来就能回答:这个月总共花了多少钱?哪个服务最烧钱?GPT-4o 和 Deepseek 的使用比例是多少?有没有异常的消耗突增?

实际落地中,网关通常对接 Prometheus + Grafana 做实时监控,同时把详细的调用日志写到数据库或日志系统做离线分析。

语义缓存:省钱的利器

这是 LLM 网关最有意思的一个功能,也是传统网关完全做不了的。

问题场景:你做了一个客服机器人,用户一天问 500 次"怎么退款",措辞略有不同——"退款流程是什么"、"我想退货怎么操作"、"退款要几天到账"。每一次都打底层模型,500 次调用、花 500 份钱,但其实答案差不多。

语义缓存的思路:不再精确匹配字符串,而是判断两个问题的"语义"是否相近。相近就直接返回之前的答案。

具体实现分三步:

  1. 收到新请求后,先把用户问题用 Embedding 模型转成一个向量(可以理解为一个"语义指纹")
  2. 到向量数据库里做相似度检索,看有没有语义接近的历史问题
  3. 如果相似度超过阈值(比如 0.92),直接返回历史答案;否则正常调 LLM,调完把问题向量和答案存入缓存
@Component
public class SemanticCache {

private final EmbeddingClient embeddingClient;
private final VectorStore vectorStore;
private final double similarityThreshold = 0.92;

public Optional<String> tryHit(String userQuery) {
// 1. 生成查询向量
float[] queryVector = embeddingClient.embed(userQuery);

// 2. 在向量库中检索相似问题
SearchRequest searchRequest = SearchRequest.query(userQuery)
.withTopK(1)
.withSimilarityThreshold(similarityThreshold);

List<Document> results = vectorStore.similaritySearch(searchRequest);

if (!results.isEmpty()) {
Document hit = results.get(0);
// 3. 检查缓存是否过期
if (!isExpired(hit)) {
return Optional.of(hit.getMetadata().get("cached_answer").toString());
}
}
return Optional.empty();
}

public void store(String userQuery, String answer, Duration ttl) {
Document doc = new Document(userQuery, Map.of(
"cached_answer", answer,
"expire_at", Instant.now().plus(ttl).toEpochMilli()
));
vectorStore.add(List.of(doc));
}
}
语义缓存的坑

阈值调太低会出现"张冠李戴"——"北京怎么退款"命中了"上海怎么退款"的缓存,答案里的地址和电话全是上海的。阈值调太高又几乎命中不了,白做了。实际操作中建议从 0.95 开始往下调,每降 0.01 观察一下误命中率。

另外,对时效性强的内容(天气、股价、库存数量)要设很短的 TTL 或者直接不缓存。

自己设计一个 LLM 网关的思路

如果面试官问你"让你设计一个 LLM Gateway,你怎么做",可以从这几个维度来组织回答:

核心模块划分

网关内部可以拆成这样几个模块:

请求预处理管道:认证(验证虚拟 Key)→ 限流检查 → 语义缓存命中检查 → Prompt 安全过滤 → 敏感信息脱敏

路由决策引擎:根据请求特征(模型指定、token 量预估、任务类型标签)决定打到哪个模型

调用执行器:实际发起对外部模型 API 的调用,处理流式/非流式两种模式,做超时控制和重试

响应后处理管道:输出安全过滤 → token 计量 → 配额扣减 → 日志记录 → 响应返回

管理面板:虚拟 Key 管理、路由规则配置、配额设置、使用量统计

高可用设计

网关本身不能成为单点故障。常见做法是网关无状态化部署多实例,配额数据和缓存放 Redis 集群。网关实例挂了一个,请求自动漂移到其他实例,用户无感。

主流方案怎么选

方案语言适合场景特点
LiteLLMPython快速搭建、模型覆盖广支持 100+ 模型,社区活跃,OpenAI 兼容
One APIGo国内团队、需要支持国产模型部署简单,国内模型支持好
Spring AI + 自研Java已有 Java 技术栈、需要深度定制灵活度高,跟现有体系集成方便
PortKey商业不想自运维、需要完整功能开箱即用,有托管版
Kong AI Gateway商业已有 Kong 基础设施基于成熟网关扩展,运维体系复用

对于 Java 技术栈的团队,一个务实的选择是基于 Spring AI 自建轻量级网关:利用 Spring AI 已有的多模型适配能力,在上面加上配额管理、路由规则、缓存策略。这样跟你现有的 Spring 生态无缝集成,运维团队也熟悉。

对于想快速跑起来的团队,LiteLLM 或 One API 是更务实的选择——几个配置文件就能把多模型统一接入搞定,把精力放在业务逻辑上。

总结

回到最开始的问题:为什么不能用 Nginx 替代 LLM 网关?

因为 LLM 网关解决的根本不是"请求转发"的问题,而是"多模型管理"的问题。它需要理解 token、理解语义、理解模型之间的能力差异、理解按团队分账的业务需求。这些都不是传统网关的守备范围。

核心价值用一句话概括:让业务代码不需要关心"底层用的是哪个模型、怎么保证可靠、怎么控制成本"这些脏活累活

🎁优惠