Claude Code官方文档Agent SDK成本

Claude Agent SDK 成本追踪 - Token 用量 / 估算 / 缓存配置

了解如何跟踪令牌使用情况、估计成本,以及使用 Claude Agent SDK 配置提示缓存。

· 阅读约 15 分钟

跟踪成本和使用情况

了解如何跟踪令牌使用情况、估计成本,以及使用 Claude Agent SDK 配置提示缓存。

Claude Agent SDK 为与 Claude 的每次交互提供详细的令牌使用信息。本指南说明如何正确跟踪使用情况和理解成本报告,特别是在处理并行工具使用和多步骤对话时。

有关完整的 API 文档,请参阅 TypeScript SDK 参考Python SDK 参考

⚠️ total_cost_usdcostUSD 字段是客户端估计值,不是权威的计费数据。SDK 从构建时捆绑的价格表在本地计算它们,因此当以下情况发生时,它们可能与您实际被计费的金额不同:

  • 定价发生变化
  • 已安装的 SDK 版本无法识别某个模型
  • 应用了客户端无法建模的计费规则

使用这些字段进行开发洞察和大致预算编制。对于权威计费,请使用使用情况和成本 APIClaude 控制台 中的使用情况页面。不要从这些字段向最终用户计费或触发财务决策。

理解令牌使用情况

TypeScript 和 Python SDK 使用不同的字段名称公开相同的使用数据:

  • TypeScript 在每个助手消息上提供每步令牌细分(message.message.idmessage.message.usage),通过结果消息上的 modelUsage 提供每个模型的成本,以及结果消息上的累积总计。
  • Python 在每个助手消息上提供每步令牌细分(message.usagemessage.message_id),通过结果消息上的 model_usage 提供每个模型的成本,以及结果消息上的累积总计(total_cost_usdusage 字典)。

两个 SDK 使用相同的底层成本模型并公开相同的粒度。区别在于字段命名和每步使用情况的嵌套位置。

成本跟踪取决于理解 SDK 如何确定使用数据的范围:

  • query() 调用: SDK 的 query() 函数的一次调用。单个调用可能涉及多个步骤(Claude 响应、使用工具、获取结果、再次响应)。每个调用在末尾产生一条 result 消息。
  • 步骤: query() 调用中的单个请求/响应周期。每个步骤产生带有令牌使用情况的助手消息。
  • 会话: 由会话 ID 链接的一系列 query() 调用(使用 resume 选项)。会话中的每个 query() 调用独立报告其自己的成本。
  1. 每个步骤产生助手消息: 当 Claude 响应时,它发送一条或多条助手消息。在 TypeScript 中,每条助手消息包含一个嵌套的 BetaMessage(通过 message.message 访问),具有 id 和一个 usage 对象,其中包含令牌计数(input_tokensoutput_tokens)。在 Python 中,AssistantMessage 数据类通过 message.usagemessage.message_id 直接公开相同的数据。当 Claude 在一个回合中使用多个工具时,该回合中的所有消息共享相同的 ID,因此按 ID 去重以避免重复计数。

  2. 结果消息提供累积估计:query() 调用完成时,SDK 发出一条结果消息,其中包含 total_cost_usd 和累积 usage。这在 TypeScript(SDKResultMessage)和 Python(ResultMessage)中都可用。如果您进行多个 query() 调用(例如,在多轮会话中),每个结果仅反映该单个调用的成本。如果您只需要估计的总计,可以忽略每步使用情况并读取此单个值。

获取查询的总成本

结果消息标记 query() 调用的代理循环的结束。它包括 total_cost_usd,即该调用中所有步骤的累积估计成本。这适用于成功和错误结果。如果您使用会话进行多个 query() 调用,每个结果仅反映该单个调用的成本。

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

TypeScript:

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}`);
  }
}

Python:

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.usageAssistantMessage.message_id 用于每步使用情况,以及 ResultMessage.model_usage 用于每个模型的细分。

跟踪每步使用情况

每条助手消息包含一个嵌套的 BetaMessage(通过 message.message 访问),具有 idusage 对象,其中包含令牌计数。当 Claude 并行使用工具时,多条消息共享相同的 id 和相同的使用数据。跟踪您已经计数的 ID,并跳过重复项以避免膨胀的总计。

⚠️ 并行工具调用产生多条助手消息,其嵌套的 BetaMessage 共享相同的 id 和相同的使用情况。始终按 ID 去重以获得准确的每步令牌计数。

以下示例累积所有步骤中的输入和输出令牌,仅计数每个唯一消息 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}`);

按模型细分使用情况

结果消息包括 modelUsage,这是一个模型名称到每个模型令牌计数和成本的映射。当您运行多个模型(例如,为子代理使用 Haiku,为主代理使用 Opus)并想查看令牌的去向时,这很有用。

以下示例运行查询并打印所使用的每个模型的成本和令牌细分:

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 添加到运行总计,并打印每个调用和合并的成本:

TypeScript:

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

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)}`);

Python:

from claude_agent_sdk import query, ResultMessage
import asyncio


async def main():
    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())

处理错误、缓存和令牌差异

为了准确的成本跟踪,需要考虑失败的对话、缓存令牌定价和偶发的报告不一致。

解决输出令牌差异

在极少数情况下,您可能会观察到具有相同 ID 的消息的 output_tokens 值不同。当这种情况发生时:

  1. 使用最高值: 一组中的最终消息通常包含准确的总计。
  2. 优先使用结果消息: 结果消息中的 total_cost_usd 反映 SDK 在所有步骤中的累积估计,因此比自己求和每步值更可靠。它仍然是一个估计值,可能与您的实际账单不同。
  3. 报告不一致:Claude Code GitHub 存储库 提交问题。

跟踪失败对话的成本

成功和错误结果消息都包括 usagetotal_cost_usd。如果对话在中途失败,您仍然消耗了到失败点为止的令牌。无论其 subtype 如何,始终从结果消息读取成本数据。

跟踪缓存令牌

Agent SDK 自动使用提示缓存来减少重复内容的成本。您不需要自己配置缓存。使用对象包括两个额外的字段用于缓存跟踪:

  • cache_creation_input_tokens:用于创建新缓存条目的令牌(按比标准输入令牌更高的速率计费)。
  • cache_read_input_tokens:从现有缓存条目读取的令牌(按降低的速率计费)。

将这些与 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 写入的缓存条目默认使用 5 分钟 TTL。如果您的工作负载针对相同的系统提示和上下文运行许多短会话,且会话之间的间隔超过 5 分钟,缓存会在会话之间过期,每个新会话都会支付完整的输入价格。

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

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

Python:

options = ClaudeAgentOptions(
    env={
        "CLAUDE_CODE_USE_BEDROCK": "1",
        "ENABLE_PROMPT_CACHING_1H": "1",
    },
)

TypeScript:

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

具有 1 小时 TTL 的缓存写入按比 5 分钟写入更高的速率计费,因此启用此功能会用更高的写入成本换取更多的缓存读取。有关详细信息,请参阅提示缓存定价。Claude 订阅用户已自动获得 1 小时 TTL,不需要设置此变量。

相关文档


本文翻译自 Anthropic Claude Code 官方文档,最近一次同步:2025-05-01。