ARTICLE DETAIL

资讯详情

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

LLM API 响应变慢时的超时与重试策略调优

LLM API 响应变慢时的超时与重试策略调优 当依赖的 LLM API 响应延迟升高生产环境里最典型的故障不是直接返回 5xx而是请求长时间挂起、直到客户端超时被掐断。如果超时参数设置不合理一个慢接口就能占满线程池如果重试策略设置不合理一次故障就会演变成重试风暴。这篇文章把超时、重试、退避和熔断拆成独立决策给出可落地的调优框架并说明批量处理、实时交互和 Agent 工具调用链应该如何差异化配置。把超时拆成三段而不是只设一个数值多数 HTTP 客户端允许设置三类超时含义完全不同。以 Python httpx 为例timeout httpx.Timeout( connect5.0, # 建立连接阶段 read30.0, # 两次数据读取之间的间隔 total60.0 # 整个请求总时长 )这三段对应不同失败模式连接超时网络不可达、DNS 异常、服务端不接受连接。读取超时请求已发出但服务端迟迟没有返回数据。LLM API 场景下这可能是服务端排队、推理慢或网络拥塞。总超时整个调用超出预算必须本地快速失败。对流式响应尤其需要注意读取超时的语义。流式生成过程中模型有时会停顿几秒这不代表连接已经失效。如果读取超时设得太短正常的长文本生成会被误杀。一个可行做法是设置“首字节超时”和“后续读超时”两个值首字节超时用于等待服务端开始响应可设置得较长后续读超时用于判断单次 read() 调用是否僵死可以基于实际数据到达间隔的 p99 来确定。但很多 SDK 只暴露一个 read timeout此时你需要确认它到底覆盖首字节还是覆盖整个读过程否则会配置错。超时值不是越大越好。建议先采集线上的延迟分布以 p99 加上网络往返和一定缓冲作为初始值然后持续观察超时占比。如果超时率超过 1%优先排查服务端延迟和队列堆积而不是盲目把超时调大一倍。超时是保护机制不是降低失败率的手段。重试之前先回答这次请求是否安全重放重试设计里最容易被忽略的是幂等性。客户端把请求提交到服务端服务端执行了但响应在网络中丢失此时客户端重试同一个请求应用层可能产生重复效应。对于 LLM API重复调用可能意味着重复计费或在业务侧写入重复结果。处理方式是在每次请求时生成唯一请求 ID放在X-Request-ID或Idempotency-Key请求头中由服务端做去重。但前提是服务端真的实现了幂等逻辑。如果 API 供应商不保证这一点重试策略就必须更保守并且只在业务的“幂等窗口”内重试。另一个问题是哪些失败允许重试。一个通用规则是网络层异常连接重置、请求未发出可以重试。429 限流和 5xx 服务端错误可能可以重试但要尊重响应头里的Retry-After。4xx 客户端错误除 429通常不值得重试因为参数错误不会因为重试而变正确。超时的情况要谨慎总超时发生时请求可能已经在服务端执行读超时发生时连接已经断裂相对安全但仍存在不确定性。不要用catch Exception统一重试。有些团队把参数错误重复了三次最终只能靠日志找原因。重试必须基于明确的可重试条件并且把重试原因记入日志。用指数退避和抖动打散重试流量固定间隔重试会让所有客户端在相同时间点再次请求容易形成同步峰。指数退避让每次重试的等待时间按 2 的幂增长但如果所有客户端使用同一个退避序列依然会在同一时刻发出请求。业界常用的 full jitter 算法是在[0, base * 2^attempt]范围内随机取等待时间def wait_time(attempt, base0.5): upper base * (2 ** attempt) return random.uniform(0, upper)这种随机化能显著降低重试流量的峰值。重试次数必须设置上限。5 次重试、退避上限 8 秒最坏情况会让单次调用阻塞数十秒。这个时间要纳入客户端总预算。如果业务流程要求 3 秒内完成那么最多重试 1 次且退避上限不能超过 200 毫秒如果是离线批量任务可以重试 3 次以上但仍然需要明确最大等待时间避免长时间占用连接池。建议在编写重试逻辑时显式声明两个常量max_attempts和max_wait_seconds并通过日志暴露实际遇到的退避时长方便后续调整。熔断把单次调用的局部决策升级为全局保护超时和重试只保护单个请求但当下游持续过载时每个新请求都会经历同样的“尝试、超时、重试、失败”过程线程池和连接池很快被占满。熔断器在错误率超过阈值后快速失败给服务端恢复的时间。以 Resilience4j 的配置为例resilience4j.circuitbreaker: instances: llmApi: slidingWindowSize: 20 failureRateThreshold: 50 slowCallRateThreshold: 80 slowCallDurationThreshold: 5s waitDurationInOpenState: 30s permittedNumberOfCallsInHalfOpenState: 5它的含义是在最近 20 次调用中如果失败率超过 50%熔断器打开后续请求直接拒绝不再进入 HTTP 调用。30 秒后进入半开状态允许 5 个探测请求观察成功与否再决定关闭或继续打开。这里有一个工程判断熔断器粒度要按业务场景隔离不能所有请求共用一个熔断器。批处理任务通常可以承受更长耗时如果它触发熔断不应该导致实时交互接口也被瞬间拒绝。更好的设计是基础调用层按 API 方法或域名配置一个熔断器业务编排层再按场景配置上层熔断器。在 Agent 工具调用链中这一点尤其重要。Agent 主流程会反复调用工具如果工具 API 已经故障Agent 不应该让每次工具调用都超时重试后返回超时错误而应该在熔断打开后快速失败把“工具暂不可用”的结构化结果交给模型让模型调整计划。否则一次 API 故障会被 Agent 的多次规划放大成严重的响应延迟。不同场景需要不同的超时与重试预算超时与重试不是一套配置走天下而是针对请求特征设置的一组预算。批量数据处理单个请求可以给较长的读取超时比如 60 秒以上重试 3 次使用指数退避。这里关注的是吞吐不是首字节时间。但要注意批处理的重试不应该和实时交互共享同一个连接池否则批量重试可能耗尽实时请求的连接。实时交互应用用户等待时间有限总预算通常不超过 3 秒。超时设得再长整体延迟也超出了交互可接受范围。此时重试的意义很小更合理的做法是快速失败并返回兜底内容。如果确实要重试只重试一次且退避不应超过 200 毫秒。Agent 工具调用链工具调用的时间由 LLM 主循环控制单个工具请求的读取超时必须考虑多轮调用的叠加。建议为每个工具调用设置独立超时并在编排层设置整体 deadline。当整体超时到达时主动取消未完成的子请求避免多个工具请求各自空转到最后。可观测性没有指标的超时策略等于盲调无论超时、重试还是熔断都需要持续用线上数据验证。至少监控以下指标超时类型占比连接超时、读取超时、总超时分别有多少。重试触发率以及重试后成功的比例。熔断器从打开到半开再到关闭的恢复情况。单次请求的 p50、p99 延迟变化。每次重试的日志应该包含请求 ID、重试次数、触发原因连接失败、429、5xx、读取超时、计算出的退避时长、收到的Retry-After值。这样在延迟波动时才能区分是正常的尾部延迟还是服务端已经过载、重试反而加剧问题。结语当 API 响应变慢单纯增加超时或取消重试都不能解决问题。真正需要的是一个有边界、有预算、可观测的策略超时分段设置重试前确认幂等退避加抖动熔断按场景隔离。这套框架不依赖特定供应商适用于大多数 LLM API 调用。接入具体产品时以官方文档为准重点确认三个问题API 是否支持幂等键错误响应是否提供Retry-After流式 SDK 如何区分“生成缓慢”和“连接已死”这三个答案决定你的参数应该保守还是激进。
返回列表