AI 服务网关架构:大模型调用链路中的限流、路由与降级设计
AI 服务网关架构:大模型调用链路中的限流、路由与降级设计
一、大模型调用链的脆弱性:从单点故障到雪崩效应
大模型服务接入企业系统后,调用链路比传统微服务更加脆弱。原因有三:其一,大模型推理延迟高且波动大,P99 延迟可能达到数十秒,远超常规 RPC 调用;其二,Token 计费模式下,单次请求成本可达数元,无限制调用会快速消耗预算;其三,多模型供应商的 SLA 参差不齐,OpenAI、Anthropic、国内模型的可用性和响应速度差异显著。
当某个模型供应商出现故障或延迟飙升时,如果没有网关层的保护,所有上游请求会堆积在故障节点,线程池耗尽后引发级联失败。更隐蔽的风险是成本失控——一个未设限的内部服务可能在数小时内消耗掉整月预算。AI 服务网关正是解决这些问题的核心基础设施。
二、AI 网关的分层架构:从请求入口到模型路由
AI 服务网关在传统 API 网关基础上,增加了模型路由、Token 限流和语义降级三个核心层。
flowchart TB
A[客户端请求] --> B[认证与鉴权层]
B --> C[Token 限流层]
C --> D[模型路由层]
D --> E1[OpenAI Provider]
D --> E2[Anthropic Provider]
D --> E3[国产模型 Provider]
E1 --> F[响应聚合与降级]
E2 --> F
E3 --> F
F --> G[监控与计费]
C -->|超限| H[降级策略]
H --> H1[缓存响应]
H --> H2[轻量模型替换]
H --> H3[排队等待]
style D fill:#e1f5fe
style C fill:#fff3e0
style H fill:#fce4ec
模型路由层是 AI 网关区别于传统网关的关键。它根据请求的模型标识、上下文长度和成本预算,将请求分发到最合适的供应商。路由策略包括:优先级路由(优先使用成本最低的供应商)、延迟路由(优先使用响应最快的供应商)、权重路由(按比例分配流量用于 A/B 测试)。
三、生产级代码实现:Spring Cloud Gateway + AI 路由
3.1 基于 Token 的限流过滤器
@Component
public class TokenRateLimitFilter implements GlobalFilter, Ordered {
private final RedisTemplate<String, String> redisTemplate;
// 每个租户每分钟的 Token 预算上限
private static final long TOKEN_BUDGET_PER_MINUTE = 50000L;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String tenantId = exchange.getRequest().getHeaders().getFirst("X-Tenant-Id");
if (tenantId == null) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
String key = "token_budget:" + tenantId;
// 获取当前租户已消耗的 Token 数
String consumed = redisTemplate.opsForValue().get(key);
long currentConsumed = consumed != null ? Long.parseLong(consumed) : 0L;
if (currentConsumed >= TOKEN_BUDGET_PER_MINUTE) {
// 超出预算,触发降级
return handleRateLimitExceeded(exchange, tenantId);
}
// 预扣减:估算本次请求的 Token 消耗
long estimatedTokens = estimateRequestTokens(exchange.getRequest());
redisTemplate.opsForValue().increment(key, estimatedTokens);
// 首次设置时添加过期时间
redisTemplate.expire(key, 60, TimeUnit.SECONDS);
// 将估算值传递给下游,用于响应后精确扣减
exchange.getAttributes().put("estimatedTokens", estimatedTokens);
return chain.filter(exchange);
}
/**
* 根据请求体估算 Token 消耗
* 简单策略:输入字符数 / 4 + 预估输出 Token
*/
private long estimateRequestTokens(ServerHttpRequest request) {
String contentLength = request.getHeaders().getFirst("Content-Length");
long bodySize = contentLength != null ? Long.parseLong(contentLength) : 0L;
// 粗略估算:1 个中文字约 2 Token,预留输出空间
return (bodySize / 2) + 1024;
}
private Mono<Void> handleRateLimitExceeded(ServerWebExchange exchange,
String tenantId) {
// 尝试降级到轻量模型
ServerHttpRequest mutatedRequest = exchange.getRequest().mutate()
.header("X-Model-Downgrade", "true")
.header("X-Target-Model", "gpt-3.5-turbo")
.build();
log.warn("租户 {} Token 预算超限,降级到轻量模型", tenantId);
return chain.filter(exchange.mutate().request(mutatedRequest).build());
}
@Override
public int getOrder() {
return -1; // 优先级最高,最先执行
}
}
3.2 多模型路由策略
@Component
public class ModelRoutePredicateFactory extends AbstractRoutePredicateFactory<ModelRoutePredicateFactory.Config> {
private final ModelHealthIndicator healthIndicator;
@Override
public Predicate<ServerWebExchange> apply(Config config) {
return exchange -> {
String targetModel = exchange.getRequest().getHeaders()
.getFirst("X-Target-Model");
// 如果指定了目标模型,直接路由
if (targetModel != null) {
return config.getModelId().equals(targetModel);
}
// 未指定时,根据健康状态和延迟动态选择
ModelHealth health = healthIndicator.getHealth(config.getModelId());
if (!health.isAvailable()) {
return false;
}
// 延迟超过阈值时降低路由权重
return health.getP95Latency() < config.getMaxLatencyMs();
};
}
@Data
public static class Config {
private String modelId;
private long maxLatencyMs = 10000L;
}
}
3.3 语义降级与缓存策略
@Service
public class SemanticFallbackService {
private final CacheManager cacheManager;
private final EmbeddingService embeddingService;
/**
* 语义缓存:对相似请求返回缓存结果
* 适用于 FAQ 类场景,降低模型调用频率
*/
public Mono<String> getSemanticCache(String query, double similarityThreshold) {
float[] queryEmbedding = embeddingService.embed(query);
Cache cache = cacheManager.getCache("semantic_cache");
// 遍历缓存,计算余弦相似度
return Mono.fromCallable(() -> {
Map<String, float[]> cachedEmbeddings = getAllCachedEmbeddings(cache);
String bestMatch = null;
double bestScore = 0.0;
for (Map.Entry<String, float[]> entry : cachedEmbeddings.entrySet()) {
double similarity = cosineSimilarity(queryEmbedding, entry.getValue());
if (similarity > bestScore) {
bestScore = similarity;
bestMatch = entry.getKey();
}
}
if (bestScore >= similarityThreshold && bestMatch != null) {
log.info("语义缓存命中: query={}, similarity={}", query, bestScore);
return getCachedResponse(cache, bestMatch);
}
return null;
}).subscribeOn(Schedulers.boundedElastic());
}
private double cosineSimilarity(float[] a, float[] b) {
double dot = 0, normA = 0, normB = 0;
for (int i = 0; i < a.length; i++) {
dot += a[i] * b[i];
normA += a[i] * a[i];
normB += b[i] * b[i];
}
return dot / (Math.sqrt(normA) * Math.sqrt(normB) + 1e-8);
}
}
四、AI 网关的架构妥协:延迟、一致性与成本三角
限流精度与延迟的矛盾:Token 限流依赖预估算,而精确 Token 数只能在模型响应后获取。预估算偏高会浪费预算配额,偏低则无法有效限流。生产中建议采用"预扣减 + 异步精算"模式——请求前按估算值扣减,响应后按实际值回补差额,Redis 中维护两套计数器。
语义缓存的命中率陷阱:语义缓存依赖 Embedding 相似度匹配,但相似度阈值设置极难。阈值过低(如 0.85),大量不相关请求命中缓存,返回错误答案;阈值过高(如 0.98),命中率极低,缓存形同虚设。建议仅在 FAQ、知识库问答等高重复度场景启用语义缓存,对话类场景禁用。
多模型路由的一致性风险:同一会话中切换模型供应商,可能导致上下文理解不一致。不同模型对相同 Prompt 的响应风格、格式和推理能力差异显著。建议在会话维度绑定模型,仅在会话初始化时执行路由决策,会话内不切换。
降级策略的用户体验代价:从 GPT-4 降级到 GPT-3.5 时,响应质量显著下降,但用户并不感知降级发生。生产中必须在响应头中标记降级状态(如 X-Downgraded: true),并在前端展示降级提示,否则会引发用户对产品质量的质疑。
五、总结
AI 服务网关的核心价值在于将大模型调用的不确定性——延迟波动、成本失控、供应商故障——收敛到可控范围内。本文方案的三个关键机制为:Token 预算限流(防止成本失控)、多模型动态路由(保障可用性)、语义降级缓存(降低调用频率)。落地时需重点配置四个参数:Token 预算上限(建议按租户日预算的 80% 设置)、限流窗口(建议 1 分钟滑动窗口)、语义缓存阈值(建议 0.92 起,根据业务调优)、路由健康检查间隔(建议 10 秒)。网关上线前必须进行供应商故障演练,验证降级链路的完整性。
更多推荐


所有评论(0)