Claude Media
Claude APIのOpenTelemetryスパン属性リファレンス — 二重計上の見分け方

Claude APIのOpenTelemetryスパン属性リファレンス — 二重計上の見分け方

Claude APIとSDKが出すOpenTelemetryスパンの名前・種別・属性の読み方と、トークン属性を足して二重計上にならない合計の取り方を示します。

Claude APIのスパンでトークン数を集計するとき、いちばん多い失敗は同じトークンを2回数えることです。1回のMessages APIリクエストには、アプリ側のSDKスパンとAPI側のスパンが同名で並び、さらにAPI側では親子の両方に使用量の属性が付きます。全部を足すと、実際より大きい数字になります。

この記事は、スパンの名前と種別、共通の属性、スパンごとの属性を読み解いたうえで、どのスパンのどの属性を足せば1回ずつ数えられるかを表にします。OpenTelemetry出力はベータ版で、スパン名と属性は変わる可能性があります。ここに載せた名前は、仕様ページが参照する規約のコミット(2026年9月24日付)に沿った内容です。

SDKスパンとAPIスパンは何が違うのか

スパンの出どころは2つあります。アプリケーションに組み込んだSDKが出すスパン(SDKスパン)と、Claude APIが出すスパン(APIスパン)です。見分けるときは、まずservice.nameを見ます。

項目SDKスパンAPIスパン
service.nameSDKスパンアプリケーション自身のservice.nameAPIスパンclaude-platform
スコープ名SDKスパンcom.anthropic.sdk.<language>(CLIはcom.anthropic.sdk.cli)APIスパンcom.anthropic.claude-platform
リクエストのスパン種別SDKスパンCLIENTAPIスパンSERVER
それ以外のスパン種別SDKスパンINTERNALAPIスパンINTERNAL

クラウドプラットフォームのクライアントでは、スコープ名の末尾に.<platform>が付きます。

スパン名の付き方にも規則があります。

  • リクエストのスパン: anthropic.の後ろにAPIメソッドのパスを、スラッシュをドットに変え、betaを除いて続けます。client.beta.sessions.events.send()ならanthropic.sessions.events.sendです
  • ツールのスパン: 固定の名前、半角スペース、ツール名の順です。たとえばanthropic.tool_use bashになります

ステータスは3値です。OKは完了、ERRORは失敗、未設定(unset)はアプリケーションが途中で打ち切った場合です。ストリームを途中で閉じると、SDKスパンは未設定になります。未設定を「失敗」として集計すると、ユーザーが中断しただけのリクエストがエラー率に混ざります。

どのスパンにも付く共通属性

リクエストのスパンには、次の属性が付きます。ただし先頭3つ以外は、リクエストでないスパンには付かない場合があります。

属性中身
gen_ai.provider.name中身anthropic(Bedrockクライアントはaws.bedrock、Vertex系はgcp.vertex_ai)
anthropic.organization.id中身組織ID
anthropic.workspace.id中身処理が動くワークスペースのID
anthropic.request.id中身レスポンスヘッダーrequest-idの値
http.request.method / http.route中身HTTPメソッドと、パスパラメータを{session_id}のように置き換えた経路
http.response.status_code中身最終レスポンスのステータス。ストリームではヘッダーと一緒に送られた値
server.address / server.port中身送信先のホストとポート
url.scheme / url.path / url.query / url.full中身URLの各部分

anthropic.request.idは、サポートへ問い合わせるときの手がかりになります。Claude Platform on AWSでは、APIスパンにserver.address・server.port・url.fullが付きません。

Managed Agentsのセッションでは、さらにセッション属性が付きます。anthropic.session.idがセッションID、gen_ai.conversation.idがセッション内のスレッドIDです。1つのセッションのスパンが複数のトレースに分かれることがあるため、会話の全体像を組み立てるときはトレースIDではなくこの2つのIDでまとめます。

messages.createスパンの属性を読む

Messages APIのリクエストを表すanthropic.messages.createが、いちばん属性の多いスパンです。SDKからもAPIからも出ます。APIはPOST /v1/messagesのうち認証を通ったリクエストにだけこのスパンを出し、OpenAI互換の/v1/chat/completionsには出しません。フォールバックモデルが応答したときも、APIが出すスパンは1つです。

読み方の要点は、次の3群です。

誰が何を答えたか

  • gen_ai.request.model: リクエストのmodel
  • gen_ai.response.model: 実際に応答したモデル。エイリアスで呼んだときやフォールバックが働いたときは、リクエスト側と値が変わります
  • anthropic.message.stop_reason: end_turnなどの停止理由
  • anthropic.response.service_tier: 実際に使われたサービスティア

どう頼んだか

gen_ai.request.max_tokens、temperature、top_p、top_kは、リクエストで指定したときだけ付きます。gen_ai.request.reasoning.levelはoutput_configのeffort、anthropic.thinking.typeとanthropic.thinking.budget_tokensはthinkingの指定です。gen_ai.request.streamはstream: trueのときだけtrueで付き、falseは付きません。

失敗と速さ

error.typeにはレート制限エラーなどのエラー種別が入ります。APIがエラー応答なしにリクエストを終えたときはapi_errorです。gen_ai.response.time_to_first_chunkはストリーミングのSDKスパンだけに付く秒数で、リクエスト送信から最初のcontent_block_deltaまでを指します。

gen_ai.usage.reasoning.output_tokensは思考トークンで、出力トークンの内数です。出力側に足すと二重になります。

サーバーツールの使用回数(web_search_requestsなど)は、このスパンの属性にはありません。回数を知りたいときは、ツールごとのスパンを数えます。

本文を含む属性は「全コンテンツ」モードでだけ付く

プロンプトとレスポンスを持つ属性には、仕様ページでContentの印が付いています。

属性中身
gen_ai.system_instructions中身システムプロンプト
gen_ai.input.messages中身リクエストのmessages
gen_ai.output.messages中身レスポンスのcontent(1件のassistantメッセージ)
gen_ai.tool.call.arguments中身ツール呼び出しのinput
anthropic.tool_result.content中身モデルへ返すツール結果

どれもJSON文字列です。コンテンツモードの既定は「メタデータのみ」で、このときスパンには識別子・時間・ステータス・トークン数と、エージェントやツールの名前・説明のように利用者が選んだ文字列だけが入ります。上の属性が見えないときは、まずモードを疑います。

大きすぎる内容は短縮され、anthropic.content.truncatedがtrueになります。上限は1スパンあたり既定0.75MBで、リクエストのスパンではシステムプロンプトと古いメッセージから順に落ちます。画像やドキュメントは種類名だけが残ります。HIPAA・FERPA・K-12のデータ取り扱い制限がある組織では、APIスパンの出力先はモードにかかわらずメタデータだけになります。SDKスパンはこの制限の対象外です。

スパンごとの親子関係と使用量の有無

トークン数の合計は、どのスパンが使用量を持つかを知らないと取れません。使用量の属性を持つのは次の4つだけです。

トークン属性

使用量を持つスパン

  • anthropic.messages.create

    Messages APIのリクエスト。SDKとAPIの両方が出します。

  • anthropic.model_request

    APIが出す、モデル1回分の実行。Messages APIではmessages.createの子です。Managed Agentsではターンの子です。

  • anthropic.compaction

    Messages APIのコンパクション1回分。messages.createの子です。

  • anthropic.session.outcome_evaluation

    Managed Agentsの成果評価1回分。入力と出力のトークンだけを持ちます。

次のスパンは使用量を持ちません。足し上げの対象から外します。

  • anthropic.messages.tool_runner: ツール実行ループ全体のスパン。使用量は子のmessages.createを読みます
  • anthropic.session.turn: Managed Agentsの1ターン。子のmodel_requestが使用量を持ちます
  • anthropic.tool_use <tool>、anthropic.mcp_tool_use <tool>、anthropic.custom_tool_use <tool>: ツールのスパン。名前の前置きが、サーバーツールやエージェントのツールセット(tool_use)、MCPツール(mcp_tool_use)、アプリ側のツールランナーが動かすツール(custom_tool_use)の別を表します。custom_tool_useはSDKだけが出します

anthropic.messages.count_tokensは、SDKだけが出すスパンです。APIはPOST /v1/messages/count_tokensにスパンを出しません。このスパンのanthropic.message.count_tokens.input_tokensはトークン数の見積もりであって使用量ではないので、合計に入れません。

トークン属性の意味と、足してはいけない組み合わせ

使用量の属性は、次の6つです。

属性意味
gen_ai.usage.input_tokens意味入力トークン。キャッシュの読み取りと書き込みを含む
gen_ai.usage.output_tokens意味出力トークン
gen_ai.usage.cache_write.input_tokens意味キャッシュへ書いたトークン
gen_ai.usage.cache_read.input_tokens意味キャッシュから読んだトークン
anthropic.usage.cache_creation.ephemeral_5m_input_tokens意味5分キャッシュへの書き込み
anthropic.usage.cache_creation.ephemeral_1h_input_tokens意味1時間キャッシュへの書き込み

gen_ai.usage.input_tokensにはキャッシュの読み書きがすでに入っています。キャッシュ系の属性を上乗せすると二重になります。この定義はClaude Codeのinput_tokensとは逆で、Claude Code側はキャッシュ分を含みません。Claude Codeのテレメトリを同じダッシュボードに載せるときの換算はOpenTelemetryの入力トークンをGenAI規約に合わせるClaude Codeの換算にあります。

cache_writeとcache_readは、outcome_evaluationのスパンには付きません。5分・1時間の書き込みは、Messages APIのリクエストのスパンだけに付き、ストリーミング応答のSDKスパンでは最初の出力トークンより前に書かれた分だけが載ります。

二重計上になる場面の早見表

1回のリクエストの使用量は、複数のスパンに重ねて載ります。足すスパンは、出どころごとに1種類に絞ります。

集計の対象足すスパン避けること
SDKスパン足すスパンanthropic.messages.create避けることコンパクションがあるときは、そのスパンのanthropic.usage.compaction.input_tokensとoutput_tokensも足す
APIスパン(Messages API)足すスパンmessages.createかmodel_requestのどちらか一方避けること両方を足さない。コンパクションがあればanthropic.compactionスパンも足す
APIスパン(Managed Agents)足すスパンmodel_requestとoutcome_evaluation避けることsession.turnは使用量を持たない
SDKとAPIの両方がある足すスパンAPIスパンの側を足す避けること同名のmessages.createを両方足さない

SDKスパンのgen_ai.usage.*はコンパクションのトークンを含みません。だからanthropic.usage.compaction.*を別に足します。一方、APIスパンのmessages.createにあるanthropic.usage.compaction.*は、anthropic.compactionスパンと同じトークンです。こちらを足すと二重になります。同じ属性名でも、足すか足さないかが出どころで逆になる点が、いちばん間違えやすいところです。

同名のanthropic.messages.createが2つあるときは、種別で見分けます。CLIENTがSDKスパン、その子のSERVERがAPIスパンです。

次のようなコードにしておくと、取り違えを防げます。スパンを辞書に展開済みという前提の、考え方を示す例です。

def api_usage(spans):
    """APIスパン(SERVER種別)の messages.create だけを足す"""
    total = {"input": 0, "output": 0}
    seen = set()
    for s in spans:
        key = (s["trace_id"], s["span_id"])
        if key in seen:        # 再送で届いた重複スパンを除く
            continue
        seen.add(key)
        if s["name"] == "anthropic.messages.create" and s["kind"] == "SERVER":
            a = s["attributes"]
            total["input"] += a.get("gen_ai.usage.input_tokens", 0)
            total["output"] += a.get("gen_ai.usage.output_tokens", 0)
    return total

重複の除去も必要です。スパンは配信の再試行で複数回届くことがあり、観測基盤が重複を除かないなら、トレースIDとスパンIDの組で1回ずつにします。

この集計は課金の正本ではない

スパンの配信はベストエフォートで、欠けることがあります。リクエストのtraceparentヘッダーでサンプリングフラグを落とせば、呼び出し側がそのリクエストのスパンを出さないこともできます。APIキーごとの使用量を確かめたい場合、たとえばキーの漏えいを疑うときは、スパンのない呼び出しも数えるUsage APIを使います。

Managed Agentsのスパンを読む

セッションでは、リクエストのスパンのほかにターンのスパンが付きます。anthropic.session.turnはgen_ai.operation.nameがinvoke_agentで、ターンの親は起こした理由で変わります。

  • 最初のターン: anthropic.sessions.create(initial_eventsが空でないとき)
  • イベント起因のターン: anthropic.sessions.events.send
  • 予算の引き上げで再開したターン: anthropic.sessions.update
  • 別スレッドで始めたターン: 呼び出し元のターンのスパン
  • 別スレッドのエージェントが返信して始まるターン: 親なし

ターンは、スレッドがrunning状態に入ったときに始まり、出たときに終わります。anthropic.session.turn.stop_reasonには、ターンが終わった理由が入ります。たとえばアプリ側のカスタムツールを待つとrequires_actionで終わり、ツールの結果を送ると新しいターンが始まります。このため、1つの依頼が2つのターンに分かれて見えることがあります。

ツールのスパンは、APIがツールを動かした場合にだけAPI側から出ます。アプリが動かすカスタムツール、セルフホストのサンドボックスで動くツール、マルチエージェントのツールとアドバイザーツール、autoポリシーで拒否されたり、アプリが確認を断った使用にはAPIのスパンが付きません。ツールのスパンが見つからないときは、障害を疑う前にこの一覧に当てはまらないかを見ます。

anthropic.session.outcome_evaluationは、成果評価の1反復ごとに付くスパンです。anthropic.session.outcome_evaluation.resultはsatisfied、needs_revision、max_iterations_reached、failed、interruptedのどれかで、interruptedのときだけステータスが未設定、ほかはOKになります。

属性を見て原因を絞る使い方

属性から読み取れることを、症状ごとに並べます。

症状見る属性
想定と違うモデルが答えた見る属性gen_ai.request.modelとgen_ai.response.modelの差
応答が途中で切れる見る属性anthropic.message.stop_reasonとgen_ai.request.max_tokens
遅い見る属性gen_ai.response.time_to_first_chunk(ストリーミングのSDKスパン)とanthropic.response.service_tier
再試行が多い見る属性SDKスパンのhttp.request.resend_count(再送がなければ付かない)
特定の呼び出しだけ失敗する見る属性error.typeとanthropic.request.id

SDKスパンのリクエスト1つは、再試行を含む全体の時間を表します。ページ付きの一覧では、次ページのリクエストごとに別のスパンになります。

Message Batchesは、SDKのメソッドごとにanthropic.messages.batches.<method>が出ます。anthropic.message.batch.idと、createのときだけanthropic.message.batch.request_countが付きます。バッチの運用例はClaude Batch APIで論文を一次スクリーニングするにあります。

ここまで読んだ属性は、組織の監査ログとは別の仕組みです。管理操作の記録との違いはClaude Compliance APIとはで比べています。

よくある質問

Claude Codeのスパンと同じ属性名ですか

同じではありません。Claude Codeはclaude_code.llm_requestのような独自のスパンとメトリクスを出し、この記事のanthropic.*スパンとは別系統です。入力トークンの範囲も違います。ダッシュボードで混ぜる場合は、上の換算を先に済ませます。

Agent SDKでも同じスパンが出ますか

仕様ページはClaude Code、Agent SDK、Coworkを別の手順に案内しています。Agent SDKの可観測化はAgent SDKをOpenTelemetryで可観測化する実装ガイドにあります。

この記事を共有:XはてブLinkedIn