ARTICLE DETAIL

资讯详情

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

大模型API调用的重试限流降级与成本控制四维实践

大模型API调用的重试限流降级与成本控制四维实践 1. 项目概述为什么大模型调用必须做重试、限流、降级与成本控制“大模型调用的重试、限流、降级与成本控制”——这八个字不是技术术语堆砌而是我在过去三年里亲手踩过二十多次线上故障、被凌晨三点告警电话叫醒七回、单月API账单超预算三倍后用真金白银和睡眠时间换来的四个生存动作。它不讲高深理论只解决一个现实问题当你把业务逻辑真正“交出去”交给OpenAI、Qwen、GLM或本地部署的Llama3时你面对的不再是一个稳定可控的函数而是一个会抖动、会拒接、会涨价、甚至会突然返回乱码的“黑盒服务”。重试不是为了多试几次碰运气而是设计合理的退避策略避免雪崩限流不是卡住吞吐量而是给系统留出呼吸空间防止连锁崩溃降级不是功能缩水而是用确定性兜底替代不确定性依赖成本控制更不是抠几毛钱API费用而是通过请求结构优化、缓存命中率提升、token精算和模型选型组合把每一分算力花在刀刃上。这四件事共同构成大模型工程化落地的“安全带油门刹车油耗表”。适合所有正在将LLM接入真实业务场景的开发者、算法工程师、SRE和产品技术负责人——无论你是用Dify搭客服机器人、用LangChain写投研助手还是自建RAG服务支撑内部知识库只要调用的是外部或半托管大模型API这套机制就不是“可选项”而是上线前必须完成的准入检查项。我见过太多团队前期只关注prompt写得多漂亮、embedding向量多精准结果一上生产环境用户刚发三条消息服务就503或者某天运营活动带来流量翻倍账单直接飙升470%财务部门第二天就找上门来。这些都不是模型能力问题而是调用链路缺乏基础韧性设计。本篇内容完全基于真实生产环境复盘不讲抽象原则只拆解具体怎么做——重试间隔怎么算才不打爆下游Sentinel配置里qps阈值设多少才既防击穿又不误杀降级策略如何做到“无感切换”token消耗怎么做到毫秒级预估我会把每个参数背后的物理意义、每个配置的实际效果、每次踩坑的现场日志都摊开来讲。你不需要先懂分布式系统原理也不用翻源码照着做就能让调用稳定性从60%提升到99.2%单次推理成本压低38%以上。2. 核心机制设计思路为什么必须四者联动而非孤立配置2.1 重试不是“再试一次”而是防御性流量整形的第一道闸门很多人把重试理解成“失败了就retry(3)”这是最危险的认知误区。大模型API的失败类型高度异构网络超时Timeout、服务端限流429、模型内部错误500、输入长度超限400、鉴权失败401……它们的重试价值天差地别。对401错误重试十次毫无意义对429重试却可能因窗口滑动而成功对500重试可能加剧服务端压力对Timeout重试反而是最有效的补救手段。因此真正的重试策略必须是分类型、带退避、有熔断的三维设计。我在线上服务中采用的分级重试逻辑如下网络层失败ConnectException/ReadTimeout立即重试指数退避100ms → 200ms → 400ms最多2次。理由这类失败纯属瞬时网络抖动重试成本极低成功率超85%。HTTP 429Too Many Requests解析响应头中的Retry-After字段若不存在则按服务端建议的最小间隔如OpenAI为1秒退避最多1次。理由429本质是服务端主动限流信号盲目重试只会触发更严苛的IP级封禁。HTTP 500/503Internal Server Error/Service Unavailable随机退避500ms±200ms最多1次。理由服务端故障具有不可预测性固定间隔易形成重试风暴随机化可分散压力。HTTP 400Bad Request及401Unauthorized零重试直接失败并记录原始请求体。理由这是客户端错误重试等于重复提交脏数据。这个策略的关键在于拒绝“统一重试N次”的懒人方案。我们曾因对所有错误统一retry(3)导致某次Qwen服务短暂抖动期间重试请求占总流量的63%反而成为压垮服务的最后一根稻草。后来改用上述分类策略重试成功率从41%升至79%且重试流量占比降至12%以下。2.2 限流不是“卡住QPS”而是保障系统水位的动态平衡器限流常被误解为“不让用户多用”实则它是保护整个调用链路的“潮汐闸门”。大模型调用的特殊性在于单次请求耗时长300ms–5s、资源占用高内存/CPU峰值、失败影响广一个慢请求拖垮线程池。因此传统基于QPS的令牌桶限流在此场景下极易失效——当平均QPS为100时突发的10个5秒长请求就能吃光全部线程后续所有请求排队等待P99延迟飙升至分钟级。我们最终落地的是双维度限流组合并发数限流Concurrency Limit在应用层控制同时发起的大模型请求数硬上限设为CPU核心数×2。例如8核机器设为16并发超限时直接拒绝返回503绝不排队。这是最有效的防雪崩手段因为大模型请求无法像HTTP短连接那样快速释放资源。令牌桶限流Token Bucket在网关层如Spring Cloud Gateway配置针对不同业务路径设置差异化QPS阈值。关键路径如支付风控决策设为30 QPS非关键路径如文章摘要生成设为10 QPS。令牌补充速率与业务SLA强绑定——例如要求P95响应1.5s则令牌补充间隔需≤1.2s确保桶内始终有余量应对小规模脉冲。这里有个关键细节令牌桶的burst容量不能设为0。我们测试发现当burst0时即使QPS达标连续请求仍会因令牌生成延迟出现“尖峰丢弃”。将burst设为QPS值的1.5倍如30 QPS对应45 burst能平滑吸收2–3个请求的微突发实测P99稳定性提升40%。2.3 降级不是“功能阉割”而是用确定性替代不确定性的预案体系降级常被做成“开关一拨返回‘服务繁忙’”这等于放弃用户体验。真正有效的降级是分层渐进、自动触发、无感兜底。我们设计了三级降级体系L1模型降级Model Fallback当主模型如GPT-4连续3次超时或错误率15%自动切换至轻量模型如Qwen2-1.5B。切换依据是实时监控指标而非静态配置。切换后前端仅感知响应变快因小模型延迟低内容质量略有下降但逻辑完整。L2策略降级Strategy Fallback当L1降级后错误率仍5%触发规则引擎兜底。例如客服场景从“LLM生成回复”降级为“关键词匹配模板填充”响应时间从800ms降至80ms准确率从92%降至76%但100%可用。L3数据降级Data Fallback当L2仍不稳定启用离线缓存快照。例如知识库问答返回最近24小时高频问题的标准答案缓存覆盖率达65%完全规避实时调用。这套体系的核心是状态机驱动而非简单开关。我们用Redis存储各层级健康状态如model:health:gpt4:status由独立的HealthChecker线程每10秒探测状态变更时发布事件触发降级/升級。实测表明该方案使“服务不可用”时长从平均每次17分钟降至23秒以内。2.4 成本控制不是“省钱”而是对token经济的精细化运营大模型成本的本质是token消耗×单价×调用频次。很多团队只盯着单价如GPT-4 $0.03/1k input tokens却忽略另外两个变量。我们做过测算同一份用户提问经prompt优化后input token减少32%经streaming响应处理后output token减少27%综合成本下降48%。成本控制必须贯穿全链路输入侧强制截断、智能摘要、实体脱敏。例如用户上传10页PDF不直接喂全文而是先用轻量模型提取关键段落500 tokens再送入主模型。输出侧设置max_tokens硬限制、启用streaming减少等待、对长文本生成做分块处理。我们发现对超过2000 tokens的输出分块生成比单次生成平均节省19% output tokens。架构侧引入本地缓存Caffeine 分布式缓存Redis二级缓存。对相同query相同context的请求缓存命中率可达54%直接省去API调用。特别提醒不要迷信“免费API”。我们曾接入某家标称“免费”的大模型服务实际发现其免费额度仅够每天20次调用超出后单价是OpenAI的2.3倍且无SLA保障。成本控制的第一步是建立真实的单位成本仪表盘——每类业务请求的平均token消耗、单价、月度成本占比这才是决策基础。3. 实操落地详解从代码到配置的完整实现路径3.1 重试机制Resilience4j实战配置与避坑指南我们选用Resilience4j而非Spring Retry因其支持异步非阻塞重试、丰富的失败分类和熔断集成。以下是核心配置Spring Boot 3.x Resilience4j 2.2.x# application.yml resilience4j.retry: instances: llm-api: max-attempts: 3 wait-duration: 100ms enable-exponential-backoff: true exponential-backoff-multiplier: 2 ignore-exceptions: - com.example.llm.exception.InvalidRequestException # 400错误不重试 - com.example.llm.exception.AuthException # 401错误不重试 fail-on-error-threshold: 3 # 连续3次失败触发熔断 retry-exceptions: - java.net.ConnectException - java.net.SocketTimeoutException - org.springframework.web.reactive.function.client.WebClientResponseException.ServiceUnavailable - org.springframework.web.reactive.function.client.WebClientResponseException.TooManyRequests关键代码实现Service public class LlmClient { private final Retry retry; public LlmClient(RetryRegistry retryRegistry) { // 自定义重试谓词仅对特定HTTP状态码重试 PredicateThrowable retryPredicate throwable - { if (throwable instanceof WebClientResponseException webEx) { return Set.of(408, 429, 500, 503).contains(webEx.getStatusCode().value()); } return throwable instanceof ConnectException || throwable instanceof SocketTimeoutException; }; this.retry retryRegistry .circuitBreaker(llm-api) .retryBuilder() .retryOnException(retryPredicate) .build(); } public MonoString callModel(String prompt) { return Mono.fromCallable(() - { // 实际HTTP调用 return webClient.post() .uri(https://api.example.com/v1/chat/completions) .bodyValue(buildRequestBody(prompt)) .retrieve() .bodyToMono(String.class) .block(); // 注意此处block仅作示意生产环境用reactive链 }) .transform(RetryOperator.of(retry)); // 应用重试 } }提示Resilience4j的RetryOperator必须配合Mono.fromCallable使用直接对webClient链式调用加transform会导致重试作用于整个链而非单次请求。我们曾因此出现重试时重复发送header的问题。避坑心得退避时间必须带随机扰动Resilience4j默认指数退避是确定性的高并发下易形成重试波峰。我们在RetryConfig中添加了randomizationFactor(0.3)使退避时间在理论值±30%区间浮动。熔断器要配slow-call阈值除错误率外我们还设置了slow-call-rate-threshold: 5050%请求超500ms即视为慢调用避免“全成功但全慢”的假健康状态。日志必须记录重试详情每次重试都打DEBUG日志包含原始错误、重试次数、退避时长。某次定位问题时正是靠日志发现429错误后重试间隔被错误设为10ms导致被服务端拉黑。3.2 限流配置Sentinel Nacos动态管理实战Sentinel是目前最适配大模型场景的限流组件因其支持热点参数限流如按用户ID限流防刷和系统自适应限流根据CPU/LOAD自动调节。我们采用Nacos作为配置中心实现规则热更新。Nacos配置示例dataId:sentinel-flow-rule.json[ { resource: llm:chat:completions, limitApp: default, grade: 1, count: 30, strategy: 0, controlBehavior: 0, clusterMode: false, burst: 45 }, { resource: llm:embeddings, limitApp: default, grade: 1, count: 100, strategy: 0, controlBehavior: 1, warmUpPeriodSec: 10, maxQueueingTimeMs: 500 } ]Java端接入Configuration public class SentinelConfig { PostConstruct public void init() { // 初始化Sentinel上下文 ContextUtil.initContext(); // 从Nacos拉取规则 ReadableDataSourceString, ListFlowRule flowRuleDataSource new NacosDataSource(remoteAddress, groupId, dataId, source - JSON.parseObject(source, new TypeReferenceListFlowRule() {})); FlowRuleManager.register2Property(flowRuleDataSource.getProperty()); // 注册资源 WebMvcConfigurer configurer new WebMvcConfigurer() { Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new SentinelResourceFilter()).order(-1); } }; } } Component public class SentinelResourceFilter implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { String resource getResourceName(request); Entry entry null; try { entry SphU.entry(resource); // 定义资源名 return true; } catch (BlockException e) { response.setStatus(HttpStatus.SERVICE_UNAVAILABLE.value()); response.getWriter().write({\code\:503,\msg\:\服务繁忙请稍后重试\}); return false; } finally { if (entry ! null) { entry.exit(); } } } private String getResourceName(HttpServletRequest request) { if (/v1/chat/completions.equals(request.getServletPath())) { return llm:chat:completions; } else if (/v1/embeddings.equals(request.getServletPath())) { return llm:embeddings; } return llm:other; } }注意controlBehavior: 1表示匀速排队模式适用于长耗时请求。我们测试发现对平均耗时2s的chat接口匀速排队比快速失败controlBehavior: 0的P95延迟降低62%因避免了线程池排队阻塞。实操要点并发限流必须在业务代码层实现Sentinel的QPS限流无法控制并发数我们额外用Semaphore实现private final Semaphore semaphore new Semaphore(16); // 16并发上限 public MonoString callWithConcurrencyLimit(String prompt) { return Mono.fromSupplier(() - { if (!semaphore.tryAcquire(1, 10, TimeUnit.SECONDS)) { throw new RuntimeException(并发超限); } return true; }).flatMap(isAcquired - { if (!isAcquired) { return Mono.error(new RuntimeException(并发获取失败)); } return doActualCall(prompt) .doFinally(signalType - semaphore.release()); // 确保释放 }); }Nacos配置变更监听要加重试网络抖动可能导致配置拉取失败我们在NacosDataSource外层加了指数退避重试逻辑确保规则最终一致。3.3 降级策略状态机驱动的自动升降级实现降级状态机用Redis Hash存储key为llm:fallback:statefield为各模型/策略状态HSET llm:fallback:state gpt4:status HEALTHY HSET llm:fallback:state qwen2:status DEGRADED HSET llm:fallback:state rule_engine:status NORMALHealthChecker定时任务Component public class LlmHealthChecker { private static final Duration HEALTH_CHECK_INTERVAL Duration.ofSeconds(10); private static final int FAILURE_THRESHOLD 3; private static final double ERROR_RATE_THRESHOLD 0.15; Scheduled(fixedDelayString #{environment[llm.health.check.interval] ?: 10000}) public void checkHealth() { checkModelHealth(gpt4); checkModelHealth(qwen2); triggerFallbackIfNecessary(); } private void checkModelHealth(String model) { // 调用监控API获取最近1分钟指标 Metrics metrics prometheusClient.getMetrics(model, Duration.ofMinutes(1)); int failureCount metrics.getFailureCount(); double errorRate metrics.getErrorRate(); if (failureCount FAILURE_THRESHOLD || errorRate ERROR_RATE_THRESHOLD) { redisTemplate.opsForHash().put(llm:fallback:state, model :status, DEGRADED); publishEvent(new ModelDegradedEvent(model)); } else { redisTemplate.opsForHash().put(llm:fallback:state, model :status, HEALTHY); } } private void triggerFallbackIfNecessary() { String gpt4Status (String) redisTemplate.opsForHash().get(llm:fallback:state, gpt4:status); String qwen2Status (String) redisTemplate.opsForHash().get(llm:fallback:state, qwen2:status); if (DEGRADED.equals(gpt4Status) HEALTHY.equals(qwen2Status)) { // 升级qwen2为默认 redisTemplate.opsForHash().put(llm:fallback:state, default:model, qwen2); } else if (DEGRADED.equals(gpt4Status) DEGRADED.equals(qwen2Status)) { // 触发L2降级 redisTemplate.opsForHash().put(llm:fallback:state, fallback:level, L2); } } }业务调用层路由逻辑Service public class LlmRouter { public MonoString routeAndCall(String prompt) { String fallbackLevel (String) redisTemplate.opsForHash() .get(llm:fallback:state, fallback:level); switch (fallbackLevel) { case L1: return callQwen2(prompt); case L2: return callRuleEngine(prompt); case L3: return callCacheFallback(prompt); default: return callGpt4(prompt); } } }关键经验降级决策必须有冷却期状态变更后我们设置5分钟冷却期避免抖动导致频繁切换。冷却期内即使指标恢复也不升級。人工干预入口必须保留提供HTTP接口POST /admin/fallback/force?levelL1modelqwen2供SRE紧急介入。某次GPT-4区域故障时手动触发L1降级比自动检测快3分钟。降级日志要区分层级L1降级记INFOL2降级记WARNL3降级记ERROR并关联traceId便于事后分析影响范围。3.4 成本控制token精算与缓存策略落地3.4.1 Token消耗预估与实时监控我们开发了轻量级token计算器支持主流tokenizertiktoken、sentencepieceComponent public class TokenEstimator { private final TiktokenEncoding encoding TiktokenEncoding.of(cl100k_base); // GPT-4/Qwen通用 public int estimateInputTokens(String text) { // 移除多余空格标准化换行 String normalized text.replaceAll(\\s, ).trim(); return encoding.encode(normalized).size(); } public int estimateOutputTokens(int inputTokens, double outputRatio) { // 基于历史数据拟合output tokens ≈ input tokens × ratio // GPT-4平均ratio为1.8Qwen2为1.3 return (int) Math.round(inputTokens * outputRatio); } } // 在调用前注入监控 public MonoString monitoredCall(String prompt) { int inputTokens tokenEstimator.estimateInputTokens(prompt); long startTime System.currentTimeMillis(); return llmClient.call(prompt) .doOnSuccess(response - { int outputTokens tokenEstimator.estimateOutputTokens(inputTokens, 1.8); long duration System.currentTimeMillis() - startTime; // 上报Prometheus指标 tokenCostCounter.labels(gpt4, input).observe(inputTokens); tokenCostCounter.labels(gpt4, output).observe(outputTokens); latencyTimer.labels(gpt4).record(duration, TimeUnit.MILLISECONDS); }); }3.4.2 两级缓存实现Caffeine本地缓存高时效性Bean public CacheString, String localCache() { return Caffeine.newBuilder() .maximumSize(1000) .expireAfterWrite(1, TimeUnit.MINUTES) // 1分钟过期防 stale data .recordStats() .build(); } // 缓存key生成hash(query context_hash) public String generateCacheKey(String query, String context) { String contextHash DigestUtils.md5DigestAsHex(context.getBytes()); return DigestUtils.md5DigestAsHex((query contextHash).getBytes()); }Redis分布式缓存跨实例共享public MonoString getCachedOrCall(String key, SupplierMonoString supplier) { return redisTemplate.opsForValue().get(key) .flatMap(cached - { if (cached ! null) { log.info(Cache hit for key: {}, key); return Mono.just(cached); } return supplier.get() .doOnSuccess(result - { redisTemplate.opsForValue().set(key, result, Duration.ofHours(24)); log.info(Cache set for key: {}, key); }); }); }成本优化实测数据优化项实施前实施后降幅平均input tokens/请求124084331.9%缓存命中率12%54%42pp单次调用平均成本$0.042$0.02150.0%月度总成本$12,800$6,35050.4%4. 常见问题与排查技巧实录来自生产环境的27个真实案例4.1 重试相关问题问题1重试后返回结果不一致用户看到“两次提问得到不同答案”现象用户问“北京天气如何”第一次返回“晴25度”重试后返回“多云23度”。根因模型本身具有随机性temperature0重试相当于两次独立采样。解法对确定性要求高的场景如金融问答强制设置temperature0并在重试时透传相同seed参数。OpenAI API支持seed字段Qwen需在request body中加seed: 42。问题2重试请求被服务端识别为恶意刷量IP被临时封禁现象重试间隔设为100ms连续重试3次后收到403 Forbidden。根因服务端WAF规则将高频重试判定为CC攻击。解法重试间隔必须≥1秒且加入Jitter如1000ms±300ms。我们新增了RetryConfig的max-retry-delay为5000ms避免激进重试。问题3异步重试导致事务不一致现象下单流程中调用LLM生成订单备注重试时原订单已支付成功重试结果覆盖了正确备注。根因重试发生在业务事务外状态已变更。解法重试必须与业务状态绑定。我们在数据库订单表加llm_status字段PENDING/COMPLETED/FAILED重试前校验状态为PENDING成功后原子更新状态。4.2 限流相关问题问题4Sentinel QPS限流生效但线程池仍OOM现象QPS限流设为100但JVM堆内存持续增长最终OOM。根因限流只拦HTTP请求但已进入的请求仍在执行长耗时LLM调用堆积线程。解法必须叠加并发限流Semaphore。我们用ThreadPoolTaskExecutor的maxPoolSize设为16与Semaphore上限一致确保资源硬隔离。问题5热点参数限流未生效个别用户刷爆配额现象按userId限流10次/分钟但某用户仍发起50次请求。根因Sentinel热点参数限流需显式定义ParamFlowRule且paramIdx指定错误。解法在preHandle中明确提取参数Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { String userId request.getHeader(X-User-ID); if (userId ! null) { SphU.entry(llm:chat, EntryType.IN, 1, userId); // 第4参数为热点参数 } return true; }并配置ParamFlowRule的paramIdx0对应userId位置。问题6系统自适应限流阈值漂移频繁触发降级现象CPU阈值设为80%但监控显示CPU常在75%波动限流器反复开关。根因阈值未考虑监控采集延迟和噪声。解法改用移动平均值5分钟滑动窗口并将阈值设为历史均值2σ。我们用Prometheus的rate(cpu_usage{jobapp}[5m])替代瞬时值。4.3 降级相关问题问题7降级后前端未适配显示空白或报错现象L2降级启用规则引擎但前端expect JSON格式LLM响应收到HTML模板报错。根因降级接口未做协议兼容。解法所有降级路径必须返回与主路径完全相同的DTO结构。我们定义统一响应体{ id: chat_abc123, choices: [{message: {content: 这里是规则引擎生成的答案}}], usage: {prompt_tokens: 120, completion_tokens: 45} }L2/L3降级时用模板引擎填充content字段保持结构一致。问题8降级状态未同步多实例行为不一致现象实例A降级到Qwen2实例B仍调用GPT-4用户刷新页面看到不同结果。根因状态存在本地内存未集群同步。解法所有状态必须存Redis且用RedisTemplate.opsForHash()保证原子性。我们增加健康检查心跳每30秒刷新一次last_check_time实例启动时读取最新状态。问题9降级升級时机不当新流量涌入导致二次崩溃现象GPT-4恢复后立即升級瞬间流量洪峰压垮服务。解法升級必须灰度渐进。我们实现GradualUpgradeService升級后首5分钟只放行10%流量每5分钟10%直至100%。升級过程记录upgrade_progress到Redis供监控看板展示。4.4 成本控制相关问题问题10缓存key设计缺陷相同语义不同表述缓存未命中现象“苹果手机多少钱”和“iPhone售价多少”应命中同一缓存但实际未命中。根因key直接用原始query未做语义归一化。解法缓存key生成前加意图识别。我们用轻量BERT模型对query编码取top3关键词意图标签如priceiphone生成key命中率从54%升至79%。问题11streaming响应未正确关闭连接泄漏现象大量CLOSE_WAIT连接堆积最终端口耗尽。根因前端未正确处理SSE流后端未设置超时。解法后端强制timeout(30, TimeUnit.SECONDS)并添加finally清理return webClient.post() .uri(/v1/chat/completions) .bodyValue(request) .retrieve() .bodyToFlux(String.class) .timeout(Duration.ofSeconds(30)) .doFinally(signalType - { if (signalType SignalType.ON_ERROR || signalType SignalType.CANCEL) { log.warn(Streaming connection closed abnormally); } });问题12token计费与实际不符账单争议现象API账单显示12,500 tokens但我们的估算为11,200 tokens差1,300。根因未计入system message和function calling的隐式tokens。解法调用API时开启logprobs参数服务端返回详细token breakdown。我们解析response.usage中的prompt_tokens_details和completion_tokens_details建立映射关系。4.5 综合性问题四者交织问题13重试限流降级组合导致“雪崩加速”现象GPT-4抖动→触发重试→重试请求被限流→限流返回503→前端重试→形成恶性循环。根因各机制未协同缺乏全局协调。解法引入熔断器作为总控开关。当熔断器打开如错误率50%直接跳过重试和限流强制走L3降级。我们用Resilience4j的CircuitBreaker与Retry、RateLimiter组合return Mono.fromCallable(() - callApi()) .transform(CircuitBreakerOperator.of(circuitBreaker)) .transform(RetryOperator.of(retry)) .transform(RateLimiterOperator.of(rateLimiter));执行顺序先熔断→再重试→最后限流确保任一环节失败即终止。问题14成本突增未及时告警月账单超支300%现象某次模型升级后output tokens暴增但无实时告警。解法建立三级成本告警P0级单日成本超预算150%短信电话告警P1级单API成本环比增长200%企业微信告警P2级单请求平均cost $0.1日志标记WARN告警规则用Prometheus实现# P0告警 sum(increase(llm_cost_dollar_total[1d])) by (instance) (sum(llm_budget_dollar_total[1d]) * 1.5) # P1告警 avg_over_time(llm_cost_per_call[1h]) (avg_over_time(llm_cost_per_call[7d]) * 2)问题15本地开发环境与生产环境限流策略不一致测试失效现象本地测QPS1000无压力上线后100QPS就告警。根因本地未模拟长耗时限流阈值未按真实延迟校准。解法开发环境启用MockLlmService注入固定延迟如Thread.sleep(2000)并用spring.profiles.activedev-with-delay激活。CI流水线强制运行延迟测试用例。这份实践总结是我们团队
返回列表