站长自营 API 中转

正在比较模型套餐?可以把多个 AI API 接到一个网关里

ZZSwitch 是我自己运营的统一 API 网关,适合需要国内支付、兑换码充值、多模型切换和 OpenAI 兼容接口的开发者。不是 OpenCode 官方服务。

Claude Agent SDK 提供每次 query() 的 token 用量与费用估算。通过 result message 的 total_cost_usd 字段可获取单次调用总费用;多步对话中需对 assistant message 按 ID 去重避免重复计数。注意 cost 字段为本地估算,定价变更或不识别模型时可能与实际账单不一致。要查看缓存节省,需单独跟踪 cache_creation_input_tokens 和 cache_read_input_tokens。对于跨会话累计,需手动累加每次 query() 的 total_cost_usd。

Claude Agent SDK 费用与用量追踪

本指南说明如何用 Claude Agent SDK 追踪 token 用量、估算费用,以及配置提示缓存。了解这些内容能帮你在开发阶段掌握成本,并为预算做粗略参考。

total_cost_usd 和 costUSD 字段是客户端估算值,不是权威结算数据。SDK 在本地根据构建时捆绑的价格表计算,因此当以下情况发生时,这些值可能与实际账单不同:

  • 价格发生变动
  • 安装的 SDK 版本无法识别所用模型
  • 客户端无法建模的计费规则生效

请将这些字段用于开发洞察和大致预算。权威账单请使用 Usage and Cost API 或 Claude Console 中的用量页面。不要依据这些字段向终端用户收费或触发财务决策。

理解 token 用量

TypeScript 和 Python SDK 暴露的用量数据相同,但字段命名不同:

  • TypeScript:每个 assistant message 提供每步 token 分解(message.message.id、message.message.usage),result message 上通过 modelUsage 提供按模型的费用,以及累积总数。
  • Python:每个 assistant message 提供每步 token 分解(message.usage、message.message_id),result message 上通过 model_usage 提供按模型的费用,以及累积总数(total_cost_usd 和 usage 字典)。

两个 SDK 使用相同的底层成本模型,暴露出相同的粒度。差异在于字段命名和每步用量的嵌套位置。

成本追踪依赖于理解 SDK 如何划分用量数据的作用域:

  • query() 调用:一次调用可能包含多个步骤(Claude 响应、使用工具、获取结果、再次响应)。每次调用最终产生一个 result 消息。
  • 步骤(Step):query() 调用内部的单个请求/响应周期。每个步骤产生带有 token 用量的 assistant messages。
  • 会话(Session):通过会话 ID(使用 resume 选项)连接的一系列 query() 调用。会话中的每个 query() 调用独立报告自身成本。

下图展示了一次 query() 调用中的消息流,每个步骤报告 token 用量,末尾显示累积估算:

Diagram showing a query producing two steps of messages. Step 1 has four assistant messages sharing the same ID and usage (count once), Step 2 has one assistant message with a new ID, and the final result message shows the estimated total_cost_usd.

当 Claude 响应时,会发送一个或多个 assistant message。在 TypeScript 中,每个 assistant message 包含一个嵌套的 BetaMessage(通过 message.message 访问),其中有 id 和 usage 对象(包含 input_tokens、output_tokens)。在 Python 中,AssistantMessage 数据类通过 message.usage 和 message.message_id 直接暴露相同数据。当 Claude 在一次 turn 中使用多个工具时,该 turn 中的所有消息共享同一个 ID,因此需要按 ID 去重以避免重复计数。

当 query() 调用完成时,SDK 会发出一个 result message,其中包含 total_cost_usd 和累积的 usage。这在 TypeScript(SDKResultMessage)和 Python(ResultMessage)中均可获得。如果你进行了多次 query() 调用(例如在多轮会话中),每个 result 仅反映该次调用的成本。如果只需要总估算,可以忽略每步用量而直接读取这个单一值。

获取单次查询的总费用

result message(TypeScript、Python)标志着一次 query() 调用的 agent 循环结束。它包含 total_cost_usd,即该调用中所有步骤的累积估算成本。这对成功和错误结果都有效。如果你使用会话进行多个 query() 调用,每个 result 只反映该次调用的成本。

以下示例迭代 query() 调用的消息流,并在 result 消息到达时打印总成本:

import { query } from "@anthropic-ai/claude-agent-sdk";

for await (const message of query({ prompt: "Summarize this project" })) {
  if (message.type === "result") {
    console.log(`Total cost: $${message.total_cost_usd}`);
  }
}
from claude_agent_sdk import query, ResultMessage
import asyncio


async def main():
    async for message in query(prompt="Summarize this project"):
        if isinstance(message, ResultMessage):
            print(f"Total cost: ${message.total_cost_usd or 0}")


asyncio.run(main())

追踪每步用量和按模型用量

本节示例使用 TypeScript 字段名。在 Python 中,等效字段是 AssistantMessage.usage 和 AssistantMessage.message_id(每步用量),以及 ResultMessage.model_usage(按模型分解)。

追踪每步用量

每个 assistant message 包含一个嵌套的 BetaMessage(通过 message.message 访问),其中有 id 和 usage 对象(包含 token 计数)。当 Claude 并行使用工具时,多个消息共享相同的 id,且用量数据相同。跟踪你已经计数的 ID 并跳过重复,以避免膨胀的统计。

并行工具调用会产生多个 assistant message,其嵌套的 BetaMessage 共享相同的 id 和相同的用量。务必按 ID 去重以获得准确的每步 token 计数。

以下示例累加所有步骤的输入和输出 token,每个唯一消息 ID 仅计数一次:

import { query } from "@anthropic-ai/claude-agent-sdk";

const seenIds = new Set<string>();
let totalInputTokens = 0;
let totalOutputTokens = 0;

for await (const message of query({ prompt: "Summarize this project" })) {
  if (message.type === "assistant") {
    const msgId = message.message.id;

    // Parallel tool calls share the same ID, only count once
    if (!seenIds.has(msgId)) {
      seenIds.add(msgId);
      totalInputTokens += message.message.usage.input_tokens;
      totalOutputTokens += message.message.usage.output_tokens;
    }
  }
}

console.log(`Steps: ${seenIds.size}`);
console.log(`Input tokens: ${totalInputTokens}`);
console.log(`Output tokens: ${totalOutputTokens}`);

按模型分解用量

result message 包含 modelUsage,这是一个模型名称到 token 计数和成本的映射。当运行多个模型时(例如子代理使用 Haiku,主 agent 使用 Opus),这有助于了解 token 走向。

以下示例执行查询并打印每个模型使用的成本和 token 分解:

import { query } from "@anthropic-ai/claude-agent-sdk";

for await (const message of query({ prompt: "Summarize this project" })) {
  if (message.type !== "result") continue;

  for (const [modelName, usage] of Object.entries(message.modelUsage)) {
    console.log(`${modelName}: $${usage.costUSD.toFixed(4)}`);
    console.log(`  Input tokens: ${usage.inputTokens}`);
    console.log(`  Output tokens: ${usage.outputTokens}`);
    console.log(`  Cache read: ${usage.cacheReadInputTokens}`);
    console.log(`  Cache creation: ${usage.cacheCreationInputTokens}`);
  }
}

在多次调用间累积成本

每个 query() 调用返回自身的 total_cost_usd。SDK 不提供会话级别的总计,因此如果你的应用进行了多个 query() 调用(例如在多轮会话中或跨不同用户),需要自己累加。

以下示例顺序执行两个 query() 调用,将每个调用的 total_cost_usd 添加到运行总计中,并输出每次调用和合计的成本:

import { query } from "@anthropic-ai/claude-agent-sdk";

// Track cumulative cost across multiple query() calls
let totalSpend = 0;

const prompts = [
  "Read the files in src/ and summarize the architecture",
  "List all exported functions in src/auth.ts"
];

for (const prompt of prompts) {
  for await (const message of query({ prompt })) {
    if (message.type === "result") {
      totalSpend += message.total_cost_usd;
      console.log(`This call: $${message.total_cost_usd}`);
    }
  }
}

console.log(`Total spend: $${totalSpend.toFixed(4)}`);
from claude_agent_sdk import query, ResultMessage
import asyncio


async def main():
    # Track cumulative cost across multiple query() calls
    total_spend = 0.0

    prompts = [
        "Read the files in src/ and summarize the architecture",
        "List all exported functions in src/auth.ts",
    ]

    for prompt in prompts:
        async for message in query(prompt=prompt):
            if isinstance(message, ResultMessage):
                cost = message.total_cost_usd or 0
                total_spend += cost
                print(f"This call: ${cost}")

    print(f"Total spend: ${total_spend:.4f}")


asyncio.run(main())

处理错误、缓存和 token 不一致

为了准确追踪成本,需要处理失败的对话、缓存 token 定价以及偶尔的报告不一致。

输出 token 不一致如何解决

极少数情况下,相同 ID 的消息中 output_tokens 值可能不同。这时:

  1. 使用最大值: 该组中的最后一条消息通常包含准确的合计。
  2. 优先使用 result message: result message 中的 total_cost_usd 反映 SDK 在所有步骤上的累积估算,因此比你自己对各步求和更可靠。它仍然是估算值,可能与实际账单不同。
  3. 报告不一致: 在 Claude Code GitHub 仓库 中提交 issue。

在失败对话中追踪成本

无论是成功还是错误结果消息,都包含 usage 和 total_cost_usd。如果对话中途失败,你仍然消耗了失败点之前的 token。无论 result message 的 subtype 是什么,始终从中读取成本数据。

追踪缓存 token

Agent SDK 自动使用提示缓存来降低重复内容的成本。你无需自行配置缓存。usage 对象包含两个额外的缓存跟踪字段:

  • cache_creation_input_tokens:用于创建新缓存条目的 token(费率高于标准输入 token)。
  • cache_read_input_tokens:从现有缓存条目中读取的 token(费率较低)。

将这些与 input_tokens 分开跟踪,以了解缓存节省。在 TypeScript 中,这些字段已在 Usage 对象上定义。在 Python 中,它们作为键出现在 ResultMessage.usage 字典中(例如 message.usage.get("cache_read_input_tokens", 0))。

将提示缓存 TTL 延长至一小时

默认情况下,当使用 API 密钥认证或在 Amazon Bedrock、Google Cloud Vertex AI、Microsoft Foundry 上运行时,SDK 写入的缓存条目 TTL 为 5 分钟。如果你对相同的系统提示和上下文运行许多短会话,且会话间隔超过 5 分钟,那么缓存会在会话之间过期,每个新会话都将支付全价输入费用。

要请求缓存写入使用 1 小时 TTL,设置 ENABLE_PROMPT_CACHING_1H 环境变量。可以在 shell 或容器环境中导出,或者通过 options.env 传递。

以下示例为在 Bedrock 上运行的 agent 启用 1 小时 TTL:

options = ClaudeAgentOptions(
    env={
        "CLAUDE_CODE_USE_BEDROCK": "1",
        "ENABLE_PROMPT_CACHING_1H": "1",
    },
)
const options = {
  env: {
    ...process.env,
    CLAUDE_CODE_USE_BEDROCK: "1",
    ENABLE_PROMPT_CACHING_1H: "1",
  },
};

1 小时 TTL 的缓存写入费率高于 5 分钟写入,因此启用它是以更高的写入成本换取更多缓存读取。参见提示缓存定价。Claude 订阅用户已自动获得 1 小时 TTL,无需设置此变量。

相关文档

常见问题

total_cost_usd 与实际账单不一致怎么办?

total_cost_usd 是客户端本地估算,基于构建时捆绑的价格表计算。定价变更、SDK 版本不识别模型或计费规则无法建模时,估算值会与账单产生偏差。如需权威数据,请使用 Usage and Cost API 或 Claude Console 用量页面。

并行工具调用时如何避免重复计数 token?

当 Claude 在一次 turn 中调用多个工具时,所有 assistant message 共享同一个 id 和相同的用量数据。在代码中维护一个 Set<string> 记录已处理的消息 ID,遇到重复 ID 时跳过计数即可。

如何追踪多个模型(如 Haiku 和 Opus)各自的花费?

result message 的 modelUsage(TypeScript)或 model_usage(Python)字段是一个映射,键为模型名称,值为该模型的 token 计数和费用。遍历该映射即可获得每个模型的详细开销。

站长自营 API 中转

ZZSwitch API 中转

统一接入多家模型,支持兑换码充值。

打开 ZZSwitch