Claude API 用量分析怎么做?从 Token 成本结构到可归因账单的工程实践

3 阅读13分钟

一篇面向后端与 AI 应用开发者的实践文:把 Claude API 的 usage 字段变成可核算、可归因、可告警的成本数据,附可运行 Python 与 SQL 示例。价格口径为 Anthropic 官方定价页 2026-11-03 快照。

大部分团队接大模型 API 的路径都差不多:先把功能跑通,再谈优化。于是账单成了唯一的成本信号——月底财务发来一张总额,团队围着这个数字猜哪条业务线贵、哪个功能失控。等你把猜出来的结论变成优化动作,下一个月的账单又来了。

这篇文章不谈"降本大招",只谈一件更前置的事:把 Claude API 的用量数据结构化下来,让每一次调用的成本可以被计算、被归因、被对比。没有这一步,谈什么都靠感觉。

一、用量分析失效的三个断点

在真实项目里,"我们做了用量统计"和"用量数据能支撑决策"之间,通常卡在三个断点上。

断点一:只有请求数,没有 token 结构。 很多团队埋点只记了 model、latency、status,没记 token 明细。请求数相同的两个功能,成本可能差十倍——一个每请求 800 输入 token 做分类,一个每请求 60K 输入 token 做长文档抽取。请求数是个毫无成本解释力的指标。

断点二:只算输入输出,漏掉缓存与重试。 Claude API 的计费项不止输入和输出,还有缓存写入与缓存读取,且两者的单价与输入价完全不同(缓存写入通常高于输入价,缓存读取远低于输入价)。再加上失败重试带来的重复计费、流式响应中断产生的半截输出,真实成本往往比"输入×单价 + 输出×单价"高出 15% 到 40%。

断点三:没有归因维度,成本无法落到人头上。 所有调用共用一个 API Key,日志里没有业务线、功能名、租户标识。这种状态下你能看到的只有一条总额曲线,任何优化动作都无法验证效果。

这三个断点的共同点:都不是模型问题,都是采集设计问题。补起来不难,但要趁早——字段规范一旦上线半年,历史数据补不回来。

二、数据源:一次 Claude API 调用里到底有几个 token 字段

先说清楚数据从哪来。Anthropic 的 Messages API 在每次响应里返回 usage 对象,核心字段如下:

字段含义计价方式
input_tokens本次请求未被缓存命中的输入 token 数按输入单价计费
output_tokens模型生成的输出 token 数按输出单价计费
cache_creation_input_tokens写入缓存的输入 token 数按缓存写入单价计费
cache_read_input_tokens从缓存读取的输入 token 数按缓存读取单价计费

两点容易被忽略的细节:

  • input_tokens 通常只统计未命中缓存的部分,命中缓存的那一段单独出现在 cache_read_input_tokens 里。所以"总输入量"应该用三项相加,而不是只看 input_tokens。只取 input_tokens 做统计的团队,往往会得出"我们输入量很小"的错误结论。
  • 缓存有 TTL,官方定价页的缓存价格对应 5 分钟 TTL,另有扩展缓存版本。如果你的调用间隔普遍长于 TTL,cache_creation_input_tokens 会持续产生而 cache_read_input_tokens 一直为 0——这是典型的"缓存设计失效",也是用量分析最该抓出来的浪费。

顺带说一句流式场景:流式响应的 usage 通常要到最后一个事件才完整,如果你在第一个 chunk 就落库,output_tokens 会缺失或严重偏小。这个坑非常常见,建议统一在流结束回调里更新日志记录。

三、把价格表变成成本函数

成本核算的前提是一张可信的价格表。下面是 Anthropic 官方定价页(2026-11-03 快照,单位:USD / 百万 token)中四款公开模型的按量价格:

模型输入输出缓存写入缓存读取
Claude Fable 5$10$50$12.50$1.00
Claude Opus 5$5$25$6.25$0.50
Claude Sonnet 5$2$10$2.50$0.20
Claude Haiku 4.5$1$5$1.25$0.10

关于 Sonnet 5 需要额外说明一个容易踩的坑:官方定价页在展示 $2 / $10 的同时保留了一条注脚——该价格为推广价,适用于 2026 年 8 月 31 日之前的用量,此后标准价为 $3 / $15。当前页面仍未把展示价切成标准价,两种口径并存。工程上的正确做法不是二选一,而是把价格表做成带生效区间的配置,让核算代码能按调用时间选择对应价格,而不是把单价硬编码进业务逻辑。这也是我反复强调"价格表是配置不是常量"的原因。

官方页另有两项与用量分析相关的能力计价,做预算时要单独列支:Web search 为 10/1K次搜索;Codeexecution每组织每天前50小时免费,超出部分10 / 1K 次搜索;Code execution 每组织每天前 50 小时免费,超出部分 0.05 / 小时 / 容器;Managed Agents 的活跃运行时间为 $0.08 / 会话小时,token 按标准价另计。

四、可运行示例:单次调用成本核算

有了字段和价格表,成本就是一个纯函数。下面这段 Python 只依赖标准库,可直接跑:

from dataclasses import dataclass, field
from datetime import date

# 价格表:USD / 百万 token,带生效区间(示例数据,口径见官方定价页 2026-11-03)
PRICE_TABLE = {
    "claude-opus-5": [
        # (生效起始日, 输入, 输出, 缓存写, 缓存读)
        (date(2026, 1, 1), 5.00, 25.00, 6.25, 0.50),
    ],
    "claude-sonnet-5": [
        (date(2026, 1, 1), 2.00, 10.00, 2.50, 0.20),   # 推广价
        (date(2026, 9, 1), 3.00, 15.00, 3.75, 0.30),   # 标准价(按官方注脚口径)
    ],
    "claude-haiku-4-5": [
        (date(2026, 1, 1), 1.00, 5.00, 1.25, 0.10),
    ],
}
M = 1_000_000


def pick_price(model: str, when: date):
    """按调用日期取当前生效价格,避免单价硬编码。"""
    rows = sorted(PRICE_TABLE[model], key=lambda r: r[0])
    effective = rows[0]
    for r in rows:
        if when >= r[0]:
            effective = r
    return effective[1], effective[2], effective[3], effective[4]


@dataclass
class Usage:
    model: str
    input_tokens: int
    output_tokens: int
    cache_write_tokens: int = 0
    cache_read_tokens: int = 0
    when: date = field(default_factory=date.today)

    def cost_usd(self) -> float:
        p_in, p_out, p_cw, p_cr = pick_price(self.model, self.when)
        return (
            self.input_tokens * p_in
            + self.output_tokens * p_out
            + self.cache_write_tokens * p_cw
            + self.cache_read_tokens * p_cr
        ) / M

    def total_input(self) -> int:
        """真实输入量 = 未命中 + 缓存写 + 缓存读。"""
        return self.input_tokens + self.cache_write_tokens + self.cache_read_tokens

    def cache_hit_rate(self) -> float:
        total = self.total_input()
        return self.cache_read_tokens / total if total else 0.0


if __name__ == "__main__":
    # 示例数据:一次长上下文抽取调用
    u = Usage(
        model="claude-sonnet-5",
        input_tokens=1_800,
        output_tokens=900,
        cache_write_tokens=0,
        cache_read_tokens=46_000,
        when=date(2026, 11, 3),
    )
    print(f"单次成本: ${u.cost_usd():.6f}")
    print(f"真实输入量: {u.total_input():,} tokens")
    print(f"缓存命中率: {u.cache_hit_rate():.1%}")

这段代码输出三个数:单次成本、真实输入量、缓存命中率。看起来简单,但它已经把"看请求数"问题解决了——成本是每次调用的返回值,而不是月底的黑箱。

一个值得记住的观察:当缓存读占比很高时,input_tokens 那一项几乎可以忽略,真正决定成本的是输出 token 和缓存写入。这就是为什么"我们的输入很长但账单不高"和"我们输出不长但账单很贵"可以同时成立。

五、可运行示例:按模型与功能线归因的聚合查询

单次成本只是原子数据,决策需要聚合。假设你把每次调用落成一张窄表(成本分析只需要四类字段,不要用宽表):

-- 建表:只保留成本分析必需字段,按天分区
CREATE TABLE llm_call_log (
  ts            TIMESTAMP,
  model         VARCHAR,
  feature       VARCHAR,   -- 功能线:doc_extract / chat / classify ...
  tenant_id     VARCHAR,
  input_tokens  BIGINT,
  output_tokens BIGINT,
  cache_w_tokens BIGINT,
  cache_r_tokens BIGINT,
  status        VARCHAR,   -- ok / error / timeout
  retry_of      VARCHAR    -- 重试链路的原始 request_id
);

-- 按功能线看单位成本:比总额更有决策力
SELECT
  feature,
  COUNT(*)                                             AS calls,
  SUM(input_tokens + cache_w_tokens + cache_r_tokens)  AS input_total,
  SUM(output_tokens)                                   AS output_total,
  ROUND(SUM(input_tokens + cache_w_tokens + cache_r_tokens) * 1.0 / COUNT(*), 1)
                                                       AS avg_input_per_call,
  ROUND(SUM(output_tokens) * 1.0 / COUNT(*), 1)         AS avg_output_per_call,
  ROUND(SUM(CASE WHEN status <> 'ok' THEN 1 ELSE 0 END) * 100.0 / COUNT(*), 2)
                                                       AS error_rate_pct
FROM llm_call_log
WHERE ts >= DATE '2026-10-29' AND ts < DATE '2026-11-05'
GROUP BY feature
ORDER BY input_total DESC;

再把 token 量乘上单价,就得到"每千次请求成本"这个真正可比的指标。这一步是分账和排序的关键:总额不能排序,单位成本才能排序。一个功能总额高可能只是因为它调用量大;单位成本高才说明它在浪费。

如果历史日志里没有 feature / tenant_id 字段,别急着事后猜。退一步的做法是从请求参数里的场景标记做近似拆分,并在报表上明确标注"近似归因,精度受限"。这条经验很贵:字段规范定晚了,半年的数据就只能做粗粒度复盘。

六、五个必须盯的用量指标

埋点做完之后,指标不在多,在于能驱动动作。我会盯这五个:

指标定义健康信号异常时的第一反应
每千次请求成本区间总成本 / 请求数 × 1000平稳或缓降看输出 token 均值是否上升
单位会话成本区间总成本 / 会话数与产品迭代节奏匹配看单会话轮次是否变多
缓存命中率缓存读 / 真实输入量长提示场景应显著大于 0检查 TTL 与提示前缀是否稳定
输出输入比输出 token / 真实输入 token按场景设定基线检查是否放宽了 max_tokens 或提示词变长
失败重试损耗率重试调用成本 / 总成本低于 5%看超时与限流是否集中在某模型

这五个指标的价值在于互相钳制。比如"每千次请求成本上升"单独看没有信息量,但配合"输出输入比同时上升"就能定位到是生成变长了;如果"缓存命中率同时下降",那基本可以确定是提示词前缀被改动了,缓存失效率上升。单指标报警噪声大,指标对撞才有诊断力。

七、异常识别:成本突增怎么在十分钟内定位

用量分析的终点是告警。与其等财务问"这个月怎么涨了 3 倍",不如让程序在当天就喊出来。一条足够实用的规则是"同比 + 绝对阈值双保险":

from statistics import median

def is_anomaly(today_cost: float, last_7d_costs: list[float],
               ratio: float = 1.8, floor_usd: float = 5.0) -> bool:
    """同比放大且超过绝对阈值才算异常,避免小基数误报。"""
    if today_cost < floor_usd:
        return False
    base = median(last_7d_costs) or 0.0
    if base <= 0:
        return False
    return today_cost / base >= ratio

median 而不是 mean,是为了让历史里偶发的尖峰不影响基线;floor_usd 是为了让日成本几毛钱的功能不因为翻倍就叫醒人。这两个小设计能把告警噪声降一个数量级。

告警响了之后,按顺序排查这五步,基本十分钟内能出结论:

  1. 切维度:按 feature 分组看是谁涨,九成情况是单一功能线。
  2. 看 token 结构:是输入涨了还是输出涨了。输入涨看上下文拼接逻辑,输出涨看 max_tokens 与提示词。
  3. 看缓存:缓存命中率是否断崖下降,若下降,对比近两次发布的提示词前缀。
  4. 看重试:status <> 'ok' 占比与重试链路成本,限流引发的重试会成倍放大成本。
  5. 看模型:是否有调用被路由到了更贵的模型,灰度发布与降级策略经常在这里出问题。

这套排查法的核心是"先分组再归因",而不是从头通读日志。用量数据的价值不在于存了多少行,而在于能不能把一次模糊的"贵了"变成一句具体的"doc_extract 功能的输出 token 均值从 700 涨到 1500,因为上一次发布把 max_tokens 从 1024 提到了 4096"。

八、把三个技能串成闭环:查价 → 算账 → 复盘

前面几节都是方法论,落地时有三件现成的事可以用 Link AI 团队维护的技能来做,对应流程的三个环节:

查价(Claude API 价格查询)。解决"价格表是配置不是常量"的问题。它会按需返回当前官方价目,避免你把单价写死在代码里、等官方调价后算出一份错误报表。做成本核算的团队最怕的不是贵,是算错——用错价的报表会误导所有后续决策。

算账(AI Token 成本计算器)。输入 token 结构,输出各方案成本对比。适合在方案评审阶段用:同一份需求,用 Opus 5 跑还是 Sonnet 5 跑,缓存做不做,批量接口要不要上,差多少钱一次算清楚。这是把"成本讨论"从会后闲聊变成会前材料的工具。

复盘(Claude API 用量分析)。面向已上线系统的用量复盘:按模型、功能线、时间窗口拆解实际消耗,定位异常增长点。它的定位不是替代你的埋点,而是当你还没有完整日志管线时,先用它拿到一份可行动的用量视图——很多团队就是用它的第一版结论反推出"我们该补哪几个字段"。

三个技能的组合逻辑很简单:查价保证口径正确,算账保证决策前有数,复盘保证上线后不失控。单看任何一个都只是工具,串起来才是一条闭环。

九、可溯源与边界说明

本文的价格数据取自 Anthropic 官方定价页(核验快照 2026-11-03),模型能力计价与缓存 TTL 口径同样来自官方页面。文内所有金额、token 数均为示例,用于演示计算过程,不代表任何真实账单或客户数据。

需要如实说明两点局限:其一,Sonnet 5 的推广价与标准价在官方页面上目前并存,本文按"带生效区间"的方式处理,若官方后续统一口径,价格表配置随之更新即可;其二,各云平台与区域部署可能叠加额外的传输或专用推理计价(例如美国境内专用推理的官方说明为输入输出 1.1 倍价格),跨区域部署的成本核算需以实际账单为准,本文未覆盖这部分差异。

另外提醒一句:不要拿"省成本"当目标本身。用量分析的目的是让成本与价值挂钩——如果一个功能的单位成本涨了 30%,但带来的有效产出涨了 200%,那不是问题,是投资。数据的作用是让你清楚自己在做什么交换,而不是把所有数字都往下压。

十、落地:这周就能做的三件事

不用等排期,这三件事一周内能做完:

  1. 补四个字段:input_tokens、output_tokens、cache_creation_input_tokens、cache_read_input_tokens,外加 feature 与 status。流式场景务必在流结束时回填 usage。
  2. 把价格表挪出代码:改为带生效日期的配置,核算函数按调用时间选价。顺手写个每周一次的官方页面 diff 检查,价格变动时告警。
  3. 上线一个指标看板:先只做"每千次请求成本 + 缓存命中率 + 失败重试损耗率"三块,够用。指标先跑两周建立基线,再谈告警规则。

做完这三步,你对 Claude API 成本的掌控力会从"月底被动接收"变成"当天主动发现"。剩下的模型选型、缓存策略、批量调度,才有讨论的基础。

相关技能与内链:「Claude API 用量分析」技能详情页:skillhub.cn/team-skills… ;配套的「Claude API 价格查询」见 skillhub.cn/team-skills… ,「AI Token 成本计算器」见 skillhub.cn/team-skills… 。

更多模型价格与用量数据,欢迎访问 Link AI 控制台(linkai.kuangdc.com:19080)注册体验,用数据指导模型选型与成本治理。

声明:文中所述技能为第三方技能,价格为官方定价页实时数据,示例金额仅为演示;最终预算与选型请以官方信息与实际账单为准。

(内容由AI生成,仅供参考)