Claude Media
Claude Codeのworkflow.run_idでWorkflow実行の全イベントを束ねる

Claude Codeのworkflow.run_idでWorkflow実行の全イベントを束ねる

OTelのworkflow.run_id(wf_接頭辞)で1回のWorkflow実行のAPI・ツールイベントを復元します。workflow.nameがcustomに伏せられる条件と、parent_agent_idで親を辿る方法もまとめます。

Workflowを何度も走らせていると、「このAPI呼び出しとツール実行は、どの実行のものか」が分からなくなります。workflow.run_idは、その問いに答えるためのOTel属性です。値はwf_で始まる実行ごとの識別子で、これで絞り込むと1回のWorkflow実行が起こしたAPIリクエストとツール結果だけが取り出せます。

属性が加わったのはv2.1.202です。prompt.idはユーザーのプロンプト単位、workflow.run_idはWorkflow実行単位、と粒度が違います。

workflow.run_idが付くイベントと付かないもの

Claude Codeのモニタリング資料では、workflow.run_idは「イベントにだけ付き、メトリクスには付かない属性」に分類されています。値が実行ごとに変わるため、メトリクスに載せると系列が際限なく増えるからです。prompt.idも同じ扱いです。

付く先は、Workflowツールの実行に属するエージェントが出したAPIイベントとツールイベントです。範囲は次のとおり説明されています。

  • Workflowスクリプトが起動したエージェント
  • そのエージェントがさらに起動したエージェント(スキルの呼び出しなど)

つまり、スクリプトの直下だけでなく、孫の世代までが同じwf_の値を持ちます。値はWorkflowツールの結果が報告する実行IDと一致するので、画面や結果で見た実行を、テレメトリ側の検索キーとしてそのまま使えます。

トレースのスパンにも同じ属性が定義されています。claude_code.llm_requestとclaude_code.toolのスパンの表に、workflow.run_idとworkflow.nameの行があります。トレースは別途ベータ機能の有効化が必要です。

1回の実行を復元する

収集先では、workflow.run_idの値で絞るだけです。次のクエリは、ログを表形式で扱える基盤を想定した例です。テーブル名や列名は収集基盤ごとに変わるため、読み替えが要ります。

-- 例: 1回のWorkflow実行が起こしたイベントを時系列で取り出す
SELECT event_name, agent_id, parent_agent_id, tool_name, model
FROM claude_code_events
WHERE workflow_run_id = 'wf_...'
ORDER BY event_timestamp, event_sequence;

並べ替えにはevent.timestampを使い、同じ時刻のイベントだけevent.sequenceで順序を付けます。event.sequenceはプロセス単位のカウンターで、セッションを再開すると値が前後することがあるためです。

この絞り込みで、次のような疑問に答えられます。

  • 1回の実行で、APIリクエストは何回発行されたか
  • どのツールが失敗し、tool_resultのsuccessがfalseになったか
  • どのモデルが何件のリクエストを処理したか

トークンや費用の合計は、イベントではなくカウンターで見る設計です。サブエージェント関連のイベントの説明にも、トークンと費用の集計にはトークンカウンターとコストカウンターを使うよう書かれています。実行単位のコスト把握の考え方は、/workflowsビューの解説の「規模とコストを抑える」と併せて読むと整理しやすくなります。

workflow.nameが伏せられる条件

workflow.nameはworkflow.run_idと一緒に出る属性で、値はスクリプトのmeta.nameです。ここに条件があります。

実行されたスクリプトworkflow.nameの値
同梱の組み込みスクリプトを無改変で実行workflow.nameの値名前がそのまま出る
ユーザーが作ったスクリプトworkflow.nameの値custom
組み込みスクリプトを編集したコピーworkflow.nameの値custom

OTEL_LOG_TOOL_DETAILS=1を設定すると、ユーザー作成の名前も実名で出ます。この変数は名前のためだけのスイッチではありません。Bashコマンド、MCPサーバー名とツール名、スキル名、ツール入力まで、ツール詳細をまとめて載せます。実名が要るかどうかだけで有効化せず、組織のテレメトリ方針と合わせて決める必要があります。変数の範囲はOTEL_LOG_TOOL_DETAILSの解説にまとめました。

実務では、名前がcustomのままでも困る場面は多くありません。workflow.run_idは実名と無関係に付くので、実行の束ね方は変わらないからです。困るのは「どのスクリプトが重かったか」を名前で集計したいときだけです。その場合は、名前で集計するのではなく、実行IDとスクリプトの対応を自分たちの側で記録する手もあります。なお、customへの置き換えの対象に/deep-researchのような組み込みの名前は入りません。

parent_agent_idでサブエージェントの親を辿る

workflow.run_idは「同じ実行に属する」ことは示しますが、誰が誰を起動したかまでは示しません。親子関係は別の2属性で見ます。

  • agent_id: イベントを出したサブエージェントの識別子。メインセッションのイベントには付かない
  • parent_agent_id: そのサブエージェントを起動した、または最後に再開したエージェントのagent_id。親がメインセッションのときは付かない

agent_idが付くのは、サブエージェントのAPIイベントとツールイベント、assistant_response、そして終了時のsubagent_completedです。subagent_completedには所要時間(duration_ms)とツール呼び出し数(total_tool_uses)が入るので、エージェントごとの重さを見るのに向きます。ただしtotal_tokensは最後のリクエスト1回ぶんで、合計ではありません。

どちらもv2.1.310以降が必要です。workflow.run_idの絞り込みにparent_agent_idを重ねると、実行の中の起動ツリーが組み立てられます。parent_agent_idが空のエージェントがツリーの根、あとはparent_agent_idの値が別のエージェントのagent_idと一致する関係を辿ります。

注意点が2つあります。

  1. 「最後に再開した」とあるため、一度止まって再開されたエージェントのparent_agent_idは、元の起動者ではなく再開した側を指すことがあります。再開の挙動は止めた実行の戻り方に書いた/workflowsの操作と関係します
  2. Workflowスクリプトが直接起動したエージェントがparent_agent_idを持つかどうかは、モニタリング資料に記載がありません。ツリーの根をどこに置くかは、手元の実イベントで確かめてから決めます

OTelのログレコードの属性数を絞るSDK側の上限(OTEL_LOGRECORD_ATTRIBUTE_COUNT_LIMIT)を下げている環境では、tool_decisionイベントからparent_agent_idとagent_idが先に欠けます。ツリーが組めないときはこの上限を疑います。

実行ごとの集計に使う

絞り込みの次の使い道は、workflow_run_idでのグループ化です。実行ごとに「APIリクエストの数」「ツール呼び出しの数」「失敗数」を並べると、重かった実行が一目で分かります。

-- 例: 実行ごとのツール失敗数とエージェント数(列名は収集基盤に合わせて読み替え)
SELECT workflow_run_id,
       COUNT(DISTINCT agent_id)                       AS agents,
       COUNT(*) FILTER (WHERE event_name = 'tool_result') AS tool_calls,
       COUNT(*) FILTER (WHERE event_name = 'tool_result'
                          AND success = 'false')      AS tool_failures
FROM claude_code_events
WHERE workflow_run_id IS NOT NULL
GROUP BY workflow_run_id
ORDER BY tool_failures DESC;

successは文字列の"true"か"false"で出るため、真偽値として比較すると取りこぼします。

エージェント数の見積もりは、workflowsの規模の指針と見比べる材料にもなります。指針でsmallを選んだのに実行のエージェント数が想定を超えていれば、スクリプト側の展開が原因です。

実行IDは1つの依頼に1つとは限らない

ultracodeを有効にしたセッションでは、1つの依頼が複数のWorkflowに分かれることがあります。コードの理解、変更、検証が順に別の実行として走る例が、Workflowの説明に挙がっています。このときwf_の値は実行の数だけ現れます。

依頼単位の合計が欲しいなら、workflow.run_idだけでは足りません。同じ時間帯・同じsession.idの実行をまとめるなど、集計側で束ね直す必要があります。

止めた実行を同じスクリプトで再起動した場合に、新しい実行IDが振られるのか、元の値が引き継がれるのかは、モニタリング資料にもWorkflowの資料にも記載がありません。再開前後を同じ実行として数えるかどうかを決める前に、手元で停止と再起動を1回行い、wf_の値を見比べておくと安全です。

起動ツリーを組む例

親子関係の読み方を、架空の値で示します。値は説明用で、実際のIDの形とは異なります。

agent_idparent_agent_id読み方
a1parent_agent_id(空)読み方ツリーの根。親はメインセッション
a2parent_agent_ida1読み方a1が起動したエージェント
a3parent_agent_ida1読み方a1が起動した、a2と同じ階層のエージェント
a4parent_agent_ida3読み方a3がスキル呼び出しなどで起動した孫

同じwf_の中に、この形の行が並びます。再帰的な検索(SQLなら再帰CTE)でparent_agent_idを辿れば、起動の深さごとにAPIやツールの件数を数えられます。孫の世代まで同じworkflow.run_idを持つので、深さで切っても実行単位の合計とは食い違いません。

誰が走らせた実行かも同じ行に載る

イベントには、実行の識別子のほかに標準属性も付きます。user.emailは取得できる場合に常に含まれ、user.account_uuidとorganization.idも取れます。workflow.run_idで束ねた行をそのまま見ると、実行を走らせた人まで特定できます。

チームで共有するダッシュボードに実行単位の表を載せるときは、この点を先に決めておきます。個人を出したくなければ、収集側でuser.emailを落とすか、集計で潰す運用になります。セキュリティ監査の文脈で実行を扱う場合は、OTELイベントと監査項目の対応表の読み方が参考になります。

設定して出力を確かめる

ログのエクスポーターが有効なら、追加の設定は要りません。確認用に標準出力へ出す最小構成は次のとおりです。

export CLAUDE_CODE_ENABLE_TELEMETRY=1
export OTEL_LOGS_EXPORTER=console
claude

Workflowを1回走らせたあと、出力の中でworkflow.run_idを探します。wf_で始まる値が見つかれば成功です。見つからないときの切り分けは次の順です。

  • CLAUDE_CODE_ENABLE_TELEMETRYが1か。リポジトリの.claude/settings.jsonに書いたOTel変数は無視されるので、シェルか~/.claude/settings.json、管理設定に置く
  • OTEL_LOGS_EXPORTERがnoneでないか。リポジトリ側からnoneにして信号を切ることはできる
  • Claude Codeがv2.1.202以降か
  • 見ているのはメインセッションのイベントではないか。メインセッション自身のイベントにはworkflow.run_idが付きません

本番ではconsoleではなくotlpにし、収集先のエンドポイントを指定します。

v2.1.202より古いクライアントが混ざる環境では、その端末の実行にはこの属性が付きません。実行単位の集計から漏れて見えるので、app.versionを有効にしてバージョン別の件数も並べておくと、欠けた理由を切り分けられます。app.versionは既定では出ない属性で、OTEL_METRICS_INCLUDE_VERSIONで有効にします。

prompt.idとの使い分け

知りたいこと使う属性
1回のプロンプトで何が起きたか使う属性prompt.id
1回のWorkflow実行で何が起きたか使う属性workflow.run_id
実行の中の誰が誰を起動したか使う属性agent_idとparent_agent_id
1回のAPI呼び出しに対応するツール結果使う属性request_idとtool_use_id

Workflowはバックグラウンドで走り、1回のプロンプトから複数の実行が始まることもあります。Workflowのイベントにprompt.idも付くかどうかは、モニタリング資料に記載がありません。プロンプト起点で見る調査と、実行起点で見る調査は、別のキーで行う前提にしておくと混乱しません。

拒否や承認待ちを実行単位で見たいときは、safety_eventの集計と組み合わせる手があります。属性の読み方はsafety_eventの集計で扱っています。Workflowを動かす側の使い方はダイナミックワークフローの解説にあります。

参照した一次ソース

この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。

  1. code.claude.com/docs/en/monitoring-usage
  2. code.claude.com/docs/en/workflows
  3. code.claude.com/docs/en/changelog
この記事を共有:XはてブLinkedIn