ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

一个站搞定全模型AI:AI网关设计、号池调度与缓存治理

一个站搞定全模型AI:AI网关设计、号池调度与缓存治理 这两年做 AI 应用落地团队最头疼的往往不是模型能力不够而是“模型太多、入口太散、账号太乱、成本不可控”。对话要接 GPT、Claude、国产模型生图要接 Midjourney、SD、ComfyUI每个服务商一套 Key、一套协议、一套计费规则前端对接时恨不得给每个模型写一个适配器。后来我们把这块梳理成了一个统一的 AI 网关站对话、生图、号池管理、缓存加速都在一个系统里完成。这篇文章就围绕这套系统的设计与实现展开重点讲多模型聚合、Pro 号池调度、以及如何把缓存命中率做到 95% 以上并稳定对公服务。如果你正在做 AI 聚合类项目、企业内部 AI 平台或者想了解网关层怎么做模型路由与缓存治理这篇文章可以作为一份系统性的工程参考。内容不挑模型供应商核心思路和代码在任何支持 OpenAI 风格协议的服务上都适用。1. 一个站搞定全模型 AI到底要解决什么问题1.1 多模型接入的痛点先看一个很常见的场景公司要做一款面向客户的 AI 产品产品里需要对话、图片生成、图片理解等多个能力。于是你开始接各家 API问题马上来了不同模型服务商的接口风格不统一有的兼容 OpenAI有的是自定义协议。Key 分散在各业务线手里轮换、过期、限流全靠人工盯。对话和生图是两套体系前端要维护两套请求逻辑。同一个 Prompt 反复请求服务商侧成本重复消耗响应速度也没有优化。部分渠道需要维护“Pro 号”这类高配额账号如果只用一个号触发限流后整个服务就不可用。这些问题累积到一定程度就不是“多写几个 Service”能解决的了必须有一个统一入口层来做协议转换、流量调度、账号池和缓存治理。这套入口层就是我们常说的 AI 网关。1.2 聚合网关的核心职责AI 网关站在大模型服务和业务系统之间看起来只是一个反向代理实际核心职责要重得多职责说明协议统一把不同供应商的请求/响应格式转换为内部统一协议模型路由根据模型名、渠道权重、业务线配置选择实际服务商号池管理维护多个 Key/Token 的健康状态自动剔除异常、自动切换配额控制对每个 Key、每个业务线做限流和费用统计缓存加速对可复用结果、语义相似请求、Token 级缓存做分层处理稳定性保障熔断、降级、重试、超时控制保证对公服务不中断一个站搞定全模型 AI并不是把所有模型装进一个进程里而是让所有模型通过同一个入口对外提供标准化服务。上游业务不需要关心你接的是哪家供应商也不需要关心 Key 是否还有余额它只需要说“我要什么模型传什么参数”。1.3 适用场景与读者范围这套设计适合以下场景企业内部搭建统一的 AI 能力平台供多个业务线使用。开发 AI 聚合站、AI 助手类产品需要同时支持对话和生图。需要高可用保障不能因为某个供应商限流就导致线上故障。希望在大模型 API 调用成本上做优化通过缓存降低重复消费。如果你是后端开发、架构师或独立开发者读完这篇文章可以掌握一套从网关设计、号池调度到缓存治理的完整落地思路。下面我们先从整体架构开始。2. 总体架构与模块拆分2.1 系统整体分层我们最终落地的系统分为五层每层职责单一不跨层调用客户端 / 业务后端 ↓ 接入网关层统一API入口、认证、限流 ↓ 路由调度层模型路由、号池选择、重试 ↓ 核心服务层对话服务、生图服务、任务队列 ↓ 缓存层Redis Cluster 本地缓存 ↓ 模型供应商适配层OpenAI、Claude、SD、MJ 等接入网关层负责所有外部请求的统一认证、参数校验和基础限流。路由调度层是核心它决定这次请求应该打到哪个供应商、使用哪个 Key、是否先查缓存。核心服务层把对话和生图拆成独立服务避免互相影响。缓存层采用 Redis 集群作为主缓存热点场景再用本地缓存兜底。最底层是供应商适配层不同服务商通过 SPI 方式接入新增渠道不需要改动上层代码。2.2 模块职责清单从工程实现角度我们把代码拆成了下面几个模块模块职责关键点gateway-api对外 API 接口层统一协议、参数校验、鉴权gateway-router模型路由与负载均衡权重、按模型名路由account-pool号池管理Key 健康检查、自动剔除、自动恢复chat-service对话服务流式输出、上下文管理image-service生图服务异步任务、结果查询、图生图cache-core缓存核心多级缓存、命中率统计、失效策略adapter-openaiOpenAI 风格适配器兼容开源模型、兼容服务商中转adapter-image图片供应商适配器SD、MJ 等渠道接入这个拆分的好处是每个团队可以并行开发也方便后续扩展新供应商。下面我们直接进入环境准备和代码实现环节。3. 环境准备与版本选型3.1 运行环境本文示例以 Java 17 Spring Boot 3.x 为基础这也是目前企业级 AI 网关项目比较常用的组合。如果你用 Python FastAPI 或 Go核心设计思路同样适用只是代码示例需要按语言迁移。操作系统LinuxCentOS 7/Ubuntu 20.04本地开发可 Windows/macOS。JDK17 及以上。构建工具Maven 3.8。中间件Redis 6.x 以上建议使用 Redis Cluster。消息队列RabbitMQ 或 RocketMQ用于生图异步任务。数据库MySQL 8.x用于保存账号池、费用记录、任务状态。版本需要根据你的项目实际情况调整本文示例以常见环境为例重点演示配置思路。如果你的 Spring Boot 版本不同个别注解和依赖坐标需要按官方文档更新。3.2 项目基础依赖dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-webflux/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-redis/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-aop/artifactId /dependency dependency groupIdcom.alibaba.fastjson2/groupId artifactIdfastjson2/artifactId version2.0.48/version /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency /dependenciesWebFlux 主要用于生图服务的异步任务推送和 SSE 流式对话转发。实际项目中如果不想引入 WebFlux也可以用 Spring MVC 的异步请求 SseEmitter效果接近。3.3 项目基础配置server: port: 8080 spring: application: name: ai-gateway data: redis: cluster: nodes: 10.0.0.11:6379,10.0.0.12:6379,10.0.0.13:6379 timeout: 3000ms lettuce: pool: max-active: 50 max-idle: 20 min-idle: 5 gateway: # 统一请求前缀用于区分业务线 prefix: /api/v1 # 默认超时时间 timeout: 60s # 生图任务超时时间 image-timeout: 120sRedis 使用 Lettuce 连接池超时时间不要设置太长避免某个 Redis 节点抖动导致请求线程堆积。后续缓存治理中我们会专门处理超时和降级。4. 核心模块一对话服务设计与实现4.1 统一对话协议对外暴露的对话接口不直接透传供应商参数而是封装成内部协议。前端只需要传模型名、消息列表和参数即可public class ChatRequest { private String model; // 模型标识如 gpt-4o、claude-3-5-sonnet、qwen-max private ListMessage messages; // 对话消息列表 private Double temperature; // 温度参数 private Integer maxTokens; // 最大生成 token 数 private Boolean stream; // 是否流式输出 } public class Message { private String role; // system / user / assistant private String content; }4.2 路由与号池选择收到请求后路由层先根据 model 找到对应的渠道配置再从该渠道下的号池中选择一个健康 Key。选择策略我们默认使用加权随机权重在渠道管理后台配置。Service public class ChatRouter { Resource private AccountPoolManager accountPoolManager; public ChannelAccount selectAccount(String model) { // 根据模型名获取可用渠道 ListString channelIds channelConfigService.getChannelsByModel(model); // 从号池中按权重选一个可用账号 return accountPoolManager.selectAccount(channelIds); } }号池选择的关键不是随机而是必须在选号时判断账号当前是否可用、是否触发限流、是否处于冷却期。我们稍后会单开一节讲号池调度。4.3 流式对话转发对话接口最大的特点就是流式输出。用户发出的请求需要以 SSEServer-Sent Events方式把 token 逐个推给前端。如果用普通 HTTP 同步等待用户会感觉“卡住”体验很差。RestController RequestMapping(/api/v1/chat) public class ChatController { private final ChatService chatService; public ChatController(ChatService chatService) { this.chatService chatService; } PostMapping(/completions) public SseEmitter completions(RequestBody ChatRequest request) { SseEmitter emitter new SseEmitter(0L); chatService.streamChat(request, emitter); return emitter; } }ChatService 内部调用供应商适配层把上游返回的流式数据实时转发到 SseEmitterService public class ChatService { public void streamChat(ChatRequest request, SseEmitter emitter) { // 1. 先查缓存如果命中直接返回缓存结果 String cacheKey CacheKeyBuilder.buildChatKey(request); String cached cacheService.get(cacheKey); if (StringUtils.hasText(cached)) { sendCacheResult(emitter, cached); return; } // 2. 选择渠道和账号 ChannelAccount account router.selectAccount(request.getModel()); // 3. 调用供应商适配器流式返回 adapterInvoker.streamChat(request, account, new StreamCallback() { Override public void onToken(String token) { try { emitter.send(SseEmitter.event().data(token)); } catch (IOException e) { // 客户端断开终止上游调用 adapterInvoker.cancel(request.getRequestId()); } } Override public void onComplete(String fullText) { // 4. 完整结果写缓存 cacheService.set(cacheKey, fullText, CacheTtl.CHAT); emitter.complete(); } Override public void onError(Throwable t) { emitter.completeWithError(t); } }); } }这里要注意 SseEmitter 的超时时间设置为 0L表示不自动超时由我们自己控制结束时机。客户端断开后必须调用上游取消接口否则会造成供应商侧资源浪费还可能产生费用。4.4 对话缓存策略对话请求是否适合缓存这是很多团队纠结的点。我们实际落地中采用“混合策略”完全相同的请求直接命中缓存不再调用供应商。system 相同、user 内容语义相似但没有完全相同文本的不做强缓存只做提示词优化。流式请求边生成边返回但完整结果会缓存一份后面对相同请求再次访问时直接返回。为了实现“相同请求”的缓存命中我们需要对请求做标准化排序避免因为 messages 数组顺序不同导致缓存不命中。public class CacheKeyBuilder { public static String buildChatKey(ChatRequest request) { // 模型名小写 消息序列化 参数序列化 String model request.getModel().toLowerCase(); String messagesJson JSON.toJSONString(request.getMessages()); String param request.getTemperature() _ request.getMaxTokens(); String raw model | messagesJson | param; // 使用 SHA-256 压缩 key 长度 return DigestUtils.sha256Hex(raw); } }这样设计后相同请求第二次访问的延迟从秒级降到了毫秒级。后面缓存治理部分还会详细讲命中率监控。5. 核心模块二生图服务设计与实现5.1 生图与对话的区别生图服务和对话服务有一个本质区别生图是异步任务。文生图模型通常需要几十秒甚至更久HTTP 长连接直接等待不现实。我们的方案是任务化提交生图任务后立即返回 taskId前端轮询任务状态生成完成后通过通知或轮询拿到图片 URL。5.2 异步任务流程Service public class ImageTaskService { public ImageTask submit(ImageGenRequest request) { ImageTask task new ImageTask(); task.setTaskId(UUID.randomUUID().toString().replace(-, )); task.setStatus(TaskStatus.PENDING); task.setModel(request.getModel()); task.setPrompt(request.getPrompt()); task.setCreateTime(LocalDateTime.now()); // 保存任务状态 imageTaskMapper.insert(task); // 发送到消息队列异步执行 rabbitTemplate.convertAndSend(ai.image.task, JSON.toJSONString(task)); return task; } }消费者收到任务后先查结果缓存如果之前生成过完全相同的 prompt直接复用图片 URL不再调用供应商接口。这一点对生图成本控制非常重要。Component public class ImageTaskConsumer { RabbitListener(queues ai.image.task) public void onMessage(String message) { ImageTask task JSON.parseObject(message, ImageTask.class); // 1. 查结果缓存 String cacheKey img:result: DigestUtils.sha256Hex(task.getPrompt() | task.getModel()); String cachedUrl cacheService.get(cacheKey); if (StringUtils.hasText(cachedUrl)) { finishTask(task, cachedUrl); return; } // 2. 选择账号并调用生图供应商 ChannelAccount account accountPoolManager.selectAccount(task.getModel()); String imageUrl imageProvider.generate(task, account); // 3. 写缓存TTL 根据业务需要设置 cacheService.set(cacheKey, imageUrl, CacheTtl.IMAGE); finishTask(task, imageUrl); } }生图缓存不仅缓存最终图片 URL还可以对供应商的“中间结果”做缓存。比如 SD 的放大参数、修复参数如果变化不大可以按 prompt参数 组合做结果级缓存减少重复计算成本。5.3 生图模型路由生图模型路由和对话路由类似但更需要注意的是图片模型之间的差异很大SD 开源模型适合私有化部署成本低但画质需要调优。Midjourney 类渠道画质好但需要通过第三方接口中转形态多样。ComfyUI 工作流适合复杂图生图、局部重绘等场景。所以生图路由我们增加了“能力标签”概念。每个模型配置里声明支持的能力路由时先过滤掉不满足能力的渠道再按权重选择。比如用户传了 reference_image 参数路由时只选择支持图生图的渠道避免请求发过去后报参数错误。gateway: channels: - id: sd-local type: image capabilities: [ txt2img, img2img, inpaint ] weight: 10 - id: mj-proxy type: image capabilities: [ txt2img, upscale ] weight: 56. 核心模块三Pro 号池与调度策略6.1 号池的本质很多文章把“号池”描述得很神秘其实它就是一个账号/Key 的集合管理模块。在 AI 网关里一个“号”代表一个可调用的凭证可能是 OpenAI 的 API Key也可能是某个聚合平台的 Token。号池要解决的核心问题是多个凭证如何分配、如何保证高可用、如何避免单个凭证超限导致整个服务不可用。我们提到的 Pro 号本质上是一个配额更高、稳定性更好的凭证。号池服务负责在所有可用凭证之间做调度并自动处理故障切换。6.2 账号状态机每个账号都有状态流转AVAILABLE可用 ↓ 调用失败/触发限流 COOLING冷却中 ↓ 冷却时间结束 AVAILABLE可用 ↓ 连续失败达到阈值 DISABLED禁用 ↓ 人工干预/定时探活成功 AVAILABLE可用6.3 核心实现Service public class AccountPoolManager { Resource private ListAccountHealthChecker healthCheckers; public ChannelAccount selectAccount(String model) { // 获取该模型下所有可用账号 ListChannelAccount accounts accountRepository.findAvailableByModel(model); if (CollectionUtils.isEmpty(accounts)) { throw new BizException(当前模型无可用的渠道账号); } // 过滤掉冷却中和禁用的账号 ListChannelAccount candidates accounts.stream() .filter(a - a.getStatus() AccountStatus.AVAILABLE) .collect(Collectors.toList()); // 加权随机选择一个 return weightedRandom(candidates); } public void reportFailure(String accountId, String errorCode) { ChannelAccount account accountRepository.findById(accountId); if (isRateLimitError(errorCode)) { // 触发了限流进入冷却状态 account.setStatus(AccountStatus.COOLING); account.setCoolingUntil(LocalDateTime.now().plusMinutes(5)); accountRepository.update(account); } else if (account.getFailCount() 5) { // 连续失败5次禁用账号 account.setStatus(AccountStatus.DISABLED); accountRepository.update(account); } else { account.setFailCount(account.getFailCount() 1); accountRepository.update(account); } } }这里有个细节账号进入冷却状态后不能只等时间结束应该有一个异步探活线程主动检测账号是否恢复。恢复的判断方式是发一个最小的测试请求比如对话模型发送“ping”生图模型发送一个极小的生成任务。探活成功则立即恢复 AVAILABLE避免可用账号白白浪费。6.4 号池配额与限流号池在对外服务时还要做到“配额隔离”。比如 A 业务线使用了 100 万 tokenB 业务线只能使用 10 万 token两者不能互相挤占。我们在网关层按业务线账号维度做了令牌桶限流Component public class QuotaLimitService { // key: bizId:accountId, value: 剩余配额 public boolean tryAcquire(String bizId, String accountId, int tokens) { String key quota: bizId : accountId; Long remain redisTemplate.opsForValue().decrement(key, tokens); if (remain null || remain 0) { // 配额不足回滚 redisTemplate.opsForValue().increment(key, tokens); return false; } return true; } }配额扣减和回滚不是原子的严格场景下需要用 Lua 脚本保证原子性。实际项目里我们使用 Lua 脚本完成“检查额度、扣减、回滚”逻辑。7. 缓存稳定性把命中率做到 95% 的治理实践7.1 缓存分层与 key 设计缓存是这套系统降本增效的核心。我们的缓存分为三层层级存储位置适用场景命中耗时L1 本地缓存JVM 内存Caffeine热点 Prompt、账号状态 1msL2 Redis 缓存Redis Cluster对话完整结果、生图结果、限流计数1-5msL3 数据库/对象存储MySQL / OSS长期保存的任务记录、图片 URL10msKey 设计遵循“业务域:对象:标识:版本”的规则例如chat:result:sha256(标准化请求) img:result:sha256(prompt|model|params) account:status:acct_123456 quota:bizA:acct_123456统一前缀方便在 Redis 中按业务域排查问题也方便后续做数据迁移。7.2 Token 缓存命中和不命中在大模型调用场景里Token 级缓存是一个容易被忽略但收益很高的优化点。对于 Prompt 中固定不变的前缀部分例如系统提示词、上下文模板它们每次请求都要发送给模型实际上这部分 token 在供应商侧是可以缓存的。在我们自建网关里对 Token 的缓存命中做了两层处理缓存 Key 复用上下文前缀相同、只有尾部问题不同的请求尽量复用前缀的拼接结果减少重复处理。完全缓存的请求不消耗 Token如果请求和之前完全一样直接从缓存返回不调用供应商也就不产生 Token 费用。这里的命中和不命中直接影响成本与延迟。命中缓存时请求耗时平均在 50ms 以内不命中时加上模型推理时间通常需要 1-10 秒。所以我们做了一个“Token 缓存命中率看板”按模型、按业务线分别展示命中率低于阈值会触发告警。7.3 缓存穿透、击穿、雪崩治理命中率做到 95% 不算难难的是高命中率下依旧稳定。这里必须处理三个经典问题缓存穿透反复请求一个一定不存在的结果每次都会打到供应商层。解决方式是布隆过滤器 空值缓存。public String getChatCache(String key) { String value cacheService.get(key); if (StringUtils.hasText(value)) { return value; } // 空值缓存防止穿透 if (cacheService.hasEmptyMark(key)) { return null; } return null; }缓存击穿某个热点 key 过期瞬间大量请求同时打到上游。解决方式是互斥锁重建缓存只允许一个线程去调用供应商其他线程等待。public String getOrLoad(String key, SupplierString loader) { String value cacheService.get(key); if (StringUtils.hasText(value)) { return value; } String lockKey lock: key; boolean locked redisTemplate.opsForValue().setIfAbsent(lockKey, 1, Duration.ofSeconds(5)); if (locked) { try { value loader.get(); cacheService.set(key, value, CacheTtl.DEFAULT); return value; } finally { redisTemplate.delete(lockKey); } } else { // 等待后重试 try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } return getOrLoad(key, loader); } }缓存雪崩大量 key 在同一时间过期。解决方式是 TTL 增加随机偏移量让过期时间均匀分布。public class CacheTtl { public static Duration randomTtl(Duration base) { long randomSeconds ThreadLocalRandom.current().nextLong(0, 60); return base.plusSeconds(randomSeconds); } }7.4 缓存命中率监控与告警没有监控的命中率没有意义。我们在网关层封装了统一的缓存访问切面自动记录每个 key 的命中状态Aspect Component public class CacheMonitorAspect { private final MeterRegistry meterRegistry; Around(annotation(cacheable)) public Object around(ProceedingJoinPoint pjp, Cacheable cacheable) throws Throwable { boolean hit true; long start System.currentTimeMillis(); try { return pjp.proceed(); } catch (CacheMissException e) { hit false; throw e; } finally { long cost System.currentTimeMillis() - start; meterRegistry.counter(cache.hit.total, cache, cacheable.cacheName(), hit, String.valueOf(hit)) .increment(); meterRegistry.timer(cache.cost, cache, cacheable.cacheName()) .record(Duration.ofMillis(cost)); } } }通过这些指标我们在 Grafana 上配置了“缓存命中率趋势图”和“缓存耗时分布图”。一旦某个模型的命中率连续 5 分钟低于 90%就推送告警到企业微信/钉钉运维需要立刻排查是不是 key 设计变了或者缓存被误清理了。7.5 缓存稳定性保障缓存系统稳定性比命中率优先。Redis 万一抖动不能拖垮整个 AI 网关。我们做了以下降级策略Redis 读取超时时间设置为 200ms超时直接跳过缓存调用上游。Redis 不可用时本地 Caffeine 缓存继续工作保护热点数据。本地缓存容量限制避免 OOM。Caffeine 配置最大条目数和空闲淘汰。Configuration public class LocalCacheConfig { Bean public CacheString, String localCache() { return Caffeine.newBuilder() .maximumSize(10_000) .expireAfterWrite(Duration.ofMinutes(30)) .recordStats() .build(); } }对公服务最怕的是下游模型供应商正常业务却因为缓存中间件挂掉而不可用。所以缓存必须设计为“旁路依赖”缓存不可用时直接直连供应商保证核心业务可用。8. 常见问题与排查思路8.1 常见问题表格问题现象常见原因解决思路不同用户请求相同 Prompt缓存命中率仍然很低key 里包含了用户 ID、时间戳等无关参数标准化 key只保留模型、消息、参数维度流式对话出现乱序或丢失 tokenSseEmitter 并发发送导致对每个 emitter 加锁使用阻塞队列生图任务一直 PENDING消费者不消费RabbitMQ 队列堆积或消费者异常检查消费者日志查看队列积压数量账号被限流后服务恢复时间过长只依赖冷却时间恢复增加异步探活线程主动探测并恢复Redis 抖动导致上游调用高延迟缓存超时时间设置过长缩短 cache timeout加入本地缓存降级token 缓存明明配置了却不生效请求体 messages 顺序不一致标准化消息顺序按 rolecontent 排序生图结果缓存命中但图片 URL 失效对象存储过期清理生图 URL 使用 CDN 绝对地址并设置合理 TTL对公接口偶发 5xx账号切换时没有做重试增加幂等重试切换备用渠道8.2 排查清单如果你遇到缓存命中率上不去的问题按下面顺序排查确认业务请求是否真的存在重复。如果用户每次都输入不同 Prompt缓存命中率低是正常的。检查缓存 key 的生成逻辑排除用户 ID、时间戳等干扰项。确认 Redis 缓存是否被提前清理检查 TTL 设置是否过短。看监控面板确认是 L1 未命中还是 L2 未命中。抓取几个典型请求对比标准化后的 key 是否真的相同。如果遇到渠道账号频繁被限流的问题优先检查号池是否均衡分配了请求而不是一直打到同一个账号上。权重随机算法要配合账号的实时健康状态避免把请求路由到已经快触达限额的账号。9. 最佳实践与工程建议9.1 配置管理AI 网关涉及大量渠道配置、模型参数、号池信息这些都属于敏感配置。建议使用 Nacos 或 Apollo 做配置中心模型权重、渠道启停支持动态刷新。账号密钥加密存储禁止明文保存在数据库或配置文件里。配置变更要走发布流程生产环境需要审批和回滚方案。多环境隔离dev、test、prod 使用不同的配置命名空间。9.2 日志与链路追踪每次请求都要能追踪到完整的调用链生成 requestId在网关入口注入。日志中记录模型、渠道、账号、耗时、token 消耗、缓存命中状态。使用 MDC 把 requestId 写入日志上下文。对公服务建议接入 SkyWalking 或 Zipkin拿到跨服务调用链。9.3 安全边界网关层必须做身份认证不能裸暴露大模型 API。对不同业务线做配额隔离防止某个业务线无限调用拖垮整体服务。Prompt 内容做合规过滤涉及敏感信息需要拦截。生图内容也需要做审核不能只依赖模型供应商。9.4 生产环境注意发布时先灰度一个渠道确认稳定再全量。账号池容量要预留 20% 以上余量避免批量账号失效时无号可用。缓存命中率告警和上游供应商故障告警要分开避免误报。定期做压测验证高并发下的缓存策略和号池切换能力。9.5 成本治理一个站搞定全模型 AI很重要的价值是成本可控。我们建议对每个业务线、每个模型、每个账号分别做费用统计月度汇总后分析成本构成。缓存命中率与成本之间有直接关系每提升 5% 的命中率重复请求费用就能减少一大块。10. 总结围绕“一个站搞定全模型 AI”本文从工程角度拆解了 AI 网关的核心模块统一对话服务、异步生图服务、Pro 号池调度和多层缓存治理。关键点可以归纳为三条第一协议统一是基础。上游业务只跟你的网关打交道不直接对接任何模型供应商这样后续新增模型、切换渠道对业务无感。第二号池调度是稳定性的保障。账号健康检查、自动冷却、自动恢复、加权随机分配这些机制保证了单个账号限流或失效时系统不受影响。第三缓存命中率是成本和性能的胜负手。通过标准化请求 key、分层缓存、防穿透防击穿防雪崩、命中率监控完全可以把重复请求的缓存命中率稳定在 95% 以上同时保证缓存组件故障时核心链路不中断。下一步建议你先从对话模块入手把统一协议和号池调度跑通再逐步接入生图和缓存治理。关于多级缓存、流式转发、账号探活这几个模块都可以单独深入优化。实际项目里踩过坑的同学会发现网关层做得好不好直接决定后面模型越接越多时是越来越顺还是越来越乱。
返回列表