Agent Observability SDK リファレンス

This product is not supported for your selected Datadog site. ().

概要

Agent Observability SDK は、LLM アプリケーションの可観測性とインサイトを提供するために、自動インスツルメンテーションおよび手動インスツルメンテーション API を提供します。

セットアップ

要件

  • 最新の ddtrace パッケージがインストールされていること (Python 3.7 以降が必要です)。
    pip install ddtrace
    
  • 最新の dd-trace パッケージがインストールされていること (Node.js 16 以降が必要です)。
    npm install dd-trace
    
  • 最新の dd-trace-java JAR をダウンロード済みであること。Agent Observability SDK は dd-trace-java v1.51.0 以降でサポートされています (Java 8 以降が必要です)。

ddtrace-run コマンドを使用してアプリケーションを実行し、必要な環境変数を指定することで、Agent Observability を有効にします。

: ddtrace-run は、すべての Agent Observability インテグレーションを自動的に有効にします。

DD_SITE=<YOUR_DATADOG_SITE> DD_API_KEY=<YOUR_API_KEY> DD_LLMOBS_ENABLED=1 \
DD_LLMOBS_ML_APP=<YOUR_ML_APP_NAME> ddtrace-run <YOUR_APP_STARTUP_COMMAND>

コマンドラインセットアップ用の環境変数

DD_SITE
必須 - 文字列
LLM データ送信先の Datadog サイト。使用するサイトは です。
DD_LLMOBS_ENABLED
必須 - 整数または文字列
Agent Observability へのデータ送信を有効にするための切り替えスイッチ。1 または true に設定する必要があります。
DD_LLMOBS_ML_APP
オプション - 文字列
すべてのトレースとスパンがグループ化される、LLM アプリケーション、サービス、またはプロジェクトの名前。これは、異なるアプリケーションや実験を区別するのに役立ちます。使用可能な文字やその他の制約については、アプリケーション命名ガイドラインを参照してください。特定のルートスパンに対してこの値を上書きするには、複数のアプリケーションのトレースを参照してください。指定しない場合、DD_SERVICE の値、またはアップストリームサービスから伝播された DD_LLMOBS_ML_APP の値がデフォルトで使用されます。
: バージョン ddtrace==3.14.0 より前では、これは必須フィールドです。
DD_LLMOBS_AGENTLESS_ENABLED
オプション - 整数または文字列 - デフォルト: false
Datadog Agent を使用していない場合にのみ必要です。その場合は、1 または true に設定する必要があります。
DD_LLMOBS_SAMPLE_RATE
オプション - 浮動小数点数 - デフォルト: 1.0
Agent Observability によって保持されるトレースの割合。トレースサンプリングを参照してください。
DD_API_KEY
オプション - 文字列
Datadog API キー。Datadog Agent を使用していない場合にのみ必要です。
DD_MCP_CAPTURE_INTENT
オプション - 整数または文字列 - デフォルト: false
1 または true に設定すると、呼び出し元のモデルに対してツールを呼び出した理由を説明するよう要求する引数がすべての MCP サーバーツールに追加されます。インテントはツールのスパンに記録されます。

アプリケーションを NODE_OPTIONS="--import dd-trace/initialize.mjs" で実行し、必要な環境変数を指定することで、Agent Observability を有効にします。

: dd-trace/initialize.mjs は、すべての APM インテグレーションを自動的に有効にします。

DD_SITE=<YOUR_DATADOG_SITE> DD_API_KEY=<YOUR_API_KEY> DD_LLMOBS_ENABLED=1 \
DD_LLMOBS_ML_APP=<YOUR_ML_APP_NAME> NODE_OPTIONS="--import dd-trace/initialize.mjs" node <YOUR_APP_ENTRYPOINT>

コマンドラインセットアップ用の環境変数

DD_SITE
必須 - 文字列
LLM データを送信する Datadog サイト。使用するサイトは です。
DD_LLMOBS_ENABLED
必須 - 整数または文字列
Agent Observability へのデータ送信を有効にするための切り替えスイッチ。1 または true に設定する必要があります。
DD_LLMOBS_ML_APP
オプション - 文字列
すべてのトレースとスパンがグループ化される、LLM アプリケーション、サービス、またはプロジェクトの名前。これは、異なるアプリケーションや実験を区別するのに役立ちます。使用可能な文字やその他の制約については、アプリケーション命名ガイドラインを参照してください。特定のルートスパンに対してこの値を上書きするには、複数のアプリケーションのトレースを参照してください。指定しない場合、DD_SERVICE の値、またはアップストリームサービスから伝播された DD_LLMOBS_ML_APP の値がデフォルトで使用されます。
: バージョン dd-trace@5.66.0 より前では、これは必須フィールドです。
DD_LLMOBS_AGENTLESS_ENABLED
オプション - 整数または文字列 - デフォルト: false
Datadog Agent を使用していない場合にのみ必要です。その場合は、1 または true に設定する必要があります。
DD_LLMOBS_SAMPLE_RATE
オプション - 浮動小数点数 - デフォルト: 1.0
Agent Observability によって保持されるトレースの割合。トレースサンプリングを参照してください。
DD_API_KEY
オプション - 文字列
Datadog API キー。Datadog Agent を使用していない場合にのみ必要です。

アプリケーションを dd-trace-java で実行し、必要なパラメータを環境変数またはシステムプロパティとして指定することで、Agent Observability を有効にします。

DD_SITE=<YOUR_DATADOG_SITE> DD_API_KEY=<YOUR_API_KEY> \
java -javaagent:path/to/your/dd-trace-java-jar/dd-java-agent-SNAPSHOT.jar \
-Ddd.service=my-app -Ddd.llmobs.enabled=true -Ddd.llmobs.ml.app=my-ml-app -jar path/to/your/app.jar

環境変数およびシステムプロパティ

次のパラメータを環境変数 (例: DD_LLMOBS_ENABLED) または Java システムプロパティ (例: dd.llmobs_enabled) として指定できます。

DD_SITEまたは dd.site
必須 - 文字列
LLM データ送信先の Datadog サイト。使用するサイトは です。
DD_LLMOBS_ENABLED または dd.llmobs.enabled
必須 - 整数または文字列
Agent Observability へのデータ送信を有効にするための切り替えスイッチ。1 または true に設定する必要があります。
DD_LLMOBS_ML_APPまたは dd.llmobs.ml.app
オプション - 文字列
すべてのトレースとスパンがグループ化される、LLM アプリケーション、サービス、またはプロジェクトの名前。これは、異なるアプリケーションや実験を区別するのに役立ちます。使用可能な文字やその他の制約については、アプリケーション命名ガイドラインを参照してください。特定のルートスパンに対してこの値を上書きするには、複数のアプリケーションのトレースを参照してください。指定しない場合、DD_SERVICE の値、またはアップストリームサービスから伝播された DD_LLMOBS_ML_APP の値がデフォルトで使用されます。
: dd-trace-java のバージョン 1.54.0 より前では、これは必須フィールドです。
DD_LLMOBS_AGENTLESS_ENABLEDまたは dd.llmobs.agentless.enabled
オプション - 整数または文字列 - デフォルト: false
Datadog Agent を使用していない場合にのみ必要です。その場合は、1 または true に設定する必要があります。
DD_API_KEYまたは dd.api.key
オプション - 文字列
Datadog API キー。Datadog Agent を使用していない場合にのみ必要です。

コマンドラインセットアップを使用する代わりに、プログラムで Agent Observability を有効にすることもできます。

LLMObs.enable() 関数を使用して Agent Observability を有効にします。

このセットアップ方法は、 ddtrace-run コマンドと一緒に使用しないでください。
from ddtrace.llmobs import LLMObs
LLMObs.enable(
  ml_app="<YOUR_ML_APP_NAME>",
  api_key="<YOUR_DATADOG_API_KEY>",
  site="<YOUR_DATADOG_SITE>",
  agentless_enabled=True,
)
パラメータ
ml_app
オプション - 文字列
すべてのトレースとスパンがグループ化される、LLM アプリケーション、サービス、またはプロジェクトの名前。これは、異なるアプリケーションや実験を区別するのに役立ちます。使用可能な文字やその他の制約については、アプリケーション命名ガイドラインを参照してください。特定のトレースに対してこの値を上書きするには、複数のアプリケーションのトレースを参照してください。指定しない場合、DD_LLMOBS_ML_APP の値がデフォルトで使用されます。
integrations_enabled- デフォルト: true
オプション - ブール値
Datadog がサポートする LLM インテグレーションについて、LLM 呼び出しの自動トレースを有効にするフラグ。指定しない場合、サポートされているすべての LLM インテグレーションがデフォルトで有効になります。LLM インテグレーションを使用しないようにするには、この値を false に設定してください。
agentless_enabled
オプション - ブール値 - デフォルト: false
Datadog Agent を使用していない場合にのみ必要です。その場合は、True に設定する必要があります。これは、Datadog Agent を必要とするデータを送信しないように ddtrace ライブラリを設定するものです。指定しない場合、DD_LLMOBS_AGENTLESS_ENABLED の値がデフォルトで使用されます。
site
オプション - 文字列
LLM データを送信する Datadog サイト。使用するサイトは です。指定しない場合、DD_SITE の値がデフォルトで使用されます。
api_key
オプション - 文字列
Datadog API キー。Datadog Agent を使用していない場合にのみ必要です。指定しない場合、DD_API_KEY の値がデフォルトで使用されます。
env
オプション - 文字列
アプリケーションの環境の名前 (例: prodpre-prodstaging)。指定しない場合、DD_ENV の値がデフォルトで使用されます。
service
オプション - 文字列
アプリケーションに使用されるサービスの名前。指定しない場合、DD_SERVICE の値がデフォルトで使用されます。
sample_rate
オプション - 浮動小数点数
Agent Observability によって保持されるトレースの割合。ddtrace 4.12.0 以降が必要です。設定されている場合、DD_LLMOBS_SAMPLE_RATE よりも優先されます。トレースサンプリングを参照してください。
capture_intent
オプション - ブール値 - デフォルト: false
True に設定すると、呼び出し元のモデルに対してツールを呼び出した理由を説明するよう要求する引数がすべての MCP サーバーツールに追加されます。インテントはツールのスパンに記録されます。指定しない場合、DD_MCP_CAPTURE_INTENT の値がデフォルトで使用されます。
このセットアップ方法は、 dd-trace/initialize.mjs コマンドと一緒に使用しないでください。

init() 関数を使用して Agent Observability を有効にします。

const tracer = require('dd-trace').init({
  llmobs: {
    mlApp: "<YOUR_ML_APP_NAME>",
    agentlessEnabled: true,
  },
  site: "<YOUR_DATADOG_SITE>",
  env: "<YOUR_ENV>",
});

const llmobs = tracer.llmobs;

llmobs 設定のオプション

mlApp
オプション - 文字列
すべてのトレースとスパンがグループ化される、LLM アプリケーション、サービス、またはプロジェクトの名前。これは、異なるアプリケーションや実験を区別するのに役立ちます。使用可能な文字やその他の制約については、アプリケーション命名ガイドラインを参照してください。特定のトレースに対してこの値を上書きするには、複数のアプリケーションのトレースを参照してください。指定しない場合、DD_LLMOBS_ML_APP の値がデフォルトで使用されます。
agentlessEnabled
オプション - ブール値 - デフォルト: false
Datadog Agent を使用していない場合にのみ必要です。その場合は、true に設定する必要があります。これは、Datadog Agent を必要とするデータを送信しないように dd-trace ライブラリを設定するものです。指定しない場合、DD_LLMOBS_AGENTLESS_ENABLED の値がデフォルトで使用されます。
sampleRate
オプション - 数値
Agent Observability によって保持されるトレースの割合。dd-trace 5.110.0 以降が必要です。設定されている場合、DD_LLMOBS_SAMPLE_RATE よりも優先されます。トレースサンプリングを参照してください。

一般的なトレーサー設定のオプション:

site
オプション - 文字列
LLM データを送信する Datadog サイト。使用するサイトは です。指定しない場合、DD_SITE の値がデフォルトで使用されます。
env
オプション - 文字列
アプリケーションの環境の名前 (例: prodpre-prodstaging)。指定しない場合、DD_ENV の値がデフォルトで使用されます。
service
オプション - 文字列
アプリケーションに使用されるサービスの名前。指定しない場合、DD_SERVICE の値がデフォルトで使用されます。
環境変数

次の値を環境変数として設定します。これらはプログラムで設定することはできません。

DD_API_KEY
オプション - 文字列
Datadog API キー。Datadog Agent を使用していない場合にのみ必要です。

既存の AWS Lambda 関数を Agent Observability でインスツルメンテーションするには、Datadog 拡張機能と各言語レイヤーを使用します。

  1. AWS コンソールで Cloudshell を開きます。
  2. Datadog CLI クライアントをインストールします。
npm install -g @datadog/datadog-ci
  1. Datadog API キーとサイトを設定します。
export DD_API_KEY=<YOUR_DATADOG_API_KEY>
export DD_SITE=<YOUR_DATADOG_SITE>

すでに Secrets Manager にシークレットがある場合や、シークレットを使用することが望ましい場合は、シークレット ARN を使用して API キーを設定できます。

export DATADOG_API_KEY_SECRET_ARN=<DATADOG_API_KEY_SECRET_ARN>
  1. Agent Observability を使用して Lambda 関数をインストールします (これには Datadog 拡張機能レイヤーのバージョン 77 以上が必要です)。
datadog-ci lambda instrument -f <YOUR_LAMBDA_FUNCTION_NAME> -r <AWS_REGION> -v 127 -e 99 --llmobs <YOUR_LLMOBS_ML_APP>
datadog-ci lambda instrument -f <YOUR_LAMBDA_FUNCTION_NAME> -r <AWS_REGION> -v 143 -e 99 --llmobs <YOUR_LLMOBS_ML_APP>
datadog-ci lambda instrument -f <YOUR_LAMBDA_FUNCTION_NAME> -r <AWS_REGION> -v 28 -e 99 --llmobs <YOUR_LLMOBS_ML_APP>
  1. Lambda 関数を呼び出し、Datadog UI で Agent Observability のトレースが表示されることを確認します。

Lambda 関数が終了する前に、flush メソッドを使用して Agent Observability のトレースを手動でフラッシュします。

from ddtrace.llmobs import LLMObs
def handler():
  # function body
  LLMObs.flush()
import tracer from 'dd-trace';
const llmobs = tracer.llmobs;

export const handler = async (event) => {
  // your function body
  llmobs.flush();
};

SDK をインストールしてアプリケーションを実行すると、自動インスツルメンテーションによる Agent Observability のデータが表示されるはずです。手動インスツルメンテーションは、カスタム構築されたフレームワークや、まだサポートされていないライブラリの操作をキャプチャするために使用できます。

トレースサンプリング

トレースサンプリングは、Python SDK (ddtrace 4.12.0以降) および Node.js SDK (dd-trace 5.110.0以降) で利用可能です。Java SDK はトレースサンプリングをサポートしていません。

トレースサンプリングは、Agent Observability が保持するトレースの割合を設定します。Agent Observability の課金は送信するスパンの量に基づくため、サンプルレートの設定は Agent Observability のコストを管理する 1 つの方法です。SDK はルートスパンでサンプリングの決定を行い、分散トレーシングを通じてダウンストリームサービスで作成されたスパンを含む、そのルートスパンのすべての子スパンに適用します。

サンプリングは、トークンやコストのメトリクス、その他の運用メトリクスなど、Agent Observability のメトリクスには影響しません。サンプリングされていないスパンは Datadog がトレースを取り込んだ後に破棄されるため、これらのメトリクスは、指定されたサンプルレートに関係なく、アプリケーションのインスツルメンテーションされたトラフィックの 100% に基づきます。トレースサンプリングは、取り込み後に適用される自動化ルールAPM トレースサンプリングといったアプリ内コントロールからも独立しています。

次の 2 つのメカニズムのいずれかを通じてサンプルレートを設定します。

サンプルレートは 0.0 (トレースを保持しない) から 1.0 (すべてのトレースを保持する) までの浮動小数点数です。デフォルトは 1.0 です。範囲外の値は無視されます。

環境変数を使用してサンプルレートを設定します。

DD_LLMOBS_SAMPLE_RATE=0.5 ddtrace-run <YOUR_APP_STARTUP_COMMAND>

または sample_rateLLMObs.enable() に渡します。これは環境変数よりも優先されます。

from ddtrace.llmobs import LLMObs

LLMObs.enable(
  ml_app="<YOUR_ML_APP_NAME>",
  sample_rate=0.5,
)

環境変数を使用してサンプルレートを設定します。

DD_LLMOBS_SAMPLE_RATE=0.5 NODE_OPTIONS="--import dd-trace/initialize.mjs" <YOUR_APP_STARTUP_COMMAND>

または llmobssampleRateinit() に渡します。これは環境変数よりも優先されます。

const tracer = require('dd-trace').init({
  llmobs: {
    mlApp: "<YOUR_ML_APP_NAME>",
    sampleRate: 0.5,
  },
});

const llmobs = tracer.llmobs;

手動インスツルメンテーション

LLM 操作をキャプチャするために、関数デコレータを使用してワークフローを簡単にインスツルメンテーションできます。

from ddtrace.llmobs.decorators import workflow

@workflow
def handle_user_request():
    ...

または、きめ細かな操作をキャプチャするためにコンテキストマネージャーベースのアプローチを使用します。

from ddtrace.llmobs import LLMObs

with LLMObs.llm(model="gpt-4o"):
    call_llm()
    LLMObs.annotate(
        metrics={
            "input_tokens": ...,
            "output_tokens": ...,
        },
    )

利用可能なスパンの種類の一覧については、スパンの種類のドキュメントを参照してください。関数内の操作をより詳細にトレースするには、インラインメソッドを使用したスパンのトレースを参照してください。

スパンをトレースするには、トレースしたい関数の関数ラッパーとして llmobs.wrap(options, function) を使用します。利用可能なスパンの種類の一覧については、スパンの種類のドキュメントを参照してください。関数内の操作をより詳細にトレースするには、インラインメソッドを使用したスパンのトレースを参照してください。

スパンの種類

スパンの種類は必須であり、llmobs トレース関数 (tracewrap、および decorate) に渡される options オブジェクトで指定されます。サポートされているスパンの種類の一覧については、スパンの種類のドキュメントを参照してください。

注: 無効なスパンの種類を持つスパンは、Agent Observability に送信されません。

関数の引数/出力/名前の自動キャプチャ

llmobs.wrap (TypeScript 用の llmobs.decorate も同様) は、トレース対象の関数の入力、出力、および名前を自動的にキャプチャしようとします。スパンに手動でアノテーションを付ける必要がある場合は、スパンのエンリッチメントを参照してください。アノテーションを付けた入力と出力は、自動キャプチャを上書きします。さらに、関数名を上書きするには、options オブジェクトの name プロパティを llmobs.wrap 関数に渡します。

function processMessage () {
  ... // user application logic
  return
}
processMessage = llmobs.wrap({ kind: 'workflow', name: 'differentFunctionName' }, processMessage)

ラップされた関数のスパンを終了するための条件

llmobs.wrap は、tracer.wrap の基盤となる動作を拡張します。関数が呼び出されたときに作成される基盤となるスパンは、次の条件で終了します。

  • 関数が Promise を返す場合、その Promise が解決または拒否されたときにスパンが終了します。
  • 関数が最後のパラメータとしてコールバックを受け取る場合、そのコールバックが呼び出されたときにスパンが終了します。
  • 関数がコールバックを受け取らず、Promise も返さない場合、関数実行の終了時にスパンが終了します。

次の例は、最後の引数がコールバックである 2 番目の条件を示しています。

const express = require('express')
const app = express()

function myAgentMiddleware (req, res, next) {
  const err = ... // user application logic
  // the span for this function is finished when `next` is called
  next(err)
}
myAgentMiddleware = llmobs.wrap({ kind: 'agent' }, myAgentMiddleware)

app.use(myAgentMiddleware)

アプリケーションでコールバック関数を使用しない場合は、代わりにインラインのトレースブロックを使用することをお勧めします。詳細については、インラインメソッドを使用したスパンのトレースを参照してください。

const express = require('express')
const app = express()

function myAgentMiddleware (req, res) {
  // the `next` callback is not being used here
  return llmobs.trace({ kind: 'agent', name: 'myAgentMiddleware' }, () => {
    return res.status(200).send('Hello World!')
  })
}

app.use(myAgentMiddleware)

スパンの開始

開始するスパンの種類に基づいて、スパンを開始する方法は複数あります。サポートされているスパンの種類の一覧については、スパンの種類のドキュメントを参照してください。

すべてのスパンは、LLMObsSpan のオブジェクトインスタンスとして開始されます。各スパンには、スパンと対話してデータを記録するために使用できるメソッドがあります。

スパンの終了

トレースを送信して Datadog アプリで表示されるようにするには、スパンを終了する必要があります。

スパンを終了するには、スパンオブジェクトインスタンスで finish() を呼び出します。可能であれば、例外が発生した場合でもスパンが確実に送信されるように、スパンを try/finally ブロックでラップします。

    try {
        LLMObsSpan workflowSpan = LLMObs.startWorkflowSpan("my-workflow-span-name", "ml-app-override", "session-141");
        // user logic
        // interact with started span
    } finally {
      workflowSpan.finish();
    }

LLM 呼び出し

Datadog の LLM インテグレーションでサポートされている LLM プロバイダーやフレームワークを使用している場合、それらの操作をトレースするために手動で LLM スパンを開始する必要はありません。
LLM スパンを手動でインスツルメンテーションしている場合は、トークン数 ( input_tokensoutput_tokenstotal_tokensなど) をスパンにアノテーションを付けて自分で記録する必要があります。詳細については、スパンのエンリッチメントを参照してください。

LLM 呼び出しをトレースするには、関数デコレータ ddtrace.llmobs.decorators.llm() を使用します。

model_name
必須 - 文字列
呼び出された LLM の名前。
name
オプション - 文字列
操作の名前。指定しない場合、name はデフォルトでトレース対象関数の名前に設定されます。
model_provider
オプション - 文字列 - デフォルト: "custom"
モデルプロバイダーの名前。
: 推定コストを米ドルで表示するには、model_provideropenaiazure_openai、または anthropic のいずれかの値に設定してください。
session_id
オプション - 文字列
基盤となるユーザーセッションの ID。詳細については、ユーザーセッションの追跡を参照してください。
ml_app
オプション - 文字列
操作が属する ML アプリケーションの名前。詳細については、複数のアプリケーションのトレースを参照してください。

from ddtrace.llmobs import LLMObs
from ddtrace.llmobs.decorators import llm

@llm(model_name="claude", name="invoke_llm", model_provider="anthropic")
def llm_call(prompt):
    completion = ... # user application logic to invoke LLM
    LLMObs.annotate(
        input_data=[{"role": "user", "content": prompt}],
        output_data=[{"role": "assistant", "content": completion}],
        metrics={"input_tokens": 4, "output_tokens": 6, "total_tokens": 10},
    )
    return completion

LLM 呼び出しをトレースするには、スパンの種類を llm として指定し、必要に応じて options オブジェクトで次の引数を指定します。

modelName
オプション - 文字列 - デフォルト: "custom"
呼び出された LLM の名前。
name
オプション - 文字列
操作の名前。指定しない場合、name はデフォルトでトレース対象関数の名前に設定されます。
modelProvider
オプション - 文字列 - デフォルト: "custom"
モデルプロバイダーの名前。
: 推定コストを米ドルで表示するには、modelProvideropenaiazure_openai、または anthropic のいずれかの値に設定してください。
sessionId
オプション - 文字列
基盤となるユーザーセッションの ID。詳細については、ユーザーセッションの追跡を参照してください。
mlApp
オプション - 文字列
操作が属する ML アプリケーションの名前。詳細については、複数のアプリケーションのトレースを参照してください。

function llmCall (prompt) {
  const completion = ... // user application logic to invoke LLM
  llmobs.annotate({
    inputData: [{ role: "user", content: prompt }],
    outputData: [{ role: "assistant", content: completion }],
    metrics: { input_tokens: 4, output_tokens: 6, total_tokens: 10 }
  })
  return completion
}
llmCall = llmobs.wrap({ kind: 'llm', name: 'invokeLLM', modelName: 'claude', modelProvider: 'anthropic' }, llmCall)

LLM 呼び出しをトレースするには、次のメソッドをインポートし、下記の引数を指定して呼び出します。

import datadog.trace.api.llmobs.LLMObs;
LLMObs.startLLMSpan(spanName, modelName, modelProvider, mlApp, sessionID);
spanName
オプション - 文字列
操作の名前。指定しない場合、spanName はデフォルトでスパンの種類に設定されます。
modelName
オプション - 文字列 - デフォルト: "custom"
呼び出された LLM の名前。
modelProvider
オプション - 文字列 - デフォルト: "custom"
モデルプロバイダーの名前。
: 推定コストを米ドルで表示するには、modelProvideropenaiazure_openai、または anthropic のいずれかの値に設定してください。
mlApp
オプション - 文字列
操作が属する ML アプリケーションの名前。null 以外の値を指定すると、アプリケーションの開始時に指定された ML アプリケーション名が上書きされます。詳細については、複数のアプリケーションのトレースを参照してください。
sessionId
オプション - 文字列
基盤となるユーザーセッションの ID。詳細については、ユーザーセッションの追跡を参照してください。

import datadog.trace.api.llmobs.LLMObs;

public class MyJavaClass {
  public String invokeModel() {
    LLMObsSpan llmSpan = LLMObs.startLLMSpan("my-llm-span-name", "my-llm-model", "my-company", "maybe-ml-app-override", "session-141");
    String inference = ... // user application logic to invoke LLM
    llmSpan.annotateIO(...); // record the input and output
    llmSpan.setMetrics(Map.of(
      "input_tokens", 617,
      "output_tokens", 338,
      "total_tokens", 955
    ));
    llmSpan.finish();
    return inference;
  }
}

ワークフロー

ワークフロースパンをトレースするには、関数デコレータ ddtrace.llmobs.decorators.workflow() を使用します。

name
オプション - 文字列
操作の名前。指定しない場合、name はデフォルトでトレース対象関数の名前に設定されます。
session_id
オプション - 文字列
基盤となるユーザーセッションの ID。詳細については、ユーザーセッションの追跡を参照してください。
ml_app
オプション - 文字列
操作が属する ML アプリケーションの名前。詳細については、複数のアプリケーションのトレースを参照してください。

from ddtrace.llmobs.decorators import workflow

@workflow
def process_message():
    ... # user application logic
    return

ワークフロースパンをトレースするには、スパンの種類を workflow として指定し、必要に応じて options オブジェクトで引数を指定します。

name
オプション - 文字列
操作の名前。指定しない場合、name はデフォルトでトレース対象関数の名前に設定されます。
sessionId
オプション - 文字列
基盤となるユーザーセッションの ID。詳細については、ユーザーセッションの追跡を参照してください。
mlApp
オプション - 文字列
操作が属する ML アプリケーションの名前。詳細については、複数のアプリケーションのトレースを参照してください。

function processMessage () {
  ... // user application logic
  return
}
processMessage = llmobs.wrap({ kind: 'workflow' }, processMessage)

ワークフロースパンをトレースするには、次のメソッドをインポートし、下記の引数を指定して呼び出します。

import datadog.trace.api.llmobs.LLMObs;
LLMObs.startWorkflowSpan(spanName, mlApp, sessionID);
spanName
オプション - 文字列
操作の名前。指定しない場合、spanName はデフォルトでスパンの種類に設定されます。
mlApp
オプション - 文字列
操作が属する ML アプリケーションの名前。null 以外の値を指定すると、アプリケーションの開始時に指定された ML アプリケーション名が上書きされます。詳細については、複数のアプリケーションのトレースを参照してください。
sessionId
オプション - 文字列
基盤となるユーザーセッションの ID。詳細については、ユーザーセッションの追跡を参照してください。

import datadog.trace.api.llmobs.LLMObs;

public class MyJavaClass {
  public String executeWorkflow() {
    LLMObsSpan workflowSpan = LLMObs.startWorkflowSpan("my-workflow-span-name", null, "session-141");
    String workflowResult = workflowFn(); // user application logic
    workflowSpan.annotateIO(...); // record the input and output
    workflowSpan.finish();
    return workflowResult;
  }
}

エージェント

エージェントの実行をトレースするには、関数デコレータ ddtrace.llmobs.decorators.agent() を使用します。

name
オプション - 文字列
操作の名前。指定しない場合、name はデフォルトでトレース対象関数の名前に設定されます。
session_id
オプション - 文字列
基盤となるユーザーセッションの ID。詳細については、ユーザーセッションの追跡を参照してください。
ml_app
オプション - 文字列
操作が属する ML アプリケーションの名前。詳細については、複数のアプリケーションのトレースを参照してください。

from ddtrace.llmobs.decorators import agent

@agent
def react_agent():
    ... # user application logic
    return

エージェントの実行をトレースするには、スパンの種類を agent として指定し、必要に応じて options オブジェクトで引数を指定します。

name
オプション - 文字列
操作の名前。指定しない場合、name はデフォルトでトレース対象関数の名前に設定されます。
sessionId
オプション - 文字列
基盤となるユーザーセッションの ID。詳細については、ユーザーセッションの追跡を参照してください。
mlApp
オプション - 文字列
操作が属する ML アプリケーションの名前。詳細については、複数のアプリケーションのトレースを参照してください。

function reactAgent () {
  ... // user application logic
  return
}
reactAgent = llmobs.wrap({ kind: 'agent' }, reactAgent)

エージェントの実行をトレースするには、次のメソッドをインポートし、下記の引数を指定して呼び出します。

import datadog.trace.api.llmobs.LLMObs;
LLMObs.startAgentSpan(spanName, mlApp, sessionID);
spanName
オプション - 文字列
操作の名前。指定しない場合、spanName はデフォルトでトレース対象関数の名前に設定されます。
mlApp
オプション - 文字列
操作が属する ML アプリケーションの名前。null 以外の値を指定すると、アプリケーションの開始時に指定された ML アプリケーション名が上書きされます。詳細については、複数のアプリケーションのトレースを参照してください。
sessionId
オプション - 文字列
基盤となるユーザーセッションの ID。詳細については、ユーザーセッションの追跡を参照してください。

ツール呼び出し

ツール呼び出しをトレースするには、関数デコレータ ddtrace.llmobs.decorators.tool() を使用します。

name
オプション - 文字列
操作の名前。指定しない場合、name はデフォルトでトレース対象関数の名前に設定されます。
session_id
オプション - 文字列
基盤となるユーザーセッションの ID。詳細については、ユーザーセッションの追跡を参照してください。
ml_app
オプション - 文字列
操作が属する ML アプリケーションの名前。詳細については、複数のアプリケーションのトレースを参照してください。

from ddtrace.llmobs.decorators import tool

@tool
def call_weather_api():
    ... # user application logic
    return

ツール呼び出しをトレースするには、スパンの種類を tool として指定し、必要に応じて options オブジェクトで引数を指定します。

name
オプション - 文字列
操作の名前。指定しない場合、name はデフォルトでトレース対象関数の名前に設定されます。
sessionId
オプション - 文字列
基盤となるユーザーセッションの ID。詳細については、ユーザーセッションの追跡を参照してください。
mlApp
オプション - 文字列
操作が属する ML アプリケーションの名前。詳細については、複数のアプリケーションのトレースを参照してください。

function callWeatherApi () {
  ... // user application logic
  return
}
callWeatherApi = llmobs.wrap({ kind: 'tool' }, callWeatherApi)

ツール呼び出しをトレースするには、次のメソッドをインポートし、下記の引数を指定して呼び出します。

import datadog.trace.api.llmobs.LLMObs;
LLMObs.startToolSpan(spanName, mlApp, sessionID);
spanName
オプション - 文字列
操作の名前。指定しない場合、spanName はデフォルトでトレース対象関数の名前に設定されます。
mlApp
オプション - 文字列
操作が属する ML アプリケーションの名前。null 以外の値を指定すると、アプリケーションの開始時に指定された ML アプリケーション名が上書きされます。詳細については、複数のアプリケーションのトレースを参照してください。
sessionId
オプション - 文字列
基盤となるユーザーセッションの ID。詳細については、ユーザーセッションの追跡を参照してください。

タスク

タスクスパンをトレースするには、関数デコレータ LLMObs.task() を使用します。

name
オプション - 文字列
操作の名前。指定しない場合、name はデフォルトでトレース対象関数の名前に設定されます。
session_id
オプション - 文字列
基盤となるユーザーセッションの ID。詳細については、ユーザーセッションの追跡を参照してください。
ml_app
オプション - 文字列
操作が属する ML アプリケーションの名前。詳細については、複数のアプリケーションのトレースを参照してください。

from ddtrace.llmobs.decorators import task

@task
def sanitize_input():
    ... # user application logic
    return

タスクスパンをトレースするには、スパンの種類を task として指定し、必要に応じて options オブジェクトで引数を指定します。

name
オプション - 文字列
操作の名前。指定しない場合、name はデフォルトでトレース対象関数の名前に設定されます。
sessionId
オプション - 文字列
基盤となるユーザーセッションの ID。詳細については、ユーザーセッションの追跡を参照してください。
mlApp
オプション - 文字列
操作が属する ML アプリケーションの名前。詳細については、複数のアプリケーションのトレースを参照してください。

function sanitizeInput () {
  ... // user application logic
  return
}
sanitizeInput = llmobs.wrap({ kind: 'task' }, sanitizeInput)

タスクスパンをトレースするには、次のメソッドをインポートし、下記の引数を指定して呼び出します。

import datadog.trace.api.llmobs.LLMObs;
LLMObs.startTaskSpan(spanName, mlApp, sessionID);
spanName
オプション - 文字列
操作の名前。指定しない場合、spanName はデフォルトでトレース対象関数の名前に設定されます。
mlApp
オプション - 文字列
操作が属する ML アプリケーションの名前。null 以外の値を指定すると、アプリケーションの開始時に指定された ML アプリケーション名が上書きされます。詳細については、複数のアプリケーションのトレースを参照してください。
sessionId
オプション - 文字列
基盤となるユーザーセッションの ID。詳細については、ユーザーセッションの追跡を参照してください。

埋め込み

埋め込み操作をトレースするには、関数デコレータ LLMObs.embedding() を使用します。

: 埋め込みスパンの入力にアノテーションを付けるには、他のスパンタイプとは異なる形式が必要です。埋め込み入力の指定方法の詳細については、スパンのエンリッチメントを参照してください。

model_name
必須 - 文字列
呼び出された LLM の名前。
name
オプション - 文字列
操作の名前。指定しない場合、name はトレース対象関数の名前に設定されます。
model_provider
オプション - 文字列 - デフォルト: "custom"
session_id
オプション - 文字列
基盤となるユーザーセッションの ID。詳細については、ユーザーセッションの追跡を参照してください。
ml_app
オプション - 文字列
操作が属する ML アプリケーションの名前。詳細については、複数のアプリケーションのトレースを参照してください。

from ddtrace.llmobs.decorators import embedding

@embedding(model_name="text-embedding-3", model_provider="openai")
def perform_embedding():
    ... # user application logic
    return

埋め込み操作をトレースするには、スパンの種類を embedding として指定し、必要に応じて options オブジェクトで引数を指定します。

: 埋め込みスパンの入力にアノテーションを付けるには、他のスパンタイプとは異なる形式が必要です。埋め込み入力の指定方法の詳細については、スパンのエンリッチメントを参照してください。

modelName
オプション - 文字列 - デフォルト: "custom"
呼び出された LLM の名前。
name
オプション - 文字列
操作の名前。指定しない場合、name はトレース対象関数の名前に設定されます。
modelProvider
オプション - 文字列 - デフォルト: "custom"
モデルプロバイダーの名前。
sessionId
オプション - 文字列
基盤となるユーザーセッションの ID。詳細については、ユーザーセッションの追跡を参照してください。
mlApp
オプション - 文字列
操作が属する ML アプリケーションの名前。詳細については、複数のアプリケーションのトレースを参照してください。

function performEmbedding () {
  ... // user application logic
  return
}
performEmbedding = llmobs.wrap({ kind: 'embedding', modelName: 'text-embedding-3', modelProvider: 'openai' }, performEmbedding)

検索

検索スパンをトレースするには、関数デコレータ ddtrace.llmobs.decorators.retrieval() を使用します。

: 検索スパンの出力にアノテーションを付けるには、他のスパンタイプとは異なる形式が必要です。検索出力の指定方法の詳細については、スパンのエンリッチメントを参照してください。

name
オプション - 文字列
操作の名前。指定しない場合、name はデフォルトでトレース対象関数の名前に設定されます。
session_id
オプション - 文字列
基盤となるユーザーセッションの ID。詳細については、ユーザーセッションの追跡を参照してください。
ml_app
オプション - 文字列
操作が属する ML アプリケーションの名前。詳細については、複数のアプリケーションのトレースを参照してください。

from ddtrace.llmobs.decorators import retrieval

@retrieval
def get_relevant_docs(question):
    context_documents = ... # user application logic
    LLMObs.annotate(
        input_data=question,
        output_data = [
            {"id": doc.id, "score": doc.score, "text": doc.text, "name": doc.name} for doc in context_documents
        ]
    )
    return

検索スパンをトレースするには、スパンの種類を retrieval として指定し、必要に応じて options オブジェクトで次の引数を指定します。

: 検索スパンの出力にアノテーションを付けるには、他のスパンタイプとは異なる形式が必要です。検索出力の指定方法の詳細については、スパンのエンリッチメントを参照してください。

name
オプション - 文字列
操作の名前。指定しない場合、name はデフォルトでトレース対象関数の名前に設定されます。
sessionId
オプション - 文字列
基盤となるユーザーセッションの ID。詳細については、ユーザーセッションの追跡を参照してください。
mlApp
オプション - 文字列
操作が属する ML アプリケーションの名前。詳細については、複数のアプリケーションのトレースを参照してください。

ここには、スパンにアノテーションを付ける例も含まれています。詳細については、スパンのエンリッチメントを参照してください。

function getRelevantDocs (question) {
  const contextDocuments = ... // user application logic
  llmobs.annotate({
    inputData: question,
    outputData: contextDocuments.map(doc => ({
      id: doc.id,
      score: doc.score,
      text: doc.text,
      name: doc.name
    }))
  })
  return
}
getRelevantDocs = llmobs.wrap({ kind: 'retrieval' }, getRelevantDocs)

スパンのネスト

現在のスパンが終了する前に新しいスパンを開始すると、2 つのスパン間の親子関係が自動的にトレースされます。親スパンは大きな操作を表し、子スパンはその中の小さなネストされたサブ操作を表します。

from ddtrace.llmobs.decorators import task, workflow

@workflow
def extract_data(document):
    preprocess_document(document)
    ... # performs data extraction on the document
    return

@task
def preprocess_document(document):
    ... # preprocesses a document for data extraction
    return
function preprocessDocument (document) {
  ... // preprocesses a document for data extraction
  return
}
preprocessDocument = llmobs.wrap({ kind: 'task' }, preprocessDocument)

function extractData (document) {
  preprocessDocument(document)
  ... // performs data extraction on the document
  return
}
extractData = llmobs.wrap({ kind: 'workflow' }, extractData)
import datadog.trace.api.llmobs.LLMObs;
import datadog.trace.api.llmobs.LLMObsSpan;

public class MyJavaClass {
  public void preprocessDocument(String document) {
  LLMObsSpan taskSpan = LLMObs.startTaskSpan("preprocessDocument", null, "session-141");
   ...   // preprocess document for data extraction
   taskSpan.annotateIO(...); // record the input and output
   taskSpan.finish();
  }

  public String extractData(String document) {
    LLMObsSpan workflowSpan = LLMObs.startWorkflowSpan("extractData", null, "session-141");
    preprocessDocument(document);
    ... // perform data extraction on the document
    workflowSpan.annotateIO(...); // record the input and output
    workflowSpan.finish();
  }
}

スパンのエンリッチメント

ここでの metrics パラメータは、個々のスパンに属性として付与される数値のことであり、Datadog プラットフォームのメトリクスではありません。特定の認識されたキー ( input_tokensoutput_tokenstotal_tokensなど) について、Datadog はこれらのスパン属性を使用して、ダッシュボードやモニターで使用するための対応するプラットフォームのメトリクス ( ml_obs.span.llm.input.tokensなど) を生成します。

SDK には、入力、出力、メタデータでスパンをエンリッチするためのメソッド LLMObs.annotate() が用意されています。

LLMObs.annotate() メソッドは、次の引数を受け入れます。

span
オプション - スパン - デフォルト: 現在のアクティブなスパン
アノテーションを付けるスパン。span が指定されていない場合 (関数デコレータを使用する場合など)、SDK は現在のアクティブなスパンにアノテーションを付けます。
input_data
オプション - JSON のシリアライズ可能な型、または辞書のリスト
JSON のシリアライズ可能な型 (LLM 以外のスパンの場合)、または辞書のリスト (形式: {"content": "...", "role": "...", "tool_calls": ..., "tool_results": ..., "audio_parts": ..., "image_parts": ...})。ここで、"tool_calls" は、必須のキー "name""arguments" とオプションのキー "tool_id""type" を持つツール呼び出し辞書のオプションのリストです。"tool_results" は、必須のキー "result" とオプションのキー "name""tool_id""type" (関数呼び出しシナリオ用) を持つツール結果辞書のオプションのリストです。"audio_parts" および "image_parts" は、マルチモーダルスパン用のメディア辞書のオプションのリストであり、それぞれ必須の "mime_type""content" (インラインで保持される base64 エンコードされたメディア) または "attachment_key" のいずれか一方を持ちます。: 埋め込みスパンは特殊なケースであり、{"text": "..."} の形式の文字列または辞書 (あるいは辞書のリスト) が必要です。
output_data
オプション - JSON のシリアライズ可能な型、または辞書のリスト
JSON のシリアライズ可能な型 (LLM 以外のスパンの場合)、または辞書のリスト(形式: {"content": "...", "role": "...", "tool_calls": ..., "audio_parts": ..., "image_parts": ...})。ここで、"tool_calls" は、必須のキー "name""arguments" とオプションのキー "tool_id""type" (関数呼び出しシナリオ用) を持つツール呼び出し辞書のオプションのリストです。"audio_parts" および "image_parts" は、マルチモーダルスパン用のメディア辞書のオプションのリストであり、それぞれ必須の "mime_type""content" (インラインで保持される base64 エンコードされたメディア) または "attachment_key" のいずれか一方を持ちます。: 検索スパンは特殊なケースであり、{"text": "...", "name": "...", "score": float, "id": "..."} の形式の文字列または辞書 (あるいは辞書のリスト) が必要です。
tool_definitions
オプション - 辞書のリスト
関数呼び出しシナリオ用のツール定義辞書のリスト。各ツール定義には、必須の "name": "..." キーとオプションの "description": "..." キーおよび "schema": {...} キーが含まれます。
metadata
オプション - 辞書
スパンによって記述される入力操作または出力操作に関連するメタデータ情報としてユーザーが追加できる、JSON のシリアライズ可能なキーと値のペアの辞書 (model_temperaturemax_tokenstop_k など)。
metrics
オプション - 辞書
スパンによって記述される操作に関連するメトリクスとしてユーザーが追加できる、JSON のシリアライズ可能なキーと数値の辞書 (input_tokensoutput_tokenstotal_tokenstime_to_first_token など)。time_to_first_token の単位は秒であり、デフォルトで出力される duration メトリクスと同様です。
tags
オプション - 辞書
ユーザーがスパンにタグとして追加できる、JSON のシリアライズ可能なキーと値のペアの辞書。キーの例: sessionenvsystem、および version。タグの詳細については、タグの使用を開始するを参照してください。
cost_tags
オプション - 文字列のリスト
生成される LLM のコストメトリクスおよびトークンメトリクスにカスタムタグとして伝播させるタグキーのリスト (tags で設定済みか、同じスパン上で以前にアノテーション付けされたもの)。既存のタグキーを参照していないエントリはスキップされます。詳細については、コスト監視を参照してください。

from ddtrace.llmobs import LLMObs
from ddtrace.llmobs.decorators import embedding, llm, retrieval, workflow

@llm(model_name="model_name", model_provider="model_provider")
def llm_call(prompt):
    resp = ... # llm call here
    LLMObs.annotate(
        span=None,
        input_data=[{"role": "user", "content": "Hello world!"}],
        output_data=[{"role": "assistant", "content": "How can I help?"}],
        metadata={"temperature": 0, "max_tokens": 200},
        metrics={"input_tokens": 4, "output_tokens": 6, "total_tokens": 10},
        tags={"host": "host_name"},
    )
    return resp

@workflow
def extract_data(document):
    resp = llm_call(document)
    LLMObs.annotate(
        input_data=document,
        output_data=resp,
        tags={"host": "host_name"},
    )
    return resp

@embedding(model_name="text-embedding-3", model_provider="openai")
def perform_embedding():
    ... # user application logic
    LLMObs.annotate(
        span=None,
        input_data={"text": "Hello world!"},
        output_data=[0.0023064255, -0.009327292, ...],
        metrics={"input_tokens": 4},
        tags={"host": "host_name"},
    )
    return

@retrieval(name="get_relevant_docs")
def similarity_search():
    ... # user application logic
    LLMObs.annotate(
        span=None,
        input_data="Hello world!",
        output_data=[{"text": "Hello world is ...", "name": "Hello, World! program", "id": "document_id", "score": 0.9893}],
        tags={"host": "host_name"},
    )
    return

@llm(model_name="gpt-realtime", model_provider="openai")
def voice_turn(user_audio_bytes):
    import base64
    resp = ... # multimodal (audio) llm call here
    LLMObs.annotate(
        span=None,
        input_data=[
            {
                "role": "user",
                "content": "Hey, how are you?",  # transcript of the input audio
                "audio_parts": [
                    {"mime_type": "audio/wav", "content": base64.b64encode(user_audio_bytes).decode("utf-8")}
                ],
            }
        ],
        output_data=[
            {
                "role": "assistant",
                "content": "Hey! I'm doing great, thanks for asking. How about you?",
                "audio_parts": [
                    {"mime_type": "audio/wav", "content": base64.b64encode(resp.audio_bytes).decode("utf-8")}
                ],
            }
        ],
    )
    return resp

@llm(model_name="gpt-4o", model_provider="openai")
def describe_image(image_bytes):
    import base64
    resp = ... # multimodal (vision) llm call here
    LLMObs.annotate(
        span=None,
        input_data=[
            {
                "role": "user",
                "content": "What is in this image?",
                "image_parts": [
                    {"mime_type": "image/png", "content": base64.b64encode(image_bytes).decode("utf-8")}
                ],
            }
        ],
        output_data=[{"role": "assistant", "content": "The image shows a golden retriever puppy."}],
    )
    return resp

audio_parts または image_parts でアノテーションが付けられたメッセージは、トレースビューでインラインオーディオプレーヤーおよび画像としてレンダリングされます。

Agent Observability トレースビューの LLM スパン。USER の入力メッセージに「Hey, how are you?」というトランスクリプト付きのインラインオーディオプレーヤーが表示され、出力の ASSISTANT メッセージに「Click to play audio」というコントロールと「Hey!I'm doing great, thanks for asking.How about you?」というトランスクリプトが表示されています。
Agent Observability トレースビューの LLM スパン。入力の USER メッセージに「What is in this image?」というプロンプトが表示され、黒い子犬のインライン写真が添えられており、出力の ASSISTANT メッセージで、それが木の床の上にいる黒いラブラドール・レトリバーの子犬であると説明されています。

SDK には、入力、出力、メタデータでスパンにアノテーションを付けるためのメソッド llmobs.annotate() が用意されています。

LLMObs.annotate() メソッドは、次の引数を受け入れます。

span
オプション - スパン - デフォルト: 現在のアクティブなスパン
アノテーションを付けるスパン。span が指定されていない場合 (関数ラッパーを使用する場合など)、SDK は現在のアクティブなスパンにアノテーションを付けます。
annotationOptions
必須 - オブジェクト
スパンにアノテーションを付けるための、さまざまな種類のデータを含むオブジェクト。

annotationOptions オブジェクトには、次のものを含めることができます。

inputData
オプション - JSON のシリアライズ可能な型、またはオブジェクトのリスト
JSON のシリアライズ可能な型 (LLM 以外のスパンの場合)、または辞書のリスト (形式: {role: "...", content: "...", audioParts: [...], imageParts: [...]} (LLM スパンの場合) のいずれか。audioParts および imageParts は、マルチモーダルスパン用のメディアオブジェクトのオプションのリストであり、それぞれ必須の mimeTypecontent (インラインで保持される base64 エンコードされたメディア) または attachmentKey のいずれか一方を持ちます。: 埋め込みスパンは特殊なケースであり、{text: "..."} の形式の文字列またはオブジェクト (あるいはオブジェクトのリスト) が必要です。
outputData
オプション - JSON のシリアライズ可能な型、またはオブジェクトのリスト
JSON のシリアライズ可能な型 (LLM 以外のスパンの場合)、またはオブジェクトのリスト (形式: {role: "...", content: "...", audioParts: [...], imageParts: [...]}) (LLM スパンの場合)。audioParts および imageParts は、マルチモーダルスパン用のメディアオブジェクトのオプションのリストであり、それぞれ必須の mimeTypecontent (インラインで保持される base64 エンコードされたメディア) または attachmentKey のいずれか一方を持ちます。: 検索スパンは特殊なケースであり、{text: "...", name: "...", score: number, id: "..."} の形式の文字列またはオブジェクト (あるいはオブジェクトのリスト) が必要です。
metadata
オプション - オブジェクト
スパンによって記述される入力操作または出力操作に関連するメタデータ情報としてユーザーが追加できる、JSON のシリアライズ可能なキーと値のペアのオブジェクト (model_temperaturemax_tokenstop_k など)。
metrics
オプション - オブジェクト
スパンによって記述される操作に関連するメトリクスとしてユーザーが追加できる、JSON のシリアライズ可能なキーと数値のオブジェクト (input_tokensoutput_tokenstotal_tokens など)。
tags
オプション - オブジェクト
スパンのコンテキストに関するタグとしてユーザーが追加できる、JSON のシリアライズ可能なキーと値のペアのオブジェクト (sessionenvironmentsystemversioning など)。タグの詳細については、タグの使用を開始するを参照してください。
costTags
オプション - 文字列の配列
生成される LLM のコストメトリクスおよびトークンメトリクスにカスタムタグとして伝播させるタグキーのリスト (tags で設定済みか、同じスパン上で以前にアノテーション付けされたもの)。既存のタグキーを参照していないエントリはスキップされます。詳細については、コスト監視を参照してください。

function llmCall (prompt) {
  const completion = ... // user application logic to invoke LLM
  llmobs.annotate({
    inputData: [{ role: "user", content: "Hello world!" }],
    outputData: [{ role: "assistant", content: "How can I help?" }],
    metadata: { temperature: 0, max_tokens: 200 },
    metrics: { input_tokens: 4, output_tokens: 6, total_tokens: 10 },
    tags: { host: "host_name" }
  })
  return completion
}
llmCall = llmobs.wrap({ kind:'llm', modelName: 'modelName', modelProvider: 'modelProvider' }, llmCall)

function extractData (document) {
  const resp = llmCall(document)
  llmobs.annotate({
    inputData: document,
    outputData: resp,
    tags: { host: "host_name" }
  })
  return resp
}
extractData = llmobs.wrap({ kind: 'workflow' }, extractData)

function performEmbedding () {
  ... // user application logic
  llmobs.annotate(
    undefined, { // this can be set to undefined or left out entirely
      inputData: { text: "Hello world!" },
      outputData: [0.0023064255, -0.009327292, ...],
      metrics: { input_tokens: 4 },
      tags: { host: "host_name" }
    }
  )
}
performEmbedding = llmobs.wrap({ kind: 'embedding', modelName: 'text-embedding-3', modelProvider: 'openai' }, performEmbedding)

function similaritySearch () {
  ... // user application logic
  llmobs.annotate(undefined, {
    inputData: "Hello world!",
    outputData: [{ text: "Hello world is ...", name: "Hello, World! program", id: "document_id", score: 0.9893 }],
    tags: { host: "host_name" }
  })
  return
}
similaritySearch = llmobs.wrap({ kind: 'retrieval', name: 'getRelevantDocs' }, similaritySearch)

function voiceTurn (userAudioBytes) {
  const resp = ... // multimodal (audio) llm call here
  llmobs.annotate({
    inputData: [
      {
        role: "user",
        content: "Hey, how are you?", // transcript of the input audio
        audioParts: [{ mimeType: "audio/wav", content: userAudioBytes.toString("base64") }]
      }
    ],
    outputData: [
      {
        role: "assistant",
        content: "Hey! I'm doing great, thanks for asking. How about you?",
        audioParts: [{ mimeType: "audio/wav", content: resp.audioBuffer.toString("base64") }]
      }
    ]
  })
  return resp
}
voiceTurn = llmobs.wrap({ kind: 'llm', modelName: 'gpt-audio', modelProvider: 'openai' }, voiceTurn)

function describeImage (imageBytes) {
  const resp = ... // multimodal (vision) llm call here
  llmobs.annotate({
    inputData: [
      {
        role: "user",
        content: "What is in this image?",
        imageParts: [{ mimeType: "image/png", content: imageBytes.toString("base64") }]
      }
    ],
    outputData: [{ role: "assistant", content: "The image shows a golden retriever puppy." }]
  })
  return resp
}
describeImage = llmobs.wrap({ kind: 'llm', modelName: 'gpt-4o', modelProvider: 'openai' }, describeImage)

audioParts または imageParts でアノテーションが付けられたメッセージは、トレースビューでインラインオーディオプレーヤーおよび画像としてレンダリングされます。

Agent Observability トレースビューの LLM スパン。USER の入力メッセージに「Hey, how are you?」というトランスクリプト付きのインラインオーディオプレーヤーが表示され、出力の ASSISTANT メッセージに「Click to play audio」というコントロールと「Hey!I'm doing great, thanks for asking.How about you?」というトランスクリプトが表示されています。
Agent Observability トレースビューの LLM スパン。入力の USER メッセージに「What is in this image?」というプロンプトが表示され、黒い子犬のインライン写真が添えられており、出力の ASSISTANT メッセージで、それが木の床の上にいる黒いラブラドール・レトリバーの子犬であると説明されています。

OpenAI の音声チャット補完の場合、audioPartsDatadog の LLM インテグレーションによって自動的にキャプチャされます。手動でのアノテーション付けは不要です。audioParts とは異なり、imageParts は現在自動的にキャプチャされず、手動でアノテーションを付ける必要があります。自動キャプチャは将来のリリースで予定されています。

SDK には、入力、出力、メトリクス、メタデータでスパンにアノテーションを付けるための複数のメソッドが用意されています。

入力と出力のアノテーション付け

LLMObsSpan インターフェースの annotateIO() メンバーメソッドを使用して、構造化された入力データと出力データを LLMObsSpan に追加します。これには、オプションの引数と LLM メッセージオブジェクトが含まれます。

引数

引数が null または空の場合、何も起こりません。たとえば、inputData が空ではない文字列で outputData が null の場合、inputData のみが記録されます。

inputData
オプション - String または List<LLMObs.LLMMessage>
文字列 (LLM 以外のスパンの場合) または LLMObs.LLMMessage のリスト (LLM スパンの場合) のいずれか。
outputData
オプション - String または List<LLMObs.LLMMessage>
文字列 (LLM 以外のスパンの場合) または LLMObs.LLMMessage のリスト (LLM スパンの場合) のいずれか。

LLM メッセージ

LLM スパンには、LLMObs.LLMMessage オブジェクトを使用して LLM メッセージをアノテーションとして付ける必要があります。

LLMObs.LLMMessage オブジェクトは、次の引数を指定して LLMObs.LLMMessage.from() を呼び出すことでインスタンス化できます。

role
必須 - String
メッセージの作成者の役割を説明する文字列。
content
必須 - String
メッセージの内容を含む文字列。

import datadog.trace.api.llmobs.LLMObs;

public class MyJavaClass {
  public String invokeChat(String userInput) {
    LLMObsSpan llmSpan = LLMObs.startLLMSpan("my-llm-span-name", "my-llm-model", "my-company", "maybe-ml-app-override", "session-141");
    String systemMessage = "You are a helpful assistant";
    Response chatResponse = ... // user application logic to invoke LLM
    llmSpan.annotateIO(
      Arrays.asList(
        LLMObs.LLMMessage.from("user", userInput),
        LLMObs.LLMMessage.from("system", systemMessage)
      ),
      Arrays.asList(
        LLMObs.LLMMessage.from(chatResponse.role, chatResponse.content)
      )
    );
    llmSpan.finish();
    return chatResponse;
  }
}

メトリクスの追加

メトリクスの一括追加

LLMObsSpan インターフェースの setMetrics() メンバーメソッドは、複数のメトリクスを一括で付与するために次の引数を受け入れます。

引数
metrics
必須 - Map<String, Number>
スパンによって記述される操作に関連するメトリクスを記録するためにユーザーが追加できる、JSON のシリアライズ可能なキーと数値のマップ (input_tokensoutput_tokenstotal_tokens など)。

単一のメトリクスの追加

setMetric() インターフェースの LLMObsSpan メンバーメソッドは、単一のメトリクスを付与するために次の引数を受け入れます。

引数
key
必須 - CharSequence
メトリクスの名前。
value
必須 - intlong、または double
メトリクスの値。

import datadog.trace.api.llmobs.LLMObs;

public class MyJavaClass {
  public String invokeChat(String userInput) {
    LLMObsSpan llmSpan = LLMObs.startLLMSpan("my-llm-span-name", "my-llm-model", "my-company", "maybe-ml-app-override", "session-141");
    String chatResponse = ... // user application logic to invoke LLM
    llmSpan.setMetrics(Map.of(
      "input_tokens", 617,
      "output_tokens", 338,
      "time_per_output_token", 0.1773
    ));
    llmSpan.setMetric("total_tokens", 955);
    llmSpan.setMetric("time_to_first_token", 0.23);
    llmSpan.finish();
    return chatResponse;
  }
}

タグの追加

タグの詳細については、タグの使用を開始するを参照してください。

タグの一括追加

setTags() インターフェースの LLMObsSpan メンバーメソッドは、複数のタグを一括で付与するために次の引数を受け入れます。

引数
tags
必須 - Map<String, Object>
スパンのコンテキストを記述するためにユーザーがタグとして追加できる、JSON のシリアライズ可能なキーと値のペアのマップ (sessionenvironmentsystemversion など)。

単一のタグの追加

setTag() インターフェースの LLMObsSpan メンバーメソッドは、単一のタグを付与するために次の引数を受け入れます。

引数
key
必須 - String
タグのキー。
value
必須 - intlongdoubleboolean、または String
タグの値。

import datadog.trace.api.llmobs.LLMObs;

public class MyJavaClass {
  public String invokeChat(String userInput) {
    LLMObsSpan llmSpan = LLMObs.startLLMSpan("my-llm-span-name", "my-llm-model", "my-company", "maybe-ml-app-override", "session-141");
    String chatResponse = ... // user application logic to invoke LLM
    llmSpan.setTags(Map.of(
      "chat_source", "web",
      "users_in_chat", 3
    ));
    llmSpan.setTag("is_premium_user", true);
    llmSpan.finish();
    return chatResponse;
  }
}

エラーのアノテーション付け

addThrowable() インターフェースの LLMObsSpan メンバーメソッドは、スタックトレース付きの throwable を付与するために次の引数を受け入れます。

引数
throwable
必須 - Throwable
発生した throwable/例外。

エラーメッセージの追加

setErrorMessage() インターフェースの LLMObsSpan メンバーメソッドは、エラー文字列を付与するために次の引数を受け入れます。

引数
errorMessage
必須 - String
エラーのメッセージ。

エラーフラグの設定

setError() インターフェースの LLMObsSpan メンバーメソッドは、操作のエラーを示すために次の引数を受け入れます。

引数
error
必須 - boolean
スパンがエラーになった場合は true

import datadog.trace.api.llmobs.LLMObs;

public class MyJavaClass {
  public String invokeChat(String userInput) {
    LLMObsSpan llmSpan = LLMObs.startLLMSpan("my-llm-span-name", "my-llm-model", "my-company", "maybe-ml-app-override", "session-141");
    String chatResponse = "N/A";
    try {
      chatResponse = ... // user application logic to invoke LLM
    } catch (Exception e) {
      llmSpan.addThrowable(e);
      throw new RuntimeException(e);
    } finally {
      llmSpan.finish();
    }
    return chatResponse;
  }
}

メタデータのアノテーション付け

setMetadata() インターフェースの LLMObsSpan メンバーメソッドは、次の引数を受け入れます。

metadata
必須 - Map<String, Object>
スパンによって記述される入力操作または出力操作に関連するメタデータを含む、JSON のシリアライズ可能なキーと値のペアのマップ。

import datadog.trace.api.llmobs.LLMObs;

public class MyJavaClass {
  public String invokeChat(String userInput) {
    LLMObsSpan llmSpan = LLMObs.startLLMSpan("my-llm-span-name", "my-llm-model", "my-company", "maybe-ml-app-override", "session-141");
    llmSpan.setMetadata(
      Map.of(
        "temperature", 0.5,
        "is_premium_member", true,
        "class", "e1"
      )
    );
    String chatResponse = ... // user application logic to invoke LLM
    return chatResponse;
  }
}

自動インスツルメンテーションスパンのアノテーション付け

SDK の LLMObs.annotation_context() メソッドは、アノテーションコンテキストがアクティブな間に開始されたすべての自動インスツルメンテーションスパンを変更するために使用できるコンテキストマネージャーを返します。

LLMObs.annotation_context() メソッドは、次の引数を受け入れます。

name
オプション - 文字列
アノテーションコンテキスト内で開始されるすべての自動インスツルメンテーションスパンのスパン名を上書きする名前。
prompt
オプション - 辞書
LLM 呼び出しに使用されるプロンプトを表す辞書。完全なスキーマとサポートされているキーについては、Prompt オブジェクトのドキュメントを参照してください。Prompt オブジェクトを ddtrace.llmobs.utils からインポートし、prompt 引数として渡すこともできます。: この引数は LLM スパンにのみ適用されます。
tags
オプション - 辞書
ユーザーがスパンにタグとして追加できる、JSON のシリアライズ可能なキーと値のペアの辞書。キーの例: sessionenvsystem、および version。タグの詳細については、タグの使用を開始するを参照してください。
cost_tags
オプション - 文字列のリスト
生成される LLM のコストメトリクスおよびトークンメトリクスにカスタムタグとして伝播させるタグキーのリスト。各エントリは、スパン開始時に tags に存在するキー (同じコンテキストまたは親コンテキストに提供されたもの) を参照する必要があります。LLMObs.annotate() で後から追加されたタグキーは保持されません。詳細については、コスト監視を参照してください。

from ddtrace.llmobs import LLMObs
from ddtrace.llmobs.decorators import workflow

@workflow
def rag_workflow(user_question):
    context_str = retrieve_documents(user_question).join(" ")

    with LLMObs.annotation_context(
        prompt = Prompt(
            id="chatbot_prompt",
            version="1.0.0",
            template="Please answer the question using the provided context: {{question}}\n\nContext:\n{{context}}",
            variables={
                "question": user_question,
                "context": context_str,
            }
        ),
        tags = {
            "retrieval_strategy": "semantic_similarity"
        },
        name = "augmented_generation"
    ):
        completion = openai_client.chat.completions.create(...)
    return completion.choices[0].message.content

SDK の llmobs.annotationContext() は、コールバック関数のスコープ内で開始されたすべての自動インスツルメンテーションスパンを変更するために使用できるコールバック関数を受け入れます。

llmobs.annotationContext() メソッドは、最初の引数で次のオプションを受け入れます。

name
オプション - 文字列
アノテーションコンテキスト内で開始されるすべての自動インスツルメンテーションスパンのスパン名を上書きする名前。
tags
オプション - オブジェクト
ユーザーがスパンにタグとして追加できる、JSON のシリアライズ可能なキーと値のペアのオブジェクト。キーの例: sessionenvsystem、およびversion。タグの詳細については、タグの使用を開始するを参照してください。
costTags
オプション - 文字列の配列
生成される LLM のコストメトリクスおよびトークンメトリクスにカスタムタグとして伝播させるタグキーのリスト。各エントリは、スパン開始時に tags に存在するキー (同じコンテキストまたは親コンテキストに提供されたもの) を参照する必要があります。llmobs.annotate() で後から追加されたタグキーは保持されません。詳細については、コスト監視を参照してください。

const { llmobs } = require('dd-trace');

function ragWorkflow(userQuestion) {
    const contextStr = retrieveDocuments(userQuestion).join(" ");

    const completion = await llmobs.annotationContext({
      tags: {
        retrieval_strategy: "semantic_similarity"
      },
      name: "augmented_generation"
    }, async () => {
      const completion = await openai_client.chat.completions.create(...);
      return completion.choices[0].message.content;
    });
}

プロンプト追跡

構造化されたプロンプトメタデータを LLM スパンに付与することで、結果の再現、変更の監査、およびバージョン間でのプロンプトパフォーマンスの比較が可能になります。テンプレートを使用する場合、Agent Observability はテンプレートコンテンツの変更に基づいたバージョン追跡も提供します。

LLM 呼び出しの前にプロンプトメタデータを付与するには、LLMObs.annotation_context(prompt=...) を使用します。スパンアノテーションの詳細については、スパンのエンリッチメントを参照してください。

引数

prompt
必須 - 辞書
下記のプロンプトスキーマに従う型付き辞書。

サポートされているキー:

  • id (str): このプロンプトの論理識別子。ml_app ごとに一意である必要があります。デフォルトは {ml_app}-unnamed_prompt です。
  • version (str): プロンプトのバージョンタグ (例: “1.0.0”)。詳細については、バージョン追跡を参照してください。
  • variables(Dict[str, str]): テンプレートのプレースホルダーに値を入力するために使用される変数。
  • template(str): プレースホルダーを含むテンプレート文字列 (例: "Translate {{text}} to {{lang}}\")。
  • chat_template(List[Message]): マルチメッセージテンプレート形式。{ "role": "<role>", "content": "<template string with placeholders>" } オブジェクトのリストを指定します。
  • tags(Dict[str, str]): プロンプト実行に付与するタグ。
  • rag_context_variables(List[str]): グラウンドトゥルースやコンテキストコンテンツを含む変数キー。ハルシネーション検出に使用されます。
  • rag_query_variables(List[str]): ユーザーのクエリを含む変数キー。ハルシネーション検出に使用されます。

例: 単一テンプレートプロンプト

from ddtrace.llmobs import LLMObs

def answer_question(text):
    # Attach prompt metadata to the upcoming LLM span using LLMObs.annotation_context()
    with LLMObs.annotation_context(prompt={
        "id": "translation-template",
        "version": "1.0.0",
        "chat_template": [{"role": "user", "content": "Translate to {{lang}}: {{text}}"}],
        "variables": {"lang": "fr", "text": text},
        "tags": {"team": "nlp"}
    }):
        # Example provider call (replace with your client)
        completion = openai_client.chat.completions.create(
            model="gpt-4o",
            messages=[{"role": "user", "content": f"Translate to fr: {text}"}]
        )
    return completion

例: LangChain プロンプトテンプレート

LangChain のプロンプトテンプレートを自動インスツルメンテーションで使用する場合は、意味のある名前を持つ変数にテンプレートを割り当ててください。自動インスツルメンテーションでは、これらの名前を使用してプロンプトを識別します。

# "translation_template" will be used to identify the template in Datadog
translation_template = PromptTemplate.from_template("Translate {text} to {language}")
chain = translation_template | llm

LLM 呼び出しの前にプロンプトメタデータを付与するには、llmobs.annotationContext({ prompt: ... }, () => { ... }) を使用します。スパンアノテーションの詳細については、スパンのエンリッチメントを参照してください。

引数

prompt
必須 - オブジェクト
下記のプロンプトスキーマに従うオブジェクト。

サポートされているプロパティ:

  • id (string): このプロンプトの論理識別子。ml_app ごとに一意である必要があります。デフォルトは {ml_app}-unnamed_prompt です。
  • version (string): プロンプトのバージョンタグ (例: “1.0.0”)。詳細については、バージョン追跡を参照してください。
  • variables(Record<string, string>): テンプレートのプレースホルダーに値を入力するために使用される変数。
  • template(string | List[Message]): プレースホルダーを含むテンプレート文字列 (例: "Translate {{text}} to {{lang}}"). Alternatively, a list of { "role": "<role>", "content": "<template string with placeholders>" } オブジェクトのリスト。
  • tags(Record<string, string>): プロンプト実行に付与するタグ。
  • contextVariables(string[]): グラウンドトゥルースやコンテキストコンテンツを含む変数キー。ハルシネーション検出に使用されます。
  • queryVariables(string[]): ユーザーのクエリを含む変数キー。ハルシネーション検出に使用されます。

例: 単一テンプレートプロンプト

const { llmobs } = require('dd-trace');

function answerQuestion(text) {
    // Attach prompt metadata to the upcoming LLM span using LLMObs.annotation_context()
    return llmobs.annotationContext({
      prompt: {
        id: "translation-template",
        version: "1.0.0",
        chat_template: [{"role": "user", "content": "Translate to {{lang}}: {{text}}"}],
        variables: {"lang": "fr", "text": text},
        tags: {"team": "nlp"}
      }
    }, () => {
      // Example provider call (replace with your client)
      return openaiClient.chat.completions.create({
          model: "gpt-4o",
          messages: [{"role": "user", "content": f"Translate to fr: {text}"}]
        });
    });
}

注記

  • プロンプトのアノテーション付けは LLM スパンでのみ利用可能です。
  • 正しい LLM スパンに適用されるよう、プロバイダー呼び出しの直前にアノテーションを配置してください。
  • アプリケーション内の異なるプロンプトを区別するために、一意のプロンプト id を使用してください。
  • 次のようなプレースホルダー構文を使用してテンプレートを静的に保ち (例:{{variable_name}}) and define dynamic content in the variables` セクションで動的コンテンツを定義します。
  • ブロック内で複数の自動インスツルメンテーション LLM 呼び出しを行う場合は、アノテーションコンテキストを使用して、呼び出し全体に同じプロンプトメタデータを適用してください。自動インスツルメンテーションスパンのアノテーション付けを参照してください。

バージョン追跡

Agent Observability は、明示的なバージョンが指定されていない場合に、プロンプトの自動バージョニングを提供します。プロンプトメタデータで version タグなしで template または chat_template を指定すると、システムはテンプレートコンテンツのハッシュを計算してバージョンを自動的に生成します。version タグを指定した場合、Agent Observability は自動生成の代わりに指定されたバージョンラベルを使用します。

バージョニングシステムは次のように機能します。

  • 自動バージョニング: version タグが指定されていない場合、Agent Observability は template または chat_template のコンテンツのハッシュを計算して、数値のバージョン識別子を自動的に生成します。
  • 手動バージョニング: version タグが指定されている場合、Agent Observability は指定されたバージョンラベルをそのまま使用します。
  • バージョン履歴: 自動生成されたバージョンと手動で指定されたバージョンの両方がバージョン履歴に保持され、時間の経過に伴うプロンプトの進化を追跡します。

これにより、テンプレートコンテンツの変更に基づく自動バージョン管理に依存するか、独自のバージョンラベルを使用してバージョン管理を完全に制御するかを選択できる柔軟性が得られます。

MCP インテントキャプチャ

MCP ツールが呼び出された理由を把握するには、MCP サーバーでインテントキャプチャを有効にします。有効にすると、呼び出し元のモデルに対してツールを呼び出した理由を説明するよう要求する引数がすべての MCP サーバーツールに追加されます。インテントはツールのスパンに記録されるため、ツールの定義や説明を改善するのに役立ちます。

DD_MCP_CAPTURE_INTENT 環境変数を使用して MCP インテントキャプチャを有効にします。

DD_MCP_CAPTURE_INTENT=1 DD_SITE=<YOUR_DATADOG_SITE> DD_API_KEY=<YOUR_API_KEY> DD_LLMOBS_ENABLED=1 \
DD_LLMOBS_ML_APP=<YOUR_ML_APP_NAME> ddtrace-run <YOUR_APP_STARTUP_COMMAND>

または、LLMObs.enable()capture_intent パラメータを使用してプログラムで有効にします。

from ddtrace.llmobs import LLMObs
LLMObs.enable(
  ml_app="<YOUR_ML_APP_NAME>",
  capture_intent=True,
)

コスト監視

LLM/埋め込みスパンにトークンメトリクス (自動コスト追跡用) またはコストメトリクス (手動コスト追跡用) を付与します。トークンメトリクスを使用すると、Datadog はプロバイダーの価格設定を使用してコストを計算できます。一方、コストメトリクスを使用すると、カスタムモデルやサポートされていないモデルを使用する際に独自の価格設定を提供できます。詳細については、コストを参照してください。

自動インスツルメンテーションを使用している場合、トークンメトリクスとコストメトリクスはスパンに自動的に表示されます。手動でインスツルメンテーションを行う場合は、下記のガイダンスに従ってください。

このコンテキストにおいて、「トークンメトリクス」および「コストメトリクス」とは、 metrics パラメータ ( LLMObs.annotate() メソッド) を通じてスパンに付与する数値のキーと値のペアを指します。これらは、Datadog プラットフォームの Agent Observability メトリクスとは異なります。認識されたキー ( input_tokensoutput_tokensinput_costoutput_costなど) について、Datadog はこれらのスパン属性を使用して、ダッシュボードやモニターで使用するための対応するプラットフォームのメトリクス ( ml_obs.span.llm.input.costなど) を生成します。

使用例: 一般的なモデルプロバイダーの使用

Datadog は、OpenAI、Azure OpenAI、Anthropic、Google Gemini などの一般的なモデルプロバイダーをサポートしています。これらのプロバイダーを使用する場合、LLM リクエストにモデル名、モデルプロバイダー、およびトークン使用量をアノテーションするだけで済みます。Datadog は、プロバイダーの価格設定に基づいて推定コストを自動的に計算します。

各トークンが何を表しているか、Datadog がそれらをどのように計算するかの詳細については、トークン数の計算方法を参照してください。

from ddtrace.llmobs import LLMObs
from ddtrace.llmobs.decorators import llm

@llm(model_name="gpt-5.1", model_provider="openai")
def llm_call(prompt):
    resp = ... # llm call here
    # Annotate token metrics
    LLMObs.annotate(
        metrics={
          "input_tokens": 50,
          "output_tokens": 120,
          "total_tokens": 170,
          "non_cached_input_tokens": 13,  # optional
          "cache_read_input_tokens": 22,  # optional
          "cache_write_input_tokens": 15, # optional
        },
    )
    return resp
function llmCall (prompt) {
  const resp = ... // llm call here
  llmobs.annotate({
    metrics: {
      input_tokens: 50,
      output_tokens: 120,
      total_tokens: 170,
      non_cached_input_tokens: 13,  // optional
      cache_read_input_tokens: 22,  // optional
      cache_write_input_tokens: 15  // optional
    }
  })
  return resp
}
llmCall = llmobs.wrap({ kind: 'llm', modelName: 'gpt-5.1', modelProvider: 'openai' }, llmCall)
import datadog.trace.api.llmobs.LLMObs;
import datadog.trace.api.llmobs.LLMObsSpan;
import java.util.Map;

public class MyJavaClass {
  public String llmCall(String prompt) {
    LLMObsSpan llmSpan = LLMObs.startLLMSpan("llm-call", "gpt-5.1", "openai", null, null);
    String resp = ... // llm call here
    llmSpan.setMetrics(Map.of(
      "input_tokens", 50,
      "output_tokens", 120,
      "total_tokens", 170,
      "non_cached_input_tokens", 13,  // optional
      "cache_read_input_tokens", 22,  // optional
      "cache_write_input_tokens", 15  // optional
    ));
    llmSpan.finish();
    return resp;
  }
}

使用例: カスタムモデルの使用

カスタムモデルやサポートされていないモデルの場合は、スパンにドル単位のコストデータを手動でアノテーションする必要があります。

from ddtrace.llmobs import LLMObs
from ddtrace.llmobs.decorators import llm

@llm(model_name="custom_model", model_provider="model_provider")
def llm_call(prompt):
    resp = ... # llm call here
    # Annotate cost metrics
    LLMObs.annotate(
        metrics={
          "input_cost": 3,
          "output_cost": 7,
          "total_cost": 10,
          "non_cached_input_cost": 1,    # optional
          "cache_read_input_cost": 0.6,  # optional
          "cache_write_input_cost": 1.4, # optional
        },
    )
    return resp
function llmCall (prompt) {
  const resp = ... // llm call here
  llmobs.annotate({
    metrics: {
      input_cost: 3,
      output_cost: 7,
      total_cost: 10,
      non_cached_input_cost: 1,    // optional
      cache_read_input_cost: 0.6,  // optional
      cache_write_input_cost: 1.4  // optional
    }
  })
  return resp
}
llmCall = llmobs.wrap({ kind: 'llm', modelName: 'custom_model', modelProvider: 'model_provider' }, llmCall)
import datadog.trace.api.llmobs.LLMObs;
import datadog.trace.api.llmobs.LLMObsSpan;
import java.util.Map;

public class MyJavaClass {
  public String llmCall(String prompt) {
    LLMObsSpan llmSpan = LLMObs.startLLMSpan("llm-call", "custom_model", "model_provider", null, null);
    String resp = ... // llm call here
    llmSpan.setMetrics(Map.of(
      "input_cost", 3,
      "output_cost", 7,
      "total_cost", 10,
      "non_cached_input_cost", 1,    // optional
      "cache_read_input_cost", 0.6,  // optional
      "cache_write_input_cost", 1.4  // optional
    ));
    llmSpan.finish();
    return resp;
  }
}

コストメトリクスおよびトークンメトリクスへのカスタムタグの追加

デフォルトでは、LLM のコストトークンおよびトークンメトリクスには、model_namemodel_providerml_app などの一連の固定された OOTB タグが含まれます。チーム、顧客、機能など、アプリケーション固有の属性で LLM の支出を分析するには、スパンの既存のタグキーのサブセットをマークして、それらのメトリクスにカスタムタグとして伝播させます。カスタムのダッシュボードやモニターなどの使用例については、コストメトリクスおよびトークンメトリクスのカスタムタグを参照してください。

各エントリは文字列である必要があり、アノテーションが適用される時点でスパンの tags パラメータを通じてすでに提供されているキーを参照する必要があります。単一のスパンにアノテーションを付ける場合、キーは同じアノテーション呼び出し内の tags、または同じスパンに対する以前のアノテーションを通じて提供できます。アノテーションコンテキストを使用する場合、スパン開始時に tags に存在するキーのみが対象となります。個別のスパンアノテーションを通じて後から追加されたキーは保持されません。既存のタグキーを参照していないエントリはスキップされます。

from ddtrace.llmobs import LLMObs
from ddtrace.llmobs.decorators import llm

@llm(model_name="gpt-5.1", model_provider="openai")
def llm_call(prompt):
    resp = ... # llm call here
    LLMObs.annotate(
        metrics={"input_tokens": 50, "output_tokens": 120, "total_tokens": 170},
        tags={"team": "nlp", "customer_tier": "enterprise", "host": "host_name"},
        cost_tags=["team", "customer_tier"],
    )
    return resp
function llmCall (prompt) {
  const resp = ... // llm call here
  llmobs.annotate({
    metrics: { input_tokens: 50, output_tokens: 120, total_tokens: 170 },
    tags: { team: 'nlp', customer_tier: 'enterprise', host: 'host_name' },
    costTags: ['team', 'customer_tier']
  })
  return resp
}
llmCall = llmobs.wrap({ kind: 'llm', modelName: 'gpt-5.1', modelProvider: 'openai' }, llmCall)

この方法でアノテーションコンテキストを通じてタグを伝播させ、コンテキスト内で開始されたすべての自動インスツルメンテーションスパンに適用することもできます。

with LLMObs.annotation_context(
    tags={"team": "nlp", "customer_tier": "enterprise"},
    cost_tags=["team", "customer_tier"],
):
    resp = ... # llm call here
llmobs.annotationContext({
  tags: { team: 'nlp', customer_tier: 'enterprise' },
  costTags: ['team', 'customer_tier']
}, () => {
  const resp = ... // llm call here
})

評価

Agent Observability SDK には、評価を Datadog にエクスポートおよび送信するためのメソッドが用意されています。

再利用可能なクラスベースの評価器 (BaseEvaluatorBaseSummaryEvaluator) を構築し、詳細な結果メタデータを含めるには、評価開発者ガイドを参照してください。

評価は単一のスパンに結合する必要があります。ターゲットスパンは、次の 2 つの方法のいずれかで識別できます。

  • タグベースの結合 - 単一のスパンに設定された一意のキーと値のタグペアを使用して、評価を結合します。タグのキーと値のペアが複数のスパンに一致する場合、またはどのスパンにも一致しない場合、評価の結合は失敗します。
  • 直接スパン参照 - スパンの一意のトレース ID とスパン ID の組み合わせを使用して、評価を結合します。

スパンのエクスポート

LLMObs.export_span() は、スパンからスパンコンテキストを抽出するために使用できます。このメソッドは、評価を対応するスパンに関連付ける際に役立ちます。

引数

LLMObs.export_span() メソッドは、次の引数を受け入れます。

span
オプション - スパン
スパンコンテキスト (スパン ID とトレース ID) を抽出する対象のスパン。指定しない場合 (関数デコレータを使用する場合など)、SDK は現在のアクティブなスパンをエクスポートします。

from ddtrace.llmobs import LLMObs
from ddtrace.llmobs.decorators import llm

@llm(model_name="claude", name="invoke_llm", model_provider="anthropic")
def llm_call():
    completion = ... # user application logic to invoke LLM
    span_context = LLMObs.export_span(span=None)
    return completion

llmobs.exportSpan() は、スパンからスパンコンテキストを抽出するために使用できます。評価を対応するスパンに関連付けるには、このメソッドを使用する必要があります。

引数

llmobs.exportSpan() メソッドは、次の引数を受け入れます。

span
オプション - スパン
スパンコンテキスト (スパン ID とトレース ID) を抽出する対象のスパン。指定しない場合 (関数ラッパーを使用する場合など)、SDK は現在のアクティブなスパンをエクスポートします。

function llmCall () {
  const completion = ... // user application logic to invoke LLM
  const spanContext = llmobs.exportSpan()
  return completion
}
llmCall = llmobs.wrap({ kind: 'llm', name: 'invokeLLM', modelName: 'claude', modelProvider: 'anthropic' }, llmCall)

評価の送信

LLMObs.submit_evaluation() は、特定のスパンに関連付けられたカスタム評価を送信するために使用できます。

LLMObs.submit_evaluation_for は非推奨であり、ddtrace の次のメジャーバージョン (4.0) で削除される予定です。移行するには、 LLMObs.submit_evaluation_for 呼び出しの名前を LLMObs.submit_evaluationに変更してください。

: カスタム評価は、自分で独自に実装してホストする評価器です。これらは、Datadog が組み込みの評価器を使用して自動的に計算する既成の評価とは異なります。すぐに使える評価をアプリケーション用に設定するには、Datadog の [Agent Observability] > [Settings] (設定) > [Evaluations] (評価) ページを使用してください。

LLMObs.submit_evaluation() メソッドは、次の引数を受け入れます。

label
必須 - 文字列
評価の名前。
metric_type
必須 - 文字列
評価のタイプ。categoricalscoreboolean、または json である必要があります。
value
必須 - 文字列、数値型、または辞書
評価の値。文字列 (metric_type==categorical)、整数/浮動小数点数 (metric_type==score)、ブール値 (metric_type==boolean)、または辞書 (metric_type==json) である必要があります。
span
オプション - 辞書
この評価に関連付けられたスパンを一意に識別する辞書。span_id (文字列) と trace_id (文字列) を含む必要があります。この辞書の生成には LLMObs.export_span() を使用します。
span_with_tag_value
オプション - 辞書
この評価に関連付けられたスパンを一意に識別する辞書。tag_key (文字列) と tag_value (文字列) を含む必要があります。

: spanspan_with_tag_value は、いずれか一方のみを指定する必要があります。両方を指定した場合、またはどちらも指定しなかった場合は、ValueError が発生します。

ml_app
必須 - 文字列
ML アプリケーションの名前。
timestamp_ms
オプション - 整数
評価メトリクス結果が生成されたミリ秒単位の Unix タイムスタンプ。指定しない場合、現在の時刻がデフォルトで使用されます。
tags
オプション - 辞書
評価に関するタグとしてユーザーが追加できる、文字列のキーと値のペアの辞書。タグの詳細については、タグの使用を開始するを参照してください。
assessment
オプション - 文字列
この評価の評価結果。指定可能な値は pass および fail です。
reasoning
オプション - 文字列
評価結果のテキストによる説明。
metadata
オプション - 辞書
評価結果に関連付けられた任意の構造化メタデータを含む辞書。

from ddtrace.llmobs import LLMObs
from ddtrace.llmobs.decorators import llm

@llm(model_name="claude", name="invoke_llm", model_provider="anthropic")
def llm_call():
    completion = ... # user application logic to invoke LLM

    # joining an evaluation to a span via a tag key-value pair
    msg_id = get_msg_id()
    LLMObs.annotate(
        tags = {'msg_id': msg_id}
    )

    LLMObs.submit_evaluation(
        span_with_tag_value = {
            "tag_key": "msg_id",
            "tag_value": msg_id
        },
        ml_app = "chatbot",
        label="harmfulness",
        metric_type="score",
        value=10,
        tags={"evaluation_provider": "ragas"},
        assessment="fail",
        reasoning="Malicious intent was detected in the user instructions.",
        metadata={"details": ["jailbreak", "SQL injection"]}
    )

    # joining an evaluation to a span via span ID and trace ID
    span_context = LLMObs.export_span(span=None)
    LLMObs.submit_evaluation(
        span_context = span_context,
        ml_app = "chatbot",
        label="harmfulness",
        metric_type="score",
        value=10,
        tags={"evaluation_provider": "ragas"},
        assessment="fail",
        reasoning="Malicious intent was detected in the user instructions.",
        metadata={"details": ["jailbreak", "SQL injection"]}
    )
    return completion

llmobs.submitEvaluation() は、特定のスパンに関連付けられたカスタム評価を送信するために使用できます。

llmobs.submitEvaluation() メソッドは、次の引数を受け入れます。

span_context
必須 - 辞書
評価を関連付けるスパンコンテキスト。これは LLMObs.export_span() の出力でなければなりません。
evaluationOptions
必須 - オブジェクト
評価データのオブジェクト。

evaluationOptions オブジェクトには、次のものを含めることができます。

label
必須 - 文字列
評価の名前。
metricType
必須 - 文字列
評価のタイプ。“categorical”、“score”、“boolean”、または “json” のいずれかである必要があります。
value
必須 - 文字列または数値型
評価の値。文字列 (metric_type が categorical の場合)、数値 (metric_type が score の場合)、ブール値 (metric_type が boolean の場合)、または JSON オブジェクト (metric_type が json の場合) である必要があります。
tags
オプション - 辞書
評価に関するタグとしてユーザーが追加できる、文字列のキーと値のペアの辞書。タグの詳細については、タグの使用を開始するを参照してください。
assessment
オプション - 文字列
この評価の評価結果。指定可能な値は pass および fail です。
reasoning
オプション - 文字列
評価結果のテキストによる説明。
metadata
オプション - 辞書
評価結果に関連付けられた任意の構造化メタデータを含む JSON オブジェクト。

function llmCall () {
  const completion = ... // user application logic to invoke LLM
  const spanContext = llmobs.exportSpan()
  llmobs.submitEvaluation(spanContext, {
    label: "harmfulness",
    metricType: "score",
    value: 10,
    tags: { evaluationProvider: "ragas" }
  })
  return completion
}
llmCall = llmobs.wrap({ kind: 'llm', name: 'invokeLLM', modelName: 'claude', modelProvider: 'anthropic' }, llmCall)

LLMObs.SubmitEvaluation() を使用して、特定のスパンに関連付けられたカスタム評価を送信します。

LLMObs.SubmitEvaluation() メソッドは、次の引数を受け入れます。

llmObsSpan
必須 - LLMObsSpan
評価を関連付けるスパンコンテキスト。
label
必須 - String
評価の名前。
categoricalValueまたは scoreValue
必須 - String または double
評価の値。文字列 (評価が categorical の場合) または倍精度浮動小数点数 (評価が score の場合) である必要があります。
tags
オプション - Map<String, Object>
評価のタグ付けに使用される文字列のキーと値のペアの辞書。タグの詳細については、タグの使用を開始するを参照してください。

import datadog.trace.api.llmobs.LLMObs;

public class MyJavaClass {
  public String invokeChat(String userInput) {
    LLMObsSpan llmSpan = LLMObs.startLLMSpan("my-llm-span-name", "my-llm-model", "my-company", "maybe-ml-app-override", "session-141");
    String chatResponse = "N/A";
    try {
      chatResponse = ... // user application logic to invoke LLM
    } catch (Exception e) {
      llmSpan.addThrowable(e);
      throw new RuntimeException(e);
    } finally {
      llmSpan.finish();

      // submit evaluations
      LLMObs.SubmitEvaluation(llmSpan, "toxicity", "toxic", Map.of("language", "english"));
      LLMObs.SubmitEvaluation(llmSpan, "f1-similarity", 0.02, Map.of("provider", "f1-calculator"));
    }
    return chatResponse;
  }
}

エンドユーザーフィードバックの送信

エンドユーザーフィードバックは、LLM アプリケーションのユーザーからの入力 (高評価や低評価、ユーザーがエージェントの変更を受け入れたかどうか、自由記述のコメントなど) を収集します。評価とは異なり、フィードバックには送信者の ID が含まれ、スパン、トレース、セッション、または顧客定義のエンティティを対象にすることができます。詳細については、エンドユーザーフィードバックを参照してください。

LLMObs.submit_feedback() を使用して、スパン、トレース、セッション、または顧客定義のエンティティに関連付けられたエンドユーザーフィードバックを送信します。

LLMObs.submit_feedback() メソッドは、次の引数を受け入れます。

label
必須 - 文字列
フィードバックメトリクスの名前。. を含めてはなりません。
metric_type
必須 - 文字列
フィードバックのタイプ。categoricalscorebooleanjson、または text である必要があります。
value
必須 - 文字列、数値型、ブール値、または辞書
フィードバックの値。文字列 (metric_type==categorical または metric_type==text)、整数/浮動小数点数 (metric_type==score)、ブール値 (metric_type==boolean)、または辞書 (metric_type==json) である必要があります。
submitter
必須 - 辞書
フィードバックの送信者を識別する辞書。空でない id (文字列) を含めなければなりません。さらに、オプションで user などの type (文字列) を含めることができます。
span
オプション - 辞書
このフィードバックに関連付けられたスパンを識別する辞書。この辞書の生成には LLMObs.export_span() を使用します。
span_id
オプション - 文字列
このフィードバックに関連付けられたスパンの ID。
trace_id
オプション - 文字列
このフィードバックに関連付けられたトレースの ID。
session_id
オプション - 文字列
このフィードバックに関連付けられたセッションの ID。
feedback_join_key
オプション - 文字列
このフィードバックに関連付けられた顧客定義のキー (インシデント ID やチケット ID など)。フィードバックをスパンに接続するには、まず同じ値を持つ feedback_join_key タグでそれらにアノテーションを付けてください。スパンのエンリッチメントを参照してください。

: spanspan_idtrace_idsession_id、または feedback_join_key のいずれか 1 つのみを指定する必要があります。複数指定した場合、または何も指定しなかった場合は、ValueError が発生します。

ml_app
オプション - 文字列
ML アプリケーションの名前。指定しない場合、SDK に対して設定された ML アプリケーションがデフォルトで使用されます。
timestamp_ms
オプション - 整数
フィードバックが生成されたミリ秒単位の Unix タイムスタンプ。指定しない場合、現在の時刻がデフォルトで使用されます。
tags
オプション - 辞書
フィードバックに関するタグとしてユーザーが追加できる、文字列のキーと値のペアの辞書。タグの詳細については、タグの使用を開始するを参照してください。
assessment
オプション - 文字列
このフィードバックの評価結果。指定可能な値は pass および fail です。
reasoning
オプション - 文字列
フィードバックのテキストによる説明。

from ddtrace.llmobs import LLMObs
from ddtrace.llmobs.decorators import llm

@llm(model_name="claude", name="invoke_llm", model_provider="anthropic")
def llm_call():
    completion = ... # user application logic to invoke LLM
    span_context = LLMObs.export_span(span=None)

    # submitting feedback for a trace
    LLMObs.submit_feedback(
        label="thumbs",
        metric_type="categorical",
        value="down",
        submitter={"id": "user-123", "type": "user"},
        trace_id=span_context["trace_id"],
        assessment="fail",
    )

    # connecting the span to a customer-defined entity
    LLMObs.annotate(tags={"feedback_join_key": "incident-123"})

    # submitting feedback for that entity
    LLMObs.submit_feedback(
        label="user_comment",
        metric_type="text",
        value="The investigation missed the customer impact.",
        submitter={"id": "user-123", "type": "user"},
        feedback_join_key="incident-123",
    )
    return completion

llmobs.submitFeedback() を使用して、スパン、トレース、セッション、または顧客定義のエンティティに関連付けられたエンドユーザーフィードバックを送信します。

llmobs.submitFeedback() メソッドは、次のプロパティを持つ options オブジェクトを受け入れます。

label
必須 - 文字列
フィードバックメトリクスの名前。. を含めてはなりません。
metricType
必須 - 文字列
フィードバックのタイプ。categoricalscorebooleanjson、または text のいずれかである必要があります。
value
必須 - 文字列、数値、ブール値、またはオブジェクト
フィードバックの値。文字列 (categorical および text のメトリクスタイプの場合)、数値 (score の場合)、ブール値 (boolean の場合)、または JSON オブジェクト (json の場合) である必要があります。
submitter
必須 - オブジェクト
フィードバックの送信者を識別するオブジェクト。空でない id (文字列) を含めなければなりません。さらに、オプションで user などの type (文字列) を含めることができます。
span
オプション - オブジェクト
フィードバックを付与するスパンのスパンコンテキスト。これは llmobs.exportSpan() の出力でなければなりません。
spanId
オプション - 文字列
フィードバックを付与するスパンの ID。
traceId
オプション - 文字列
フィードバックを付与するトレースの ID。
sessionId
オプション - 文字列
フィードバックを付与するセッションの ID。
feedbackJoinKey
オプション - 文字列
フィードバックを付与する顧客定義のキー (インシデント ID やチケット ID など)。フィードバックをスパンに接続するには、スパンに同じキーを設定してください。

: spanspanIdtraceIdsessionId、または feedbackJoinKey のいずれか 1 つのみを指定する必要があります。複数指定した場合、または何も指定しなかった場合は、エラーが発生します。

mlApp
オプション - 文字列
ML アプリケーションの名前。指定しない場合、SDK に対して設定された ML アプリケーションがデフォルトで使用されます。
timestampMs
オプション - 数値
フィードバックが生成されたミリ秒単位の Unix タイムスタンプ。指定しない場合、現在の時刻がデフォルトで使用されます。
tags
オプション - オブジェクト
フィードバックに関するタグとしてユーザーが追加できる、文字列のキーと値のペアのオブジェクト。タグの詳細については、タグの使用を開始するを参照してください。
assessment
オプション - 文字列
このフィードバックの評価結果。指定可能な値は pass および fail です。
reasoning
オプション - 文字列
フィードバックのテキストによる説明。

function llmCall () {
  const completion = ... // user application logic to invoke LLM
  const spanContext = llmobs.exportSpan()

  // submitting feedback for a trace
  llmobs.submitFeedback({
    label: 'thumbs',
    metricType: 'boolean',
    value: true,
    submitter: { id: 'user-123', type: 'user' },
    traceId: spanContext.traceId,
    assessment: 'pass'
  })

  // connecting the span to a customer-defined entity
  llmobs.annotate({
    tags: { feedback_join_key: 'incident-123' }
  })

  // submitting feedback for that entity
  llmobs.submitFeedback({
    label: 'user_comment',
    metricType: 'text',
    value: 'This answer was helpful.',
    submitter: { id: 'user-123', type: 'user' },
    feedbackJoinKey: 'incident-123'
  })
  return completion
}
llmCall = llmobs.wrap({ kind: 'llm', name: 'invokeLLM', modelName: 'claude', modelProvider: 'anthropic' }, llmCall)

LLMObs.submitFeedback() を使用して、スパン、トレース、セッション、または顧客定義のエンティティに関連付けられたエンドユーザーフィードバックを送信します。LLMObs.Feedback.builder() を使用してフィードバックを構築します。

builder は次のメソッドを受け入れます。

label(String label)
必須
フィードバックメトリクスの名前。. を含めてはなりません。
categoricalValue(String)scoreValue(double)booleanValue(boolean)jsonValue(Map<String, Object>)、または textValue(String)
必須
フィードバックの値。これらのメソッドのいずれか 1 つのみを設定します。これによりメトリクスのタイプも決まります。
submitter(String id, String type)または submitter(Submitter submitter)
必須
フィードバックの送信者を識別します。id は空ではない文字列である必要があります。type は、user のようなオプションの修飾子です。
span(LLMObsSpan span)spanId(String)traceId(String)sessionId(String)、または feedbackJoinKey(String)
必須
フィードバックを付与するエンティティ。これらのメソッドのいずれか 1 つのみを設定します。顧客定義のエンティティ (インシデント ID やチケット ID など) には feedbackJoinKey を使用し、スパンに同じキーを設定してフィードバックを接続します。
mlApp(String mlApp)
オプション
ML アプリケーションの名前。指定しない場合、トレーサー用に設定された ML アプリケーションがデフォルトで使用されます。
timestampMs(long timestampMs)
オプション
フィードバックが生成されたミリ秒単位の Unix タイムスタンプ。指定しない場合、現在の時刻がデフォルトで使用されます。
tags(Map<String, Object> tags)または tag(String key, Object value)
オプション
フィードバックのタグ付けに使用するキーと値のペア。タグの詳細については、タグの使用を開始するを参照してください。
assessment(Assessment assessment)
オプション
このフィードバックの評価結果。指定可能な値は LLMObs.Feedback.Assessment.PASS および LLMObs.Feedback.Assessment.FAIL です。
reasoning(String reasoning)
オプション
フィードバックのテキストによる説明。

: LLMObs.submitFeedback() はフィードバックを検証し、Agent Observability が有効でフィードバックが無効な場合 (ターゲット、値、または送信者が欠落している場合など) に IllegalArgumentException をスローします。Agent Observability が無効な場合、または Agent が接続されていない場合、この呼び出しは何も行いません。

import datadog.trace.api.llmobs.LLMObs;

public class MyJavaClass {
  public String invokeChat(String userInput) {
    LLMObsSpan llmSpan = LLMObs.startLLMSpan("my-llm-span-name", "my-llm-model", "my-company", "maybe-ml-app-override", "session-141");
    String chatResponse = "N/A";
    try {
      chatResponse = ... // user application logic to invoke LLM
    } catch (Exception e) {
      llmSpan.addThrowable(e);
      throw new RuntimeException(e);
    } finally {
      // connecting the span to a customer-defined entity
      llmSpan.setTag("feedback_join_key", "incident-123");
      llmSpan.finish();

      // submitting feedback for a trace
      LLMObs.submitFeedback(
          LLMObs.Feedback.builder()
              .traceId(llmSpan.getTraceId().toString())
              .label("thumbs")
              .booleanValue(true)
              .submitter("user-123", "end_user")
              .assessment(LLMObs.Feedback.Assessment.PASS)
              .reasoning("answered the question")
              .build());

      // submitting feedback for that entity
      LLMObs.submitFeedback(
          LLMObs.Feedback.builder()
              .feedbackJoinKey("incident-123")
              .label("user_comment")
              .textValue("The answer missed the customer impact.")
              .submitter("user-123", "end_user")
              .assessment(LLMObs.Feedback.Assessment.FAIL)
              .build());
    }
    return chatResponse;
  }
}

スパン処理

スパンの入出力データを変更するには、プロセッサ関数を設定します。プロセッサ関数はスパンタグにアクセスできるため、条件付きの入出力変更が可能になります。プロセッサ関数は、変更後のスパンを返して出力するか、None/nullを返してスパンの出力を完全に防ぐことができます。これは、機密データを含むスパンや特定の基準を満たすスパンを除外する場合に役立ちます。

from ddtrace.llmobs import LLMObs
from ddtrace.llmobs import LLMObsSpan

def redact_processor(span: LLMObsSpan) -> LLMObsSpan:
    if span.get_tag("no_output") == "true":
        for message in span.output:
            message["content"] = ""
    return span


# If using LLMObs.enable()
LLMObs.enable(
  ...
  span_processor=redact_processor,
)
# else when using `ddtrace-run`
LLMObs.register_processor(redact_processor)

with LLMObs.llm("invoke_llm_with_no_output"):
    LLMObs.annotate(tags={"no_output": "true"})

例: 自動インスツルメンテーションによる条件付き変更

自動インスツルメンテーションを使用する場合、スパンが常にコンテキスト的にアクセス可能であるとは限りません。自動インスツルメンテーションスパンの入出力を条件付きで変更するには、スパンプロセッサに加えて annotation_context() を使用します。

from ddtrace.llmobs import LLMObs
from ddtrace.llmobs import LLMObsSpan

def redact_processor(span: LLMObsSpan) -> LLMObsSpan:
    if span.get_tag("no_input") == "true":
        for message in span.input:
            message["content"] = ""
    return span

LLMObs.register_processor(redact_processor)


def call_openai():
    with LLMObs.annotation_context(tags={"no_input": "true"}):
        # make call to openai
        ...

例: スパンの出力を防ぐ

from ddtrace.llmobs import LLMObs
from ddtrace.llmobs import LLMObsSpan
from typing import Optional

def filter_processor(span: LLMObsSpan) -> Optional[LLMObsSpan]:
    # Skip spans that are marked as internal or contain sensitive data
    if span.get_tag("internal") == "true" or span.get_tag("sensitive") == "true":
        return None  # This span will not be emitted

    # Process and return the span normally
    return span

LLMObs.register_processor(filter_processor)

# This span will be filtered out and not sent to Datadog
with LLMObs.workflow("internal_workflow"):
    LLMObs.annotate(tags={"internal": "true"})
    # ... workflow logic

const tracer = require('dd-trace').init({
  llmobs: {
    mlApp: "<YOUR_ML_APP_NAME>"
  }
})

const llmobs = tracer.llmobs

function redactProcessor(span) {
  if (span.getTag("no_output") === "true") {
    for (const message of span.output) {
      message.content = ""
    }
  }
  return span
}

llmobs.registerProcessor(redactProcessor)

例: 自動インスツルメンテーションによる条件付き変更

自動インスツルメンテーションを使用する場合、スパンが常にコンテキスト的にアクセス可能であるとは限りません。自動インスツルメンテーションスパンの入出力を条件付きで変更するには、スパンプロセッサに加えて llmobs.annotationContext() を使用します。

const { llmobs } = require('dd-trace');

function redactProcessor(span) {
  if (span.getTag("no_input") == "true") {
    for (const message of span.input) {
      message.content = "";
    }
  }

  return span;
}

llmobs.registerProcessor(redactProcessor);

async function callOpenai() {
  await llmobs.annotationContext({ tags: { no_input: "true" } }, async () => {
    // make call to openai
  });
}

例: スパンの出力を防ぐ

const tracer = require('dd-trace').init({
  llmobs: {
    mlApp: "<YOUR_ML_APP_NAME>"
  }
})

const llmobs = tracer.llmobs

function filterProcessor(span) {
  // Skip spans that are marked as internal or contain sensitive data
  if (span.getTag("internal") === "true" || span.getTag("sensitive") === "true") {
    return null  // This span will not be emitted
  }

  // Process and return the span normally
  return span
}

llmobs.registerProcessor(filterProcessor)

// This span will be filtered out and not sent to Datadog
function internalWorkflow() {
  return llmobs.trace({ kind: 'workflow', name: 'internalWorkflow' }, (span) => {
    llmobs.annotate({ tags: { internal: "true" } })
    // ... workflow logic
  })
}

ユーザーセッションの追跡

セッショントラッキングを使用すると、特定のユーザーに複数のインタラクションを関連付けることができます。

新しいトレースのルートスパンを開始する場合や新しいプロセスでスパンを開始する場合は、session_id 引数に基盤となるユーザーセッションの文字列 ID を指定します。これはスパンのタグとして送信されます。必要に応じて、user_handleuser_name、および user_id タグを指定することもできます。

from ddtrace.llmobs.decorators import workflow

@workflow(session_id="<SESSION_ID>")
def process_user_message():
    LLMObs.annotate(
        ...
        tags = {"user_handle": "poodle@dog.com", "user_id": "1234", "user_name": "poodle"}
    )
    return

セッショントラッキングタグ

タグ説明
session_id単一のユーザーセッション (チャットセッションなど) を表す ID。
user_handleチャットセッションのユーザーのハンドル。
user_nameチャットセッションのユーザーの名前。
user_idチャットセッションのユーザーのID。

新しいトレースのルートスパンを開始する場合や新しいプロセスでスパンを開始する場合は、sessionId 引数に基盤となるユーザーセッションの文字列 ID を指定します。

function processMessage() {
    ... # user application logic
    return
}
processMessage = llmobs.wrap({ kind: 'workflow', sessionId: "<SESSION_ID>" }, processMessage)

新しいトレースのルートスパンを開始する場合や新しいプロセスでスパンを開始する場合は、sessionId 引数に基盤となるユーザーセッションの文字列 ID を指定します。

import datadog.trace.api.llmobs.LLMObs;

public class MyJavaClass {
  public String processChat(int userID) {
    LLMObsSpan workflowSpan = LLMObs.startWorkflowSpan("incoming-chat", null, "session-" + System.currentTimeMillis() + "-" + userID);
    String chatResponse = answerChat(); // user application logic
    workflowSpan.annotateIO(...); // record the input and output
    workflowSpan.finish();
    return chatResponse;
  }
}

分散トレーシング

SDK は、分散したサービス間やホスト間でのトレースをサポートしています。分散トレースは、Web リクエスト間でスパン情報を伝播させることで機能します。

ddtrace ライブラリには、一般的な Web フレームワークおよび HTTP ライブラリの分散トレースをサポートする、すぐに使えるインテグレーションが用意されています。これらのサポートされているライブラリを使用してアプリケーションでリクエストを行う場合、次のコマンドを実行することで分散トレースを有効にできます。

from ddtrace import patch
patch(<INTEGRATION_NAME>=True)

これらのサポートされているライブラリをアプリケーションで使用していない場合は、HTTP ヘッダーとの間でスパン情報を手動で伝播させることにより、分散トレースを有効にできます。SDK には、リクエストヘッダーにトレースコンテキストを注入および有効化するためのヘルパーメソッド LLMObs.inject_distributed_headers() および LLMObs.activate_distributed_headers() が用意されれています。

分散ヘッダーの注入

LLMObs.inject_distributed_headers() メソッドは、スパンを受け取り、リクエストに含める HTTP ヘッダーにそのコンテキストを注入します。このメソッドは、次の引数を受け入れます。

request_headers
必須 - 辞書
トレースコンテキスト属性で拡張する HTTP ヘッダー。
span
オプション - スパン - デフォルト: The current active span.
指定されたリクエストヘッダーにコンテキストを注入するスパン。すべてのスパン (関数デコレータを使用する場合も含む) において、現在の有効なスパンがデフォルトで使用されます。

分散ヘッダーの有効化

LLMObs.activate_distributed_headers() メソッドは、HTTP ヘッダーを受け取り、新しいサービスで有効にするトレースコンテキスト属性を抽出します。

: ダウンストリームサービスでスパンを開始する前に LLMObs.activate_distributed_headers() を呼び出す必要があります。それ以前に開始されたスパン (関数デコレータのスパンを含む) は、分散トレースでキャプチャされません。

このメソッドは、次の引数を受け入れます。

request_headers
必須 - 辞書
トレースコンテキスト属性を抽出する HTTP ヘッダー。

client.py

from ddtrace.llmobs import LLMObs
from ddtrace.llmobs.decorators import workflow

@workflow
def client_send_request():
    request_headers = {}
    request_headers = LLMObs.inject_distributed_headers(request_headers)
    send_request("<method>", request_headers)  # arbitrary HTTP call

server.py

from ddtrace.llmobs import LLMObs

def server_process_request(request):
    LLMObs.activate_distributed_headers(request.headers)
    with LLMObs.task(name="process_request") as span:
        pass  # arbitrary server work

dd-trace ライブラリには、一般的な Webフレームワーク の分散トレースをサポートする、すぐに使えるインテグレーションが用意されています。トレーサーを要求すると、これらのインテグレーションが自動的に有効になりますが、必要に応じて次のように無効にすることもできます。

const tracer = require('dd-trace').init({
  llmobs: { ... },
})
tracer.use('http', false) // disable the http integration

高度なトレース

インラインメソッドを使用したスパンのトレース

スパンの種類ごとに、ddtrace.llmobs.LLMObs クラスは、特定のコードブロックに伴う操作を自動的にトレースするための対応するインラインメソッドを提供します。これらのメソッドは、関数デコレータの対応するものと同じ引数シグネチャを持ちますが、name が指定されていない場合は、スパンの種類 (llmworkflow など) がデフォルトで使用されるという点が異なります。これらのメソッドはコンテキストマネージャーとして使用でき、囲まれたコードブロックが完了した後にスパンを自動的に終了させることができます。

from ddtrace.llmobs import LLMObs

def process_message():
    with LLMObs.workflow(name="process_message", session_id="<SESSION_ID>", ml_app="<ML_APP>") as workflow_span:
        ... # user application logic
    return

コンテキスト間でのスパンの永続化

異なるコンテキストやスコープ間でスパンを手動で開始および停止するには、次のようにします。

  1. コンテキストマネージャーとしてではなく、通常の関数呼び出しとして、同じメソッド (例: ワークフローのスパン用の LLMObs.workflow メソッド) を使用してスパンを手動で開始します。
  2. スパンオブジェクトを引数として他の関数に渡します。
  3. span.finish() メソッドを使用して、スパンを手動で停止します。: スパンは手動で終了させる必要があります。そうしないと送信されません。

from ddtrace.llmobs import LLMObs

def process_message():
    workflow_span = LLMObs.workflow(name="process_message")
    ... # user application logic
    separate_task(workflow_span)
    return

def separate_task(workflow_span):
    ... # user application logic
    workflow_span.finish()
    return

サーバーレス環境での強制フラッシュ

LLMObs.flush() は、バッファリングされたすべての Agent Observability データを Datadog バックエンドに送信するブロッキング関数です。これは、すべての Agent Observability トレースが送信されるまでアプリケーションが終了しないようにする必要があるサーバーレス環境で役立ちます。

複数のアプリケーションのトレース

SDK は、同一サービスから複数の LLM アプリケーションをトレースすることをサポートしています。

環境変数 DD_LLMOBS_ML_APP を LLM アプリケーションの名前に設定できます。デフォルトでは、生成されたすべてのスパンがこの名前にグループ化されます。

この設定を上書きして、特定のルートスパンに別の LLM アプリケーション名を使用するには、新しいトレースのルートスパンまたは新しいプロセスのスパンを開始する際に、基盤となる LLM アプリケーションの文字列名を指定して ml_app 引数を渡します。

from ddtrace.llmobs.decorators import workflow

@workflow(name="process_message", ml_app="<NON_DEFAULT_ML_APP_NAME>")
def process_message():
    ... # user application logic
    return

インラインメソッドを使用したスパンのトレース

llmobsSDK には、特定のコードブロックを伴う操作を自動的にトレースするための対応するインラインメソッドが用意されています。これらのメソッドは、関数ラッパーの対応するものと同じ引数シグネチャを持ちますが、匿名コールバックからは名前を推論できないため、name が必須であるという点が異なります。このメソッドは、次の条件でスパンを終了します。

  • 関数が Promise を返す場合、その Promise が解決または拒否されたときにスパンが終了します。
  • 関数が最後のパラメータとしてコールバックを受け取る場合、そのコールバックが呼び出されたときにスパンが終了します。
  • 関数がコールバックを受け取らず、Promise も返さない場合、関数実行の終了時にスパンが終了します。

コールバックを使用しない例

function processMessage () {
  return llmobs.trace({ kind: 'workflow', name: 'processMessage', sessionId: '<SESSION_ID>', mlApp: '<ML_APP>' }, workflowSpan => {
    ... // user application logic
    return
  })
}

コールバックを使用する例

function processMessage () {
  return llmobs.trace({ kind: 'workflow', name: 'processMessage', sessionId: '<SESSION_ID>', mlApp: '<ML_APP>' }, (workflowSpan, cb) => {
    ... // user application logic
    let maybeError = ...
    cb(maybeError) // the span will finish here, and tag the error if it is not null or undefined
    return
  })
}

この関数の戻り値の型は、トレースする関数の戻り値の型と一致します。

function processMessage () {
  const result = llmobs.trace({ kind: 'workflow', name: 'processMessage', sessionId: '<SESSION_ID>', mlApp: '<ML_APP>' }, workflowSpan => {
    ... // user application logic
    return 'hello world'
  })

  console.log(result) // 'hello world'
  return result
}

TypeScript における関数デコレータ

Node.js の Agent Observability SDK は、TypeScript アプリケーションの関数デコレータとして機能する llmobs.decorate 関数を提供しています。この関数のトレース動作は llmobs.wrap と同じです。

// index.ts
import tracer from 'dd-trace';
tracer.init({
  llmobs: {
    mlApp: "<YOUR_ML_APP_NAME>",
  },
});

const { llmobs } = tracer;

class MyAgent {
  @llmobs.decorate({ kind: 'agent' })
  async runChain () {
    ... // user application logic
    return
  }
}

サーバーレス環境での強制フラッシュ

llmobs.flush() は、バッファリングされたすべての Agent Observability データを Datadog バックエンドに送信するブロッキング関数です。これは、すべての Agent Observability トレースが送信されるまでアプリケーションが終了しないようにする必要があるサーバーレス環境で役立ちます。

複数のアプリケーションのトレース

SDK は、同一サービスから複数の LLM アプリケーションをトレースすることをサポートしています。

環境変数 DD_LLMOBS_ML_APP を LLM アプリケーションの名前に設定できます。デフォルトでは、生成されたすべてのスパンがこの名前にグループ化されます。

この設定を上書きして、特定のルートスパンに別の LLM アプリケーション名を使用するには、新しいトレースのルートスパンまたは新しいプロセスのスパンを開始する際に、基盤となる LLM アプリケーションの文字列名を指定して mlApp 引数を渡します。

function processMessage () {
  ... // user application logic
  return
}
processMessage = llmobs.wrap({ kind: 'workflow', name: 'processMessage', mlApp: '<NON_DEFAULT_ML_APP_NAME>' }, processMessage)

アプリケーション命名ガイドライン

アプリケーション名 (DD_LLMOBS_ML_APP の値) は、次のガイドラインに従う必要があります。

  • 小文字の Unicode 文字列であること
  • 最大 193 文字までであること
  • 連続するアンダースコアや末尾のアンダースコアを含まないこと
  • 次の文字を使用すること
    • 英数字
    • アンダースコア
    • マイナス
    • コロン
    • ピリオド
    • スラッシュ

参考資料