Claude Batch API 实战:50 份周报一晚跑完,账单砍半,Python 批处理管线全流程

在这里插入图片描述

先说结论

如果你的任务是"50 份周报、几百份合同、上千条工单"这类离线批量处理,别再用同步 Messages API 一条条调了。把同样的请求打包进 Anthropic 的 Message Batches API,input 和 output 的 token 计费直接打五折,全模型适用。最近大家都在讨论「DeepSeek涨价你换了吗」——与其纠结换不换模型,先看看自己账单里有多少本来就该走批量的调用,半价折扣就是现成的对冲手段。

代价只有一个:结果不保证实时回来。官方给的是 24 小时窗口,实际上大多数批次不到 1 小时就跑完。你今晚提交,睡前轮询几次,明早收结果,节奏完全够用。

下面用 Python 把整条管线拆开讲:submit 提交、poll 轮询、retrieve 取结果三阶段,外加单条失败隔离。文末附一个不需要 API key 的 dry-run 验证思路,控制流先跑通,再接真实调用。

一、三阶段状态流转:submit → poll → retrieve

Batch API 的核心流程只有三步,对应三个 endpoint:

阶段Endpoint说明
提交POST /v1/messages/batches一次最多 100,000 条请求,上限 256MB
轮询GET /{batch_id}processing_status
取结果GET /{batch_id}/resultsJSONL 流式返回

processing_status 只有三个值:in_progress(处理中)、canceling(取消中)、ended(已结束)。轮询逻辑就是围绕这三个状态做分派,收到 ended 才去拉结果。批次处理完还有一层兜底:结果在 Anthropic 那边保留 29 天,错过当晚再取也没问题。

在这里插入图片描述

二、核心代码:三个函数撑起整条管线

先构建请求。每份文档一条请求,custom_id 用文件名做主键,params 里的内容和同步 Messages API 完全一样——model、max_tokens、messages,改造成本为零:

import pathlib

def build_requests(docs_dir, model="claude-sonnet-4-5"):
    """每份文档打包一条批处理请求,custom_id 用文件名做主键"""
    requests = []
    for path in sorted(pathlib.Path(docs_dir).glob("*.txt")):
        requests.append({
            "custom_id": path.stem,               # weekly-01, weekly-02 ...
            "params": {
                "model": model,
                "max_tokens": 1024,
                "messages": [{
                    "role": "user",
                    "content": f"提炼周报要点并给出一句话总结:\n{path.read_text()}"
                }],
            },
        })
    return requests

轮询函数做三态分派:endedRETRIEVEcanceling 直接 ABORT,其余情况继续等。轮询间隔建议放在 60–300 秒,别每秒去戳接口——批内任务既不会因为你轮询得勤而变快,还会白白消耗请求配额:

def poll_once(client, batch_id):
    """三态分派:ended→RETRIEVE / canceling→ABORT / 其他→KEEP_POLLING"""
    batch = client.messages.batches.retrieve(batch_id)
    status = batch.processing_status
    if status == "ended":
        return "RETRIEVE"
    if status == "canceling":
        return "ABORT"
    return "KEEP_POLLING"

取回结果后逐行处理。JSONL 里每行带 result.type,四种类型各走各的分支,这就是单条失败隔离的关键——一条坏了不会拖垮整批:

def handle_result(line):
    """四态分派:succeeded/errored/expired/canceled"""
    result = line["result"]
    if result["type"] == "succeeded":
        return "OK"
    if result["type"] == "errored":
        err_type = result["error"]["type"]
        # invalid_request 是校验错误,修正请求体后重发
        return "FIX_AND_RESEND" if err_type == "invalid_request" else "RETRY_LATER"
    if result["type"] == "expired":
        return "RESUBMIT"        # 24h 窗口超时,重新提交即可
    return "SKIP"                # canceled:主动取消的,跳过

errored 要再细分一次:invalid_request 是请求体校验问题(比如参数写错),本地修正后重发就行;server error 是对方临时故障,稍后重试即可。两者的处理动作完全不同,混在一起会把能救的请求也丢掉。

这套三阶段管线可以直接复制到你的项目里,收藏备用。

三、custom_id:整个批次唯一的秩序锚点

Batch API 的结果不保证按提交顺序返回。50 份周报按顺序发出去,结果可能是乱的——第 7 行结果对应的可能是第 23 份周报。所以 custom_id 不是可选项,是你把结果拼回输入的唯一依据。

实践上有两条铁律:

  1. custom_id 用源数据的主键或文件名,别用自增序号以外的临时值,更别在多个批次里复用导致对不上账;
  2. 处理结果时以 custom_id 做 dict 映射,写回原文件时再按主键查找,永远不要假设"第 i 行结果对应第 i 条输入"。

顺序不保证这个坑值得收藏,接 Batch 之前先定好主键,后面的隔离和写回逻辑都建立在它之上。

四、dry-run 验证:不花一分钱跑通控制流

接真实 API 之前,我先用脚本 claude_batch_pipeline.py --dry-run 把控制流验证了一遍——50 份周报的请求全部构建成功,状态机按预期走完三阶段,四种结果类型各自落到正确分支:

requests built: 50(custom_id 从 weekly-01 起)
poll actions: ['KEEP_POLLING', 'KEEP_POLLING', 'RETRIEVE']
result dispositions: ['OK', 'FIX_AND_RESEND', 'RESUBMIT', 'SKIP']
final counts: 47 ok / 2 err / 50 total

三个动作都符合预期:轮询两次后拿到 ended,进入取结果阶段;四种 disposition 各自触发——47 条直接成功,2 条走了错误分支(一条修正重发、一条重新提交,被 canceled 的跳过),50 条总输入全部有了归宿,没有一条请求被静默丢弃。

dry-run 驱动的是纯状态序列,没有任何 API 调用,也就没有任何计费。它的价值在于:等你真正拿到 key 提交批次时,代码里每个分支你都见过它怎么走。

五、成本到底省在哪

直接上图。为了避免美元单价随官方调价过时,这里用归一化成本指数:同步调用记为 100,Batch 记为 50——每一个 token 都打五折,input 和 output 都算

在这里插入图片描述

更狠的是它可以和 prompt caching 叠加:cache 读取的折扣是在 Batch 五折的基础上再乘,如果你批量处理的文档有公共前缀(比如同一套周报模板、同一个 system 提示词),叠加之后的实际成本远不止减半。

还有一点常被忽略:Batch 走独立的限流池,不占你实时调用的 RPM/TPM 配额。白天跑在线业务,晚上跑批量任务,两条通道互不干扰。

六、三个踩坑点,提前避开

#踩坑行为后果正确姿势
1假设结果按提交顺序返回custom_id 没对齐,周报内容张冠李戴用源数据主键做 dict 映射回写
2每秒轮询状态接口浪费配额,对进度毫无帮助轮询间隔 60–300 秒
3把 expired 当失败放弃白白丢掉整批已构建的请求24h 超时后原样重新提交

这张表建议收藏,接 Batch 前对照检查一遍,能省掉至少一次半夜排查。

最后补一个结构性的提醒:批内请求相互独立、无共享上下文。Batch 不适合跑 Agent 循环——Agent 的每一步都依赖上一步的输出,那是串行依赖,塞进批次里只会拿到一堆互相看不见的孤儿请求。批量的归批量,实时的归实时。

七、和前几篇的边界

有读者可能会问:这和之前写 Claude 状态机、循环编排、Prompt 契约的几篇是不是一回事?不是。那几篇讲的全部是实时单次调用视角——一次请求、同步返回、立刻处理。本篇是离线批量视角:请求先攒批、异步执行、按主键收割结果,两者的工程结构完全不同,甚至可以组合使用(在线链路用实时接口,夜间任务走 Batch)。如果你手上的任务没有"必须 30 秒内返回"的约束,批量视角大概率能帮你把账单砍掉一半。

参考

  1. Message Batches API 官方文档
  2. Anthropic Pricing 定价页
  3. Messages API 参考

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值