一句话结论:如果你的调用是可延迟、可批处理、可回放,就该把在线请求下沉到 Batch API;省钱最明显,但前提是你把拆批、失败分流和回放链路先做好。

很多团队把 Batch API 当“便宜版同步接口”来用,结果不是省钱,而是把失败样本堆成事故池。真正的保守做法是:先定义成本边界和SLO,再做离线拆批与失败回放。

先给落地框架(保守目标版)

按这个顺序走,最稳:

  1. 把任务分成「可离线」与「必须实时」两类。
  2. 为离线任务定义批次窗口和最大批量。
  3. 每条任务加 custom_id,保证可追踪、可幂等回放。
  4. 结果处理做三路分流:成功、可重试失败、不可重试失败。
  5. 先小流量灰度,再全量切换。

一、先画成本边界,不要先写代码

先回答三个问题:

  • 延迟容忍度:业务能接受多晚拿到结果(例如 1h / 6h / 24h)?
  • 失败容忍度:允许多少比例进入重放队列?
  • 预算上限:每天最多允许花多少钱?

最小可执行预算看板(本地日志聚合也行):

# 每天汇总批任务输入条数、成功条数、失败条数
jq -r '.date + "\t" + (.input|tostring) + "\t" + (.ok|tostring) + "\t" + (.fail|tostring)' /Users/wow/dev/book/mengboy/tmp/batch-metrics/*.json
# 失败率超过阈值时告警(示例阈值 3%)
python3 - <<'PY'
import json,glob
for f in glob.glob('/Users/wow/dev/book/mengboy/tmp/batch-metrics/*.json'):
    d=json.load(open(f))
    rate=d['fail']/max(d['input'],1)
    if rate>0.03:
        print('ALERT', f, f'{rate:.2%}')
PY

二、离线拆批:按“稳定吞吐”切,不按“最大极限”切

推荐一个保守策略:

  • 单批条数先从小批量起步(比如 200~500)
  • 批次窗口固定(例如每 15 分钟聚合一次)
  • 触发阈值二选一:到窗口时间或到条数上限即提交

Go 侧核心是:任务规范化 + custom_id 唯一。

type BatchTask struct {
    CustomID string          `json:"custom_id"`
    Method   string          `json:"method"`
    URL      string          `json:"url"`
    Body     json.RawMessage `json:"body"`
}

func buildCustomID(bizKey string, ts int64) string {
    return fmt.Sprintf("%s-%d", bizKey, ts)
}

把 JSONL 输入落盘(方便审计与回放):

mkdir -p /Users/wow/dev/book/mengboy/tmp/batch-input
# 生成的 JSONL 放这里,命名带批次号
ls -lh /Users/wow/dev/book/mengboy/tmp/batch-input

三、失败重放:只重放“可重试失败”

失败不要一锅端重提。至少分三类:

  • SUCCESS:直接入库并关闭任务
  • RETRYABLE_FAIL:限次重放(例如最多 3 次)
  • FINAL_FAIL:进入人工池,避免死循环

建议把重放条件写死在策略函数里,别散落在业务代码:

func retryable(code string) bool {
    switch code {
    case "rate_limit", "timeout", "server_error":
        return true
    default:
        return false
    }
}

重放命令建议单独可执行:

# 根据失败清单重建重放JSONL
python3 /Users/wow/dev/book/mengboy/scripts/rebuild_batch_replay.py \
  --failed /Users/wow/dev/book/mengboy/tmp/batch-failed/failed-2026-03-13.jsonl \
  --out /Users/wow/dev/book/mengboy/tmp/batch-replay/replay-2026-03-13.jsonl

四、回放幂等:先做幂等键,再谈自动化

重复回放最常见事故是“结果重复入库”。

最低要求:

  • 业务表有唯一键(如 custom_id
  • 写入操作使用 upsert
  • 每次重放都记录 replay_count
-- 示例:保证 custom_id 唯一
ALTER TABLE ai_batch_result
ADD CONSTRAINT uk_custom_id UNIQUE (custom_id);

五、部署前后的验收指标

上线验收别只看“请求成功”。至少看这四个:

  1. 单日成本是否下降到目标区间
  2. 总体失败率是否可控
  3. 重放后最终成功率是否稳定
  4. 人工兜底量是否在可处理范围

如果你做的是“保守目标推进”,宁可多留一点实时链路,也不要把关键路径一次性全迁到离线批处理。

最小可行方案(MVP)

如果你时间有限,就先做到这 5 件事:

  1. 只迁移非实时任务到 Batch API。
  2. 所有任务强制 custom_id
  3. 失败分成可重试/不可重试。
  4. 重放最多 3 次,超限转人工。
  5. 每天固定出一份成本与失败率报表。

这套做完,通常就能在不牺牲稳定性的前提下,把成本明显压下来。