idempotent-retry-after-timeout · ZH · 2026-10-28

在一把 key 上超时后重试而不重复计费:跨后端的幂等策略

本文讨论从 LLM API 聚合站调用多个后端模型时,请求超时后如何设计重试策略,避免重复计费与答案分叉。涵盖幂等键、服务端去重、超时探测与重试退避等概念性方法,帮助开发者在不重复扣费的前提下安全重试。

超时并不等于失败

调用 LLM API 时,客户端超时只代表「没等到响应」,不代表服务端没有处理。请求可能已经完成生成并扣费,只是响应在网络中丢失。若直接重试,可能付两次钱,甚至得到两个不一致的答案。

在聚合站场景下,同一个 key 背后路由到不同模型后端(Claude、GPT、DeepSeek 等),各后端的超时与重试语义可能不同,因此需要客户端自己建立幂等层。

幂等键:让重试可以安全重复

核心做法是为「一次逻辑请求」生成稳定的幂等键(idempotency key),并在重试时复用同一个键。

  • 键的生成:用业务侧唯一标识(如任务 ID)或请求内容的哈希 + 时间窗口,而不是每次重试随机生成。
  • 键的传递:放在自定义请求头中,随每次重试一起发送。
  • 键的生命周期:覆盖整个重试链,直到收到确定性结果或人工放弃。

聚合站若支持透传该头到后端,服务端就能识别重复请求并返回第一次的结果,而不是重新计费。

服务端去重与客户端去重

理想情况下,服务端按幂等键去重:第一次请求处理并缓存结果,后续同键请求直接返回缓存。但并非所有后端都原生支持,因此聚合层通常需要维护一份短期的「键 → 结果」映射。

客户端也应做本地去重:在内存或本地存储中记录已发出的幂等键及其状态(进行中、已完成、已失败),避免同一进程内并发重试。

超时探测:先问状态,再决定重试

超时后不要立刻重发完整请求,先做一次轻量探测:

  1. 用同一幂等键发起查询或状态接口(若聚合站提供)。
  2. 若返回「进行中」,等待并轮询,而不是重试生成。
  3. 若返回「已完成」,直接取结果,不重新计费。
  4. 若返回「未找到」,才用同一键重试。

若没有状态接口,可先重试一次同键请求;服务端应保证同键幂等,返回相同结果。

重试的退避与上限

  • 使用指数退避加随机抖动,避免瞬间打满后端。
  • 为每个幂等键设置最大重试次数与总时限。
  • 超过上限后标记为「未知状态」,交由人工或异步对账处理,而不是无限重试。

处理「答案分叉」

分叉通常来自重试时生成了新请求。用同一幂等键可从根本上避免:所有重试共享同一逻辑请求,服务端只产生一次生成结果。

若确实需要重新生成(例如结果质量不合格),应使用新的幂等键,并明确这是第二次计费,而不是重试。

与计费模型的关系

聚合站按官方价 ×1.3 扣费,贡献 key 按 ×1.1(优质 ×1.2)返 USDC。幂等设计只影响「请求是否被重复处理」,不改变单次计费倍率。正确去重意味着你只为一次逻辑调用付费,而不是为网络抖动买单。

实践清单

  • 每次逻辑请求生成稳定幂等键,重试复用。
  • 超时后先探测状态,再决定是否重试。
  • 客户端记录键状态,防止并发重复。
  • 设置重试上限与退避策略。
  • 需要重新生成时换新键,并接受二次计费。