mcpField
A
agent-observability-replay-trace

agent-observability-replay-trace

github.com/datadog-labsテスト・品質180411 回インストール6 日前に更新
リポジトリ

agent-observability-replay-trace について

Datadog Agent Observability のトレースをローカルコードで再実行し、新しい出力を元の結果と比較するスキルです。トレース取得、ローカル実行環境の準備、分離された再実行、変更と確認の反復を案内します。

主な機能

  1. 01トレースを取得して基準出力を特定
  2. 02ローカルコードでトレースを実行
  3. 03元の出力と再実行結果を比較
  4. 04-local のアプリ名で再実行を分離
  5. 05MCP または pup によるトレース取得

活用シーン

  1. 01不満のある本番トレース結果を再現
  2. 02コード変更による出力の改善を確認
  3. 03ローカル再実行と差分を使って調整

MCP 対応

datadog-llmo MCP が利用可能な場合はトレースアクセスに使い、なければ pup に切り替えます。元のトレースを取得し、再実行後のトレースを確認します。

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/datadog-labs/agent-skills --skill agent-observability-replay-trace

インストール後、Claude Code で/agent-observability-replay-traceを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
details.md
local-setup.md
scripts/
replay_runner_template.py

詳細

ローカルコードに対してトレースを再実行する

本番トレース1件を使った高速な反復ループです。開発者が出力に満足しなかったトレースを取得し、必要に応じてコードを変更して、開発者のローカルコードに対して再実行し、新旧の出力を簡潔な差分で表示します。満足するまでこれを繰り返します。プロジェクトの構成は問いません。

開発者のコーディングエージェントから呼び出します:/agent-observability-replay-trace <trace-id> [<changes to test>]。 変更がない場合は、再実行と差分表示のみを行い(再現/リグレッションチェック)、その後ループに入るかどうかを提示します。

このファイルはワークフローの要点をまとめたものです。意図的に簡潔にしています。詳しい内容は references/details.md(トレースバックエンドと pup のフラグ、ランナーの契約、エクスポートモード、ポーリング、トレースリンクのスコープ修正)および references/local-setup.md(デプロイ済みアプリをローカルで実行可能にする方法)に記載されています。pup を使ったり、ランナーを生成したりする前に details.md を読んでください。

コードを書く際は、コメントを最小限にするか、付けないでください。 生成または編集するもの(アノテーション、ランナーの ENTRYPOINTS エントリー、ローカルハーネス、反復編集)はすべて周囲のコードに合わせ、不要なコメントを含めないでください。コードから明らかな処理を説明せず、真に自明でない 理由 がある場合にのみコメントを追加してください。

インテントのタグ付け: datadog-llmo MCP ツールを呼び出すたびに、telemetry.intent の先頭に skill:agent-observability-replay-trace[<inv_id>] — を付けてください(実行ごとに一度生成し、すべての呼び出しで再利用する短い ID)。その後に、ツールを呼び出す理由を記述します。最初の MCP ツール呼び出しのみ、代わりに skill:agent-observability-replay-trace:start[<inv_id>] — を使ってください(:start サフィックスに注意してください)。最初の呼び出しの例:skill:agent-observability-replay-trace:start[3a9f1c2b] — fetch the original trace's baseline output。pup CLI の呼び出しには telemetry.intent がないため、これは MCP 経由の場合のみ適用されます。

インタラクションモデル — 選択式ゲートであり、そこで終了しない

これはライブなループです。各判断ポイントでは、回答を待ってターンを終える単なる質問ではなく、AskUserQuestion セレクター(プランモード形式のメニュー)で選択肢を提示してください。ゲートは2つあります。(a)コード変更を提案した後、再実行する前。(b)各差分の表示後。自由入力の選択肢を使うと、ユーザーは修正内容をその場で入力できます。追加質問はせず、その内容に直接対応してください。ユーザーが「ここで停止」を選ぶまで、再実行のたびにセレクターを提示し続けてください。

対象範囲 — まず確認

  • ddtrace/LLM Obs でトレースされていること(ml_app と、検出可能なエントリーポイントがあること)。Python を第一級の言語として扱います。ほかの言語も使用できますが、その SDK/ビルドツールの契約に従ってランナーを作成します。
  • JSON でシリアライズ可能なエントリーポイント入力と、ルートスパン用の呼び出し可能なシーム(ステップ3.5を参照。「実行可能かどうか」という二択ではありません。デプロイ済みのみのアプリでも、通常の呼び出し可能関数が公開されている場合があります)。
  • トレースアクセス用バックエンド — datadog-llmo MCP(利用可能な場合に使用)または pup CLI(フォールバック。どちらもない場合はこちらのほうが簡単にインストールできます)(ステップ0)。
  • 認証情報: DD_API_KEY + DD_SITE + プロバイダーキー。DD_APP_KEY は不要です — Experiment ではなく通常のトレースを扱います(agent-observability-replay-experiment を参照)。
  • 副作用と取り消し不能な処理: 再実行すると実際のコードが再度実行されます(モデル利用料の発生 + 実データへの書き込み)。また、LLM Obs のトレースは削除できません。スコープを誤って再実行すると(誤った ml_app を指定した場合)、本番アプリのダッシュボードや評価セットを恒久的に汚染します。そのため、<ml_app>-local による分離(ステップ4/6/7)は、単なる整理ではなく不可欠です。最初の再実行の前に警告してください。

ワークフロー

0. トレースにアクセスするバックエンドを確保する

次の順に選択します。(1)mcp__datadog-llmo-mcp__* ツールがある場合は MCP — これがデフォルトです(読み取り機能がやや充実しており、構造化ツリーと content_info を利用できます)。(2)それ以外で pup がインストールされていて、pup auth がアプリの組織を対象としている場合は pup。(3)どちらもない場合は、ユーザーに pup のインストールを案内します(MCP よりセットアップが簡単なため、ここでは pup を推奨します)。

brew tap datadog-labs/pack && brew install datadog-labs/pack/pup
pup auth login

(MCP の代替手段:claude mcp add --scope user --transport http "datadog-llmo-mcp" "https://mcp.datadoghq.com/v1/mcp?toolsets=llmobs"。https://docs.datadoghq.com/bits_ai/mcp_server/setup/ を参照してください。)バックエンドなしでは先に進まないでください。 バックエンドと操作の対応関係、および pup の正確なフラグと注意点は details.md にあります。pup を使う前に該当セクションを読んでください。 pup では次の 2 点が必須です。(1)結果は data.spans[] またはトップレベルの spans[] に返されます(バージョンや --no-agent の有無によって異なります)。そのため、どちらか存在する方を解析してください。そうしないと、取り込み済みトレースを検出できず(ステップ 7 で、検出失敗が通知されない偽陰性になります)、(2)認証が存在することだけでなく、トークンの有効期限も確認してください(pup auth status)。ループの途中で期限が切れると、「トレースが見つからない」ように見えます。

1. コマンドを解析する

<trace-id> と任意の自由記述による変更内容(ID の後ろのすべて)が指定されます。何もなければ差分のみのモードです。プロジェクト(LLMObs.enable(ml_app=…) / DD_LLMOBS_ML_APP)またはトレースから ml_app を特定し、曖昧な場合は確認してください。

2. トレースを取得し、ベースラインを特定する

バックエンド経由でトレースを取得し、total_duration_ms(ステップ 7 で使用)、trace_url、および存在する場合は metadata.replay_input / replay_entrypoint を記録します。**ベースラインとなるフィールドを特定してください。常にルート出力とは限りません。**開発者が問題視している値は、ツール呼び出しの入力や、数階層下にある中間出力の場合があります。また、アプリがスパンに記録する前に値を後処理していることもあります。コード変更で実際に変えられるフィールドを選んでください。そうしないと、差分がノイズに埋もれてしまいます。

2.5. ファンアウトを確認する

ルートスパンが繰り返される兄弟サブツリーにファンアウトしている場合(N 個の並列サブルーチンを処理するバッチ/マップ)、テスト対象の変更は通常、単一のブランチで確認できます。ルート全体をリプレイすると、追加の情報が得られないまま、コストと時間が約 N 倍になります。代表的なブランチを 1 つリプレイする方法を提案し、スキップした内容を記録してください。慎重に選んでください。終端のツール、または副作用のあるツールまで到達したブランチのうち、最もコストの低いものを選びます(大半のブランチは何もせず、何の検証にもなりません)。入力はルートではなく、子スパンの入力から復元してください。変更が本質的にブランチをまたぐ場合に限り、ルート全体を対象にします。

3. エントリーポイントと入力を解決する
  • エントリーポイント: metadata.replay_entrypoint があればそれを使います。それ以外の場合は、ルートスパン(名前/種類)とコードから推定し、ユーザーに確認してください。
  • 入力: metadata.replay_input があればそれを使います。それ以外の場合は、提案する入力を導出し(表示されたプロンプトは情報が失われているため、コードのシグネチャを優先します)、ユーザーに確認・編集してもらいます。
3.5. ローカルで実行する経路を確保する(最も内側の呼び出し可能な境界を見つける)

「このルートスパンにおける最も内側の呼び出し可能な境界は何ですか。また、JSON を渡して直接呼び出せますか?」と尋ねてください。「アプリを実行できますか?」ではありません。すでに直接呼び出し可能であれば、スキップして続行します。ハンドラー/サービスの内部に隠れている場合(デプロイ環境でしか動かず、ローカルの __main__ がなく、稼働中のインフラに依存する場合)は、references/local-setup.md に従います(検出 → 提案 → 承認 → 構築)。よくある中間ケースとして、デプロイ済みサービスの中核ロジックがすでに単純な呼び出し可能オブジェクト(ポートとアダプターの構成)である場合は、その境界を切り出して呼び出すだけで十分です。ローカルセットアップ全体を行うのは過剰です。

4. 2つの永続的な成果物を用意します(初回のみ)
  • a)エントリーポイント内のアノテーションを、アプリの実際のエントリーポイントに追加し、今後のすべてのトレース(本番環境も含む)が自己記述されるようにします。成功時/遅延終了時の処理ではなく、スパン開始時に記録してください。失敗した実行にも replay_input が残る必要があります(再実行したいのは、まさにこうした実行です)。
    LLMObs.annotate(span=span, metadata={"replay_entrypoint": "<stable id>", "replay_input": <extractor>})
    
    replay_output は不要です。元のトレースがベースラインになります。(Python 以外ではアノテーション API が異なります。例:Go の span.Annotate(llmobs.WithAnnotatedMetadata(...))。details.md を参照してください。)
  • 分離の事前確認(ランナーを書く前に実施): エントリーポイントの呼び出し経路で、スパンごと/呼び出しごとの ml_app オーバーライド(Go の llmobs.WithMLApp、Python のデコレーター上または LLMObs.annotate 内の ml_app=)を grep してください。これらは初期化レベルの -local より優先されるため、アプリのスパンが本番環境に送られる可能性があります。トレーサーレベルの設定は、分離されている証拠にはなりません。該当するものがある場合、アプリの ml_app は環境変数から解決される必要があります。これにより -local が優先されます。
  • b)ランナー — details.md の言語に依存しないランナーの契約を満たします(環境変数を読み込む → <ml_app>-local を導出する → JSON を使って1つのエントリーポイントを実行する → エラーを含むすべての終了経路でフラッシュする → ml_app が -local で終わらない場合は起動を拒否する → -local の ml_app を表示する)。Python: scripts/replay_runner_template.py をコピーし、ENTRYPOINTS を埋めてください。その他の言語: 契約に沿って実装してください。Python API をそのまま使えると想定しないでください(Go API とエクスポートモードに関する注意点は details.md を参照)。また、その言語にプロセス内 dotenv がない場合は、プロジェクトの環境変数を読み込み、周囲のプロバイダー変数を解除し、-local のオーバーライドをエクスポートする実行ラッパー(成果物 c)を追加してください。実行コマンドを推定し、確認してください。ホストリポジトリのビルドファイルの慣例に従ってください(Bazel/Gazelle → cmd/<name>/、Gazelle を実行し、リプレイ前にビルドします)。
5. (変更を依頼された場合)編集してから判定する

コードを変更し、変更差分を開発者に提示してから、AskUserQuestion セレクターを表示します: 今すぐリプレイ / 先に変更を調整 / キャンセル。「今すぐリプレイ」の場合のみリプレイします。

6. リプレイ

最初のリプレイの前に、警告し、環境をサニタイズしてください。リプレイを再実行すると実際に処理が行われます。モデル利用料が発生し、実際の書き込みも行われます。コーディングエージェント自身の環境変数(Claude Code が設定する ANTHROPIC_API_KEY/ANTHROPIC_BASE_URL や、その他のプロバイダーキー)によって、アプリの SDK が設定済みのモデルゲートウェイを迂回することがあります。これは差分からは見えない再現性のギャップです。デフォルトでは周囲のプロバイダー変数を解除し、そのことを報告してください(単に確認を求めるだけにしないでください)。また、アプリに独自の環境変数キーのガード処理がないか grep してください。さらに、認証情報の組織がトレースの組織と一致していることも確認してください。一致しないと、リプレイは検索できない場所に送信されます(取り込みの遅延のように見えます)。 確認を得たら、t0 を記録して実行します。プロジェクトの環境変数ファイルを読み込んでください。シークレットをコマンドに直接記述しないでください(コマンドにマーカータグを付けるのは問題ありませんが、DD_API_KEY=<value> を直接記述すると権限判定でブロックされ、履歴/トランスクリプトに漏れるため、ラッパーまたは環境変数ファイルを使用してください):

DD_TAGS=replay_run_id:<unique-id> <run cmd or wrapper> --entrypoint <id> --input-file <path>

ランナーは <ml_app>-local の下で出力し(冪等なので、リプレイによって本番環境が汚染されることはありません)、その名前を表示します。新しいトレースはその名前の下でポーリングしてください。

7. 新しいトレースを待つ
  • ランナーのサブプロセスのタイムアウト = max(120s, ~3 × total_duration_ms)。
  • 取り込みのポーリング: 戻り値を受け取ったら、<ml_app>-local の下にある replay_run_id タグを対象に、バックエンドを 約5秒ごとに最大約2分間 ポーリングします(pup:--query "replay_run_id:<id>"、プレーンな key:value)。「見つかりません」と報告する前に、必ずタグフィルターなしで再クエリしてください(<ml_app>-local と時間範囲のみ)。スパンが返された場合、フィルター/解析/スコープのいずれかに問題があり、取り込みの問題ではありません。誤って「トレースなし」と判断すると、正常に見えて無駄な再実行を招きます。
  • 一致するたびに分離を確認してください。一致するタグは証明にはなりません。 --query/タグ一致では、実際の ml_app が別のアプリであるスパンが返されることがあります(--ml-app フィルターが無視されます)。返されたスパンごとに ml_app を読み取り、クリーンなリプレイとして報告する前に -local で終わることをアサートしてください。そうしなければ、実際には本番環境にあるトレースなのに、「-local でクリーンにリプレイできました」と報告することになります(取り消すことはできません)。この誤った 確信 は、偽陰性よりも有害です。タイムアウトで失敗扱いにせず、待機を続けるか尋ねてください。
8. 差分(両方のトレースへのリンク付き)

新しい出力と古い出力の違いを簡潔にまとめます。意味のある違いだけを記載してください。実環境の変化にも注意してください。また、非決定的なエージェントは実行ごとに出力が異なるため、デフォルトでは2回リプレイし(モデル向けの編集だけでなく、差分のみのモードでも)、リプレイ同士を比較してください。2つのローカル実行の差が本番環境との差と同程度なら、その差分は変更ではなくサンプリングのばらつきです。リプレイで副作用のあるインテグレーションが無効化されている(ドライラン)場合、そのインテグレーションのサブツリーは存在しません。スパン数を比較する前に 両側から除外してください。そうしないと、構造上の差分が無意味になります。差分の先頭に両方のトレースリンクを記載してください。

  • 古いトレース: trace_url を そのまま記載します。ただし ファンアウトがある場合(1つの分岐だけをリプレイした場合)は、ルート全体の URL ではなく、分岐スパンにリンクしてください。
  • 新しいトレース(リプレイ): 空のトレースが開かないよう、ml_app=<ml_app>-local を含める必要があります。また、trace_url は組織切り替え用のラッパー(…/switch_to_user/<id>?next=<encoded /llm/traces …>&flow=org_switch)なので、デコードした next のクエリに ml_app=<ml_app>-local を挿入して再エンコードしてください。外側の URL には追加 しないでください(手順は details.md を参照)。ここからブラウザーで検証できないため、空でないトレースが開くことを確認してください。
9. 判定 — 反復するか、不具合のあるハーネスで停止する

ハーネス不具合の判定(差分の前): リプレイでハーネスに問題があることが判明した場合(トレースが誤った ml_app に記録された、手順7の健全性チェック後もトレースがない、フラッシュがない、または認証/組織のルーティングが誤っているなど)、不正なデータで差分に進んでは いけません。停止して、ハーネスを修正する選択肢を提示してください(ml_app のスコープを変更/フラッシュを追加/環境を修正)し、再リプレイします。 それ以外の場合、差分を提示した後、AskUserQuestion の選択肢を表示します:問題ありません — ここで終了(完了。編集内容は作業ツリーに残す)/さらに変更する(インラインの自由入力 → 手順5に戻る)。リプレイのたびに選択肢を再提示し、「ここで終了」が選ばれた場合にのみ終了します。

参考資料

  • references/details.md — トレースバックエンドと pup の正確なフラグ、ランナーの仕様(Go、エクスポートモードを含む)、ポーリングと偽陰性を防ぐ健全性チェック、トレースリンクのスコープ修正、制限事項。pup/ランナーを使う前に読んでください。
  • references/local-setup.md — デプロイ済みのアプリをローカルで実行できるようにする方法(手順3.5)。その不足が見つかった場合に読んでください。
  • scripts/replay_runner_template.py — コピーして内容を埋める Python ランナー。

コメント