DeepSeek API 调用失败或响应超时处理方法(AI 视频下载总结器实战)

第一版只有单次同步调用:请求发出去,等模型返回,再把结果渲染到页面。上线第一周就吃了教训——下午高峰时段用户点”生成总结”,页面经常转圈二十几秒,然后抛出一个光秃秃的错误提示,好几个用户以为功能坏了直接放弃。翻请求日志才发现,故障无非三类:网络层超时、限流(HTTP 429)、服务端 5xx。当时我对照 DeepSeek 的接口文档把每个错误码梳理了一遍,最终把处理策略定成”重试 + 降级 + 兜底文案”三层组合。重试解决瞬时抖动,降级在 DeepSeek 不稳定时切换备用模型,兜底保证即便所有模型都失败,用户也能拿到一段可读结果。改动量不大,总结功能却从高频翻车变成了稳定可用,下面把每一层的取舍和踩过的坑拆开讲。

一、异常分类与处理策略

先分类再谈处理。没有分类的重试很危险:把 400 输入违规当成瞬时故障去重试,只会浪费请求配额,还拖慢用户等待;反过来,把 5xx 当成不可重试,又会白白丢请求。我按”能否重试、是否降级、如何兜底”三个维度给异常分了五类,这张分类表是实际排障时反复调整后定下来的。

异常类型 HTTP 重试 降级 兜底
网络超时 – 是 切备用厂商 提示稍后重试
限流 429 按 Retry-After 切备用模型 排队提示
服务端 5xx 5xx 是(≤ 3 次) 切本地小模型 文本摘要
输入违规 400 否 不重试 报错并回退到原字幕
计费异常 402 否 切免费模型 提示充值

分类表落地后,重试逻辑变得可以推导:什么错误进重试、重试几次、失败后往哪走,都有一一对应的路径。其中输入违规和计费异常两类,最初没有单列,结果出过一次尴尬局面——账号余额不足,所有请求都被按 429 重试,白白耗光剩余配额。把这五类跑明白,后面的重试、降级、兜底才不是拍脑袋。

二、重试设计

重试的目标不是”多试几次”,而是”用更少的次数换回可用性”。如果所有失败都立刻重试,服务端还在恢复中,请求会像蜂群一样反复冲击,这就是常说的”雷鸣群”问题,反而把后端打得更慢。我在项目里给重试套上了指数退避和随机抖动:第一次失败等 1 秒,第二次等 2 秒,每次再叠加一个随机偏移,让并发重试的请求错开,而不是同时撞上来。

import asyncio, random

async def call_with_retry(payload, max_attempts=3):
    delay = 1.0
    for attempt in range(1, max_attempts + 1):
        try:
            return await deepseek_client.summarize(payload)
        except TimeoutError:
            if attempt == max_attempts:
                raise
            await asyncio.sleep(delay + random.uniform(0, 0.3))
            delay *= 2

这段代码解决的核心问题是”失败后等多久再试”。随机抖动加的是 0 到 0.3 秒的小偏移,成本很低,但能避免多线程同时超时后一起重试的踩踏。实现时有三条硬约束,是我从线上事故里总结出来的:

  1. 仅对可重试错误(超时、429、5xx)启用;
  2. 退避基数 ≤ 8s,避免长时间占用用户;
  3. 携带 trace_id,便于日志定位。

第三条尤其容易漏。没有 trace_id,重试链路断了之后,排查时根本分不清哪次是原请求、哪次是重试,只能靠时间戳猜测,效率很低。

三、限流处理

DeepSeek 限流时会在响应头里带上 Retry-After,告诉客户端等多久再试。起初我按自己写的固定退避等待,效果很差——限流刚恢复,别的实例已经把配额占满,再次撞上 429,白白多等一轮。后来改成直接读取响应头里的数值:

retry_after = int(response.headers.get("Retry-After", "1"))
await asyncio.sleep(retry_after)

服务端给的等待时长比本地猜的准得多。还有一点要注意:DeepSeek 的限流是按账号配额算的,不是每个请求独立,所以指标里要把单日 429 次数盯紧,接近配额 80% 时告警,提前切备用模型,而不是等彻底打满才反应。实际跑下来,这个 80% 预警线帮我们躲过了好几次高峰期的全量失败。

四、降级到备用模型

重试解决不了持续性的故障,比如 DeepSeek 服务端连着 5xx。这时候就要降级。选备用模型时我对比过几个候选,最终定下三条标准:与 DeepSeek 接口兼容(不用改调用代码)、价格更低、长上下文足够支撑视频字幕总结。切换要放在统一 client 后面,让业务层完全无感:

class AISummarizer:
    def __init__(self):
        self.providers = [
            ("deepseek", deepseek_client),
            ("backup",   backup_client),
        ]

    async def summarize(self, text):
        for name, client in self.providers:
            try:
                return await client.summarize(text)
            except (TimeoutError, RateLimitError, ServerError):
                continue
        raise AllProvidersFailed()

这段代码把”供应商选择”收敛到一个循环里:主模型不行就轮到备用模型,都失败才抛异常。踩过的坑是备用模型的输出质量差异——有的小模型总结完句子不通顺,所以降级成功后要给结果打上”由备用模型生成”的标记,让用户知道内容可能不如主模型精炼。备用模型的选择也要做成配置,别写死在代码里,方便随时替换。

五、用户层兜底

即便有重试和降级,模型全挂的情况依然存在。我的原则是:用户不能因为后端模型不可用,就看到一个空白页。按失败程度给了三种兜底文案:

场景 兜底
完全失败 返回原字幕前 200 字 + “稍后整理”提示
部分失败 返回已生成段落 + 缺失段占位
降级 注明”由备用模型生成”,并允许用户重新生成

这里的取舍是”宁可给部分结果,也不给空页面”。原字幕本身就是下载工具里已有的数据,把它作为兜底内容几乎零成本,却能保住用户对功能的信任。加上”重新生成”按钮后,用户被安抚的概率明显上升,客诉里”总结不出来”的占比掉了一截。

六、可观测性

故障处理做得再全,看不见故障就等于白做。我们给每次失败都上报了完整字段:

  • 上报:traceid、model、status、latency、errortype;
  • 看板:失败率、平均重试次数、限流次数;
  • 报警:失败率 > 2% 持续 5 分钟。

报警阈值要留余量。最开始设的是失败率 5%,结果发现 5% 已经是用户体验明显变差的水平,等报警出来,用户早就反馈完了。改成 2% 后,一般波动不会误报,真正出问题又能提前介入。

七、配置项

重试、超时、降级这类参数,一开始写在代码常量里,每次调优都要改代码发版,太慢。后来我把它们全部抽到配置文件,调参只需改配置再重载:

ai:
  providers:
    - name: deepseek
      timeout_ms: 12000
      max_retries: 3
    - name: backup
      timeout_ms: 15000
      max_retries: 2
  fallback_text: "AI 总结暂时不可用,已为你保留原始内容。"

配置文件带来的明显好处是降级策略能随线上表现动态调整。比如某段时间备用模型响应变慢,就把它的超时放宽到 15 秒,不用动一行代码。注意 fallback_text 这类用户可见文案也放这里统一管理,方便运营同学直接改措辞。

八、回归测试

这类容错逻辑担心的是”测试时一切正常,生产一压就露馅”。我给重试、降级链路做了故障注入式的回归测试:

  • 用 VCR 录制正常响应,人工注入超时与 429;
  • CI 每次跑故障注入,验证重试与降级;
  • 压测时把限流阈值调到 80%,观察切换是否平滑。

测试的价值在于把”运气”变成”预期”。VCR 录制的好处是不用真调模型,测试稳定又不花钱;CI 每次提交都跑一遍,防止改业务代码时把容错链路改坏。压测那一步尤其重要,限流阈值调低后,降级切换是否平滑、用户有没有感知到延迟,只有压力下才看得出来。

到这里,DeepSeek 调用失败处理的完整链路就齐了:分类定策略,重试扛瞬时,降级扛持续,兜底保体验,日志做验证。

常见问题(FAQ)

Q1:429 要不要无限重试?

不要。按 Retry-After 等待后最多再试 2 次,避免长时间占用 worker。

Q2:降级后用户会重复扣费吗?

不会。同一任务只扣一次,扣费与降级解耦。

Q3:兜底文案会不会让用户失望?

配合”重新生成”按钮和透明状态,失望感会下降。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 qiqicto@qq.com 举报,一经查实,本站将立刻删除。
赞 (0)
其AI的头像其AI普通用户

相关推荐

返回顶部