gen_ai.agent.name や gen_ai.conversation.id などの GenAI セマンティック規約 に基づく属性がタグ付けされます。
始める前に
まず、Agent Lens SDK をインストールし、project を初期化します。このステップで entity と project が Agent Lens に登録され、SDK がスパンを UI 上の正しい場所に送信できるようになります。SDK はWANDB_API_KEY 環境変数から APIキーを読み取ります。
- Python
- TypeScript
[YOUR-TEAM] を Forge の entity 名に、[YOUR-PROJECT] をプロジェクト名に置き換えます。entity は必須です。start_conversation()、start_turn()、start_llm()、start_tool()、start_subagent() のいずれかを呼び出す前に、tracing.init() を呼び出してください。init() の実行前や shutdown() の実行後は、トレース関数はエラーを出さずに no-op となります。そのため、インストルメンテーションを本番コードに残したまま、設定で有効・無効を制御できます。プロセスの終了時に tracing.shutdown() を呼び出して、バッファに残っているスパンをフラッシュしてください。この関数は atexit にも登録されています。エージェントのデータモデル
Agent Lens は、エージェントの動作を一対多の関係による階層としてモデル化します。1 つのエージェントは複数の会話を持つことができ、1 つの会話は複数のターンを、1 つのターンは複数の LLM Call を持つことができます。さらに、1 つの LLM Call から複数のツール呼び出しをトリガーできます。
次の図は、1 つのエージェントが複数の会話を持ち、1 つの会話が複数のターンを持つ、といった階層関係を示しています。
会話は、親スパンではなく共通の
conversation_id 属性によってターンをグループ化します。そのため、各ターンはそれぞれ独自の OTel トレースを開始します。この設計により、分散トレースと並列実行がサポートされます。クライアントはサーバー側で集約することなく、スパンを OTel コレクターに直接送信します。
エージェントトレース API
以下のセクションでは、各トップレベルのトレース関数と、その関数が受け入れる引数について説明します。これらの関数を使用して、前のセクションで説明したデータモデルの会話、ターン、LLM Call、ツール呼び出しの各レイヤーをインストルメントします。 Agent Lens は、以下のトップレベル関数を提供します。各関数が返すオブジェクトは、コンテキストマネージャーとして使用する (Python ではwith、TypeScript では try/finally を使用) か、.end() を呼び出して手動で終了することができます。
会話を開始する
start_conversation() (Python) または startConversation() (TypeScript) は、すべての子スパンに conversation_id 属性を付与し、ターンが Conversations タブでグループ化されるようにします。conversation_id / conversationId を指定する場合は、会話の有効期間を通じて同じ値を使用する必要があります。既存の会話に新しいターンを追加するには、同じ ID を再利用してください。省略した場合は、SDK が UUID を自動的に生成します。
実行中の会話はコンテキスト (Python の ContextVar または Node.js の AsyncLocalStorage) に保存されます。そのため、同じ非同期コンテキスト内で実行されるコードであれば、会話オブジェクトを明示的に渡さなくても tracing.get_current_conversation() / tracing.getCurrentConversation() で取得できます。
- Python
- TypeScript
ターンを開始する
start_turn() (Python) および startTurn() (TypeScript) は、新しい OTel トレースのルートとなる invoke_agent スパンを作成します。Agent Lens は、タイムラインビューでユーザーとエージェント間の 1 回のやり取り全体をこのスパンで表します。
- Python
- TypeScript
- トップレベル関数として呼び出す (
tracing.start_turn(...)/tracing.startTurn(...)) : 以下のサンプルではこの形式を使用しています。コンテキストから実行中の会話を解決し、その会話 ID を引き継ぎます。実行中の会話がない場合、ターンはconversation_idなしで作成され、他のターンとはグループ化されません。 - インスタンスメソッドとして呼び出す (
conversation.start_turn(...)/conversation.startTurn(...)) : 参照を保持している会話に対して呼び出します。コンテキストマネージャーのブロック内など、会話オブジェクトが明示的にスコープ内にある場合に便利です。このガイドの後半で紹介する「コンテキストマネージャーまたは try-finally パターン」のサンプルでは、この形式を使用しています。両 SDK のConversation、Turn、LLM、Tool、SubAgentのリファレンスページへの直接リンクについては、前述のデータモデルの 表を参照してください。
LLM Call を開始する
start_llm() / startLLM() は、現在のターンの下にネストされた chat スパンを作成します。Agent Lens はこのスパンを使用して、トークン使用量、モデル名、入力メッセージと出力メッセージ、推論を UI に表示します。
- Python
- TypeScript
llm オブジェクトが閉じる前に応答データを割り当てます。
- Python
- TypeScript
provider_name / providerName は明示的に指定してください。Agent Lens はモデル文字列からプロバイダーを推測しません。
ツール呼び出しを開始する
start_tool() / startTool() は execute_tool スパンを作成します。このスパンは、コンテキスト内でアクティブな OTel スパン (通常は、そのツール呼び出しを生成した LLM Call の chat スパン) の子になります。
- Python
- TypeScript
- Python
- TypeScript
エージェントトレースの使用パターン
以下のセクションでは、エージェントのコード構成に応じてこれらの関数を組み合わせる方法について説明します。 以下のサンプルでは、Agent Lens SDK の 2 つのタイプを使用します。Message(Python · TypeScript) は、会話内の 1 つのエントリ (ユーザー入力、アシスタントの応答、システムプロンプト、ツール結果のいずれか) を表します。メッセージのリストをllm.input_messages/llm.inputMessagesに割り当てるとモデルが受け取った内容が記録され、llm.output_messages/llm.outputMessagesに割り当てるとモデルが生成した内容が記録されます。Usage(Python · TypeScript) は、LLM の応答からトークン数を取得するもので、llm.usageに割り当てます。
コンテキストマネージャーまたは try-finally パターン
ほとんどのエージェントでは、Python ではコンテキストマネージャーパターンを、TypeScript では try-finally パターンを使用してください。例外が発生した場合でも、スパンはブロックの終了時に閉じられ、送信されます。 Agent Lens は実行中の会話、ターン、LLM Call をコンテキストに保持します。そのため、ブロック内で呼び出される関数は、親への参照を明示的に保持していなくてもstart_llm() / startLLM() や start_tool() / startTool() を呼び出せます。コードが同じ非同期コンテキスト内で実行される限り、モジュールの境界をまたいでも機能します。コールスタックの任意の場所から実行中のオブジェクトを取得するには、tracing.get_current_conversation() / tracing.getCurrentConversation()、tracing.get_current_turn() / tracing.getCurrentTurn()、tracing.get_current_llm() / tracing.getCurrentLLM() を使用します。
- Python
- TypeScript
手動で開始と終了を行うパターン
with ブロックや try/finally を使用できない場合は、.end() を明示的に呼び出します。たとえば、スパンの開始と終了を別々の関数の Call で行う場合や、コルーチンの外部で非同期のライフサイクルを管理する場合が該当します。スパンを閉じてコレクターにフラッシュするために、作成したすべてのオブジェクトで必ず .end() を呼び出してください。TypeScript では、ターンまたは会話を終了すると、その配下でまだ開いている子孫もすべて閉じられます。
- Python
- TypeScript
スパンの時刻の一貫性を保つ
UI では、Agent Lens はターンとスパンを開始・終了のタイムスタンプに基づいて並べ替えて表示します。 ターンのステップを正しい順序で表示し、正しい所要時間を報告するには、そのスパンが次の 2 つの要件を満たす必要があります。- すべてのタイムスタンプが同じクロックから取得されていること。 デフォルトでは、OTel スパンは自身を作成したプロセスのクロックからタイムスタンプを取得します。ブラウザーとサーバーのように、クロックが一致しない複数のプロセスからターンのスパンが作成されると、Agent Lens UI でターンのステップが誤った順序で表示されることがあります。たとえば、ツール呼び出しが、それを要求した LLM Call よりも前に表示される場合があります。
- ルートスパンが子スパンを包含していること。 ターンはどの子スパンよりも先に開始し、すべての子スパンが終了してから終了してください。コンテキストマネージャーと try-finally パターン を使用すると、これが自動的に行われます。
- TypeScript SDK: スパンの開始時に
startTimeを、end()の呼び出し時にendTimeを渡します。 - Python SDK: ターンの完了後に
tracing.log_turnでターンをログします。log_turnにstarted_atとended_atを渡し、各子スパンにもこれらを設定します。log_turnにこれらを渡さない場合、ターンの開始時刻は最も早い子スパンの開始時刻、終了時刻は最も遅い子スパンの終了時刻になります。 - OpenTelemetry SDK: スパンの開始時に開始時刻を、終了時に終了時刻を渡します。Python では
start_timeとend_time、TypeScript ではstartTimeとendTimeを使用します。
[TEAM-NAME] と [PROJECT-NAME] を、Forge のチーム名とプロジェクト名に置き換えてください。
- Python
- TypeScript
セマンティック規約
Agent Lens SDK は、GenAI セマンティック規約および GenAI エージェント スパン規約に準拠した OTel スパンを出力します。Agent Lens はあらゆる OTel スパンを受け入れ、すべての属性を保存してクエリできるようにします。任意の Agent Lens トレース オブジェクトでset_attributes() / setAttributes() を使用すると、スパンに任意の属性を追加できます。詳しくは、エージェント スパンに属性を設定するを参照してください。
SDK は専用のプライベートな OpenTelemetry トレーサープロバイダーを持っています。このプロバイダーは Agent Lens を通じて作成されたスパンのみをエクスポートします。アプリケーションのグローバル プロバイダーを置き換えることはなく、無関係なインストルメンテーションのスパンをエクスポートすることもありません。
Agent Lens UI でのデータの表示
前述のパターンでエージェントをインストルメントして実行すると、トレースが Agent Lens project の Conversations タブ (https://forge.coreweave.com/agent-lens/[your-team-name]/[your-project-name]/conversations) に表示されます。
- Conversations タブには、すべての会話が、ターンのアクティビティを示すミニマップとともに表示されます。
- 会話の詳細ビューは、会話をクリックすると開きます。このビューには、その会話のすべてのターン、LLM Call、ツール実行、トークン数、および関連付けられたフィードバックが表示されます。