重试
xiangxin.RetryPolicy
dataclass(frozen=True)
SDK 重试行为的配置。网络抖动、限流(429)和服务过载(529)都是暂时性的,SDK 默认会按指数退避加随机抖动等待后重试,最多重试 2 次;服务端给出 retry-after 时优先按它等待。大多数应用不需要改任何配置。
可以在客户端上设置(XiangxinClient(retry=...)),也可以在单次调用中覆盖(system_one(..., retry=...)、models.list(retry=...))。
示例
from xiangxin import RetryPolicy, XiangxinClient
client = XiangxinClient(
retry=RetryPolicy(
max_retries=3, timeout=10.0, retry_statuses={429, 500, 502, 503, 504, 529}
)
)构造时会校验参数:max_retries 不能为负,backoff_jitter 必须在 0 到 1 之间,退避时间不能为负,否则抛出 ValueError。
max_retries
max_retries: int = 2首次请求之外的最大重试次数;0 表示不重试。不能为负。
retry_statuses
retry_statuses: frozenset[int] = frozenset({429, 500, 502, 503, 504, 529})会触发重试的 HTTP 状态码。也可以传普通的 set,构造时会转换为 frozenset。
backoff_initial
backoff_initial: float = 0.5首次退避的秒数,之后每次翻倍,直到 backoff_max;为 0 时不退避。
backoff_max
backoff_max: float = 8.0单次退避的上限(秒);为 0 时不退避。
backoff_jitter
backoff_jitter: float = 0.25每次退避中随机扣减的比例,0–1,用来错开大量客户端的重试时间。
respect_retry_after
respect_retry_after: bool = True是否遵守 retry-after 与 retry-after-ms 响应头。
max_retry_after
max_retry_after: float = 60.0服务端建议的等待时间上限(秒);建议等待超过它时不再重试,直接抛出异常。
retry_connection_errors
retry_connection_errors: bool = True是否重试 APIConnectionError,即请求无法连到服务端或读取响应失败。
retry_timeouts
retry_timeouts: bool = True是否重试 APITimeoutError,即请求超过了配置的超时。
predicate
predicate: Callable[[BaseException], bool] | None = None可选的判断函数,以抛出的异常为参数;返回 True 时在其他规则之外额外触发重试。
timeout
timeout: float | None = 60.0每次 SDK 调用的重试总预算(秒),包括首次请求和所有等待;None 表示不限。如果下一次重试的等待会达到或超过预算,就不再重试,抛出最后一次的异常。
should_retry
should_retry(error: BaseException) -> bool判断某个异常是否属于可重试的类型(不考虑已重试次数与总预算)。
backoff
backoff(retry_index: int) -> float第 retry_index 次重试(从 0 起)前的退避秒数,已含抖动。
delay_for
delay_for(error: BaseException, retry_index: int) -> float | None下一次重试前应等待的秒数:优先采用 retry-after 提示,否则使用 backoff();返回 None 表示不应重试(例如服务端建议的等待超过了 max_retry_after)。
xiangxin.retries.DEFAULT_RETRY_STATUSES
DEFAULT_RETRY_STATUSES: frozenset[int] = frozenset({429, 500, 502, 503, 504, 529})默认会重试的 HTTP 状态码,即 retry_statuses 的默认值。
哪些错误会重试
默认情况下:
| 情况 | 异常 | 重试? |
|---|---|---|
| 超出速率限制 | RateLimitError(429) | 是 |
| 服务过载 | OverloadedError(529) | 是 |
| 服务端内部错误 | InternalServerError(500、502、503、504) | 是 |
| 连接失败 | APIConnectionError | 是 |
| 超时 | APITimeoutError | 是 |
| 密钥无效 | AuthenticationError(401) | 否 |
| 余额不足 | InsufficientBalanceError(402) | 否 |
| 模型不存在 | NotFoundError(404) | 否 |
| 请求校验失败 | UnprocessableEntityError(422) | 否 |
| 本地参数错误 | XiangxinError | 否 |
4xx 错误(429 除外)说明请求本身有问题,原样重发只会得到同样的结果,所以不重试。
等待多久
第 n 次重试(从 0 开始计)之前的等待时间为:
基础等待 = min(backoff_max, backoff_initial × 2ⁿ)
实际等待 = 基础等待 × (1 − backoff_jitter × 随机数[0, 1))按默认值,两次重试前分别等待约 0.375–0.5 秒和 0.75–1 秒。
如果错误响应带有 retry-after-ms 或 retry-after(秒数或 HTTP 日期格式),且 respect_retry_after=True,则直接按服务端给的时间等待,不再套用上面的公式。429 响应通常会带这个头。服务端要求的等待超过 max_retry_after 时,SDK 放弃重试并立即抛出异常,让你的代码自己决定怎么办。
此外,如果下一次等待会让整次调用超出 RetryPolicy.timeout 的总预算,SDK 也会放弃重试,抛出最后一次的异常。
每次重试前,SDK 会通过 xiangxin logger 输出一条 WARNING 日志,包含路径、等待时间、第几次重试和原因。
常见配置
关闭重试
from xiangxin import RetryPolicy, XiangxinClient
client = XiangxinClient(retry=RetryPolicy(max_retries=0))适合你已经有自己的重试框架(例如任务队列会自动重投)的场景,避免两层重试叠加。
离线批处理:多等一会儿
批处理不在乎单条延迟,更在乎最终成功率:
from xiangxin import RetryPolicy, XiangxinClient
batch_client = XiangxinClient(
retry=RetryPolicy(max_retries=6, backoff_max=30.0, max_retry_after=120.0, timeout=300.0),
)在线接口:快速失败
面向用户的接口宁可快速降级,也不要让用户干等:
from xiangxin import RetryPolicy, XiangxinClient
api_client = XiangxinClient(
timeout=3.0,
retry=RetryPolicy(max_retries=1, backoff_max=0.5, max_retry_after=1.0, timeout=5.0),
)单次调用覆盖
system_one() 和 models.list() 都接受 retry= 参数,只对这一次调用生效:
resp = client.system_one(state=text, questions=questions, retry=RetryPolicy(max_retries=0))自定义判断
predicate 可以让额外的异常也触发重试。例如,把偶发的 400 也当作可重试(仅作演示):
from xiangxin import APIError, RetryPolicy
policy = RetryPolicy(predicate=lambda e: isinstance(e, APIError) and e.status_code == 400)predicate 只能“追加”重试条件,不能阻止默认会重试的情况;要缩小范围,请修改 retry_statuses、retry_connection_errors 或 retry_timeouts。
重试安全吗?会重复扣费吗?
POST /v1/systemone 只读取输入、返回判断,不修改任何数据,所以重复调用是安全的。
计费只针对成功的请求:429、529、5xx 等失败的尝试不扣费。如果某次请求其实已在服务端成功完成、只是响应在网络上丢失(表现为超时或连接中断),SDK 的重试会再发一次,这两次成功的请求都会计费。按 ¥0.042 / 百万输入 token 计算,这种情况的额外成本通常可以忽略;如果你对此敏感,可以设置 retry_timeouts=False。
不用 SDK 时
直接调用 HTTP API 时,请自己实现同样的逻辑:对 429 和 529 做指数退避,遵守 retry-after 响应头,不要立即重发。详见 HTTP 错误码。

