OpenTelemetry LLMオブザーバビリティ実践:2026年AIアプリケーションのエンドツーエンド監視ガイド

DevOps

2026年、なぜLLMアプリケーションに専門的なオブザーバビリティが必要なのか

従来のアプリケーション監視はレイテンシ、スループット、エラー率に焦点を当てています。LLMアプリケーションは全く新しいオブザーバビリティ次元を導入します:トークン使用量、API呼び出しコスト、ハルシネーション率、コンテキスト品質

次元 従来の監視 LLM監視
レイテンシ リクエストレイテンシ 最初のトークンまでの時間(TTFT)、合計レイテンシ
スループット QPS トークン/秒
エラー エラー率 エラー率 + ハルシネーション率
コスト サーバーコスト API呼び出しコスト(トークン課金)
品質 N/A 精度、関連性、完全性
コンテキスト N/A プロンプト長、コンテキストウィンドウ利用率

OpenTelemetry LLMセマンティック規約

Span: gen_ai.client.chat
  Attributes:
    gen_ai.system              = "openai"
    gen_ai.request.model       = "gpt-4o"
    gen_ai.usage.input_tokens  = 1523
    gen_ai.usage.output_tokens = 847
    gen_ai.usage.total_tokens  = 2370

Python実装:LLM呼び出しのトレース

pip install opentelemetry-api opentelemetry-sdk \
  opentelemetry-exporter-otlp openai
from opentelemetry import trace, metrics
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter

trace_provider = TracerProvider()
trace_provider.add_span_processor(
    BatchSpanProcessor(OTLPSpanExporter(endpoint="http://localhost:4317", insecure=True))
)
trace.set_tracer_provider(trace_provider)
tracer = trace.get_tracer("llm-app", "1.0.0")
meter = metrics.get_meter("llm-app", "1.0.0")

LLM呼び出しの手動トレース

import openai, time
from opentelemetry.trace import Status, StatusCode

client = openai.OpenAI(api_key="sk-xxx")
token_counter = meter.create_counter("gen_ai.client.token.usage", unit="tokens")
llm_latency = meter.create_histogram("gen_ai.client.operation.latency", unit="ms")

def traced_chat_completion(messages, model="gpt-4o", temperature=0.7, max_tokens=4096):
    with tracer.start_as_current_span("gen_ai.client.chat") as span:
        span.set_attribute("gen_ai.system", "openai")
        span.set_attribute("gen_ai.request.model", model)
        start_time = time.time()
        try:
            response = client.chat.completions.create(
                model=model, messages=messages,
                temperature=temperature, max_tokens=max_tokens
            )
            content = response.choices[0].message.content
            usage = {
                "prompt_tokens": response.usage.prompt_tokens,
                "completion_tokens": response.usage.completion_tokens,
                "total_tokens": response.usage.total_tokens
            }
            latency_ms = (time.time() - start_time) * 1000
            span.set_attribute("gen_ai.usage.input_tokens", usage["prompt_tokens"])
            span.set_attribute("gen_ai.usage.output_tokens", usage["completion_tokens"])
            span.set_status(Status(StatusCode.OK))
            token_counter.add(usage["prompt_tokens"], {"gen_ai.token.type": "input", "gen_ai.request.model": model})
            token_counter.add(usage["completion_tokens"], {"gen_ai.token.type": "output", "gen_ai.request.model": model})
            llm_latency.record(latency_ms, {"gen_ai.request.model": model})
            return {"content": content, "usage": usage, "latency_ms": latency_ms}
        except Exception as e:
            span.set_status(Status(StatusCode.ERROR, str(e)))
            raise

コスト監視

COST_PER_TOKEN = {
    "gpt-4o": {"input": 0.000005, "output": 0.000015},
    "gpt-4o-mini": {"input": 0.00000015, "output": 0.0000006},
}

def calculate_cost(model, input_tokens, output_tokens):
    rates = COST_PER_TOKEN.get(model, {"input": 0, "output": 0})
    return (input_tokens * rates["input"]) + (output_tokens * rates["output"])

品質監視:ハルシネーション検出

def evaluate_answer_quality(question, answer, context):
    with tracer.start_as_current_span("llm.quality.evaluation") as span:
        eval_prompt = [
            {"role": "system", "content": "回答品質を評価。JSONで返信:{grounded, relevance, completeness}"},
            {"role": "user", "content": f"質問:{question}\n参考:{context}\n回答:{answer}"}
        ]
        result = traced_chat_completion(eval_prompt, model="gpt-4o-mini", temperature=0)
        import json
        evaluation = json.loads(result["content"])
        span.set_attribute("llm.quality.grounded", evaluation["grounded"])
        return evaluation

LLMオブザーバビリティツール比較

ツール OpenTelemetry トークン追跡 コスト監視 品質監視 セルフホスト
OTel + Grafana ネイティブ カスタム カスタム カスタム はい
Langfuse 統合 ネイティブ ネイティブ ネイティブ はい
Helicone プロキシ ネイティブ ネイティブ 基本 いいえ
Arize Phoenix 統合 ネイティブ ネイティブ ネイティブ はい

5つのよくある落とし穴

1. ストリーミングレスポンスのトークン計数の無視

ストリーミングレスポンスのusageフィールドが空の場合があります。

2. 不適切なサンプリングレート

高トラフィックLLMアプリで100%サンプリングすると大量のTraceデータが生成されます。

3. プロンプト内容のTraceへの漏洩

完全なプロンプトをSpan属性に記録しないでください。

4. モデルバージョンの未区別

同じモデル名が異なるバージョンに対応する場合があります。

5. エンドツーエンドのSpan相関の欠落

RAGパイプラインのSpanはparent spanで関連付ける必要があります。


10のエラートラブルシューティング

# 症状 原因 解決方法
1 TraceがGrafanaに表示されない OTLP Exporter設定エラー endpointと認証を確認
2 トークン計数がゼロ ストリーミングでusage未取得 最後のchunkで取得
3 コスト計算が不正確 価格表が古い COST_PER_TOKENを更新
4 ハルシネーション率が異常に高い RAG検索品質が低い ベクトルインデックスを確認
5 Span属性が欠落 セマンティック規約バージョン不一致 OTel SDKを更新
6 メモリリーク TracerProviderが未クローズ 終了時にshutdown()を呼び出し
7 重要なTraceがサンプリングで消失 サンプリングレートが低すぎる エラーは100%サンプリング
8 TTFTが異常に高い ネットワーク遅延またはコールドスタート ネットワークとモデルウォームアップを確認
9 品質評価自体がハルシネーション 評価モデルが信頼できない より強力な評価モデルを使用
10 MetricとTraceが不一致 タイムゾーン不一致 UTCを統一使用

おすすめツール


まとめ:LLMアプリケーションのオブザーバビリティは、トークン使用量、APIコスト、ハルシネーション率、回答品質という従来の指標を超える次元に注目する必要があります。OpenTelemetryのLLMセマンティック規約が2026年の標準化アプローチを提供します。Grafanaダッシュボードと組み合わせることで、コストから品質まで包括的なLLMアプリケーション監視を実現できます。

ブラウザローカルツールを無料で試す →

#OpenTelemetry#LLM可观测性#AI监控#Token追踪#Grafana#RAG监控#成本监控#质量监控