OpenTelemetryによるAI Agentの監視

こんにちは、エンジニアのkazukitashです。

今回は OpenTelemetryによるAI Agentの監視について紹介します。

AI Agentを運用すると、トークン使用量の急増、レスポンス低下、意図しないモデル呼び出しの連鎖など、従来のログやメトリクスでは捉えにくい課題に直面します。Legalscapeでも独自に監視していますが、標準化されていないためツール連携や運用コストに課題があります。

そこで注目しているのが、2024年から始まったOpenTelemetryの「Generative AI Observability」プロジェクトです。LLM呼び出しをSpan/Event/Metricsとして標準化し、AWS/Azure/OpenAIなどに対応した仕様を策定中で、将来の業界標準になり得ると期待されています。

この記事では、この仕様の解説と、Python(LangChain/LangGraph)での実装例、さらにJaeger/Prometheusによる可視化までを紹介します。

OpenTelemetry の GenAI 標準化の動向

OpenTelemetryとは何か

OpenTelemetryは、CNCF(Cloud Native Computing Foundation)が主導する、アプリケーションの観測性を実現するためのオープンソース標準です。トレース、メトリクス、ログという3つのシグナルを統一的に扱い、異なるベンダーのツール間でデータを相互運用できるようにします。

OpenTelemetryは従来の観測とは異なるモデル呼び出しのコストなどGenAI特有の観測量に対応するため、2024年から「Generative AI Observability」プロジェクトを立ち上げ、GenAI向けのSemantic Conventionsを策定しています。

GenAI Semantic Conventionsの構造

GenAI Semantic Conventionsは、4種類のシグナルで構成されています。

  1. Traces(トレース):LLM呼び出しのライフサイクルを追跡。リクエストからレスポンスまでの時間、エラーの発生箇所、複数モデル呼び出しの依存関係を可視化
  2. Metrics(メトリクス):トークン使用量、レイテンシ、スループットなどの集計データを記録。コスト管理やパフォーマンス分析に使用
  3. Events(イベント):プロンプトやレスポンスの詳細な内容を記録。デバッグや品質評価に活用
  4. Spans(スパン):モデル操作(Model spans)とエージェント操作(Agent spans)を区別し、それぞれに適した属性を定義

これらのシグナルはgen_ai.*プレフィックスで統一された属性体系になっています。それによりOpenAI、AWS Bedrock、Google Vertex AIなどのベンダー間の互換性を確保しデータ分析の継続性を保てます。

主要な属性

主要な属性は以下の通りです。

属性名 説明
gen_ai.provider.name モデル提供元 openai, aws.bedrock, gcp.vertex_ai
gen_ai.request.model リクエストしたモデル名 gpt-4, claude-3-opus
gen_ai.operation.name 操作タイプ chat, text_completion, embedding
gen_ai.usage.input_tokens 入力トークン数 150
gen_ai.usage.output_tokens 出力トークン数 430
gen_ai.request.temperature 温度パラメータ 0.7

なお、まだDevelopment段階であるので、これらの属性の最新版を使う場合はOTEL_SEMCONV_STABILITY_OPT_IN=gen_ai_latest_experimental環境変数でオプトインする必要があります。

策定のタイムライン

2023年の9月にissueとして提案されてから2年をかけて、実用可能な規約に成長してきています。

時期 ver. 主な変更
2023.9 - issue #327でLLM観測性向上の提案
2024.5 v1.26.0 初登場(gen_ai.systemなど)
2024.8 v1.27.0 トークン数属性を整理、メトリクス導入
2024.10 v1.28.0 プロンプト/応答をイベント方式に変更
2025.3 v1.31.0 AIエージェント用規約を導入
2025.8 v1.37.0 チャット履歴をgen_ai.input.messages/gen_ai.output.messagesに統一

この標準化の背景には、OpenLLMetryというコミュニティ主導の取り組みがあります。Traceloopが推進するOpenLLMetryは、OpenTelemetry上でLLM向けのセマンティック拡張を提案しており、多くの規約がOTel本体に統合されてきました。

OpenLLMetryからOTelへの統合状況

代表的な属性の統合状況をいくつか。

OpenLLMetryの提案 OTelでの採用 ステータス
llm.vendor gen_ai.provider.name 統合済み
llm.usage.prompt_tokens gen_ai.usage.input_tokens 統合済み
llm.usage.completion_tokens gen_ai.usage.output_tokens 統合済み
llm.temperature, llm.top_p gen_ai.request.temperature, gen_ai.request.top_p 統合済み
traceloop.span.kind (workflow/task/agent) - 議論中(#2664, #2665
ベクトルDB属性 - 未定

traceloop.span.kind は、エージェント/タスク/アクションの区別を行うための属性です。OTelへの統合が楽しみですね。

自動計装の対応状況

Development段階ということもあり自動計装の対応についてはばらつきがあります。ライブラリ実装はOpenTelemetry公式標準化だけでなくOpenLLMetry@traceloopなど別プロジェクト主導で行われているものも多いのが現状です。

サービス ライブラリ コミュニティ
OpenAI opentelemetry-instrumentation-openai-v2 OpenTelemetry
Gemini opentelemetry-instrumentation-google-generativeai OpenLLMetry
Claude opentelemetry-instrumentation-anthoropic OpenLLMetry
LlamaIndex openinference-instrumentation-llama-index OpenInference
Langchain/Langgraph opentelemetry-instrumentation-langchain OpenLLMetry

今後の展望

議論中の領域として、以下のような提案があります。

  • エージェント/タスク/アクション属性issue #2664でタスク・アクション・エージェント・チーム・アーティファクト・メモリの標準化を提案
  • 外部ストレージ参照issue #2753で長文プロンプトをS3/GCSに保存し参照する仕組みを議論
  • Reasoning/Chain-of-ThoughtPR #2797でモデルの思考過程を区別する提案

これらの議論は、OTel LLM Semantic Conventions WGで行われているようです。標準化の進展を追いたい場合は、このWGの動向をウォッチすると良いと思います。

実装例

実際にOpenTelemetryのGenAI規約を使ったシステムを構築してみます。Python+LangChain/LangGraphでチャットエージェントを実装し、Jaeger/Prometheusで可視化するまでの流れを紹介します。

環境構築

まず、必要なパッケージをインストールします。opentelemetry-instrumentation-langchainを使うことで、LangChain/LangGraphのコードを自動計装できます。

dependencies = [
    "langchain>=0.1.0",
    "langgraph>=0.2.0",
    "opentelemetry-api>=1.37,<1.38",
    "opentelemetry-sdk>=1.37,<1.38",
    "opentelemetry-instrumentation-langchain>=0.18b0",
    "opentelemetry-semantic-conventions>=0.58b0",
    "opentelemetry-exporter-otlp>=1.37.0",
]

Jaeger、Prometheusはdocker-composeでまとめて起動します。

services:
  jaeger:
    image: jaegertracing/all-in-one:latest
    ports:
      - "16686:16686" # Jaeger UI
      - "4318:4318" # OTLP HTTP receiver
    environment:
      - COLLECTOR_OTLP_ENABLED=true

  prometheus:
    image: prom/prometheus:latest
    ports:
      - "9090:9090"
    volumes:
      - ./monitoring/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml

OpenTelemetryの設定

次に、OpenTelemetry SDKを初期化します。LangchainInstrumentorを呼び出すだけで、LangChain/LangGraphの全ての操作が自動的に計装されるので、非常に簡単です。

from opentelemetry.instrumentation.langchain import LangchainInstrumentor

# OpenTelemetry SDK初期化(TracerProvider、MeterProvider、Exporterの設定)
# ... (省略)

# LangChainの自動計装
LangchainInstrumentor().instrument(
    capture_message_content=os.getenv(
        "OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT", "false"
    ).lower() == "true",
    enrich_spans=True,
    capture_provider_info=os.getenv(
        "OTEL_INSTRUMENTATION_GENAI_CAPTURE_REQUEST_ATTRIBUTES", "false"
    ).lower() == "true",
)

capture_message_contenttrueにすると、プロンプトとレスポンスの内容をEventとして記録します。本番環境では個人情報保護のためfalse推奨ですが、開発環境ではデバッグに便利です。

計装の実装

エージェント側のコードは通常通りLangGraphで実装するだけで、自動的にgen_ai.*属性が付与されます。

from langgraph.graph import StateGraph, MessagesState
from langchain_openai import ChatOpenAI

# LLMインスタンス
llm = ChatOpenAI(model="gpt-4")

# エージェントの定義(通常通り実装)
def chat_agent(state: MessagesState) -> dict:
    response = llm.invoke(state["messages"])
    return {"messages": [response]}

# LangGraphのグラフ構築
graph = StateGraph(MessagesState)
graph.add_node("agent", chat_agent)
# ... (省略)

このコードを実行すると、LangchainInstrumentorが裏で動作し、以下のような処理を自動実行します。

  • gen_ai.operation.name=chatを持つSpanを生成
  • gen_ai.request.modelgen_ai.response.modelにモデル名を記録
  • gen_ai.usage.input_tokensgen_ai.usage.output_tokensにトークン数を記録
  • gen_ai_client_operation_duration_seconds_countなどのメトリクスを自動生成

基本的なメトリクスについては手動で実装する必要はなく、自動的に収集してくれます。

可視化

トレースとメトリクスは、それぞれJaeger、Prometheusで確認できます。

Jaeger Trace

Jaeger UIでは、gen_ai.request.temperaturegen_ai.request.modelgen_ai.usage.*などの属性がSpanに記録され、LLM呼び出しの詳細を追跡できます。複数のモデル呼び出しがある場合も、親子関係が可視化されるため、どこで時間がかかっているのか一目で分かります。

Prometheus Metrics

Prometheusでは、gen_ai_client_operation_duration_seconds_countなどのメトリクスが自動記録されます。この例では、過去5分間のLLM呼び出し頻度をrate()関数で集計しています。トークン使用量やレイテンシの時系列変化を可視化すれば、パフォーマンス分析やコスト予測が可能になります。

おわりに

OpenTelemetryのGenAI Semantic Conventionsは、まだDevelopment段階ではありますが、LLM運用の標準化に向けた大きな一歩です。ベンダーロックインを避け、複数のツールやプラットフォーム間でデータを共有できるようになれば、AI Agent開発の生産性を向上させると考えています。

Legalscapeでは、この記事で紹介したような最新の技術動向を継続的に追いかけ、実際のプロダクトに取り入れる試みを続けています。そういった試行錯誤を楽しめるエンジニアと一緒に働きたいと考えています。AI Agentの監視はまだ始まったばかりの領域です。もしこの記事を読んで、最先端の技術を追いかけながらプロダクトを作ることに興味を持っていただけたなら、ぜひLegalscapeで一緒に挑戦してみませんか。