10 月 15 日早高峰,DeepSeek 的对话服务、V4 Pro 和 V4.1 Flash API 从 8 点 11 分开始中断,到 13 点 04 分才全部恢复,持续近 5 小时。官方事后致歉,并把补偿按账号 7 日内消耗全额返还赠金。值得注意的一句话是「全面梳理并剥离单点依赖」——这基本点明了根因方向。这篇借它拆一下:你自己的 AI 应用该怎么设计,才不至于跟着一起挂。

发生了什么

按官方状态页,故障 8 点 11 分被标记为「排查中」,9 点 06 分「已定位原因、正在修复」,12 点 49 分进入监控,13 点 04 分宣布解决。受影响的不止网页对话,还有 V4 Pro 和 V4.1 Flash 两条 API 线。官方没有公布具体技术根因,但「剥离单点依赖」的表述说明,某个单一环节挂了就牵动全站。这种「一个点拖垮全局」的故障,对任何把模型 API 当唯一依赖的应用都是活教材。

关键变化清单

  • 中断时长:8:11 到 13:04,约 4 小时 52 分钟。
  • 受影响:对话服务、V4 Pro API、V4.1 Flash API。
  • 补偿:按 10 月 8 日至 14 日消耗金额,以赠金全额返还,先扣赠金账户。
  • 整改方向:梳理并剥离单点依赖。

应用的兜底

第一,别把模型写死成一个。做一个轻量路由层,主模型超时或报 5xx 就切到备用模型,同一个业务语义在多个供应商都有等价实现。第二,给高频读路径加缓存,模型暂时不可用时返回上一次的缓存结果,标注「可能不是最新」。第三,明确降级提示,让用户知道是服务方的问题,而不是你应用崩了。下面是最小化的多模型降级实现:

import time

def call_with_fallback(prompt, models):
    last_err = None
    for m in models:
        try:
            return m.generate(prompt), m.name
        except Exception as e:
            last_err = e
            time.sleep(0.2)
    return f"服务暂时不可用,请稍后重试。({last_err})", None

这个 call_with_fallback 按顺序试,全挂了就返回兜底文案,而不是把异常抛给用户。再往上加就是熔断:某个模型连续失败就标记它一段时间不可用,别再每次都去撞;以及带退避的重试,避免在服务方已经过载时雪上加霜。备份模型怎么选:首选语义等价、能力接近的,比如都是通用对话或都是代码,别用一个明显更弱的模型去接核心任务,降级后的质量落差用户是能感觉到的。还有两点容易被漏:降级要能自动回切,服务方恢复后别一直停在备用模型上;切换策略最好做成配置,出故障时不用发版就能调。这几件事做齐,你的应用才算在「依赖外部模型」这件事上有了基本的安全垫。说到底,服务方会不会再宕机你控制不了,但宕机时你的应用降不降级、降级得干不干净,是完全在你手里的。

缓存与降级的边界

缓存兜底有边界:只能用在「旧结果比没结果强」的场景,比如摘要、推荐、历史问答,凡是结果时效性关乎安全或钱的,宁可失败也不要拿过期数据硬顶。降级提示要把话说清楚,告诉用户「服务方正在故障,稍后自动恢复」,而不是让用户对着一个转圈图标猜。再进一步是分级降级:核心功能(比如支付、订单)走最稳的路径,边缘功能(比如文案润色、标题生成)可以先砍掉或排队,别让非关键路径拖垮关键路径。

可观测

降级做得好不好,最终要看你能不能第一时间知道主模型挂了。给每个模型调用记录延迟、错误码、重试次数,配上告警,服务方故障时你能在用户大量投诉之前主动切走。这套东西和降级逻辑是一体的:没有可观测,你的 fallback 可能默默降级了半天,用户体验已经掉没了你都不知道。

待观察

这次事故的另一个信号是:对 AI 服务方而言,业务连续性正在变成和模型效果同等重要的卖点。对开发者来说,单点依赖永远是隐患,降级和容错应该在一开始就写进架构,而不是等某天早上服务挂了才想起来补。多模型路由、缓存兜底、降级提示这三件事,成本不高,却是这次 5 小时宕机里真正能把你的应用从「一起挂」里捞出来的东西。如果你现在还是单模型直连,花半天时间把上面的 fallback 循环和熔断加上,比继续堆功能更值——因为下一次某个模型方宕机只是时间问题,而你提前写好的兜底,就是那次宕机里用户体验没有崩掉的全部原因。