线上跑大模型接口的都知道,它不是"要么成功要么报错"这么干脆。最烦的是偶发:99% 的请求好好的,冷不丁来个 429 限流、或者 30 秒还不返回直接超时。我们这套智能体上线第一周,就因为没做退避,被一波并发把自己请求打到全军覆没。这篇把我最后定下来的重试策略和坑都写清楚。

先认清三种失败,别一把抓

不是所有失败都该重试,这是我交的第一笔学费:

  • 超时(timeout):可能服务端在算、也可能真挂了。可重试,但要小心重复扣费。

  • 限流(429):服务端明确让你慢点。必须退避后重试,而且别傻乎乎立刻重试,你越急它越拒。

  • 参数错误(400):你自己请求就不对,重试一百次也是 400。这种绝不能重试,直接抛出去。

我见过有人 for i in range(5) 无脑重试,400 也重,纯属浪费配额还拖慢响应。

为什么必须是指数退避 + 抖动

固定间隔重试(每隔 1 秒重试)在限流场景下是灾难:一堆客户端被同时限流,又在同一时刻一起重试,瞬间又把服务打满,再一起被拒——这叫重试风暴。

指数退避是让每次等待时间翻倍:1s、2s、4s、8s,给服务端喘息空间。再叠一层随机抖动(jitter),把各客户端的重试时刻打散,避免大家踩同一个点。

import time, random

def call_with_retry(fn, max_retries=4):
    for attempt in range(max_retries):
        try:
            return fn()
        except RateLimitError:          # 429,必退避
            pass
        except TimeoutError:
            if attempt == max_retries - 1:
                raise
        except BadRequestError:         # 400,绝不重试
            raise
        # 指数退避 + 满抖动:在 [0, 2^attempt] 里随机取
        sleep = random.uniform(0, 2 ** attempt)
        time.sleep(min(sleep, 16))      # 封顶,别等太久
    raise Exception("重试耗尽")

注意我用的是"满抖动"(在 0 到上界之间随机),实测比"固定退避 + 小抖动"更能打散重试风暴。

智能体那头怎么配合

这套客户端是包在我自己服务里的,智能体本身是在一个零代码配智能体的平台上搭的:RAG、prompt、工具调用都在平台里配好,对外暴露成一个 API。我的服务调这个 API 时套上面这层重试。平台负责把智能体跑起来,稳定性兜底这层我自己在外面加,职责切得很干净。

三个血泪坑

重试要带超时上限,别无限退避。我第一版没封顶,4 次重试理论最长能等几十秒,用户早把页面关了。后面加了总耗时预算:超过 8 秒不管第几次都放弃,降级返回兜底文案。用户体验比"死磕到成功"重要。

非幂等操作重试会重复执行。如果这个调用背后有写库/扣费,超时重试可能让同一笔扣两次(第一次其实成功了只是响应丢了)。这类必须配幂等键,服务端按键去重。我就因为这个,有天对账发现几条重复记录。

重试会放大延迟,要给监控。表面成功率上去了,但 P99 延迟悄悄被重试拉高。我把"重试次数"单独打点上报,某天发现重试率突然飙到 15%,一查是上游在限流,提前发现没等用户投诉。重试是创可贴,别拿它盖住真问题。

一句话收尾

这套智能体的模型推理跑在讯飞Agent 的 MaaS 上,我退避策略里那些限流阈值、超时时长,都是按它 MaaS 接口的实际表现调出来的——模型服务现成,我只需要把客户端的韧性做扎实。

你们调大模型怎么处理偶发失败?固定重试还是指数退避,有没有踩过重复执行的坑,评论区交流。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐