> ## Documentation Index
> Fetch the complete documentation index at: https://docs.coreweave.com/llms.txt
> Use this file to discover all available pages before exploring further.

# OpenTelemetry トレースを Weave に送信する

> 専用のエンドポイントを使用して OpenTelemetry 互換のトレースデータをインポートします

<Warning>
  OTel スパンを Weave の新しい Agents ビューに送信する場合は、[Weave の新しい OTel ネイティブなワークフロー](/ja/products/wandb/weave/guides/tracking/trace-agents-otel)の使用をお勧めします。このガイドは、生の OTLP トレースを Weave の標準の Traces ビューに取り込みたい場合にのみ使用してください。
</Warning>

Weave は、専用のエンドポイントを介した OpenTelemetry 互換トレースデータのインポートをサポートしています。このエンドポイントを使用すると、OTLP (OpenTelemetry Protocol) 形式のトレースデータを Weave プロジェクトに直接送信できます。既存の OTel ベースの可観測性パイプラインはそのままに、OpenTelemetry 標準でアプリケーションをインストルメントし、そのトレースを他の Weave データと一緒に表示したい場合は、このインテグレーションを使用してください。

このページでは、エンドポイントの詳細、認証、Python と TypeScript によるエンドツーエンドのサンプル、OpenTelemetry Collector を経由してトレースを転送する方法、トレースを Weave スレッドに整理する方法、そして Weave が受信スパンに適用する属性マッピングについて説明します。

<Note>
  このエンドポイントのデータは **Traces** ビューと **Threads** ビューに表示されます。マルチターンのエージェントから **Agents** ビューに OTel スパンを送信する場合は、代わりに agents エンドポイントを使用してください。詳しくは、[OpenTelemetry スパンを Agents ビューに送信する](/ja/products/wandb/weave/guides/tracking/trace-agents-otel)を参照してください。
</Note>

<h2 id="endpoint-details">
  エンドポイントの詳細
</h2>

* **パス**: `/otel/v1/traces`
* **メソッド**: `POST`
* **Content-Type**: `application/x-protobuf`
* **ベース URL**: OTel トレースエンドポイントのベース URL は、W\&B のデプロイメントタイプによって異なります。

- Multi-tenant Cloud: `https://trace.wandb.ai/otel/v1/traces`
- 専用クラウドおよびセルフマネージドのインスタンス: `https://<your-subdomain>.wandb.io/traces/otel/v1/traces`

`<your-subdomain>` は、組織固有の W\&B ドメイン (例: `acme.wandb.io`) に置き換えてください。

<h2 id="authentication-and-routing">
  認証とルーティング
</h2>

Weave は、`wandb-api-key` ヘッダーでリクエストを認証し、`TracerProvider` のリソース属性に基づいてスパンを適切な entity と project にルーティングします。W\&B APIキーを `wandb-api-key` ヘッダーで渡したうえで、`TracerProvider` クラスで次のキーを OpenTelemetry の Resource 属性として指定します。

* `wandb.entity`: CoreWeave Forge のチーム名またはユーザー名。
* `wandb.project`: トレースの送信先となるプロジェクト名。

次の例は、認証と project のルーティングを設定する方法を示しています。

<Warning>
  以下の `WEAVE_OTLP_ENDPOINT` はローカル変数であり、OTel SDK の `OTEL_EXPORTER_OTLP_ENDPOINT` 環境変数ではありません。この変数には、トレースの完全なパス (`.../otel/v1/traces`) を設定しています。この値はエクスポーターの `endpoint=`/`url:` に明示的に渡されるため、SDK はその URL にそのままリクエストを送信します。一方、値を実際の `OTEL_EXPORTER_OTLP_ENDPOINT` として `export` し、引数なしでエクスポーターを構築する場合 (例: `OTLPSpanExporter()`) は、SDK が自動的に `/v1/traces` を付加します。そのため、この環境変数にはベース URL (`.../otel`) を設定する必要があります。そうしないと、パスが重複して (`.../otel/v1/traces/v1/traces`) `404` が発生し、スパンが警告なしに破棄されます。`OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` も同様です。SDK はこの変数にも /v1/traces を付加しないため、トレースの完全なパスを設定する必要があります。
</Warning>

<CodeGroup>
  ```python Python lines {7,8} theme={"system"}
  import os
  from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
  from opentelemetry.sdk import trace as trace_sdk
  from opentelemetry.sdk.resources import Resource

  WANDB_BASE_URL = "https://trace.wandb.ai"
  ENTITY = "<your-team-name>"
  PROJECT = "<your-project-name>"

  WEAVE_OTLP_ENDPOINT = f"{WANDB_BASE_URL}/otel/v1/traces"

  # https://forge.coreweave.com/settings で APIキーを作成します
  WANDB_API_KEY = os.environ["WANDB_API_KEY"]

  exporter = OTLPSpanExporter(
      endpoint=WEAVE_OTLP_ENDPOINT,
      headers={"wandb-api-key": WANDB_API_KEY},
  )

  tracer_provider = trace_sdk.TracerProvider(resource=Resource({
      "wandb.entity": ENTITY,
      "wandb.project": PROJECT,
  }))
  ```

  ```typescript TypeScript lines theme={"system"}
  import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
  import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-proto";
  import { Resource } from "@opentelemetry/resources";

  const WANDB_BASE_URL = "https://trace.wandb.ai";
  const ENTITY = "<your-team-name>";
  const PROJECT = "<your-project-name>";

  const WEAVE_OTLP_ENDPOINT = `${WANDB_BASE_URL}/otel/v1/traces`;

  // https://forge.coreweave.com/settings で APIキーを作成します
  const WANDB_API_KEY = process.env.WANDB_API_KEY!;

  const exporter = new OTLPTraceExporter({
    url: WEAVE_OTLP_ENDPOINT,
    headers: { "wandb-api-key": WANDB_API_KEY },
  });

  const provider = new NodeTracerProvider({
    resource: new Resource({
      "wandb.entity": ENTITY,
      "wandb.project": PROJECT,
    }),
  });
  ```
</CodeGroup>

<h2 id="examples">
  サンプル
</h2>

以下のサンプルでは、Python と TypeScript を使用して OpenTelemetry のトレースを Weave に送信する方法を示します。サンプルごとに異なるアプローチを取り上げます。OpenInference インストルメンテーションライブラリを使用する方法、OpenLLMetry インストルメンテーションを使用する方法、インストルメンテーションパッケージを使わずに OpenTelemetry SDK を直接使用する方法の 3 つです。

以下のコードサンプルを実行する前に、次のフィールドを設定してください。

* `WANDB_API_KEY`: [User Settings](https://forge.coreweave.com/settings) から取得できます。
* Entity: トレースをログできるのは、アクセス権のある team/entity 配下の project に限られます。entity 名を確認するには、[W\&B ダッシュボード](https://forge.coreweave.com/wandb) にアクセスし、左サイドバーの **Teams** フィールドを確認してください。
* プロジェクト名: 任意の名前を指定します。
* `OPENAI_API_KEY`: [OpenAI ダッシュボード](https://platform.openai.com/api-keys) から取得できます。

<h3 id="openinference-instrumentation">
  OpenInference インストルメンテーション
</h3>

[OpenInference](https://github.com/Arize-ai/openinference) は Arize AI が提供するオープンソースのインストルメンテーションライブラリで、LLM Call を OpenTelemetry のスパンとして取得します。この例では、OpenAI インストルメンテーションの使い方を紹介します。その他のインストルメンテーションは[公式リポジトリ](https://github.com/Arize-ai/openinference)で入手できます。

まず、必要な依存関係をインストールします。

<Tabs>
  <Tab title="Python">
    ```bash theme={"system"}
    pip install openai openinference-instrumentation-openai opentelemetry-exporter-otlp-proto-http
    ```
  </Tab>

  <Tab title="TypeScript">
    ```bash theme={"system"}
    npm install openai @opentelemetry/sdk-trace-node @opentelemetry/sdk-trace-base @opentelemetry/resources @opentelemetry/exporter-trace-otlp-proto @arizeai/openinference-instrumentation-openai @opentelemetry/api
    ```
  </Tab>
</Tabs>

<Warning>
  **パフォーマンスに関する推奨事項**: Weave にトレースを送信する際は、`SimpleSpanProcessor` ではなく必ず `BatchSpanProcessor` を使用してください。`SimpleSpanProcessor` はスパンを同期的にエクスポートするため、他のワークロードのパフォーマンスに影響を及ぼす可能性があります。これらのサンプルで使用している `BatchSpanProcessor` は、スパンを非同期で効率よくバッチ処理するため、本番環境での使用に適しています。
</Warning>

<Tabs>
  <Tab title="Python">
    次のコードを `openinference_example.py` などの Python ファイルに貼り付けます。

    ```python lines theme={"system"}
    import os
    import openai
    from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
    from opentelemetry.sdk import trace as trace_sdk
    from opentelemetry.sdk.resources import Resource
    from opentelemetry.sdk.trace.export import ConsoleSpanExporter, BatchSpanProcessor
    from openinference.instrumentation.openai import OpenAIInstrumentor

    OPENAI_API_KEY = "YOUR_OPENAI_API_KEY"
    WANDB_BASE_URL = "https://trace.wandb.ai"
    ENTITY = "<your-team-name>"
    PROJECT = "<your-project-name>"

    WEAVE_OTLP_ENDPOINT = f"{WANDB_BASE_URL}/otel/v1/traces"

    # https://forge.coreweave.com/settings でAPIキーを作成します
    WANDB_API_KEY = os.environ["WANDB_API_KEY"]

    exporter = OTLPSpanExporter(
        endpoint=WEAVE_OTLP_ENDPOINT,
        headers={"wandb-api-key": WANDB_API_KEY},
    )

    tracer_provider = trace_sdk.TracerProvider(resource=Resource({
        "wandb.entity": ENTITY,
        "wandb.project": PROJECT,
    }))
    tracer_provider.add_span_processor(BatchSpanProcessor(exporter))

    # (オプション)スパンをコンソールにも出力します。
    tracer_provider.add_span_processor(BatchSpanProcessor(ConsoleSpanExporter()))

    OpenAIInstrumentor().instrument(tracer_provider=tracer_provider)

    def main():
        client = openai.OpenAI(api_key=OPENAI_API_KEY)
        response = client.chat.completions.create(
            model="gpt-3.5-turbo",
            messages=[{"role": "user", "content": "Describe OTel in a single sentence."}],
            max_tokens=20,
            stream=True,
            stream_options={"include_usage": True},
        )
        for chunk in response:
            if chunk.choices and (content := chunk.choices[0].delta.content):
                print(content, end="")

    if __name__ == "__main__":
        main()
    ```

    コードを実行します：

    ```bash theme={"system"}
    python openinference_example.py
    ```
  </Tab>

  <Tab title="TypeScript">
    この例の TypeScript 実装には、Python 実装と比べて次のような主な違いがあります。

    * インストルメンテーションを登録する前に OpenAI をインポートする必要があります (ESM モジュールではこの順序が必須です) 。
    * W\&B のエンドポイントは protobuf のみを受け入れるため、HTTP エクスポーターではなく `@opentelemetry/exporter-trace-otlp-proto` (protobuf 形式) を使用します。
    * `BatchSpanProcessor` はスパンを非同期で書き出すため、スパンを確実に書き出せるよう、少し待機してから明示的に `provider.shutdown()` を呼び出す必要があります。

    次のコードを `openinference_example.ts` などの TypeScript ファイルに貼り付けます。

    ```typescript lines {11,12} theme={"system"}
    // 重要: インストルメンテーションがパッチを適用できるよう、OpenAI を最初にインポートしてください
    import OpenAI from "openai";
    import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
    import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base";
    import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-proto";
    import { resourceFromAttributes } from "@opentelemetry/resources";
    import { OpenAIInstrumentation, isPatched } from "@arizeai/openinference-instrumentation-openai";

    const OPENAI_API_KEY = process.env.OPENAI_API_KEY;
    const WANDB_BASE_URL = "https://trace.wandb.ai";
    const ENTITY = "<your-team-name>";
    const PROJECT = "<your-project-name>";

    const WEAVE_OTLP_ENDPOINT = `${WANDB_BASE_URL}/otel/v1/traces`;

    // APIキーは https://forge.coreweave.com/settings で作成します
    const WANDB_API_KEY = process.env.WANDB_API_KEY!;

    const exporter = new OTLPTraceExporter({
      url: WEAVE_OTLP_ENDPOINT,
      headers: { "wandb-api-key": WANDB_API_KEY },
    });

    const provider = new NodeTracerProvider({
      resource: resourceFromAttributes({
        "wandb.entity": ENTITY,
        "wandb.project": PROJECT,
      }),
      spanProcessors: [
        new BatchSpanProcessor(exporter)
      ],
    });

    provider.register();

    // OpenAI インストルメンテーションをトレーサープロバイダーに登録します
    const openAIInstrumentation = new OpenAIInstrumentation();
    openAIInstrumentation.setTracerProvider(provider);

    // ESM を使用しているため、OpenAI を手動でインストルメントします
    openAIInstrumentation.manuallyInstrument(OpenAI);

    async function main() {
      console.log("OpenAI is patched?", isPatched());

      const client = new OpenAI({ apiKey: OPENAI_API_KEY });

      console.log("Making OpenAI API call...");
      const response = await client.chat.completions.create({
        model: "gpt-3.5-turbo",
        messages: [{ role: "user", content: "Describe OTel in a single sentence." }],
        max_tokens: 50,
      });

      console.log("Response:", response.choices[0]?.message?.content);
      console.log("Waiting for spans to flush...");
    }

    (async () => {
      await main();

      // スパンのフラッシュが完了するまで時間を置きます
      console.log("Waiting 2 seconds for spans to flush...");
      await new Promise(resolve => setTimeout(resolve, 2000));

      await provider.shutdown(); // 終了前に保留中のスパンをすべてフラッシュします
      console.log("Shutdown complete");
    })();
    ```

    コードを実行します：

    ```bash theme={"system"}
    npx ts-node openinference_example.ts
    ```
  </Tab>
</Tabs>

<h3 id="openllmetry-instrumentation">
  OpenLLMetry インストルメンテーション
</h3>

[OpenLLMetry](https://github.com/traceloop/openllmetry) は Traceloop が開発しているオープンソースの可観測性ライブラリで、主要な LLM プロバイダやフレームワーク向けに OpenTelemetry インストルメンテーションを提供します。以下の例では、その OpenAI インストルメンテーションの使い方を紹介します。その他の例は [OpenLLMetry リポジトリ](https://github.com/traceloop/openllmetry)を参照してください。

まず、必要な依存関係をインストールします。

<Tabs>
  <Tab title="Python">
    ```bash theme={"system"}
    pip install openai opentelemetry-instrumentation-openai opentelemetry-exporter-otlp-proto-http
    ```
  </Tab>

  <Tab title="TypeScript">
    ```bash theme={"system"}
    npm install openai @traceloop/instrumentation-openai @opentelemetry/sdk-trace-node @opentelemetry/resources @opentelemetry/exporter-trace-otlp-http
    ```
  </Tab>
</Tabs>

<Tabs>
  <Tab title="Python">
    次のコードを `openllmetry_example.py` などの Python ファイルに貼り付けます。前の例と同じコードですが、`OpenAIInstrumentor` を `openinference.instrumentation.openai` ではなく `opentelemetry.instrumentation.openai` からインポートしている点のみが異なります。

    ```python lines theme={"system"}
    import os
    import openai
    from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
    from opentelemetry.sdk import trace as trace_sdk
    from opentelemetry.sdk.resources import Resource
    from opentelemetry.sdk.trace.export import ConsoleSpanExporter, BatchSpanProcessor
    from opentelemetry.instrumentation.openai import OpenAIInstrumentor

    OPENAI_API_KEY = "YOUR_OPENAI_API_KEY"
    WANDB_BASE_URL = "https://trace.wandb.ai"
    ENTITY = "<your-team-name>"
    PROJECT = "<your-project-name>"

    WEAVE_OTLP_ENDPOINT = f"{WANDB_BASE_URL}/otel/v1/traces"

    # APIキーは https://forge.coreweave.com/settings で作成できます
    WANDB_API_KEY = os.environ["WANDB_API_KEY"]

    exporter = OTLPSpanExporter(
        endpoint=WEAVE_OTLP_ENDPOINT,
        headers={"wandb-api-key": WANDB_API_KEY},
    )

    tracer_provider = trace_sdk.TracerProvider(resource=Resource({
        "wandb.entity": ENTITY,
        "wandb.project": PROJECT,
    }))
    tracer_provider.add_span_processor(BatchSpanProcessor(exporter))

    # オプション: スパンをコンソールにも出力します。
    tracer_provider.add_span_processor(BatchSpanProcessor(ConsoleSpanExporter()))

    OpenAIInstrumentor().instrument(tracer_provider=tracer_provider)

    def main():
        client = openai.OpenAI(api_key=OPENAI_API_KEY)
        response = client.chat.completions.create(
            model="gpt-3.5-turbo",
            messages=[{"role": "user", "content": "Describe OTel in a single sentence."}],
            max_tokens=20,
            stream=True,
            stream_options={"include_usage": True},
        )
        for chunk in response:
            if chunk.choices and (content := chunk.choices[0].delta.content):
                print(content, end="")

    if __name__ == "__main__":
        main()
    ```

    コードを実行します:

    ```bash theme={"system"}
    python openllmetry_example.py
    ```
  </Tab>

  <Tab title="TypeScript">
    次のコードを `openllmetry_example.ts` などの TypeScript ファイルに貼り付けます。このコードでは、Traceloop の OpenAI インストルメンテーションパッケージを使用しています。

    ```typescript lines theme={"system"}
    import OpenAI from "openai";
    import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
    import { BatchSpanProcessor, ConsoleSpanExporter } from "@opentelemetry/sdk-trace-base";
    import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-proto";
    import { Resource } from "@opentelemetry/resources";
    import { OpenAIInstrumentation } from "@traceloop/instrumentation-openai";
    import { registerInstrumentations } from "@opentelemetry/instrumentation";

    const OPENAI_API_KEY = process.env.OPENAI_API_KEY;
    const WANDB_BASE_URL = "https://trace.wandb.ai";
    const ENTITY = "<your-team-name>";
    const PROJECT = "<your-project-name>";

    const WEAVE_OTLP_ENDPOINT = `${WANDB_BASE_URL}/otel/v1/traces`;

    // APIキーは https://forge.coreweave.com/settings で作成します
    const WANDB_API_KEY = process.env.WANDB_API_KEY!;

    const exporter = new OTLPTraceExporter({
      url: WEAVE_OTLP_ENDPOINT,
      headers: { "wandb-api-key": WANDB_API_KEY },
    });

    const provider = new NodeTracerProvider({
      resource: new Resource({
        "wandb.entity": ENTITY,
        "wandb.project": PROJECT,
      }),
      spanProcessors: [
        new BatchSpanProcessor(exporter),
        // 必要に応じて、スパンをコンソールにも出力します。
        new BatchSpanProcessor(new ConsoleSpanExporter()),
      ],
    });

    provider.register();

    // OpenAI インストルメンテーションをトレーサープロバイダーに登録します
    const openAIInstrumentation = new OpenAIInstrumentation();
    registerInstrumentations({
      tracerProvider: provider,
      instrumentations: [openAIInstrumentation],
    });

    // ESM を使用しているため、OpenAI を手動でインストルメントします
    openAIInstrumentation.manuallyInstrument(OpenAI);

    async function main() {
      const client = new OpenAI({ apiKey: OPENAI_API_KEY });
      const stream = await client.chat.completions.create({
        model: "gpt-3.5-turbo",
        messages: [{ role: "user", content: "Describe OTel in a single sentence." }],
        max_tokens: 20,
        stream: true,
      });

      for await (const chunk of stream) {
        const content = chunk.choices[0]?.delta?.content;
        if (content) {
          process.stdout.write(content);
        }
      }
      console.log(); // ストリーミング後に改行を出力します
    }

    (async () => {
      await main();

      // スパンのフラッシュが完了するまで待機します
      await new Promise(resolve => setTimeout(resolve, 2000));

      await provider.shutdown(); // 終了前に、保留中のスパンをすべてフラッシュします
    })();
    ```

    コードを実行します:

    ```bash theme={"system"}
    npx ts-node openllmetry_example.ts
    ```
  </Tab>
</Tabs>

<h3 id="without-instrumentation">
  インストルメンテーションを使用しない場合
</h3>

インストルメンテーションパッケージを使用せずに、OTel を直接使用することもできます。この方法では、各スパンに設定する属性を完全に制御できます。Weave は、[https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-spans/](https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-spans/) で定義されている OpenTelemetry のセマンティック規約に従ってスパン属性を解析します。

まず、必要な依存関係をインストールします。

<Tabs>
  <Tab title="Python">
    ```bash theme={"system"}
    pip install openai opentelemetry-sdk opentelemetry-api opentelemetry-exporter-otlp-proto-http
    ```
  </Tab>

  <Tab title="TypeScript">
    ```bash theme={"system"}
    npm install openai @opentelemetry/api @opentelemetry/sdk-trace-node @opentelemetry/resources @opentelemetry/exporter-trace-otlp-http
    ```
  </Tab>
</Tabs>

<Tabs>
  <Tab title="Python">
    次のコードを `opentelemetry_example.py` などの Python ファイルに貼り付けます。

    ```python lines theme={"system"}
    import json
    import os
    import openai
    from opentelemetry import trace
    from opentelemetry.sdk import trace as trace_sdk
    from opentelemetry.sdk.resources import Resource
    from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
    from opentelemetry.sdk.trace.export import ConsoleSpanExporter, BatchSpanProcessor

    OPENAI_API_KEY = "YOUR_OPENAI_API_KEY"
    WANDB_BASE_URL = "https://trace.wandb.ai"
    ENTITY = "<your-team-name>"
    PROJECT = "<your-project-name>"

    WEAVE_OTLP_ENDPOINT = f"{WANDB_BASE_URL}/otel/v1/traces"

    # https://forge.coreweave.com/settings で APIキーを作成します
    WANDB_API_KEY = os.environ["WANDB_API_KEY"]

    # OTLP エクスポーターを設定します
    exporter = OTLPSpanExporter(
        endpoint=WEAVE_OTLP_ENDPOINT,
        headers={"wandb-api-key": WANDB_API_KEY},
    )

    tracer_provider = trace_sdk.TracerProvider(resource=Resource({
        "wandb.entity": ENTITY,
        "wandb.project": PROJECT,
    }))
    tracer_provider.add_span_processor(BatchSpanProcessor(exporter))

    # 必要に応じて、スパンをコンソールにも出力します。
    tracer_provider.add_span_processor(BatchSpanProcessor(ConsoleSpanExporter()))

    # トレーサープロバイダーを設定します
    trace.set_tracer_provider(tracer_provider)

    # グローバルのトレーサープロバイダーからトレーサーを作成します
    tracer = trace.get_tracer(__name__)

    def my_function():
        with tracer.start_as_current_span("outer_span") as outer_span:
            client = openai.OpenAI()
            input_messages = [{"role": "user", "content": "Describe OTel in a single sentence."}]
            outer_span.set_attribute("input.value", json.dumps(input_messages))
            outer_span.set_attribute("gen_ai.system", "openai")
            response = client.chat.completions.create(
                model="gpt-3.5-turbo",
                messages=input_messages,
                max_tokens=20,
                stream=True,
                stream_options={"include_usage": True},
            )
            out = ""
            for chunk in response:
                if chunk.choices and (content := chunk.choices[0].delta.content):
                    out += content
            outer_span.set_attribute("output.value", json.dumps({"content": out}))

    if __name__ == "__main__":
        my_function()
    ```

    コードを実行します:

    ```bash theme={"system"}
    python opentelemetry_example.py
    ```
  </Tab>

  <Tab title="TypeScript">
    次のコードを `opentelemetry_example.ts` などの TypeScript ファイルに貼り付けます。

    ```typescript lines theme={"system"}
    import OpenAI from "openai";
    import { trace } from "@opentelemetry/api";
    import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
    import { BatchSpanProcessor, ConsoleSpanExporter } from "@opentelemetry/sdk-trace-base";
    import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
    import { Resource } from "@opentelemetry/resources";

    const OPENAI_API_KEY = "YOUR_OPENAI_API_KEY";
    const WANDB_BASE_URL = "https://trace.wandb.ai";
    const ENTITY = "<your-team-name>";
    const PROJECT = "<your-project-name>";

    const WEAVE_OTLP_ENDPOINT = `${WANDB_BASE_URL}/otel/v1/traces`;

    // APIキーは https://forge.coreweave.com/settings で作成します
    const WANDB_API_KEY = process.env.WANDB_API_KEY!;

    const exporter = new OTLPTraceExporter({
      url: WEAVE_OTLP_ENDPOINT,
      headers: { "wandb-api-key": WANDB_API_KEY },
    });

    const provider = new NodeTracerProvider({
      resource: new Resource({
        "wandb.entity": ENTITY,
        "wandb.project": PROJECT,
      }),
      spanProcessors: [
        new BatchSpanProcessor(exporter),
        // オプション: スパンをコンソールに出力します。
        new BatchSpanProcessor(new ConsoleSpanExporter()),
      ],
    });

    provider.register();

    // グローバルなトレーサープロバイダーからトレーサーを作成します
    const tracer = trace.getTracer("my-app");

    async function myFunction() {
      const span = tracer.startSpan("outer_span");

      try {
        const client = new OpenAI({ apiKey: OPENAI_API_KEY });
        const inputMessages = [
          { role: "user" as const, content: "Describe OTel in a single sentence." },
        ];

        // この属性はサイドパネルにのみ表示されます
        span.setAttribute("input.value", JSON.stringify(inputMessages));
        
        // この属性はセマンティック規約に準拠しているため、ダッシュボードに表示されます
        span.setAttribute("gen_ai.system", "openai");

        const stream = await client.chat.completions.create({
          model: "gpt-3.5-turbo",
          messages: inputMessages,
          max_tokens: 20,
          stream: true,
        });

        let output = "";
        for await (const chunk of stream) {
          const content = chunk.choices[0]?.delta?.content;
          if (content) {
            output += content;
          }
          }

        // この属性はサイドパネルにのみ表示されます
        span.setAttribute("output.value", JSON.stringify({ content: output }));
      } finally {
        span.end();
      }
    }

    myFunction();
    ```

    コードを実行します:

    ```bash theme={"system"}
    npx ts-node opentelemetry_example.ts
    ```
  </Tab>
</Tabs>

Weave は、スパン属性の接頭辞 `gen_ai` と `openinference` を使用して、トレースの解釈時に適用する規約を判断します (どちらも適用しない場合もあります)。どちらのキーも検出されない場合は、すべてのスパン属性がトレースビューに表示されます。トレースを選択すると、サイドパネルでスパン全体を確認できます。

<h2 id="use-an-opentelemetry-collector">
  OpenTelemetry Collector を使用する
</h2>

これまでの例では、アプリケーションから Weave へトレースを直接エクスポートしていました。本番環境では、アプリケーションと Weave の間を仲介する [OpenTelemetry Collector](https://opentelemetry.io/docs/collector/) を使用できます。コレクターはアプリケーションからトレースを受信し、1 つ以上のバックエンドに転送します。この構成では、認証、バッチ処理、ルーティングのロジックをアプリケーションコードから切り離して一元管理できます。また、単一のパイプラインから複数の可観測性バックエンドへトレースを振り分けることもできます。

<h3 id="set-up-a-collector">
  コレクターを設定する
</h3>

このセクションでは、Docker でローカルの OpenTelemetry Collector を実行し、アプリケーションからそのコレクターにトレースを送信するよう設定する手順を説明します。次の例では、以下の方法を示します。

* OTLP トレースを受信してバッチ処理し、Weave に転送するローカルサーバー (コレクター) をデプロイするための Docker 設定ファイルを作成する。
* Docker を使用してコレクターをローカルで実行する。
* OpenAI に基本的な Call を送信し、Docker コンテナー内で実行中のコレクターにトレースを転送する。

コレクターを使用するには、まず `collector-config.yaml` ファイルを作成し、OTLP トレースを受信して Weave にエクスポートするようコレクターを設定します。

```yaml lines {23,26} collector-config.yaml title="collector-config.yaml"  theme={"system"}
receivers:
  otlp:
    protocols:
      http:
        endpoint: 0.0.0.0:4318

exporters:
  otlphttp/weave:
    endpoint: ${env:WANDB_OTLP_ENDPOINT}
    headers:
      wandb-api-key: ${env:WANDB_API_KEY}
    sending_queue:
      batch:

processors:
  resource:
    attributes:
      - key: wandb.entity # リソース属性フィールド
        value: ${env:DEFAULT_WANDB_ENTITY}  # 注入する値
        action: insert # 未設定の場合のみ注入
      - key: wandb.project
        value: ${env:DEFAULT_WANDB_PROJECT}
        action: insert 

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [resource]
      exporters: [otlphttp/weave]
```

この設定ファイルでは、次の処理を行います。

* ポート `4318` (HTTP) で OTLP トレースを受信します。
* `wandb-api-key` ヘッダーを使用して、Weave の OTLP エンドポイントにトレースをエクスポートします。エンドポイント URL は `WANDB_OTLP_ENDPOINT` から、APIキーは `WANDB_API_KEY` から読み取ります。
* `resource` プロセッサを使用して、`wandb.entity` と `wandb.project` をリソース属性として設定します。値は `DEFAULT_WANDB_ENTITY` と `DEFAULT_WANDB_PROJECT` から読み取ります。`insert` action は、アプリケーションコード側でこれらの属性が設定されていない場合にのみ、属性を注入します。
* エクスポーターに組み込まれている `sending_queue` をバッチ処理付きで有効にし、ネットワークのオーバーヘッドを削減します。

コレクターの設定が完了したら、次の Docker コマンドの API と entity の値を書き換えてから実行します。

```bash lines {3,5} theme={"system"}
docker run \
  -v ./config.yaml:/etc/otelcol-contrib/config.yaml \
  -e WANDB_API_KEY="<your-wandb-api-key>" \
  -e WANDB_OTLP_ENDPOINT="https://trace.wandb.ai/otel" \
  -e DEFAULT_WANDB_ENTITY="<your-team-name>" \
  -e DEFAULT_WANDB_PROJECT="YOUR_PROJECT" \
  -p 4318:4318 \
  otel/opentelemetry-collector-contrib:latest
```

コレクターが起動したら、`OTEL_EXPORTER_OTLP_ENDPOINT` 環境変数を設定し、アプリケーションからコレクターへトレースをエクスポートするように構成します。OTel SDK はこの変数を自動的に読み取るため、エンドポイントをエクスポーターに渡す必要はありません。

アプリケーションの `TracerProvider` で `wandb.entity` または `wandb.project` をリソース属性として設定した場合、コレクターの設定で定義されたデフォルトよりもそちらが優先されます。

<CodeGroup>
  ```python Python lines theme={"system"}
  import os
  import openai
  from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
  from opentelemetry.sdk import trace as trace_sdk
  from opentelemetry.sdk.trace.export import BatchSpanProcessor
  from openinference.instrumentation.openai import OpenAIInstrumentor

  os.environ["OTEL_EXPORTER_OTLP_ENDPOINT"] = "http://localhost:4318"

  OPENAI_API_KEY = "YOUR_OPENAI_API_KEY"

  tracer_provider = trace_sdk.TracerProvider()
  tracer_provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter()))

  OpenAIInstrumentor().instrument(tracer_provider=tracer_provider)

  def main():
      client = openai.OpenAI(api_key=OPENAI_API_KEY)
      response = client.chat.completions.create(
          model="gpt-3.5-turbo",
          messages=[{"role": "user", "content": "Describe OTel in a single sentence."}],
          max_tokens=20,
      )
      print(response.choices[0].message.content)

  if __name__ == "__main__":
      main()
  ```

  ```typescript TypeScript lines theme={"system"}
  import OpenAI from "openai";
  import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
  import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base";
  import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-proto";
  import { OpenAIInstrumentation, isPatched } from "@arizeai/openinference-instrumentation-openai";

  process.env.OTEL_EXPORTER_OTLP_ENDPOINT = "http://localhost:4318";

  const OPENAI_API_KEY = process.env.OPENAI_API_KEY;

  const provider = new NodeTracerProvider({
    spanProcessors: [new BatchSpanProcessor(new OTLPTraceExporter())],
  });

  provider.register();

  const openAIInstrumentation = new OpenAIInstrumentation();
  openAIInstrumentation.setTracerProvider(provider);
  openAIInstrumentation.manuallyInstrument(OpenAI);

  async function main() {
    console.log("OpenAI is patched?", isPatched());

    const client = new OpenAI({ apiKey: OPENAI_API_KEY });
    const response = await client.chat.completions.create({
      model: "gpt-3.5-turbo",
      messages: [{ role: "user", content: "Describe OTel in a single sentence." }],
      max_tokens: 20,
    });

    console.log("Response:", response.choices[0]?.message?.content);
  }

  (async () => {
    await main();
    await new Promise(resolve => setTimeout(resolve, 2000));
    await provider.shutdown();
  })();
  ```
</CodeGroup>

`OpenAIInstrumentor` は OpenAI の Call をラップしてトレースを作成し、コレクターにエクスポートします。認証と Weave へのルーティングはコレクターが担当します。

スクリプトの実行後、Weights & Biases の UI で[トレースを表示](/ja/products/wandb/weave/guides/tracking/trace-tree)できます。

他のバックエンドにもトレースを送信するには、エクスポーターを追加して `service.pipelines.traces.exporters` リストに含めます。たとえば、1 つの コレクター インスタンスから Weave と Jaeger の両方にエクスポートすることもできます。

<h2 id="organize-otel-traces-into-threads">
  OTel トレースをスレッドに整理する
</h2>

[Weave スレッド](/ja/products/wandb/weave/guides/tracking/threads)を使用すると、関連するトレースをグループ化し、マルチターンの会話やユーザーセッションを 1 つの単位として分析できます。特定のスパン属性を追加して OpenTelemetry トレースをスレッドに整理すると、Weave の Thread UI で、マルチターンの会話やユーザーセッションなどの関連する操作を分析できます。

スレッドによるグループ化を有効にするには、OTel スパンに次の属性を追加します。

* `wandb.thread_id`: スパンを特定のスレッドにグループ化します。
* `wandb.is_turn`: スパンを会話のターンとしてマークします (スレッドビューに行として表示されます) 。

次のサンプルでは、OTel トレースを Weave スレッドに整理する方法を示します。`wandb.thread_id` で関連する操作をグループ化し、`wandb.is_turn` でスレッドビューに行として表示される上位の操作をマークします。

<Accordion title="初期設定">
  これらのサンプルを実行するには、次の設定を使用します。

  <Tabs>
    <Tab title="Python">
      ```python lines theme={"system"}
      import json
      import os
      from opentelemetry import trace
      from opentelemetry.sdk import trace as trace_sdk
      from opentelemetry.sdk.resources import Resource
      from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
      from opentelemetry.sdk.trace.export import ConsoleSpanExporter, BatchSpanProcessor

      # 設定
      ENTITY = "<your-team-name>"
      PROJECT = "<your-project-name>"
      WANDB_API_KEY = os.environ["WANDB_API_KEY"]

      WEAVE_OTLP_ENDPOINT = "https://trace.wandb.ai/otel/v1/traces"

      exporter = OTLPSpanExporter(
          endpoint=WEAVE_OTLP_ENDPOINT,
          headers={"wandb-api-key": WANDB_API_KEY},
      )

      tracer_provider = trace_sdk.TracerProvider(resource=Resource({
          "wandb.entity": ENTITY,
          "wandb.project": PROJECT,
      }))
      tracer_provider.add_span_processor(BatchSpanProcessor(exporter))

      # 必要に応じて、スパンをコンソールに出力します
      tracer_provider.add_span_processor(BatchSpanProcessor(ConsoleSpanExporter()))

      trace.set_tracer_provider(tracer_provider)

      # グローバルなトレーサープロバイダーからトレーサーを作成します
      tracer = trace.get_tracer(__name__)
      ```
    </Tab>

    <Tab title="TypeScript">
      ```typescript lines theme={"system"}
      import { trace, context } from "@opentelemetry/api";
      import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
      import {
        BatchSpanProcessor,
        ConsoleSpanExporter,
      } from "@opentelemetry/sdk-trace-base";
      import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-proto";
      import { Resource } from "@opentelemetry/resources";

      // 設定
      const ENTITY = "<your-team-name>";
      const PROJECT = "<your-project-name>";
      const WANDB_API_KEY = process.env.WANDB_API_KEY;

      if (!WANDB_API_KEY) {
        console.error("Error: WANDB_API_KEY environment variable is not set");
        console.error("Run: export WANDB_API_KEY=your_api_key_here");
        process.exit(1);
      }

      // OTel のセットアップ
      const WEAVE_OTLP_ENDPOINT = "https://trace.wandb.ai/otel/v1/traces";

      const exporter = new OTLPTraceExporter({
        url: WEAVE_OTLP_ENDPOINT,
        headers: { "wandb-api-key": WANDB_API_KEY },
      });

      // スパンプロセッサーを指定してトレーサープロバイダーを初期化します
      const provider = new NodeTracerProvider({
        resource: new Resource({
          "wandb.entity": ENTITY,
          "wandb.project": PROJECT,
        }),
        spanProcessors: [
          new BatchSpanProcessor(exporter),
          new BatchSpanProcessor(new ConsoleSpanExporter()),
        ],
      });

      // トレーサープロバイダーを登録します
      provider.register();

      // グローバルなトレーサープロバイダーからトレーサーを作成します
      const tracer = trace.getTracer("threads-examples");
      ```
    </Tab>
  </Tabs>
</Accordion>

<Accordion title="基本的なシングルターンのスレッドをトレースする">
  <Tabs>
    <Tab title="Python">
      ```python lines theme={"system"}
      def example_1_basic_thread_and_turn():
          """Example 1: Basic thread with a single turn"""
          print("\n=== Example 1: Basic Thread and Turn ===")

          # スレッドコンテキストを作成する
          thread_id = "thread_example_1"

          # このスパンはターンを表す（スレッドの直下の子）
          with tracer.start_as_current_span("process_user_message") as turn_span:
              # スレッド属性を設定する
              turn_span.set_attribute("wandb.thread_id", thread_id)
              turn_span.set_attribute("wandb.is_turn", True)

              # サンプルの属性をいくつか追加する
              turn_span.set_attribute("input.value", "Hello, help me with setup")

              # ネストされたスパンで処理をシミュレートする
              with tracer.start_as_current_span("generate_response") as nested_span:
                  # ターン内でネストされた Call なので、is_turn は false にするか未設定のままにする
                  nested_span.set_attribute("wandb.thread_id", thread_id)
                  # ネストされた Call では wandb.is_turn を設定しないか、False に設定する

                  response = "I'll help you get started with the setup process."
                  nested_span.set_attribute("output.value", response)

              turn_span.set_attribute("output.value", response)
              print(f"Turn completed in thread: {thread_id}")

      def main():
          example_1_basic_thread_and_turn()

      if __name__ == "__main__":
          main()
      ```
    </Tab>

    <Tab title="TypeScript">
      ```typescript twoslash lines theme={"system"}
      // @noErrors
      function example_1_basic_thread_and_turn() {
        console.log("\n=== Example 1: Basic Thread and Turn ===");

        // スレッドコンテキストを作成する
        const threadId = "thread_example_1";

        // このスパンはターンを表す（スレッドの直下の子）
        tracer.startActiveSpan("process_user_message", (turnSpan) => {
          // スレッド属性を設定する
          turnSpan.setAttribute("wandb.thread_id", threadId);
          turnSpan.setAttribute("wandb.is_turn", true);

          // サンプルの属性をいくつか追加する
          turnSpan.setAttribute("input.value", "Hello, help me with setup");

          let response: string;
          
          // ネストされたスパンで処理をシミュレートする
          tracer.startActiveSpan("generate_response", (nestedSpan) => {
            // ターン内でネストされた Call なので、is_turn は false にするか未設定のままにする
            nestedSpan.setAttribute("wandb.thread_id", threadId);
            // ネストされた Call では wandb.is_turn を設定しないか、false に設定する

            response = "I'll help you get started with the setup process.";
            nestedSpan.setAttribute("output.value", response);
            nestedSpan.end();
          });
          
          turnSpan.setAttribute("output.value", response!);
          console.log(`Turn completed in thread: ${threadId}`);
          turnSpan.end();
        });
      }

      function main() {
        example_1_basic_thread_and_turn();
      }

      main();
      ```
    </Tab>
  </Tabs>
</Accordion>

<Accordion title="1 つのスレッド ID を共有するマルチターンの会話をトレースする">
  <Tabs>
    <Tab title="Python">
      ```python lines theme={"system"}
      def example_2_multiple_turns():
          """例 2: 1 つのスレッド内の複数のターン"""
          print("\n=== Example 2: Multiple Turns in Thread ===")

          thread_id = "thread_conversation_123"

          # ターン 1
          with tracer.start_as_current_span("process_message_turn1") as turn1_span:
              turn1_span.set_attribute("wandb.thread_id", thread_id)
              turn1_span.set_attribute("wandb.is_turn", True)
              turn1_span.set_attribute("input.value", "What programming languages do you recommend?")

              # ネストされた操作
              with tracer.start_as_current_span("analyze_query") as analyze_span:
                  analyze_span.set_attribute("wandb.thread_id", thread_id)
                  # ネストされたスパンでは、is_turn 属性を設定しないか False に設定します

              response1 = "I recommend Python for beginners and JavaScript for web development."
              turn1_span.set_attribute("output.value", response1)
              print(f"Turn 1 completed in thread: {thread_id}")

          # ターン 2
          with tracer.start_as_current_span("process_message_turn2") as turn2_span:
              turn2_span.set_attribute("wandb.thread_id", thread_id)
              turn2_span.set_attribute("wandb.is_turn", True)
              turn2_span.set_attribute("input.value", "Can you explain Python vs JavaScript?")

              # ネストされた操作
              with tracer.start_as_current_span("comparison_analysis") as compare_span:
                  compare_span.set_attribute("wandb.thread_id", thread_id)
                  compare_span.set_attribute("wandb.is_turn", False)  # ネストされたスパンなので明示的に False を設定

              response2 = "Python excels at data science while JavaScript dominates web development."
              turn2_span.set_attribute("output.value", response2)
              print(f"Turn 2 completed in thread: {thread_id}")

      def main():
          example_2_multiple_turns()

      if __name__ == "__main__":
          main()
      ```
    </Tab>

    <Tab title="TypeScript">
      ```typescript twoslash lines theme={"system"}
      // @noErrors
      function example_2_multiple_turns() {
        console.log("\n=== Example 2: Multiple Turns in Thread ===");

        const threadId = "thread_conversation_123";

        // ターン 1
        tracer.startActiveSpan("process_message_turn1", (turn1Span) => {
          turn1Span.setAttribute("wandb.thread_id", threadId);
          turn1Span.setAttribute("wandb.is_turn", true);
          turn1Span.setAttribute(
            "input.value",
            "What programming languages do you recommend?"
          );

          // ネストされた操作
          tracer.startActiveSpan("analyze_query", (analyzeSpan) => {
            analyzeSpan.setAttribute("wandb.thread_id", threadId);
            // ネストされたスパンでは、is_turn 属性を設定しないか false に設定します
            analyzeSpan.end();
          });

          const response1 =
            "I recommend Python for beginners and JavaScript for web development.";
          turn1Span.setAttribute("output.value", response1);
          console.log(`Turn 1 completed in thread: ${threadId}`);
          turn1Span.end();
        });

        // ターン 2
        tracer.startActiveSpan("process_message_turn2", (turn2Span) => {
          turn2Span.setAttribute("wandb.thread_id", threadId);
          turn2Span.setAttribute("wandb.is_turn", true);
          turn2Span.setAttribute("input.value", "Can you explain Python vs JavaScript?");

          // ネストされた操作
          tracer.startActiveSpan("comparison_analysis", (compareSpan) => {
            compareSpan.setAttribute("wandb.thread_id", threadId);
            compareSpan.setAttribute("wandb.is_turn", false); // ネストされたスパンなので明示的に false を設定
            compareSpan.end();
          });

          const response2 =
            "Python excels at data science while JavaScript dominates web development.";
          turn2Span.setAttribute("output.value", response2);
          console.log(`Turn 2 completed in thread: ${threadId}`);
          turn2Span.end();
        });
      }

      function main() {
        example_2_multiple_turns();
      }

      main();
      ```
    </Tab>
  </Tabs>
</Accordion>

<Accordion title="深くネストされた操作をトレースし、最も外側のスパンのみをターンとしてマークする">
  <Tabs>
    <Tab title="Python">
      ```python lines theme={"system"}
      def example_3_complex_nested_structure():
          """例 3: 複数レベルにわたる複雑なネスト構造"""
          print("\n=== Example 3: Complex Nested Structure ===")

          thread_id = "thread_complex_456"

          # 複数レベルのネストを含むターン
          with tracer.start_as_current_span("handle_complex_request") as turn_span:
              turn_span.set_attribute("wandb.thread_id", thread_id)
              turn_span.set_attribute("wandb.is_turn", True)
              turn_span.set_attribute("input.value", "Analyze this code and suggest improvements")

              # レベル 1 のネストされた操作
              with tracer.start_as_current_span("code_analysis") as analysis_span:
                  analysis_span.set_attribute("wandb.thread_id", thread_id)
                  # ネストされた操作には is_turn を設定しない

                  # レベル 2 のネストされた操作
                  with tracer.start_as_current_span("syntax_check") as syntax_span:
                      syntax_span.set_attribute("wandb.thread_id", thread_id)
                      syntax_span.set_attribute("result", "No syntax errors found")

                  # もう 1 つのレベル 2 のネストされた操作
                  with tracer.start_as_current_span("performance_check") as perf_span:
                      perf_span.set_attribute("wandb.thread_id", thread_id)
                      perf_span.set_attribute("result", "Found 2 optimization opportunities")

              # もう 1 つのレベル 1 のネストされた操作
              with tracer.start_as_current_span("generate_suggestions") as suggest_span:
                  suggest_span.set_attribute("wandb.thread_id", thread_id)
                  suggestions = ["Use list comprehension", "Consider caching results"]
                  suggest_span.set_attribute("suggestions", json.dumps(suggestions))

              turn_span.set_attribute("output.value", "Analysis complete with 2 improvement suggestions")
              print(f"Complex turn completed in thread: {thread_id}")

      def main():
          example_3_complex_nested_structure()

      if __name__ == "__main__":
          main()
      ```
    </Tab>

    <Tab title="TypeScript">
      ```typescript twoslash lines theme={"system"}
      // @noErrors
      function example_3_complex_nested_structure() {
        console.log("\n=== Example 3: Complex Nested Structure ===");

        const threadId = "thread_complex_456";

        // 複数レベルのネストを含むターン
        tracer.startActiveSpan("handle_complex_request", (turnSpan) => {
          turnSpan.setAttribute("wandb.thread_id", threadId);
          turnSpan.setAttribute("wandb.is_turn", true);
          turnSpan.setAttribute(
            "input.value",
            "Analyze this code and suggest improvements"
          );

          // レベル 1 のネストされた操作
          tracer.startActiveSpan("code_analysis", (analysisSpan) => {
            analysisSpan.setAttribute("wandb.thread_id", threadId);
            // ネストされた操作には is_turn を設定しない

            // レベル 2 のネストされた操作
            tracer.startActiveSpan("syntax_check", (syntaxSpan) => {
              syntaxSpan.setAttribute("wandb.thread_id", threadId);
              syntaxSpan.setAttribute("result", "No syntax errors found");
              syntaxSpan.end();
            });

            // もう 1 つのレベル 2 のネストされた操作
            tracer.startActiveSpan("performance_check", (perfSpan) => {
              perfSpan.setAttribute("wandb.thread_id", threadId);
              perfSpan.setAttribute("result", "Found 2 optimization opportunities");
              perfSpan.end();
            });

            analysisSpan.end();
          });

          // もう 1 つのレベル 1 のネストされた操作
          tracer.startActiveSpan("generate_suggestions", (suggestSpan) => {
            suggestSpan.setAttribute("wandb.thread_id", threadId);
            const suggestions = ["Use list comprehension", "Consider caching results"];
            suggestSpan.setAttribute("suggestions", JSON.stringify(suggestions));
            suggestSpan.end();
          });

          turnSpan.setAttribute(
            "output.value",
            "Analysis complete with 2 improvement suggestions"
          );
          console.log(`Complex turn completed in thread: ${threadId}`);
          turnSpan.end();
        });
      }

      function main() {
        example_3_complex_nested_structure();
      }

      main();
      ```
    </Tab>
  </Tabs>
</Accordion>

<Accordion title="スレッドに属するがターンではないバックグラウンド操作をトレースする">
  <Tabs>
    <Tab title="Python">
      ```python lines theme={"system"}
      def example_4_non_turn_operations():
          """Example 4: Operations that are part of a thread but not turns"""
          print("\n=== Example 4: Non-Turn Thread Operations ===")

          thread_id = "thread_background_789"

          # スレッドに属するがターンではないバックグラウンド操作
          with tracer.start_as_current_span("background_indexing") as bg_span:
              bg_span.set_attribute("wandb.thread_id", thread_id)
              # wandb.is_turn が未設定または false の場合、ターンとして扱われません
              bg_span.set_attribute("wandb.is_turn", False)
              bg_span.set_attribute("operation", "Indexing conversation history")
              print(f"Background operation in thread: {thread_id}")

          # 同じスレッド内の実際のターン
          with tracer.start_as_current_span("user_query") as turn_span:
              turn_span.set_attribute("wandb.thread_id", thread_id)
              turn_span.set_attribute("wandb.is_turn", True)
              turn_span.set_attribute("input.value", "Search my previous conversations")
              turn_span.set_attribute("output.value", "Found 5 relevant conversations")
              print(f"Turn completed in thread: {thread_id}")

      def main():
          example_4_non_turn_operations()

      if __name__ == "__main__":
          main()
      ```
    </Tab>

    <Tab title="TypeScript">
      ```typescript twoslash lines theme={"system"}
      // @noErrors
      function example_4_non_turn_operations() {
        console.log("\n=== Example 4: Non-Turn Thread Operations ===");

        const threadId = "thread_background_789";

        // スレッドに属するがターンではないバックグラウンド操作
        tracer.startActiveSpan("background_indexing", (bgSpan) => {
          bgSpan.setAttribute("wandb.thread_id", threadId);
          // wandb.is_turn が未設定または false の場合、ターンとして扱われません
          bgSpan.setAttribute("wandb.is_turn", false);
          bgSpan.setAttribute("operation", "Indexing conversation history");
          console.log(`Background operation in thread: ${threadId}`);
          bgSpan.end();
        });

        // 同じスレッド内の実際のターン
        tracer.startActiveSpan("user_query", (turnSpan) => {
          turnSpan.setAttribute("wandb.thread_id", threadId);
          turnSpan.setAttribute("wandb.is_turn", true);
          turnSpan.setAttribute("input.value", "Search my previous conversations");
          turnSpan.setAttribute("output.value", "Found 5 relevant conversations");
          console.log(`Turn completed in thread: ${threadId}`);
          turnSpan.end();
        });
      }

      function main() {
        example_4_non_turn_operations();
      }

      main();
      ```
    </Tab>
  </Tabs>
</Accordion>

これらのトレースを送信すると、Weights & Biases UI の **Threads** タブで確認できます。トレースは `thread_id` ごとにグループ化され、各ターンはそれぞれ別の行に表示されます。

<h2 id="attribute-mappings">
  属性マッピング
</h2>

Weave は、さまざまなインストルメンテーションフレームワークから送られる OpenTelemetry のスパン属性を、内部データモデルにマッピングします。このマッピングにより、既存のインストルメンテーションの属性をリネームしたり変換したりしなくても、Weave で充実したビューを利用できます。複数の属性名が同じフィールドにマッピングされる場合、Weave は優先順位に従ってそれらを適用します。そのため、同じトレース内で複数のフレームワークを共存させることができます。

<h3 id="supported-frameworks">
  サポートされるフレームワーク
</h3>

Weave は、以下の可観測性フレームワークおよび SDK の属性規約をサポートしています。

* **OpenTelemetry GenAI**: 生成 AI 向けの標準セマンティック規約 (`gen_ai.*`)。
* **OpenInference**: Arize AI のインストルメンテーションライブラリ (`input.value`、`output.value`、`llm.*`、`openinference.*`)。
* **Vercel AI SDK**: Vercel AI SDK の属性 (`ai.prompt`、`ai.response`、`ai.model.*`、`ai.usage.*`)。
* **MLflow**: MLflow のトラッキング属性 (`mlflow.spanInputs`、`mlflow.spanOutputs`)。
* **Traceloop**: OpenLLMetry のインストルメンテーション (`traceloop.entity.*`、`traceloop.span.kind`)。
* **Google Vertex AI**: Vertex AI のエージェント属性 (`gcp.vertex.agent.*`)。
* **OpenLit**: OpenLit の可観測性属性 (`gen_ai.content.completion`)。
* **Logfire / Pydantic AI**: Logfire による Pydantic AI のインストルメンテーション (`gen_ai.input.messages`、`gen_ai.output.messages`、`pydantic_ai.all_messages`、`final_result`)。
* **Langfuse**: Langfuse のトレース属性 (`langfuse.startTime`、`langfuse.endTime`)。

<h3 id="attribute-reference">
  属性リファレンス
</h3>

| 属性フィールド名 | W\&B マッピング | 説明 | タイプ | 例 |
| :- | :- | :- | :- | :- |
| `ai.prompt` | `inputs` | ユーザー プロンプトのテキストまたはメッセージ。 | String, list, dict | `"Write a short haiku about summer."` |
| `gen_ai.prompt` | `inputs` | AI モデルのプロンプトまたはメッセージ配列。 | List, dict, string | `[{"role":"user","content":"abc"}]` |
| `input.value` | `inputs` | モデル呼び出しの入力値。 | String, list, dict | `{"text":"Tell a joke"}` |
| `mlflow.spanInputs` | `inputs` | スパンの入力データ。 | String, list, dict | `["prompt text"]` |
| `gen_ai.input.messages` | `inputs` | Logfire が出力する入力メッセージ。 | List, dict, string | `[{"role":"user","parts":[...]}]` |
| `pydantic_ai.all_messages` | `inputs` | Pydantic AI エージェントの実行における全メッセージ。 | List | `[{"role":"user","content":"abc"}]` |
| `traceloop.entity.input` | `inputs` | エンティティの入力データ。 | String, list, dict | `"Translate this to French"` |
| `gcp.vertex.agent.tool_call_args` | `inputs` | ツール呼び出しの引数。 | Dict | `{"args":{"query":"weather in SF"}}` |
| `gcp.vertex.agent.llm_request` | `inputs` | LLM リクエストのペイロード。 | Dict | `{"contents":[{"role":"user","parts":[...]}]}` |
| `input` | `inputs` | 汎用の入力値。 | String, list, dict | `"Summarize this text"` |
| `inputs` | `inputs` | 汎用の入力配列。 | List, dict, string | `["Summarize this text"]` |
| `ai.response` | `outputs` | モデルの応答テキストまたはデータ。 | String, list, dict | `"Here is a haiku..."` |
| `gen_ai.completion` | `outputs` | AI による補完結果。 | String, list, dict | `"Completion text"` |
| `output.value` | `outputs` | モデルの出力値。 | String, list, dict | `{"text":"Answer text"}` |
| `gen_ai.output.messages` | `outputs` | Logfire が出力する出力メッセージ。 | List, dict, string | `[{"role":"assistant","parts":[...]}]` |
| `mlflow.spanOutputs` | `outputs` | スパンの出力データ。 | String, list, dict | `["answer"]` |
| `final_result` | `outputs` | Pydantic AI エージェントの実行の最終出力。 | String, dict | `"Answer text"` |
| `gen_ai.content.completion` | `outputs` | コンテンツの補完結果。 | String | `"Answer text"` |
| `traceloop.entity.output` | `outputs` | エンティティの出力データ。 | String, list, dict | `"Answer text"` |
| `gcp.vertex.agent.tool_response` | `outputs` | ツール実行の応答。 | Dict, string | `{"toolResponse":"ok"}` |
| `gcp.vertex.agent.llm_response` | `outputs` | LLM 応答のペイロード。 | Dict, string | `{"candidates":[...]}` |
| `output` | `outputs` | 汎用の出力値。 | String, list, dict | `"Answer text"` |
| `outputs` | `outputs` | 汎用の出力配列。 | List, dict, string | `["Answer text"]` |
| `gen_ai.usage.input_tokens` | `usage.input_tokens` | 消費された入力トークン数。 | Int | `42` |
| `gen_ai.usage.prompt_tokens` | `usage.prompt_tokens` | 消費されたプロンプト トークン数。 | Int | `30` |
| `llm.token_count.prompt` | `usage.prompt_tokens` | プロンプト トークン数。 | Int | `30` |
| `ai.usage.promptTokens` | `usage.prompt_tokens` | 消費されたプロンプト トークン数。 | Int | `30` |
| `gen_ai.usage.completion_tokens` | `usage.completion_tokens` | 生成された補完トークン数。 | Int | `40` |
| `llm.token_count.completion` | `usage.completion_tokens` | 補完トークン数。 | Int | `40` |
| `ai.usage.completionTokens` | `usage.completion_tokens` | 生成された補完トークン数。 | Int | `40` |
| `llm.usage.total_tokens` | `usage.total_tokens` | リクエストで使用された合計トークン数。 | Int | `70` |
| `llm.token_count.total` | `usage.total_tokens` | 合計トークン数。 | Int | `70` |
| `gen_ai.system` | `attributes.system` | システムプロンプトまたは指示。 | String | `"You are a helpful assistant."` |
| `llm.system` | `attributes.system` | システムプロンプトまたは指示。 | String | `"You are a helpful assistant."` |
| `gen_ai.system_instructions` | `attributes.system` | システムプロンプトまたは指示。 | String | `"You are a helpful assistant."` |
| `weave.span.kind` | `attributes.kind` | スパンのタイプまたはカテゴリ。 | String | `"llm"` |
| `traceloop.span.kind` | `attributes.kind` | スパンのタイプまたはカテゴリ。 | String | `"llm"` |
| `openinference.span.kind` | `attributes.kind` | スパンのタイプまたはカテゴリ。 | String | `"llm"` |
| `gen_ai.response.model` | `attributes.model` | モデル識別子。 | String | `"gpt-4o"` |
| `llm.model_name` | `attributes.model` | モデル識別子。 | String | `"gpt-4o-mini"` |
| `ai.model.id` | `attributes.model` | モデル識別子。 | String | `"gpt-4o"` |
| `llm.provider` | `attributes.provider` | モデルプロバイダー名。 | String | `"openai"` |
| `ai.model.provider` | `attributes.provider` | モデルプロバイダー名。 | String | `"openai"` |
| `gen_ai.request` | `attributes.model_parameters` | モデルの生成パラメーター。 | Dict | `{"temperature":0.7,"max_tokens":256}` |
| `llm.invocation_parameters` | `attributes.model_parameters` | モデルの呼び出しパラメーター。 | Dict | `{"temperature":0.2}` |
| `wandb.display_name` | `display_name` | UI 用のカスタム表示名。 | String | `"User Message"` |
| `gcp.vertex.agent.session_id` | `thread_id` | セッションまたはスレッドの識別子。 | String | `"thread_123"` |
| `wandb.thread_id` | `thread_id` | 会話のスレッド識別子。 | String | `"thread_123"` |
| `wb_run_id` | `wb_run_id` | 関連付けられた W\&B run の識別子。 | String | `"abc123"` |
| `wandb.wb_run_id` | `wb_run_id` | 関連付けられた W\&B run の識別子。 | String | `"abc123"` |
| `gcp.vertex.agent.session_id` | `is_turn` | スパンを会話のターンとしてマークします。 | Boolean | `true` |
| `wandb.is_turn` | `is_turn` | スパンを会話のターンとしてマークします。 | Boolean | `true` |
| `langfuse.startTime` | `start_time` (override) | スパンの開始タイムスタンプを上書きします。 | タイムスタンプ (ISO8601/unix ns) | `"2024-01-01T12:00:00Z"` |
| `langfuse.endTime` | `end_time` (override) | スパンの終了タイムスタンプを上書きします。 | タイムスタンプ (ISO8601/unix ns) | `"2024-01-01T12:00:01Z"` |

<h2 id="limitations">
  制限事項
</h2>

Weights & Biases の UI では、OTel トレースのツール呼び出しを Chat ビューでレンダリングすることはサポートされていません。ツール呼び出しは、代わりに生の JSON として表示されます。


## Related topics

- [Weave セルフマネージドの取り込みサンプリングを設定する](/ja/products/wandb/weave/guides/platform/ingest-sampling.md)
