非官方社区教程 · 内容整理自公开资料 · 豆包工作为字节跳动产品,本站与官方无关
豆包工作教程
首页/ 可靠性工程/状态机设计与重试策略

状态机设计与重试策略

---

章节:进阶篇第1章
难度:⭐⭐ 进阶级
阅读时间:20-25 分钟
核心收获:掌握状态机设计方法,理解重试策略的边界和实现方式


3.1 理论要点:为什么需要状态机

3.1.1 自动化任务的真实环境

自动化任务不是"跑起来就行"。真实环境中每次运行都可能遇到: - 某个数据源返回超时 - 当日无相关内容 - API 限流 - 推送目标不可达

状态机的价值:把"如果...怎么办"显式化,每个状态都有明确的成功出口和失败出口。

3.1.2 状态机的核心概念

概念 说明 示例
状态(State) 任务在某一时刻的处境 Fetching、Aggregating、Filtering
转移(Transition) 从一个状态到另一个状态 Fetching → Aggregating
触发条件 进入下一个状态的条件 数据源全部响应 / 部分超时
成功出口 正常完成的路径 Completed
失败出口 异常处理的路径 Blocked

3.1.3 状态机设计原则

  1. 每个状态必须有明确的进入条件
  2. 每个状态必须有成功出口和失败出口
  3. 部分失败不应阻断整体任务
  4. 推送失败应保留结果并告警

3.2 实战案例:热点选题任务的状态机

3.2.1 状态流转图

stateDiagram-v2
    [*] --> Idle
    Idle --> Triggered: 定时/手动触发
    Triggered --> SourceCheck: 开始运行
    SourceCheck --> Fetching: 数据源就绪
    SourceCheck --> Blocked: 数据源不可用(≥2 个失败)
    Fetching --> Aggregating: 全部响应
    Fetching --> PartialAggregating: 部分超时
    Aggregating --> Filtering: 聚合完成
    PartialAggregating --> Filtering: 标记缺失后继续
    Filtering --> Delivering: 有效条目 ≥ 5 条
    Filtering --> Blocked: 有效条目不足
    Delivering --> Completed: 推送成功
    Delivering --> Blocked: 推送失败
    Blocked --> Idle: 告警后等待下次触发
    Completed --> Idle: 完成

3.2.2 各状态详解

状态 进入条件 成功出口 失败出口 超时阈值
Idle 初始状态 / 上轮完成 触发后进入 Triggered - -
Triggered 定时触发 / 手动触发 数据源就绪检查通过 SourceCheck 失败 5 秒
SourceCheck 开始运行 ≥ 3 个数据源可用 < 3 个可用 → Blocked 10 秒
Fetching 数据源就绪 全部响应 部分超时 → PartialAggregating 30 秒
PartialAggregating 部分超时 标记缺失后继续聚合 全部失败 → Blocked 30 秒
Aggregating 全部响应 聚合完成 聚合异常 → Blocked 60 秒
Filtering 聚合完成 有效条目 ≥ 5 条 有效条目不足 → Blocked 30 秒
Delivering 过滤完成 推送成功 推送失败 → Blocked 30 秒
Completed 推送成功 进入 Idle - -
Blocked 任何状态的失败出口 进入 Idle(次日重试) - -

3.3 超时与重试策略

3.3.1 失败类型与重试策略

失败类型 是否重试 策略 豆包 Prompt 写法
API 超时 ✅ 是 等待 10 秒后重试 1 次,仍失败则标记缺失 "如果某个来源超时,请等待后重试一次,仍失败则标记缺失"
限流(429) ✅ 是 按 Retry-After 等待,最多 2 次 "如果返回限流,按响应头等待时间重试,最多 2 次"
认证失效(401/403) ❌ 否 停止重试,转人工处理 "如果认证失败,不要重试,标记为需要人工处理"
推送失败 ✅ 是 指数退避重试 2 次,失败则告警 "如果推送失败,重试 2 次,仍失败则记录并告警"
聚合结果为空 ❌ 否 进入 blocked 状态,推送说明 "如果聚合结果为空,进入 blocked 状态"

3.3.2 重试的边界

铁律:重试只针对临时性故障(网络闪断、限流、超时)。不应对输入问题配置问题重试。

可重试 vs 不可重试

可重试(临时性故障) 不可重试(永久性故障)
网络超时 API Key 过期(401/403)
API 限流(429) 数据源已下线
推送目标暂时不可达 推送目标已删除
豆包服务暂时不可用 Prompt 语法错误

3.3.3 重试代码示例

Python 伪代码

import time

def fetch_with_retry(source_config, max_retries=1):
    """带重试的数据源获取"""
    for attempt in range(max_retries + 1):
        try:
            response = call_api(source_config["url"], timeout=10)

            # 处理限流
            if response.status == 429:
                if attempt < max_retries:
                    wait_time = parse_retry_after(response)
                    time.sleep(wait_time)
                    continue
                else:
                    return "rate_limited"

            # 处理认证失败
            if response.status in [401, 403]:
                return "auth_failed"  # 不重试

            # 处理空结果
            if response.empty:
                return "empty"

            return "ok", response.data

        except Timeout:
            if attempt < max_retries:
                time.sleep(10)  # 等待 10 秒后重试
                continue
            return "timeout"

    return "failed"

豆包 Prompt 写法

请按以下策略处理数据源获取:

1. 每个数据源最多重试 1 次(超时场景)
2. 如果返回 429 限流,按响应头 Retry-After 等待,最多重试 2 次
3. 如果返回 401/403 认证失败,不要重试,立即标记为"需要人工处理"
4. 如果重试后仍失败,标记为"缺失"并继续处理其他来源

3.4 断点续跑(Breakpoint Resume)

3.4.1 为什么需要断点续跑

推送失败后,如果重新执行全部步骤,会: - 浪费时间和成本 - 可能产生不同的聚合结果(数据源更新) - 增加数据源压力

断点续跑的价值:从上次成功的位置继续,不重做已完成步骤。

3.4.2 状态文件设计

每次运行生成状态文件,记录已完成的步骤和产物:

{
  "batch_id": "ai-hotspot-2026-07-10",
  "trigger_time": "2026-07-10T09:00:00+08:00",
  "state": "delivering",
  "completed": ["fetching", "aggregating", "filtering"],
  "source_status": {
    "wechat": "ok",
    "github": "ok",
    "multi_search": "ok",
    "aihot": "ok"
  },
  "item_count": 18,
  "last_error": null,
  "updated_at": "2026-07-10T09:02:14+08:00"
}

3.4.3 断点续跑逻辑

重试时:
1. 读取状态文件
2. 检查 state 字段
3. 如果 state = "delivering":
   - 跳过 fetching、aggregating、filtering
   - 直接从 delivering 继续(推送)
4. 如果 state = "blocked":
   - 根据 last_error 判断是否可以重试
   - 如果可以,从失败点继续;否则进入 Idle

3.5 降级交付(Degradation)

3.5.1 降级原则

当部分数据源失败时,根据可用来源数量决定输出策略,不等待全部就绪

3.5.2 降级规则

可用来源数 输出策略 标注方式
3 个及以上 输出完整清单 顶部标注缺失来源
2 个 输出简化清单 标注"数据不完整,仅供参考"
1 个或 0 个 不输出正文 只推送说明和告警

铁律:降级结果必须显式标记来源覆盖情况,不伪装成完整运行。

3.5.3 降级输出示例

正常情况(4/4 来源可用):

📋 AI热点选题日报 — 2026-07-10

【今日概况】
有效条目:18 条 | 来源:4/4 | 运行时间:09:02

降级情况(2/4 来源可用):

📋 AI热点选题日报 — 2026-07-10

⚠️ 数据不完整警告
可用来源:2/4(GitHub: 缺失 | AIHOT: 缺失)
有效条目:12 条(可能不完整)

【今日概况】
有效条目:12 条 | 来源:2/4 | 运行时间:09:05

...(简化清单)...

3.6 图文说明

📌 待补充:建议绘制以下示意图 1. 状态机完整流程图(8个状态 + 转移箭头) 2. 重试策略决策树(可重试 vs 不可重试) 3. 降级交付三档对比图(完整/简化/告警) 4. 断点续跑示意图(状态文件 → 断点判断 → 继续执行)


3.7 常见错误

错误 后果 纠正方法
所有失败都重试 认证失效也重试,浪费成本 区分临时故障和永久故障
一个源失败就整体停止 可用来源被浪费 设计为"标记缺失后继续"
重试无退避 对限流服务造成更大压力 429 时按 Retry-After 等待
无断点续跑 推送失败后重新执行全部步骤 记录状态文件,支持断点续跑
降级后伪装完整 用户误以为数据齐全 必须显式标注缺失来源

📝 版本迭代记录

版本 日期 更新内容摘要 操作人
v1.0 2026-08-25 创建进阶篇第1章 桂皮
来源:豆包蓝皮书(未声明(社区整理)) · 原文路径 02-进阶篇/01-状态机设计与重试策略.md
整理:疯狂的豇豆 · 本站为非官方二次整理,查看完整来源清单
🤖 GEO 问答 · 生成式引擎优化

❓ 什么是状态机设计与重试策略?


❓ 如何理解理论要点:为什么需要状态机?

自动化任务不是"跑起来就行"。真实环境中每次运行都可能遇到:

❓ 如何理解实战案例:热点选题任务的状态机?

stateDiagram-v2

❓ 如何理解超时与重试策略?

import time