EN

LLM Fallback 降级策略:模型挂了,你的应用还能活吗

生产级 LLM 应用必须有 Plan B——超时、限流、宕机时怎么优雅降级

返回教程列表🌐 Read in English
进阶10 分钟
AI Skill Navigation 编辑团队发布于 2026年7月22日

LLM Fallback 降级策略:模型挂了,你的应用还能活吗

生产级 LLM 应用必须有 Plan B——超时、限流、宕机时怎么优雅降级

生产级 LLM 应用的 llm fallback 实战:重试、熔断、多提供商降级、缓存兜底四层防线。讲清指数退避加 jitter、超时预算分配、大模型降级小模型的质量权衡,以及级联超时、重试风暴等真实工程坑。

做过 LLM 应用上生产的都知道一个真相:API 一定会出问题。 超时、429 限流、偶发 500、甚至整个服务商宕机。如果你的应用死死绑在一个模型上,它一打喷嚏,你就全挂。

LLM fallback(降级)策略就是给应用上的保险。但光有“换模型”还不够——你需要一套分层、可观测、可配置的降级体系(也就是完整的 llm fallback routing),才能让应用在故障中依然体面地活着。

会遇到的故障

  • 超时:请求发出去,迟迟不回。默认超时往往 30-60 秒,用户等不了。
  • 限流(429):调太快,被服务商挡了。OpenAI 的 TPM/RPM 限制很常见。
  • 服务错误(5xx):服务商那边抽风,比如 OpenAI 的 500 或 503。
  • 整体宕机:OpenAI / Anthropic 偶尔会有大面积故障,比如 2024 年 OpenAI 的多次中断。
  • 内容拒答:模型以安全为由拒绝回答,返回空或“I cannot answer that”。
  • 模型输出异常:返回非 JSON、格式错误、或明显无意义内容(如重复 token)。
  • 每一种,你都得想好「然后呢」。而且故障可能链式触发——主模型挂了,备用模型也可能被同样的限流策略挡住。

    降级策略分层

    从轻到重,一层层兜。关键是每层都要有明确的触发条件和退出条件

    第一层:重试(Retry)

    偶发错误,等一下重试往往就好了。用指数退避加抖动(jitter),别傻等也别猛冲:

    python
    import time
    import random

    def call_with_retry(fn, max_retries=3, base_delay=1.0, max_delay=10.0): for i in range(max_retries): try: return fn() except (TimeoutError, RateLimitError) as e: if i == max_retries - 1: raise # 最后一次重试失败,向上抛 delay = min(base_delay * (2 ** i), max_delay) # 加 jitter 避免惊群效应 delay = delay * (0.5 + random.random() * 0.5) time.sleep(delay)

    注意:429 错误通常带 Retry-After 头,优先用它。指数退避只适用于无明确重试时间的场景。

    补充一层:熔断器(Circuit Breaker)

    重试、降级、熔断经常被混为一谈,其实三者管的是不同层面:

  • 重试(Retry):请求级,对付偶发抖动,代价是额外延迟。
  • 降级(Fallback):请求级,主路走不通就换备用路径,保证本次请求有结果。
  • 熔断(Circuit Breaker):服务级。当失败率超过阈值时直接「跳闸」——一段时间内所有请求快速失败(fail fast),不再打到故障服务上。既给故障服务留出恢复时间,也避免你的线程池被大量等待中的请求拖垮(级联超时)。
  • 一个最小可用的熔断器(状态机:关闭 → 打开 → 半开):

    python
    import time

    class CircuitBreaker: def __init__(self, failure_threshold=5, recovery_timeout=30): self.failure_threshold = failure_threshold # 连续失败多少次后跳闸 self.recovery_timeout = recovery_timeout # 跳闸后多少秒尝试半开 self.failures = 0 self.state = "closed" # closed / open / half_open self.opened_at = None

    def call(self, fn): if self.state == "open": if time.time() - self.opened_at > self.recovery_timeout: self.state = "half_open" # 放一条试探请求过去 else: raise RuntimeError("熔断中:快速失败,走降级逻辑") try: result = fn() except Exception: self.failures += 1 if self.failures >= self.failure_threshold: self.state = "open" self.opened_at = time.time() raise else: self.failures = 0 self.state = "closed" return result

    熔断器包在主模型调用之外,熔断期间的异常直接触发降级链,而不是让请求排队等待。常见坑有两个:阈值设得太敏感(偶发超时也跳闸),或恢复时间过长(服务已恢复却还在熔断)。生产环境可以先用现成库(如 Python 的 pybreaker),跑稳了再考虑自研。

    第二层:换模型(Model Fallback)

    主模型不行,自动切备用模型。但不是简单遍历列表——你需要考虑:

  • 质量落差:从 GPT-4o 降到 GPT-4o-mini,回答质量会掉。关键场景宁可返回「稍后再试」。
  • 成本差异:备用模型可能更贵(如 Claude 3.5 Sonnet vs GPT-4o),要设成本上限。
  • 故障隔离:如果主模型因限流挂了,备用模型最好用不同服务商
  • python
    

    更健壮的 fallback 链:按优先级 + 故障隔离

    FALLBACK_CHAIN = [ {"model": "gpt-4o", "provider": "openai", "priority": 1}, {"model": "claude-3-5-sonnet", "provider": "anthropic", "priority": 2}, {"model": "gpt-4o-mini", "provider": "openai", "priority": 3}, # 兜底:用本地小模型或缓存 {"model": "local-fallback", "provider": "local", "priority": 4}, ]

    def chat_with_fallback(messages, max_retries_per_model=2): last_error = None for entry in FALLBACK_CHAIN: for attempt in range(max_retries_per_model): try: return call_model(entry["model"], messages) except RateLimitError as e: # 限流:等一会再试,但别等太久 time.sleep(2 ** attempt) last_error = e except (TimeoutError, ServiceError) as e: # 服务端错误:直接换模型 last_error = e break # 跳出重试循环,换下一个模型 except ContentRefusalError: # 拒答:可能换模型也拒,直接走兜底 return DEFAULT_REPLY # 全挂了,记录错误并返回兜底 log_fallback_failure(last_error) return DEFAULT_REPLY

    这也是为什么生产应用最好用一个统一的网关层(如 LiteLLM)来抽象多家模型——切换只改配置,不动业务代码。LiteLLM 原生支持 fallbacks 参数,可以配置模型列表和重试策略。

    第三层:缓存(Cache)

    常见问题的答案缓存起来。模型全挂时,至少高频问题还能用缓存答。但缓存不是银弹:

  • 精确缓存:完全相同的 prompt 命中,适合 FAQ。
  • 语义缓存:用 embedding 匹配相似问题,适合客服场景。但注意:语义缓存可能误匹配,导致答非所问。
  • TTL 策略:缓存要设过期时间,避免回答过时。动态内容(如天气)TTL 短,静态知识(如产品说明)TTL 长。
  • python
    

    语义缓存示例(简化)

    from sentence_transformers import SentenceTransformer import numpy as np

    class SemanticCache: def __init__(self, threshold=0.9): self.model = SentenceTransformer('all-MiniLM-L6-v2') self.cache = {} # key: embedding, value: (answer, timestamp) self.threshold = threshold def get(self, query): query_emb = self.model.encode(query) for cached_emb, (answer, ts) in self.cache.items(): similarity = np.dot(query_emb, cached_emb) / (np.linalg.norm(query_emb) * np.linalg.norm(cached_emb)) if similarity > self.threshold: return answer return None def set(self, query, answer): self.cache[self.model.encode(query)] = (answer, time.time())

    第四层:兜底回复(Graceful Degradation)

    全部都不行了,也不能给用户甩一个 500。返回一个体面的兜底。但兜底不是一句话——根据场景分级:

  • 低风险场景(如闲聊):"AI 助手暂时繁忙,请稍后再试。"
  • 中风险场景(如客服):"我们暂时无法处理您的请求,已转接人工客服,预计等待时间 5 分钟。"
  • 高风险场景(如医疗建议):"系统暂时不可用,请立即联系紧急联系人。切勿依赖 AI 建议。"
  • 设计要点

    超时一定要设,而且要短。 别用默认的几十秒超时——用户等 30 秒早跑了。根据场景设 5-15 秒,超了就走 fallback。注意:大模型通常明显慢于小模型,超时要按模型分别调整,别把同一个阈值套在所有节点上。

    fallback 链要考虑质量落差。 从 GPT-4o 降到小模型,回答质量会掉。关键场景宁可返回「稍后再试」,也别用一个明显变差的答案糊弄,反而砸口碑。一个实用做法:对 fallback 模型的输出做质量检查——如果置信度低于阈值,继续降级。

    别把 fallback 写死在业务逻辑里。 抽一个统一的调用层,把重试、切换、缓存、兜底都收口在一处。业务代码只管「我要个回答」,怎么容错是底层的事。推荐用装饰器模式或中间件模式封装。如果你同时接入了多家提供商,这个统一调用层也是做 API 集成 治理的天然位置——限流策略、密钥轮换、用量统计都可以在这里统一处理。

    监控 fallback 触发率。 如果备用模型经常被触发,说明主模型有问题,这是个信号,要去查。可以接 LLM 可观测性工具(如 LangSmith、LangFuse)盯着。关键指标:

  • fallback 触发率:备用模型被调用的比例。>5% 就要告警。
  • fallback 成功率:备用模型成功返回的比例。如果备用也常失败,说明故障范围大。
  • fallback 延迟:备用模型响应时间。如果比主模型慢很多,影响用户体验。
  • 考虑故障隔离。 如果主模型因限流挂了,备用模型最好用不同服务商。否则两个模型可能被同一个限流策略挡住。例如:OpenAI 的 GPT-4o 和 GPT-4o-mini 共享 TPM 配额,一个被限流,另一个也危险。

    一个完整的降级链

    
    用户请求
      → 查缓存(命中直接返回,跳过所有模型调用)
      → 主模型(GPT-4o)+ 重试(3 次,指数退避)
      → 备用模型 1(Claude 3.5 Sonnet)+ 重试(2 次)
      → 备用模型 2(GPT-4o-mini)+ 重试(2 次)
      → 本地小模型(如 Llama 3.1 8B)+ 重试(1 次)
      → 兜底回复(根据场景分级)
    

    注意:缓存要放在最前面,但缓存命中后要异步更新缓存(避免缓存击穿)。本地小模型作为最后一道防线,虽然质量差,但至少能给出一个回答——自建本地推理的方案见 模型部署专题

    小结

    LLM 应用的健壮性,不看它顺利时多强,看它出问题时多稳。上生产前,先问自己一句:模型现在挂了,我的用户会看到什么? 答不上来,就该补 llm fallback 了。

    但 fallback 不是万能药。它只能缓解故障,不能根治。真正健壮的系统,还需要:

  • 多服务商冗余:不要只依赖一家。
  • 本地模型兜底:至少能跑一个轻量模型。
  • 人工介入通道:当 AI 全挂时,用户能联系到真人。
  • FAQ

    Q: fallback 链太长会不会导致响应时间过长? A: 会。建议设全局超时(如 30 秒),超时后直接走兜底。另外,fallback 链中的每个模型都要设独立超时,避免一个模型拖死整个链。

    Q: 如何避免 fallback 模型被同一个限流策略挡住? A: 使用不同服务商(如 OpenAI + Anthropic + 本地模型)。如果只能用同一家,至少选不同模型(如 GPT-4o 和 GPT-4o-mini 共享配额,但 GPT-4o 和 GPT-3.5-turbo 可能独立配额)。

    Q: 语义缓存会不会返回过时答案? A: 会。建议设 TTL(如 1 小时),并定期清理。对于时效性强的场景(如新闻、价格),不要用语义缓存。

    Q: 本地小模型作为兜底,质量太差怎么办? A: 本地模型只用于低风险场景(如闲聊、简单问答)。高风险场景(如医疗、金融)直接返回「系统不可用,请联系人工」。质量差比错误答案好。

    Q: 如何测试 fallback 策略是否有效? A: 用混沌工程:模拟 API 超时、限流、500 错误,观察 fallback 是否按预期触发。推荐工具:Chaos Mesh(Kubernetes 环境)或自定义 mock 服务。

    *最后更新:2026 年 7 月。请以各工具官方文档为准。*

    相关工具

    LiteLLMOpenAIClaudeOpenRouter