DeepSeek 又崩了?用模型故障转移守住夜间自动任务

2026-09-15 12 预计阅读时间: 1 分钟
来源: oschina.net AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:10 分钟

深夜自动任务最怕的不是任务本身出错,而是依赖的模型服务突然不可用。人已经休息,监控没人查看,等到第二天才发现整晚任务都没有产出,后续工作也被一起推迟。

这类问题不一定要靠复杂的高可用平台解决。以 Firefly Router 为例,模型故障转移可以通过配置一个首选模型和多个备用模型来完成:首选模型请求失败后,路由器自动尝试下一个模型。对调用方来说,仍然只需要请求同一个路由入口。

故障转移解决的是什么

一次模型调用失败,可能来自多种原因:

  • 模型服务临时不可用或发生大面积故障
  • 请求超时,尤其是长上下文或批量任务
  • 供应商触发限流
  • 账户余额、配额或区域策略导致请求被拒绝
  • 某个模型正在维护或暂时下线

如果业务代码只写死一个模型 ID,失败就会直接向上抛出。夜间批处理通常没有人工重试机制,于是一个短暂故障会变成一整晚的任务空窗。

故障转移的核心是把“使用哪个模型”的决策交给路由层:

任务程序 -> Firefly Router -> 首选模型
                         -> 备用模型 1
                         -> 备用模型 2

当首选模型返回可重试的错误时,路由器继续尝试后续模型;当请求成功时,结果按正常流程返回给任务程序。这样可以减少业务代码中的供应商判断和重复重试逻辑。

在 Firefly Router 中配置模型链

来源信息给出的做法是:填写模型 ID 时,在首选模型后配置备用模型,从而让 Router 负责故障转移。由于不同版本或部署方式的配置分隔符可能不同,下面使用一个示意配置说明模型链的结构。实际接入时,请以当前 Firefly Router 的模型 ID 格式为准。

# firefly-router.yaml
# 示例:将首选模型和备用模型放入同一条路由链
routes:
  nightly-report:
    model: "deepseek-chat, qwen-plus, gpt-4o-mini"
    timeout_seconds: 60
    max_attempts: 3

如果你的 Router 版本要求使用数组,也可以按下面的结构改造:

routes:
  nightly-report:
    models:
      - deepseek-chat
      - qwen-plus
      - gpt-4o-mini
    retry:
      max_attempts_per_model: 1
      retryable_status_codes: [408, 429, 500, 502, 503, 504]

这里的顺序很重要:deepseek-chat 是首选模型,后面的模型是备用模型。备用模型不应只是在名称上不同,还要确认它们确实来自可用的独立供应商或服务通道,否则多个模型可能同时受到同一个故障影响。

如果 Firefly Router 的实际语法支持在模型 ID 中直接填写故障转移链,那么业务侧通常只需要保留一个模型配置:

export LLM_MODEL="deepseek-chat,<备用模型ID>,<第二备用模型ID>"

请把尖括号中的内容替换为真实模型 ID,并根据 Router 文档确认分隔符。重点不是把多个模型写进业务代码,而是让路由层维护这条顺序明确的候选链。

让夜间任务真正可靠

模型故障转移只能解决“换一个模型继续请求”,不能自动解决所有业务问题。夜间任务还需要配合幂等、超时、日志和告警。

下面是一个可以直接改造的 Python 调用示例。假设 Firefly Router 提供兼容 OpenAI 的 HTTP API,具体地址、认证变量和模型链格式请按你的部署替换:

import os
import time
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["FIREfly_ROUTER_API_KEY"],
    base_url=os.environ.get(
        "FIREFLY_ROUTER_BASE_URL",
        "http://localhost:8080/v1",
    ),
)

MODEL_ROUTE = os.environ.get(
    "LLM_MODEL",
    "deepseek-chat,qwen-plus,gpt-4o-mini",
)


def generate_report(input_text: str) -> str:
    response = client.chat.completions.create(
        model=MODEL_ROUTE,
        messages=[
            {
                "role": "system",
                "content": "你是一个严谨的报告助手,只输出可核验的结论。",
            },
            {"role": "user", "content": input_text},
        ],
        timeout=60,
    )
    return response.choices[0].message.content or ""


for attempt in range(3):
    try:
        report = generate_report("请根据输入数据生成夜间运营报告。")
        with open("nightly-report.md", "w", encoding="utf-8") as file:
            file.write(report)
        print("report generated")
        break
    except Exception as exc:
        print(f"attempt {attempt + 1} failed: {exc}")
        if attempt == 2:
            raise
        time.sleep(10 * (attempt + 1))

这个示例中有三个需要留意的边界:

  1. Router 的内部故障转移应优先于业务侧重试。否则一次业务重试可能再次从首选模型开始,浪费时间,也可能放大限流。
  2. 文件写入、数据库更新和消息发送要设计成幂等。模型请求成功但进程在保存结果前退出时,重跑任务不能制造重复数据。
  3. 不要捕获异常后静默结束。达到重试上限后应该记录失败任务、发送告警,或者写入一个可恢复的任务队列。

哪些错误应该触发切换

并非所有错误都适合立即切换模型。认证失败、提示词格式错误、输入超出上下文限制等问题,换模型通常仍然失败,还会掩盖真正原因。

建议把错误分成两类:

错误类型 是否适合故障转移 处理建议
408、429、500、502、503、504 通常适合 按退避策略尝试备用模型
网络连接失败、读取超时 通常适合 限制重试次数并切换通道
401、403 通常不适合 检查密钥、权限和账户状态
400、上下文超限 通常不适合 修正请求或压缩输入
内容审核或业务策略拒绝 视情况而定 不要无条件绕过策略

如果 Router 能够按状态码和错误类型配置重试规则,优先使用 Router 的规则。业务代码只负责处理最终失败,例如记录任务状态为 failed 并触发告警。

上线前检查清单

  • 首选模型不可用时,备用模型确实能返回结果
  • 备用模型支持相同的 API 协议、消息格式和关键参数
  • 不同模型的输出长度、工具调用和结构化输出能力经过验证
  • 已设置连接超时、读取超时和最大重试次数
  • 已区分可重试错误与请求本身的业务错误
  • 夜间任务具备幂等机制,可以安全重跑
  • 失败、切换、最终成功和最终失败都有日志
  • 备用模型的价格、速率限制、数据处理策略符合要求
  • 定期执行一次故障演练,而不是等真正故障发生时才验证

模型故障转移的价值不在于保证每次调用都成功,而在于把一次供应商故障降级成一次可观测、可恢复的路由事件。对于无人值守的夜间任务,先配置一条可靠的模型链,再补上重试、告警和幂等,通常就能显著降低单一模型故障带来的影响。


相关推荐