本页目录

成本、性能与可靠性

把 LLM 接进生产,绕不开三件事:账单(token 怎么省)、延迟(怎么不卡)、稳定(限流和错误怎么扛)。它们散落在前几篇里,这篇收口成一张工程清单。

概述

原型跑通到上线,中间隔着工程化。Demo 里一次 messages.create 拿到结果就行;生产里要回答:这一万次调用要花多少钱?用户等几秒?撞了 429 怎么办?重试会不会把一封邮件发两遍?

这些问题的答案散在前几篇——prompt caching流式与 thinkingusage 字段按能力选模型。这一篇把它们按成本 / 性能 / 可靠性三条线收口,作为上线前的核对清单。

成本:token 是唯一计费单位

账单 = input token × 单价 + output token × 单价(output 通常贵 5×)。省钱的杠杆按收益排序:

1. prompt caching —— 最大的杠杆

多轮和 agent 循环里前缀高度重复。命中缓存的部分按 ~0.1x 计费(见 上下文工程)。算笔账(写入 5 分钟 TTL 是 1.25x):

  • 不缓存:每次 1.0x。
  • 缓存:首次写 1.25x,之后每次读 0.1x。⁠两次就回本⁠(1.25 + 0.1 = 1.35 < 2.0)。1 小时 TTL 写入 2x,需三次回本。

前提是前缀逐字节稳定⁠——别在 system 插 now()/UUID、别中途改工具集或换模型,否则缓存静默失效。验证看 usage.cache_read_input_tokens,长期为 0 就是有"静默失效器"。

2. 选对模型

不是所有任务都要顶配。用 Models API 查能力、按成本分层:简单分类/抽取走便宜模型,复杂推理/agent 走 Opus。多 agent 场景把子任务派给便宜模型(但别在同一循环里换模型毁缓存,另起 subagent,见 多 Agent 编排)。

3. 控输出与思考

output 比 input 贵。max_tokens 给足以防截断,但别无脑拉满;用 effort 调思考深度(见 推理与 thinking),effort 越低 token 越省——简单任务别反射式拉 xhigh

4. 批处理 —— 非实时任务半价

不要求即时返回的活(离线分析、批量分类、建索引)走 Batches API,所有 token 5 折⁠:

batch = client.messages.batches.create(requests=[
    Request(custom_id="job-1", params=MessageCreateParamsNonStreaming(
        model="claude-opus-4-8", max_tokens=1024,
        messages=[{"role": "user", "content": "..."}])),
    # ... 最多 10 万条 / 256MB
])
# 轮询 batches.retrieve(id).processing_status 直到 "ended"(多在 1h 内, 上限 24h)

结果乱序返回——必须按 custom_id 对号,绝不能靠顺序。 这与下文"绝不靠顺序归因"是同一条铁律。

估算先行

发请求前能用 count_tokens(无状态,见 Token 与采样)估 input 量、乘单价预判成本;⁠别用 tiktoken 估 Claude,少算 15–20%。

性能:延迟从哪来,怎么压

延迟主要是 output 长度的函数(每个 token 逐个解码)+ 思考开销 + 网络。

  • 流式(streaming)是默认。 长输入/长输出/大 max_tokens 一律流式:首 token 就开始返回,既改善体感(TTFT),又避开非流式请求的 HTTP 超时⁠(SDK 对大 max_tokens 的非流式请求会直接拒绝,因为可能超 10 分钟把连接拖断)。要完整结果用 .get_final_message() / .finalMessage()
  • effort 与回合数非线性。 agentic 任务上高 effort 常减少回合、反而更快更省;别假设"低 effort 一定快"。在自己的 eval 集上扫档(见 评估与可观测)。
  • 缓存也是延迟杠杆。 命中缓存的前缀跳过重算,首请求慢可用 预热⁠(启动时发一个 max_tokens: 0 请求把前缀写进缓存)消掉。
  • 并行有缓存时序坑。 N 个相同前缀的请求同时发,谁也读不到别人还在写的缓存,全付全价;先发 1 个、等它开始流式、再发其余 N−1,后面的就能命中。

可靠性:限流、重试、幂等

错误码与该不该重试

含义可重试
400请求非法(参数/格式)❌ 改请求
401 / 403认证/权限
404模型 ID 错或端点错
429限流(RPM/TPM/TPD 超额)✅ 退避后重试
500 / 529服务端错/过载✅ 退避后重试

铁律:4xx(除 429)不要重试⁠——同样的请求重发还是错。429/5xx 才退避重试。

退避与 SDK 自带重试

429 响应带 retry-after 头(等多少秒)。SDK 默认已对 408/409/429/5xx 做指数退避重试(max_retries=2)——多数情况无需自己写。只有要更激进/更克制时才自定义:指数退避 + 抖动(jitter),设上限。

超时也会被重试,所以最坏墙钟时间可达 timeout × (max_retries+1),设 timeout 时把这点算进去。

幂等:重试不能把副作用做两遍

退避重试遇上有副作用的工具⁠(发邮件、扣款、git push)是隐患:第一次其实成功了只是响应超时,重试就发了两封。对策落在宿主侧,不在模型侧:

  • 危险/不可逆动作走 human-in-the-loop 确认(见 Agent 循环)。
  • 副作用操作带幂等键⁠:同一逻辑动作用同一 key,服务端去重。
  • 把"决定做"和"真去做"分开:模型产出意图,宿主校验+幂等执行。

归因:并发/批量结果绝不靠顺序

并发请求、Batches 结果都可能乱序返回。用 custom_id / trace id 把结果对回请求(见 评估与可观测),⁠绝不靠到达顺序⁠——这是批量场景最常见的串号 bug。

最佳实践

  • prompt caching 先上,前缀焊死。 稳定前缀放前、动态后置,两次调用即回本;盯 cache_read_input_tokens 验证命中。
  • 按成本分层选模型。 简单任务便宜模型、复杂走 Opus;Models API 查能力,别猜。
  • 非实时任务走 Batches,半价。 结果按 custom_id 对号,绝不靠顺序。
  • 长输入/输出一律流式。 改善 TTFT + 避开非流式 HTTP 超时;完整结果用 get_final_message
  • 重试只给 429/5xx,4xx 不重试。 优先用 SDK 自带退避,别重造轮子。
  • 有副作用的动作要幂等 + 人工确认。 退避重试 × 副作用 = 重复执行,用幂等键 + human-in-the-loop 兜。
  • 成本/延迟进监控。 usage 三字段、stop_reason 分布、缓存命中率上仪表盘(见 评估与可观测)。

权衡与失败模式

  • 缓存静默失效⁠:system 插时间戳/中途换模型,cache_read 长期 0、成本翻几倍 → 焊死前缀,逐字节 diff 排查。
  • 无脑顶配模型⁠:简单任务也用 Opus + max,贵且慢 → 按任务分层,扫 effort。
  • max_tokens 非流式⁠:HTTP 超时、连接被拖断 → 流式 + get_final_message
  • 4xx 也重试⁠:同样请求重发还是错,白烧配额 → 只重试 429/5xx。
  • 重试踩副作用⁠:超时重发把邮件发两遍 → 幂等键 + 人工确认危险动作。
  • 靠顺序归因⁠:Batches/并发乱序导致串号 → 一律 custom_id/trace id 对号。
  • tiktoken 估 Claude 成本⁠:低估 15–20% → count_tokens

参考

Keywords: cost optimization, token 计费, prompt caching 经济学, cache_read_input_tokens, Batches API, custom_id, 乱序归因, 50% 折扣, count_tokens, 选模型, Models API, effort, streaming, TTFT, HTTP timeout, 缓存预热, max_tokens 0, rate limit, 429, retry-after, 指数退避, exponential backoff, jitter, max_retries, 幂等, idempotency, human-in-the-loop, 错误码, 4xx 不重试