概要
エンドポイント の詳細
/otel/v1/traces
Method: POST
Content-Type: application/x-protobuf
Base 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) に置き換えてください。
認証とルーティング
wandb-api-key ヘッダーに W&B APIキーを渡し、続けて次のキーを TracerProvider クラスの OpenTelemetry リソース属性として指定します。
wandb.entity: W&B のチーム名またはユーザー名。wandb.project: トレースの送信先となるプロジェクト名。
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>"
OTEL_EXPORTER_OTLP_ENDPOINT = f"{WANDB_BASE_URL}/otel/v1/traces"
# https://wandb.ai/settings で APIキーを作成します
WANDB_API_KEY = os.environ["WANDB_API_KEY"]
exporter = OTLPSpanExporter(
endpoint=OTEL_EXPORTER_OTLP_ENDPOINT,
headers={"wandb-api-key": WANDB_API_KEY},
)
tracer_provider = trace_sdk.TracerProvider(resource=Resource({
"wandb.entity": ENTITY,
"wandb.project": PROJECT,
}))
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 OTEL_EXPORTER_OTLP_ENDPOINT = `${WANDB_BASE_URL}/otel/v1/traces`;
// https://wandb.ai/settings で APIキーを作成します
const WANDB_API_KEY = process.env.WANDB_API_KEY!;
const exporter = new OTLPTraceExporter({
url: OTEL_EXPORTER_OTLP_ENDPOINT,
headers: { "wandb-api-key": WANDB_API_KEY },
});
const provider = new NodeTracerProvider({
resource: new Resource({
"wandb.entity": ENTITY,
"wandb.project": PROJECT,
}),
});
例
WANDB_API_KEY: User Settings から取得できます。- Entity: トレースをログできるのは、自分がアクセス権を持つ entity 配下のプロジェクトのみです。entity 名は、[https://wandb.ai/home] の W&B ダッシュボードにアクセスし、左サイドバーの Teams フィールドを確認するとわかります。
- Project Name: 好きな名前を付けてください。
OPENAI_API_KEY: OpenAI ダッシュボード から取得できます。
OpenInference インストルメンテーション
- Python
- TypeScript
pip install openai openinference-instrumentation-openai opentelemetry-exporter-otlp-proto-http
npm install openai @opentelemetry/sdk-trace-node @opentelemetry/sdk-trace-base @opentelemetry/resources @opentelemetry/exporter-trace-otlp-proto @arizeai/openinference-instrumentation-openai @opentelemetry/api
パフォーマンスに関する推奨事項: トレースを Weave に送信する際は、
SimpleSpanProcessor ではなく必ず BatchSpanProcessor を使用してください。SimpleSpanProcessor は span を同期的にエクスポートするため、ほかのワークロードのパフォーマンスに影響する可能性があります。これらの例では BatchSpanProcessor を使用しています。これは span を非同期かつ効率的にバッチ処理するため、本番環境での使用が推奨されます。- Python
- TypeScript
openinference_example.py などの Python ファイルに、以下のコードを貼り付けます。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>"
OTEL_EXPORTER_OTLP_ENDPOINT = f"{WANDB_BASE_URL}/otel/v1/traces"
# https://wandb.ai/settings でAPIキーを作成してください
WANDB_API_KEY = os.environ["WANDB_API_KEY"]
exporter = OTLPSpanExporter(
endpoint=OTEL_EXPORTER_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()
python openinference_example.py
この例の TypeScript 実装には、Python 実装との主な違いが次のとおりあります。次のコードを実行します。
- インストルメンテーションを登録する前に OpenAI を import する必要があります (ESM モジュールではこれが必要です) 。
- W&B のエンドポイントは protobuf のみを受け入れるため、HTTP exporter ではなく
@opentelemetry/exporter-trace-otlp-proto(protobuf 形式) を使用します。 BatchSpanProcessorは非同期にフラッシュするため、span が確実にフラッシュされるよう、シャットダウン前に遅延を入れたうえでprovider.shutdown()を明示的に呼び出す必要があります。
openinference_example.ts などの TypeScript ファイルに貼り付けてください。// 重要: インストルメンテーションがパッチを適用できるよう、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 OTEL_EXPORTER_OTLP_ENDPOINT = `${WANDB_BASE_URL}/otel/v1/traces`;
// https://wandb.ai/settings で APIキーを作成してください
const WANDB_API_KEY = process.env.WANDB_API_KEY!;
const exporter = new OTLPTraceExporter({
url: OTEL_EXPORTER_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");
})();
npx ts-node openinference_example.ts
OpenLLMetry インストルメンテーション
- Python
- TypeScript
pip install openai opentelemetry-instrumentation-openai opentelemetry-exporter-otlp-proto-http
npm install openai @traceloop/instrumentation-openai @opentelemetry/sdk-trace-node @opentelemetry/resources @opentelemetry/exporter-trace-otlp-http
- Python
- TypeScript
次のコードを、コードを実行します。
openllmetry_example.py などの Python ファイルに貼り付けてください。上記のコードと同じですが、OpenAIInstrumentor は openinference.instrumentation.openai ではなく opentelemetry.instrumentation.openai から import しています。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>"
OTEL_EXPORTER_OTLP_ENDPOINT = f"{WANDB_BASE_URL}/otel/v1/traces"
# https://wandb.ai/settings でAPIキーを作成してください
WANDB_API_KEY = os.environ["WANDB_API_KEY"]
exporter = OTLPSpanExporter(
endpoint=OTEL_EXPORTER_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()
python openllmetry_example.py
次のコードを、次のコードを実行します:
openllmetry_example.ts などの TypeScript ファイルに貼り付けてください。なお、このコードでは Traceloop の OpenAI インストルメンテーション パッケージを使用します。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 OTEL_EXPORTER_OTLP_ENDPOINT = `${WANDB_BASE_URL}/otel/v1/traces`;
// https://wandb.ai/settings でAPIキーを作成してください
const WANDB_API_KEY = process.env.WANDB_API_KEY!;
const exporter = new OTLPTraceExporter({
url: OTEL_EXPORTER_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(); // 終了前にすべての保留中のスパンをフラッシュします
})();
npx ts-node openllmetry_example.ts
インストルメンテーションを使用しない場合
- Python
- TypeScript
pip install openai opentelemetry-sdk opentelemetry-api opentelemetry-exporter-otlp-proto-http
npm install openai @opentelemetry/api @opentelemetry/sdk-trace-node @opentelemetry/resources @opentelemetry/exporter-trace-otlp-http
- Python
- TypeScript
次のコードを、コードを実行します。
opentelemetry_example.py などの Python ファイルに貼り付けてください。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>"
OTEL_EXPORTER_OTLP_ENDPOINT = f"{WANDB_BASE_URL}/otel/v1/traces"
# https://wandb.ai/settings でAPIキーを作成してください
WANDB_API_KEY = os.environ["WANDB_API_KEY"]
# OTLPエクスポーターを設定する
exporter = OTLPSpanExporter(
endpoint=OTEL_EXPORTER_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()
python opentelemetry_example.py
次のコードを、次のコードを実行します:
opentelemetry_example.ts などの TypeScript ファイルに貼り付けてください。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 OTEL_EXPORTER_OTLP_ENDPOINT = `${WANDB_BASE_URL}/otel/v1/traces`;
// https://wandb.ai/settings でAPIキーを作成してください
const WANDB_API_KEY = process.env.WANDB_API_KEY!;
const exporter = new OTLPTraceExporter({
url: OTEL_EXPORTER_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();
npx ts-node opentelemetry_example.ts
gen_ai と openinference を使って判断されます。どちらのキーも検出されない場合は、すべての span 属性が Trace view に表示されます。トレースを選択すると、サイドパネルで span 全体を確認できます。
OpenTelemetry Collector を使用する
collector を設定する
- OTLP トレースを受信し、バッチ処理して Weave に転送するローカルサーバー (collector) をデプロイする Docker の設定ファイルを設定する。
- Docker を使用して、collector をローカルで実行する。
- Docker コンテナー内で実行中の collector にトレースを転送する、OpenAI への基本的な call を送信する。
collector-config.yaml ファイルを作成します。
collector-config.yaml
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から読み取ります。insertaction は、アプリケーションコードですでにこれらの属性を設定していない場合にのみ、これらの属性を追加します。- ネットワーク負荷を減らすため、バッチ処理とともにエクスポーター組み込みの
sending_queueを有効にします。
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 はこの変数を自動的に読み取るため、exporter にエンドポイントを渡す必要はありません。
アプリケーションの TracerProvider で wandb.entity または wandb.project をリソース属性として設定すると、それらは collector の設定で定義されたデフォルトよりも優先されます。
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()
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();
})();
OpenAIInstrumentor は OpenAI の呼び出しを自動的にラップし、トレースを作成して collector にエクスポートします。collector は認証と Weave へのルーティングを処理します。
スクリプトの実行後、Weave UI でトレースを表示できます。
トレースを追加のバックエンドに送信するには、exporter を追加し、それらを service.pipelines.traces.exporters リストに含めます。たとえば、同じ Collector インスタンスから Weave と Jaeger の両方にエクスポートできます。
OTel トレースをスレッドに整理する
wandb.thread_id: span を特定のスレッドにグループ化しますwandb.is_turn: span を会話のターンとしてマークします (スレッドビューでは行として表示されます)
wandb.thread_id を使用して関連する操作をグループ化し、wandb.is_turn を使用してスレッドビューで行として表示される上位レベルの操作をマークします。
初期設定
初期設定
これらの例を実行するには、この設定を使用します。
- Python
- TypeScript
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"]
OTEL_EXPORTER_OTLP_ENDPOINT = "https://trace.wandb.ai/otel/v1/traces"
exporter = OTLPSpanExporter(
endpoint=OTEL_EXPORTER_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))
# 必要に応じて、span をコンソールに出力します
tracer_provider.add_span_processor(BatchSpanProcessor(ConsoleSpanExporter()))
trace.set_tracer_provider(tracer_provider)
# グローバル tracer provider から tracer を作成します
tracer = trace.get_tracer(__name__)
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 OTEL_EXPORTER_OTLP_ENDPOINT = "https://trace.wandb.ai/otel/v1/traces";
const exporter = new OTLPTraceExporter({
url: OTEL_EXPORTER_OTLP_ENDPOINT,
headers: { "wandb-api-key": WANDB_API_KEY },
});
// span processor を指定して tracer provider を初期化します
const provider = new NodeTracerProvider({
resource: new Resource({
"wandb.entity": ENTITY,
"wandb.project": PROJECT,
}),
spanProcessors: [
new BatchSpanProcessor(exporter),
new BatchSpanProcessor(new ConsoleSpanExporter()),
],
});
// tracer provider を登録します
provider.register();
// グローバル tracer provider から tracer を作成します
const tracer = trace.getTracer("threads-examples");
基本的な単一ターンのスレッドをトレースする
基本的な単一ターンのスレッドをトレースする
- Python
- TypeScript
def example_1_basic_thread_and_turn():
"""例 1: 単一ターンの基本的なスレッド"""
print("\n=== Example 1: Basic Thread and Turn ===")
# スレッドコンテキストを作成
thread_id = "thread_example_1"
# このspanはターンを表します(スレッドの直接の子)
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")
# ネストされたspanで処理をシミュレート
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()
function example_1_basic_thread_and_turn() {
console.log("\n=== Example 1: Basic Thread and Turn ===");
// スレッドコンテキストを作成
const threadId = "thread_example_1";
// このspanはターンを表します(スレッドの直接の子)
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;
// ネストされたspanで処理をシミュレート
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();
同じスレッド ID を共有する複数ターンの会話をトレースする
同じスレッド ID を共有する複数ターンの会話をトレースする
- Python
- TypeScript
def example_2_multiple_turns():
"""Example 2: Multiple turns in a single thread"""
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)
# ネストされたspanでは 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) # ネストされたspanであることを明示するため 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()
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);
// ネストされたspanでは 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); // ネストされたspanであることを明示するため 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();
深くネストされた処理をトレースし、最も外側の span のみをターンとしてマークする
深くネストされた処理をトレースし、最も外側の span のみをターンとしてマークする
- Python
- TypeScript
def example_3_complex_nested_structure():
"""Example 3: Complex nested structure with multiple levels"""
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")
# 別のレベル 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 のネストされた処理
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()
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();
});
// 別のレベル 2 のネストされた処理
tracer.startActiveSpan("performance_check", (perfSpan) => {
perfSpan.setAttribute("wandb.thread_id", threadId);
perfSpan.setAttribute("result", "Found 2 optimization opportunities");
perfSpan.end();
});
analysisSpan.end();
});
// 別のレベル 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();
スレッドに属しているもののターンではないバックグラウンド処理をトレースする
スレッドに属しているもののターンではないバックグラウンド処理をトレースする
- Python
- TypeScript
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()
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();
thread_id ごとにグループ化され、各ターンは個別の行として表示されます。
属性マッピング
対応フレームワーク
- 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) - Langfuse: Langfuse のトレース属性 (
langfuse.startTime,langfuse.endTime)
属性のリファレンス
| 属性フィールド名 | W&B マッピング | 説明 | タイプ | 例 |
|---|---|---|---|---|
ai.prompt | inputs | ユーザーのプロンプトのテキストまたはメッセージ。 | 文字列、list、dict | "夏について短い俳句を書いて。" |
gen_ai.prompt | inputs | AIモデルへのプロンプト、またはメッセージの配列。 | list、dict、文字列 | [{"role":"user","content":"abc"}] |
input.value | inputs | モデル呼び出し時の入力値。 | 文字列、list、dict | {"text":"ジョークを言って"} |
mlflow.spanInputs | inputs | スパンの入力データ。 | 文字列、list、dict | ["プロンプトテキスト"] |
traceloop.entity.input | inputs | entityの入力データ。 | String、list、dict | "これをフランス語に翻訳して" |
gcp.vertex.agent.tool_call_args | inputs | ツールcallの引数。 | dict | {"args":{"query":"weather in SF"}} |
gcp.vertex.agent.llm_request | inputs | LLMリクエストのペイロード。 | Dict | {"contents":[{"role":"user","parts":[...]}]} |
input | inputs | 汎用的な入力値。 | String, list, dict | "このテキストを要約して" |
inputs | inputs | 汎用的な入力配列。 | List, dict, string | ["このテキストを要約してください"] |
ai.response | outputs | モデルの応答テキストまたはデータ。 | String、list、dict | "俳句をどうぞ..." |
gen_ai.completion | outputs | AIの補完結果。 | String、list、dict | "補完テキスト" |
output.value | outputs | モデルの出力値。 | String、list、dict | {"text":"回答テキスト"} |
mlflow.spanOutputs | outputs | スパンの出力データ。 | String、list、dict | ["answer"] |
gen_ai.content.completion | outputs | コンテンツ補完の結果。 | String | "回答テキスト" |
traceloop.entity.output | outputs | entityの出力データ。 | String、list、dict | "回答テキスト" |
gcp.vertex.agent.tool_response | outputs | ツール実行のレスポンス。 | dict、文字列 | {"toolResponse":"ok"} |
gcp.vertex.agent.llm_response | outputs | LLMのレスポンスペイロード。 | dict、文字列 | {"candidates":[...]} |
output | outputs | 汎用的な出力値。 | 文字列、list、dict | "回答テキスト" |
outputs | outputs | 汎用的な出力配列。 | list、dict、string | ["回答テキスト"] |
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 | system prompt または指示。 | String | "あなたは役に立つアシスタントです。" |
llm.system | attributes.system | system prompt または指示。 | String | "あなたは役立つアシスタントです。" |
weave.span.kind | attributes.kind | Span のタイプまたはカテゴリ。 | String | "llm" |
traceloop.span.kind | attributes.kind | スパンのタイプまたはカテゴリ。 | 文字列 | "llm" |
openinference.span.kind | attributes.kind | スパンのタイプまたはカテゴリ。 | 文字列 | "llm" |
gen_ai.response.model | attributes.model | モデル識別子。 | 文字列 | "gpt-4o" |
llm.model_name | attributes.model | モデル識別子。 | 文字列 | "gpt-4o-mini" |
ai.model.id | attributes.model | モデル識別子。 | 文字列 | "gpt-4o" |
llm.provider | attributes.provider | モデルプロバイダ名。 | 文字列 | "openai" |
ai.model.provider | attributes.provider | モデルプロバイダ名。 | 文字列 | "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 用のカスタム表示名。 | 文字列 | "ユーザーメッセージ" |
gcp.vertex.agent.session_id | thread_id | セッションまたはスレッドの識別子。 | 文字列 | "thread_123" |
wandb.thread_id | thread_id | 会話のスレッド識別子。 | 文字列 | "thread_123" |
wb_run_id | wb_run_id | 関連する W&B run の識別子。 | 文字列 | "abc123" |
wandb.wb_run_id | wb_run_id | 関連付けられた W&B run の識別子。 | 文字列 | "abc123" |
gcp.vertex.agent.session_id | is_turn | スパンが会話のターンであることを示します。 | ブール値 | true |
wandb.is_turn | is_turn | スパンが会話のターンであることを示します。 | 真偽値 | true |
langfuse.startTime | start_time (上書き) | スパンの開始タイムスタンプを上書きします。 | タイムスタンプ (ISO8601/Unix ns) | "2024-01-01T12:00:00Z" |
langfuse.endTime | end_time (上書き) | スパンの終了タイムスタンプを上書きします。 | タイムスタンプ (ISO8601/Unix ns) | "2024-01-01T12:00:01Z" |
制限事項
- Weave UI では、Chat view で OTel trace の tool calls をレンダリングできません。代わりに、生の JSON として表示されます。