Claude 模型选型:Opus/Sonnet/Haiku + 成本/限速预算(Kotlin)

1)先统一概念:你要优化的是"单位成功结果的成本"

选模型时最容易犯的错是只看单价。真正该优化的是:

  • 一次任务成功的概率(失败就要重试/补救)
  • 成功一次消耗的 token(输入 + 输出)
  • 在限速下的吞吐(RPM/ITPM/OTPM 约束)

这三者乘起来,才是你线上最终的"单位成功结果成本"。


2)官方定价怎么读:输入/输出 + 缓存 + Batch

Claude 的计费至少要拆开看四项:

  • Base Input Tokens(普通输入)
  • Output Tokens(输出)
  • Prompt Caching:缓存写入与缓存命中(读取)是另一套价格
  • Batch:Message Batches API 的输入/输出都有折扣

官方价格表在这里(中文):定价

工程结论(先记住这两句就够用):

  • 长上下文/重复 system 的场景,Prompt Caching 往往比"换便宜模型"更省钱
  • 大批量非实时任务,优先考虑 Message Batches(折扣 + 削峰)

3)先按任务类型定档:不是所有请求都配得上 Opus

我用一个很粗但实用的分层:

A. 短任务、高并发(抽取/分类/格式化)

  • 首选:Haiku
  • 不够稳:升 Sonnet
  • 关键点:把 max_tokens 卡死(200-600),别让输出失控

B. 线上对话/通用助理(质量/速度/成本均衡)

  • 首选:Sonnet
  • 只有在"明显需要更强推理或更复杂代码"才上 Opus

C. 长文档/长代码理解(长输入 + 中等输出)

  • 首选:Sonnet 起步
  • 卡住再升 Opus
  • 关键点:先检索 topK,再拼上下文;能缓存就缓存

D. 攻坚型任务(失败代价高、一次值很大)

  • 首选:Opus
  • 关键点:写清验收标准 + 自检;必要时两阶段生成(提纲→分段)

4)限速预算:用 ITPM/OTPM 反推并发,而不是靠拍脑袋

Claude API 的限速是三条线:RPM/ITPM/OTPM。超过任何一条都会 429,并返回 retry-after。官方说明见:速率限制

你可以用一个简单估算把"并发"算到一个不离谱的范围:

假设每次请求平均消耗:

  • 未缓存输入 (in) tokens
  • 输出 (out) tokens

那么在 ITPM/OTPM 上限分别为 (ITPM_limit)、(OTPM_limit) 时:

req/min \\approx \\min\\left(\\frac{ITPM_limit}{in}, \\frac{OTPM_limit}{out}\\right)

最后再和 RPM 上限取最小值,就是你"理论上限吞吐"。上线后你需要用 p95 的 in/out token 重算一次,否则高峰期还是会撞。

Prompt Caching 会影响 ITPM(重点)

对大多数模型,从缓存读取的输入 token(cache_read_input_tokens)不计入 ITPM ,只有未缓存输入和缓存写入计入。官方解释见:缓存感知 ITPM

所以"同样 200K 上下文",命中缓存与不命中,吞吐差距会非常明显。


5)Kotlin 选型器:给出模型 + max_tokens + 并发建议 + 降级策略

下面这段 Kotlin 代码是"把经验固化",便于你放到配置中心或网关层。

kotlin 复制代码
enum class TaskKind {
  CLASSIFY_EXTRACT,
  CHAT_ASSISTANT,
  LONG_DOC,
  TOOL_JSON,
  CODING,
  HARD_PROBLEM
}

data class TierLimits(
  val rpm: Int,
  val itpm: Int,
  val otpm: Int
)

data class Advice(
  val model: String,
  val maxTokens: Int,
  val suggestedConcurrency: Int,
  val fallbackModel: String,
  val notes: List<String>
)

fun advise(
  kind: TaskKind,
  avgUncachedInTokens: Int,
  avgOutTokens: Int,
  limits: TierLimits,
  failureCostHigh: Boolean,
  needLowLatency: Boolean
): Advice {
  val haiku = "claude-haiku"
  val sonnet = "claude-sonnet"
  val opus = "claude-opus"

  fun throughputRpmByTokens(): Int {
    val byIn = if (avgUncachedInTokens <= 0) Int.MAX_VALUE else limits.itpm / avgUncachedInTokens
    val byOut = if (avgOutTokens <= 0) Int.MAX_VALUE else limits.otpm / avgOutTokens
    return minOf(limits.rpm, byIn, byOut).coerceAtLeast(1)
  }

  fun concurrencyFromRpm(rpm: Int): Int {
    // 粗估:并发≈每秒请求数×一个请求的平均"在途时间"
    // 这里不拿真实延迟,给个保守值:低延迟 0.6s;普通 1.2s
    val inFlightSec = if (needLowLatency) 0.6 else 1.2
    val rps = rpm / 60.0
    return (rps * inFlightSec).toInt().coerceIn(1, 64)
  }

  val maxTokens = when (kind) {
    TaskKind.CLASSIFY_EXTRACT -> 300
    TaskKind.CHAT_ASSISTANT -> 800
    TaskKind.LONG_DOC -> 1200
    TaskKind.TOOL_JSON -> 600
    TaskKind.CODING -> 1000
    TaskKind.HARD_PROBLEM -> 1400
  }.let { base -> if (needLowLatency) (base * 0.7).toInt().coerceAtLeast(128) else base }

  val model = when (kind) {
    TaskKind.CLASSIFY_EXTRACT -> if (failureCostHigh) sonnet else haiku
    TaskKind.CHAT_ASSISTANT -> sonnet
    TaskKind.LONG_DOC -> if (failureCostHigh && avgUncachedInTokens > 40_000) opus else sonnet
    TaskKind.TOOL_JSON -> if (failureCostHigh) opus else sonnet
    TaskKind.CODING -> if (failureCostHigh) opus else sonnet
    TaskKind.HARD_PROBLEM -> opus
  }

  val fallback = when (model) {
    opus -> sonnet
    sonnet -> haiku
    else -> haiku
  }

  val rpmBudget = throughputRpmByTokens()
  val conc = concurrencyFromRpm(rpmBudget)

  val notes = buildList {
    add("预算基于未缓存输入 token;如果用 Prompt Caching,ITPM 压力会明显下降")
    add("把 max_tokens 卡死,先提纲再分段输出,能降低 OTPM 压力与截断率")
    add("建议分桶:短任务/长任务分队列,避免长任务拖死短请求")
  }

  return Advice(
    model = model,
    maxTokens = maxTokens,
    suggestedConcurrency = conc,
    fallbackModel = fallback,
    notes = notes
  )
}

你把 TierLimits 替换成你在控制台看到的实际值即可。建议上线后用指标校准:

  • avg/p95 输入(未缓存)token
  • avg/p95 输出 token
  • p95 延迟
  • 429 占比

6)把策略下沉到"统一接入层",会省很多重复劳动(147api)

当你有多个服务/多个实例/多个项目同时用模型时,"每个服务各写一套限流+重试"很快会乱。

更常见的做法是把以下能力放到网关层统一处理:

  • 多 Key 池轮换与限额
  • 全局限流(跨实例共享),避免单机视角误判
  • 失败退避、熔断、降级、备用线路
  • 按项目/用户做账单与配额

如果你不想自建网关,可以了解下 147api:它提供 OpenAI 风格的聚合管理并覆盖 GPT、Claude、Gemini 等模型,适合把"接入层工程"集中处理,业务只保留配置化的 base_url/api_key/model


一页结论(适合贴团队 wiki)

  • 默认:Sonnet
  • 高并发短任务:Haiku(并把 max_tokens 卡死)
  • 高价值攻坚:Opus(写清验收 + 自检)
  • 吞吐不稳别先换模型:先算 ITPM/OTPM,再做缓存/分桶/分段输出
相关推荐
小羊没烦恼!1 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
千里马学框架1 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台1 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone1 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
伞伞悦读1 天前
【第38期】Python 模块与包详解:import、from、模块搜索路径、包结构和 __init__
开发语言·python
致远ccc1 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
C语言小火车1 天前
C/C++ 为什么需要编译器?
开发语言·c++
霍霍的袁1 天前
【C++】map 和 set 的使用 | 从用法到底层
开发语言·c++·学习·visual studio
ttyyttemo1 天前
Kotlin 协程中的 Job 结构化并发与取消
android
孙启超1 天前
【AI开发之Rust】第 11 课:智能指针与内部可变性
开发语言·后端·rust