重试逻辑
Swarmix 内部已经有多层重试,但客户端仍需要做外层兜底 —— 本章讲怎么分层不重复。
服务端已经做的重试
不用你重复实现的部分:
- Deployment 级 — 同一 model 存在多个已配置候选时,失败后可尝试后续候选
- Provider 级 — 只有候选链里配置了多个 provider 时,才可能在失败后尝试其它 provider
- 上游瞬时错误重试 — 429 / 5xx / timeout 按 Router 配置做有限重试
- 请求级重试 — 瞬时 502/503/504 自动重试,可配次数(默认 2 次)
这些处理对客户端**透明**。你收到 200 的响应时,可能已经经过内部有限重试或失败候选处理。
客户端需要做的重试
以下情况服务端不会帮你重试,需要客户端兜底:
| HTTP | 含义 | 重试策略 |
|---|---|---|
| 429 | Rate limit | 做指数退避;当前不要假设一定有 Retry-After header |
| 500 | Router 内部错误 | 立刻重试 1-2 次,仍失败则提醒用户 |
| 502 | 当前模型没有可用上游候选 | 退避 10s+ 后重试,或检查模型、provider 控制和上游状态 |
| 504 | 上游超时 | 退避 3-5s 重试 |
| 401 / 402 / 403 | 认证 / 余额 / 权限 | 不要重试 — 重试无意义还浪费配额 |
| 400 | 请求格式错 | 不要重试 |
| 连接级错误 | timeout / connection reset | 立刻重试 1 次(可能是客户端 → CDN 抖动) |
推荐的客户端退避函数
python
import time
import random
from openai import OpenAI, APIError, RateLimitError, APIStatusError
client = OpenAI(
api_key="sk-swx-xxxx",
base_url="http://router.swarmixtoken.com/v1",
max_retries=0, # 关闭 SDK 自带重试 — 我们自己实现
)
RETRYABLE = {429, 500, 502, 503, 504}
def chat_with_retry(**kwargs):
max_attempts = 4
for attempt in range(max_attempts):
try:
return client.chat.completions.create(**kwargs)
except RateLimitError as e:
# 如果服务端返回 Retry-After 则使用,否则按 1 秒起步退避
retry_after = int(e.response.headers.get("retry-after", 1))
wait = retry_after + random.uniform(0, 0.5)
print(f"429, 等 {wait:.1f}s 后重试 (attempt {attempt+1})")
time.sleep(wait)
except APIStatusError as e:
if e.status_code not in RETRYABLE:
raise # 永久性错误,不重试
wait = min(60, (2 ** attempt) + random.uniform(0, 1))
print(f"{e.status_code}, 等 {wait:.1f}s 后重试")
time.sleep(wait)
except (ConnectionError, TimeoutError):
wait = (2 ** attempt) + random.uniform(0, 1)
time.sleep(wait)
raise RuntimeError(f"达到最大重试次数 {max_attempts}")
重试的幂等性
Chat Completions 不是幂等的
同一个 request 重试会**生成两个不同的响应**(不同 token 不同 content)。如果你在做 "消息发送"类业务,注意对用户只展示第一次成功的结果,丢弃重试产生的副本。
流式请求的重试
流式请求中途断开的处理:
- 连接建立前失败(收不到第一个 chunk)—— 安全重试
- 流式途中断开 —— 已经给用户展示了一半内容,重试会重复生成。 建议:把已收到的内容展示给用户,标红"生成中断",让用户决定是否重新生成
在 Swarmix 配置上游重试次数
企业私有部署的 router_config.yaml 里可以调 retry policy:
yaml
router_settings:
retry_policy:
rate_limit: 3 # 429 重试 3 次
timeout: 2 # 超时重试 2 次
internal_server: 2 # 5xx 重试 2 次
bad_request: 0 # 400 不重试
auth: 0 # 401 不重试
cooldown_time: 60 # 失败候选冷却 60s