大模型 API 返回 500/502/503 时,业务系统应该如何兜底?

📅 2026/7/6 3:07:04
大模型 API 返回 500/502/503 时,业务系统应该如何兜底?
业务系统接入大模型 API最怕的不是输出质量差而是调用链路突然冒出个 5xx 错误。几分钟的接口瘫痪要是没有任何兜底机制很可能就变成用户面前的白屏、超时甚至订单丢失。很多开发团队把“重试”当万能药却不知道错误的重试反而会雪上加霜。这篇文章站在业务开发者的角度梳理一套从“错误接收”到“错误管理”的完整方案覆盖重试、熔断、降级、超时控制、用户提示和监控告警目标是让系统在 API 波动时依然能撑得住。一、收到 500/502/503先判断“能不能重试”不是所有 5xx 都值得重试错误的重试甚至会放大问题。我们需要按状态码和响应体里的错误类型来做决策。1.1 常见 5xx 状态码的含义和重试建议状态码典型含义重试建议500 Internal Server Error服务器内部异常可能是代码 bug 或临时故障可以重试但建议带退避502 Bad Gateway网关或代理层收到了上游无效响应可以重试大概率是临时网络抖动503 Service Unavailable服务器过载或正在维护可以重试但需要考虑负载504 Gateway Timeout上游超时没响应可以重试建议加大超时阈值后再重试不可重试的情况400参数错误、401/403鉴权失败、404请求路径错误这类客户端错误重试一百次也没用。另外如果 429限流响应的Retry-After头部明确指定了等待时间那就按它指示的等不然频繁重试只会加剧限流。1.2 错误体中的 code/type 字段很多大模型 API 在 JSON 响应体里会带error.type或error.code比如 OpenAI 兼容接口的rate_limit_exceeded、insufficient_quota。建议在代码里统一解析建立一个“可重试错误列表”。举个例子server_error、timeout、service_unavailable放行重试invalid_request_error、authentication_error直接抛出异常。二、重试策略从“稍等重试”到“指数退避 抖动”2.1 为什么不能固定间隔重试固定间隔重试比如每隔 1 秒重试 3 次会引发“惊群效应”——所有失败的请求在同一时刻集中发起很容易把已经脆弱的服务打垮。业界常用的办法是指数退避。基础公式wait_time base_delay * (2 ^ attempt_count)第 0 次失败后等 1 秒第 1 次等 2 秒第 2 次等 4 秒……但这样重试间隔完全确定多个请求仍然可能同时发出。引入随机抖动可以有效打散重试时间点。带抖动的公式wait_time min(cap, base_delay * (2 ^ attempt_count)) * random.uniform(0.5, 1.5)另外还要设置最大重试次数通常 35 次和总超时上限比如整个重试过程不超过 30 秒避免重试链把业务线程拖死。2.2 Python 实现示例用 tenacity 库fromtenacityimportretry,stop_after_attempt,wait_exponential,retry_if_exception_typeimportrequestsdefis_retryable_error(exception):# 根据响应状态码或错误体判断ifisinstance(exception,requests.exceptions.Timeout):returnTrueifisinstance(exception,requests.exceptions.HTTPError):respexception.responseifresp.status_codein(500,502,503,504):returnTrue# 解析 body 中的 error.typetry:bodyresp.json()ifbody.get(error,{}).get(type)in(server_error,service_unavailable):returnTrueexcept:passreturnFalseretry(stopstop_after_attempt(4),waitwait_exponential(multiplier1,min1,max16),retryretry_if_exception_type((requests.exceptions.Timeout,requests.exceptions.HTTPError)),retry_state_callbackis_retryable_error)defcall_llm(prompt):resprequests.post(,json{...},timeout(5,30))resp.raise_for_status()returnresp.json()2.3 Java 实现示例用 Spring RetryRetryable(value{HttpServerErrorException.class,SocketTimeoutException.class},maxAttempts4,backoffBackoff(delay1000,multiplier2,maxDelay16000,randomtrue))publicStringcallLLM(Stringprompt){// 调用 restTemplate 并判断状态码ResponseEntityStringresponserestTemplate.exchange(url,HttpMethod.POST,request,String.class);if(response.getStatusCode().is5xxServerError()){// 根据 body 中的错误类型进一步过滤thrownewHttpServerErrorException(response.getStatusCode());}returnresponse.getBody();}三、超时控制别让接口卡死整个业务没有超时限制的重试等于“死循环”。大模型 API 的响应时间波动很大5 秒到 60 秒不等必须分层设置超时。3.1 三层超时推荐值连接超时Connect Timeout5 秒。网络不通就应该快速失败。读取超时Read Timeout3060 秒。模型生成长文本时可能需要较长时间但超过 60 秒通常就算异常。写入超时Write Timeout10 秒。向服务端发送请求体不应该耗时太长。3.2 超时异常的处理超时属于可重试异常但应该计入重试次数。比如第一次请求超时第二次重试时可以临时放大读取超时到 120 秒避免因为模型处理慢导致反复超时。如果连续两次超时那就不再重试直接进入降级流程。四、熔断机制防止雪崩重试只能应对瞬时抖动当 API 持续报错时比如服务正在宕机或网络断开无休止的重试只会浪费资源。熔断器模式可以在失败率达到阈值时快速切断调用让系统“喘口气”。4.1 熔断器状态模型Closed关闭正常状态请求直接通过。失败计数递增。Open打开失败率超过阈值比如 50%后进入。此时所有请求立即失败不再调用真实 API。Half-Open半开经过一段冷却时间比如 30 秒允许少量请求通过测试服务是否恢复。如果成功就切回 Closed否则继续 Open。4.2 常用实现库Pythonpybreaker或circuitbreaker。JavaResilience4j轻量级支持 Spring Boot 集成。Python 示例用 circuitbreakerfromcircuitbreakerimportcircuitcircuit(failure_threshold5,recovery_timeout30,expected_exceptionrequests.exceptions.RequestException)defcall_llm_safe(prompt):returncall_llm(prompt)当连续 5 次失败后熔断器打开直接抛出CircuitBreakerError。业务层捕获这个异常后执行降级逻辑。五、降级方案兜底的最后防线即使重试和熔断都没能恢复也不能让用户看到错误页面。降级的目标是“有损服务但不断服”。5.1 降级方案金字塔缓存最近一次成功响应如果用户请求和上次成功请求相似比如相同的 Prompt直接返回缓存结果。注意缓存时效性建议给每个缓存项设置过期时间比如 5 分钟。切换备用模型如果主 API 不可用自动切换到备用的另一个大模型比如从 GPT-4 切换到 GPT-3.5-turbo或者切换到第三方兼容接口。切换逻辑应该在异步健康检查中完成避免熔断后立即切换造成二次冲击。异步队列暂存对于非实时场景比如日报生成、数据总结把请求放进消息队列等服务恢复后异步处理再通过回调通知用户。静态降级回复最后兜底——返回固定文案比如“服务正忙请稍后再试”或“智能助手暂时离线您可以直接联系人工客服”。前端要根据场景展示友好界面而不是代码 500。5.2 降级调用流程示例伪代码defhandle_request(prompt):try:resultcall_llm_with_circuit(prompt)cache.set(prompt_hash,result,ttl300)returnresultexcept(CircuitBreakerError,MaxRetryExceeded):cachedcache.get(prompt_hash)ifcached:returncached# 尝试备用模型try:resultcall_backup_llm(prompt)returnresultexceptException:return{content:服务正忙请稍后再试,fallback:True}六、用户侧体验优化错误不能只丢在后台前端需要感知到后端发生了什么但不能把原始错误码暴露给用户。6.1 给用户的信息原则清晰但不恐慌显示“服务繁忙正在自动重试”或“生成时间稍长请耐心等待”千万别出现“500 Internal Server Error”。提供操作入口如果降级为静态回复展示重试按钮点击后重新触发调用流程如果正在重试中显示进度动画或倒计时。区分场景流式输出场景下如果中间中断应该告知“部分内容已生成剩余内容稍后补充”或者提供重新生成选项。6.2 前端与后端的约定后端在响应体中增加字段比如{content:...,is_fallback:true,fallback_reason:model_unavailable}前端根据这个决定要不要展示重试按钮、要不要隐藏遮罩层等。七、日志与监控让兜底体系可观测没有监控的兜底等于“黑盒飞行”。必须记录每次异常的上下文并设置告警阈值。7.1 需要记录的字段字段说明request_id全局唯一 ID串联链路api_endpoint具体调用的模型地址status_codeHTTP 状态码error_type错误体中的 type/coderetry_count本次请求的重试次数total_duration_ms从发起到最终成功或失败的总耗时fallback_used是否触发了降级user_id脱敏方便定位特定用户问题7.2 告警阈值建议5xx 错误率 1%触发 Warning 5% 触发 Critical。熔断器打开次数在 5 分钟内超过 3 次说明 API 长期不稳定需要人工介入。重试成功率 80%说明重试后仍然大量失败可能需要调整退避策略或检查备用通道。推荐用 Prometheus AlertManager 采集指标日志上送到 ELK 或 Loki 做离线分析。代码里可以用装饰器自动埋点避免手动打日志。八、整体兜底流程概览下面是一个参考的调用决策链以 Python 风格描述发起请求前启动全局超时计时器比如 30 秒。检查熔断器状态如果 Open直接走降级如果 Half-Open只允许一次探测请求。调用 API设置连接超时 5 秒读取超时 30 秒。如果抛出超时或 5xx 异常判断异常是否可重试状态码错误类型。若可重试且没超过最大重试次数按指数退避等待后重试。若不可重试或已耗尽重试次数记录日志更新熔断器失败计数。如果熔断器打开或重试最终失败尝试从缓存读取然后尝试备用模型最后返回降级文案。不管成功还是失败统一记录耗时、重试次数、降级标志到日志和指标。这个流程涵盖了从“错误发生”到“用户感知”的完整闭环而且每一层都有明确的退出条件不会出现无限等待或重复调用。总结大模型 API 的 5xx 错误是业务系统必须承受的“常态”但通过合理的容错方案完全可以把影响降到最低。这篇文章介绍了可重试错误判断、指数退避重试、熔断器、多级降级、用户提示优化以及监控告警的落地方法。建议开发时先从“不重试 静态降级”的简单方案开始然后逐步引入重试和熔断每次变更都要配合监控验证效果。兜底不是万能的但没有兜底的业务系统在 API 波动面前是不堪一击的。希望这篇文章能帮你构建一套真正可用的业务系统 API 容错方案。