Chapter 08

离线活儿一律半价

如果任务不急着"秒回"——夜间批跑数据标注、全量文档向量化前的分类、周报自动生成——Batch API 砍一半价。本章详细讲提交格式、轮询策略、重试、以及搭配 Caching / 模型分层的"月账单杀手组合"。

Batch 是什么

Batch API 接受一次提交多达 10,000 条请求,Anthropic 在 24 小时内异步处理完。相对实时 API:

适合什么场景

不适合:聊天、实时工具调用、用户等待结果的场景。

提交一个 Batch

import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();

const batch = await client.messages.batches.create({
  requests: [
    {
      custom_id: "task-1",
      params: {
        model: "claude-sonnet-4-6",
        max_tokens: 256,
        messages: [{ role: "user", content: "总结这段:..." }],
      },
    },
    {
      custom_id: "task-2",
      params: {
        model: "claude-haiku-4-5",
        max_tokens: 128,
        messages: [{ role: "user", content: "分类:中性/正面/负面:..." }],
      },
    },
    // 最多 10000 条
  ],
});

console.log(batch.id);        // batch_01XYZ...
console.log(batch.processing_status);   // "in_progress"

custom_id 是你自定义的标识——用于对齐结果和输入,必须唯一。

Batch 状态流转

状态含义
in_progress处理中,请轮询
canceling你取消后的过渡
ended全部完成,可拉结果

轮询 & 拉取结果

async function waitBatch(id: string) {
  while (true) {
    const b = await client.messages.batches.retrieve(id);
    if (b.processing_status === "ended") return b;
    await new Promise((r) => setTimeout(r, 60_000));   // 每分钟查
  }
}

const finished = await waitBatch(batch.id);

// 流式拉结果
for await (const entry of await client.messages.batches.results(batch.id)) {
  if (entry.result.type === "succeeded") {
    console.log(entry.custom_id, entry.result.message.content[0].text);
  } else if (entry.result.type === "errored") {
    console.error(entry.custom_id, entry.result.error);
  }
}

结果流里每条都有一个 custom_id,你用它把输出对齐回原始输入。顺序不保证——所以 custom_id 是唯一线索。

结果的三种可能

succeeded
正常完成,result.message 就是标准 Message 对象
errored
请求本身错了(schema 问题、内容 safety 等),result.error 有详情
expired / canceled
超出 24 小时未完成,或你主动取消

取消 batch

await client.messages.batches.cancel(batch.id);
// 未开始的请求立即取消;已完成的保留结果

Batch 限额

Batch + Caching 组合

Batch 请求内也能用 cache_control——且缓存命中是 0.1x × 0.5x = 0.05x 的实际价格:

requests: allDocs.map((doc, i) => ({
  custom_id: `doc-${i}`,
  params: {
    model: "claude-sonnet-4-6",
    max_tokens: 256,
    system: [
      { type: "text", text: "你是文档分类助手。类目:A/B/C/D" },
      {
        type: "text",
        text: taxonomyDescription,   // 详细类目说明,5000 tokens
        cache_control: { type: "ephemeral" },
      },
    ],
    messages: [{ role: "user", content: doc }],
  },
})),

10000 条请求,共用 system 缓存:第一条付 1.25 × 0.5 = 0.625x 写,后面 9999 条付 0.1 × 0.5 = 0.05x 读。系统提示部分相当于原价的 5%

三件套组合 = 月账单砍 80%

最强省钱三连
模型分层:Haiku 过滤 → Sonnet 主力 → Opus 攻坚
Prompt Caching:system / 知识库 / 工具定义全缓存
Batch API:不紧急的走批量
叠加下来一笔订单的成本可以降到"原价的 20% 左右"——这是生产系统的基本功。

成本估算实战

对 50 万条评论做情感分析,每条输入 100 tokens,输出 20 tokens,Sonnet 4.x:

方案Input 成本Output 成本合计
A. 实时 API50M × $3 = $15010M × $15 = $150$300
B. Batch API50M × $1.5 = $7510M × $7.5 = $75$150
C. Batch + Haiku50M × $0.4 = $2010M × $2 = $20$40
D. 换更小模型 + 精简 prompt极限可以到 $10 以下

多模型 routing 策略

async function smartRoute(userMsg: string) {
  // 第 1 步:Haiku 做 intent 分类
  const intent = await classifyIntent(userMsg);   // Haiku,几百 token

  // 第 2 步:按 intent 路由
  switch (intent) {
    case "simple_qa":
      return callClaude("claude-haiku-4-5", userMsg);
    case "code_task":
      return callClaude("claude-sonnet-4-6", userMsg);
    case "complex_reasoning":
      return callClaudeWithThinking("claude-opus-4-7", userMsg);
  }
}

实测下来 70% 请求能被 Haiku 搞定,20% 走 Sonnet,10% 才上 Opus—— 平均成本可降 3-5 倍

Rate Limit 层级

Tier月消费RPM 上限(估算)
Build Tier 1$5+Sonnet ~50 RPM
Build Tier 2$40+Sonnet ~1000 RPM
Build Tier 3$200+更高
Build Tier 4$400+更高
Enterprise联系销售自定义

达不到就等过几天/加充值自动升级。Batch API 的限额另算,比实时接口更慷慨。

Batch 的局限

审计与报表

Anthropic console 有 Usage 页按模型/日期分组,但生产上最好自己在 DB 里存每次 usage 字段——成本能按 user_id / feature 归因:

await db.insert("llm_call", {
  user_id,
  feature: "summarize",
  model: msg.model,
  input_tokens: msg.usage.input_tokens,
  output_tokens: msg.usage.output_tokens,
  cache_read: msg.usage.cache_read_input_tokens ?? 0,
  cache_write: msg.usage.cache_creation_input_tokens ?? 0,
  thinking_tokens: msg.usage.thinking_tokens ?? 0,
  cost_usd: computeCost(msg),
  created_at: new Date(),
});

周五看看 top 10 最贵的 feature / user —— 通常能发现超限使用 / prompt 臃肿 / 没用缓存等问题。

本章小结