
Span命名移行: 古い形式は、すべての GenAI対応バックエンドには見えませんでした。
エージェント属性 — パスが間違っていました
エージェントを構築している場合(ReAct、ツール使用、複数ステップ)、仕様はアイデンティティとツール属性を定義しています:
// What OTel says:
span.setAttribute("gen_ai.agent.name", "weather-bot");
span.setAttribute("gen_ai.agent.id", "agent-001");
span.setAttribute("gen_ai.tool.name", "search");
span.setAttribute("gen_ai.tool.type", "function");
// What we had:
span.setAttribute("gen_ai.agent.tool.name", "search"); // wrong path
// gen_ai.agent.name — didn’t exist at all
gen_ai.agent.tool.name のパスは妥当なように見えます。読んだ感じでも自然です。しかし仕様はツール属性を gen_ai.tool.* に配置します — エージェントの下にはネストされていません。私たちの形式は、再び、標準に従うバックエンドには見えません。
コンテンツ記録 — 仕様も私たちに賛同しています(いい気分です)
これは私たちが最初の日から正しくできていた唯一の点であり、多くのチームがそれを間違えるため、指摘しておく価値があります。
仕様では: デフォルトではプロンプトと完了を記録しない。 計装は、明示的に有効化されない限り、コンテンツを捕捉すべきではありません。
公式の3つのパターン:
- デフォルト: 記録しない。 スパンにはプロンプトも完了もない。プライバシー優先。
-
スパン属性によるオプトイン。
gen_ai.input.messagesおよびgen_ai.output.messagesを JSON 文字列として。 - 外部ストレージ。 コンテンツを別の場所に保存し、スパンに参照を置く。
v1 からデフォルトは recordContent: false でした。仕様がこのアプローチを確認したとき、それは、直感が非常に賢い人々の委員会によって裏付けられたという、まれな瞬間の1つでした。
デフォルトでスパンにプロンプトをログしている場合は、セキュリティチームが代わりにそれを行う前に、再検討したほうがよいかもしれません。
正直なギャップ分析
ここに全体像があります。歪曲なし、抜粋なし。
初日から正しかった点
| 私たちの属性 | OTel 規格 | 判定 |
|---|---|---|
gen_ai.provider.name |
gen_ai.provider.name |
✅ 完全一致 |
gen_ai.request.model |
gen_ai.request.model |
✅ 完全一致 |
gen_ai.usage.input_tokens |
gen_ai.usage.input_tokens |
✅ 完全一致 |
error.type |
error.type |
✅ 完全一致 |
私たちが間違えた点
| 項目 | 私たちの版 | OTel 規格 | 状態 |
|---|---|---|---|
| スパン名 | gen_ai.openai.gpt-4o |
chat gpt-4o |
修正済み |
| ツール名属性 | gen_ai.agent.tool.name |
gen_ai.tool.name |
修正済み |
| カスタム属性 | gen_ai.agent.step.* |
予約済みの名前空間 | 移動先は gen_ai.toad_eye.* |
| エージェントの識別情報 | 存在しませんでした | gen_ai.agent.name |
追加済み |
仕様を超えて構築したもの
| 機能 | 名前空間 | なぜOTelに含まれていないのか |
|---|---|---|
| リクエストあたりのコスト | gen_ai.toad_eye.cost |
価格設定はベンダー依存です |
| 予算ガード | gen_ai.toad_eye.budget.* |
ランタイムの強制は観測性とは同等ではない |
| 影のガードレール | gen_ai.toad_eye.guard.* |
検証はアプリケーションレベルです |
| 意味論的ずれ | gen_ai.toad_eye.semantic_drift |
品質指標であり、トレース標準ではない |
| ReAct のステップ追跡 | gen_ai.toad_eye.agent.step.* |
ReAct は一つのパターンです;仕様はパターンに依存しません |
重要な洞察: OTel 規格は何が起こったかをカバーします。私たちはなぜ起こったのかとどれくらいの量をカバーします。 競争しているわけではなく、補完的です。あなたのカスタム指標はあなたの名前空間の下に格納します。規格の属性はバックエンドが期待する場所に配置されます。
移行: デュアル出力、ユーザーを壊さない
私たちはクリーンな分断を行いませんでした。 v2.4 は古い属性名と新しい属性名の両方を出力します:
// New (OTel spec-compliant)
span.setAttribute(\"gen_ai.tool.name\", toolName);
// Old (deprecated, still emitted for backward compat)
span.setAttribute(\"gen_ai.agent.tool.name\", toolName);

デュアルエミットアプローチ: 古い属性は @deprecated、新しい属性は仕様に従います。v3 まで両方が出力されます。
deprecated 属性の出力をいつ止めるかは、環境変数で制御します:
OTEL_SEMCONV_STABILITY_OPT_IN=gen_ai_latest_experimental
これは4つのPR(プルリクエスト)でした(#170, #171, #172, #173). v3 では非推奨の別名を完全に削除します。
皮肉
これらを実装している最中、私たちは手動のテストを一連のテストを行いました。
結局、私たちのトレースは全くエクスポートされませんでした。決して。OTel NodeSDK は spanProcessors: [] を渡すとトレースのエクスポートを静かに無効にします。私たちは252件のテストを通過しました。すべて SDK をモックしていました。
つまり、誰にも見られないトレースの属性を完全に標準化しました。
両方を修正しました。1日で6つのパッチバージョンを公開しました。記事 #2 に全文があります。
この機能を実際にサポートしているバックエンド
これが重要な理由です。今日、適切な属性を出力すると、明日6つのバックエンドがあなたのトレースを可視化します:
| バックエンド | GenAI スパンを認識 | エージェントの可視化 | コスト |
|---|---|---|---|
| Jaeger | 基本(ネストされたスパン) | 階層ビュー | 無料 |
| Arize Phoenix | 完全な GenAI UI | エージェント ワークフロー | 無料プラン |
| SigNoz | GenAI ダッシュボード | ネストされたスパン | 無料 / クラウド |
| Datadog | LLM 可観測性 | エージェントのトレース | 有料 |
| Langfuse | 完全な GenAI UI | セッション表示 | 無料プラン |
| Grafana + Tempo | 属性でのクエリ | カスタムダッシュボード | 無料 |
ベンダーロックインなし。1セットの属性。6つの可視化先。
今日すべきこと
LLM 呼び出しをトレースしている場合—カスタムコードを使っていても—現時点で仕様に合わせておくと、後での苦労を減らせます。規約は実験的ですが、方向性は確定しています。
クイックチェックリスト:
- すべての LLM スパンに
gen_ai.operation.nameを設定します:chat、invoke_agent、またはexecute_tool - スパン名を
{operation} {model_or_agent_name}の形式にします - 公式属性を使用します:
gen_ai.agent.name、gen_ai.tool.name、gen_ai.tool.type - あなたのカスタム属性を あなたの名前空間の下に置きます —
gen_ai.*ではなく - デフォルトではプロンプト/完了を記録しないでください — オプトインにしてください
- 少なくとも 2 つのバックエンドでトレースをテストしてください(Jaeger + Phoenix のような GenAI 専用のものを 1 つ)
完全仕様: OpenTelemetry GenAI セマンティック規約
エージェント・スパン: GenAI エージェント・スパン
前の記事:
toad-eye — オープンソースの LLM 観測性、OTel-native: GitHub · npm
️
