AIエージェントの監視は、APIの成功率やレスポンスタイムを見るだけでは足りません。どのモデルを呼んだか、どのツールを実行したか、検索結果をどう使ったか、途中で人間承認に止まったか、トークン消費がどの段階で膨らんだかまで追える必要があります。
結論から言うと、最初に決めるべきは「監視ツール名」ではなく「証跡の標準化」です。ベンダーロックインを避けたいチームはOpenTelemetryを土台にし、LangChain系の開発体験を重視するならLangSmith、OSSで手元検証や評価まで回したいならPhoenixを組み合わせるのが現実的です。
この記事では、OpenTelemetry、LangSmith、PhoenixをAIエージェント監視の観点で比較し、実装時に最低限入れるべきトレース設計、コード例、失敗パターンをまとめます。ベンチマーク風の架空数字は置かず、公式ドキュメントで確認できる機能と設計判断に絞ります。
この記事で参照した一次情報:OpenTelemetryのGenAI Semantic Conventionsは記事執筆時点でDevelopment扱いで、GenAIのイベント、例外、メトリクス、モデルspan、agent span、MCP向けsemantic conventionsを分けて定義しています。LangSmithはLLMアプリやAIエージェントのtrace、評価、prompt testingを扱うobservability製品として案内されています。PhoenixはOpenTelemetryを使ってAIアプリをtraceし、LlamaIndex、LangChain、OpenAIなどとの統合を持つOSS系の観測基盤として説明されています。
1. AIエージェント監視で最初に見るべきもの
従来のWebアプリ監視では、HTTPリクエスト、DBクエリ、キュー、外部API呼び出しを追えれば多くの障害を説明できます。AIエージェントでは、そこに「モデルの判断」と「ツール実行」が加わります。しかも、同じユーザー入力でも検索結果、プロンプト、コンテキスト量、モデルのバージョン、過去の会話状態で結果が変わります。監視対象が単なる処理時間から、意思決定の経路そのものへ広がるのが最大の違いです。
最低限ほしい証跡は五つあります。ユーザー入力、システム指示、モデル呼び出し、ツール呼び出し、最終出力です。ただし、個人情報や社外秘をそのままtraceへ送ると、監視基盤が新しい漏えい経路になります。入力全文を保存するか、ハッシュ化するか、要約だけにするかは、導入前に決めておく必要があります。
もう一つ重要なのが、成功だけでなく「途中の迷い」を見える化することです。RAGで何件検索したか、どのdocumentを採用したか、tool callが何回リトライされたか、timeout時に代替手段へ切り替えたか。これらが残っていないと、ユーザーから「昨日と違う答えになった」と言われたときに再現できません。
監視の目的は、かっこいいダッシュボードを作ることではありません。障害時に原因を狭める、コスト増の発生箇所を見つける、品質改善の評価データを残す、監査に耐える説明ログを作る。この四つのどれに効くかで、入れるべきツールは変わります。
- 本番障害を早く直したい:OpenTelemetryで既存APMへ接続し、HTTP・DB・LLM・toolを同じtraceに乗せる。
- プロンプトやagentの挙動を開発者が深く見たい:LangSmithのようなLLM特化UIを使う。
- OSSで検証し、評価やデータセット管理も近くに置きたい:Phoenixを候補にする。
- コンプライアンスが重い:保存項目、マスキング、retention、権限管理を先に決める。
2. 比較表:OTel・LangSmith・Phoenix
| 観点 | OpenTelemetry | LangSmith | Phoenix |
|---|---|---|---|
| 位置づけ | 標準化されたtelemetryの共通言語。trace、metrics、logsを既存監視へ流す土台。 | LLMアプリ・AIエージェントのdebug、trace、evaluation、prompt管理に寄った開発運用基盤。 | OpenTelemetryベースでtraceや評価を扱うOSS系のAI observability基盤。 |
| 向いているチーム | すでにDatadog、Grafana、New Relic、Cloud Monitoring等を使っているSRE/Platformチーム。 | LangChain/LangGraph利用が多く、実験、評価、プロンプト改善を一つの画面で回したいチーム。 | ローカル検証、OSS運用、モデルやframeworkをまたいだtrace確認をしたいチーム。 |
| 強い領域 | ベンダー中立、標準化、APM連携、MCPやGenAI semantic conventionsへの追従。 | LLM runの見やすさ、デバッグ体験、評価データとの接続、チームでのprompt改善。 | OTLP受け口、trace可視化、評価、LlamaIndex/LangChain/OpenAI等との統合。 |
| 注意点 | GenAI semantic conventionsはDevelopment項目があるため、属性名の更新に追従する運用が必要。 | LangSmith固有の概念に寄るため、既存APMとの統合範囲やデータ持ち出し要件を確認する。 | PhoenixとArize AXは別製品として扱われるため、導入前に使う製品とAPIを明確にする。 |
| 最初の導入 | アプリの境界にtrace idを入れ、model span、tool span、retrieval spanを手で切る。 | SDKやframework統合でrunを可視化し、失敗例をdataset化する。 | OTLP exporterまたはintegrationでtraceを送り、ローカルで一連のflowを確認する。 |
選定の軸は「どれが一番高機能か」ではありません。既存の監視基盤とつなぐ必要があるならOpenTelemetryが主役です。開発者がpromptとtraceを見ながらagentを磨くならLangSmithが強いです。OSSで手元にtraceと評価の実験場を作りたいならPhoenixが合います。多くの現場では、OpenTelemetryを共通基盤にしつつ、LLM特化UIを用途別に足す構成が扱いやすくなります。
3. OpenTelemetryを土台にする理由
OpenTelemetryは「AI専用の便利ツール」ではなく、telemetryを標準化するための共通仕様と実装群です。AIエージェントの観測でも価値があるのは、Webアプリ、ジョブ、RAG、LLM、MCP tool、外部APIを同じtraceの中でつなげられる点です。ユーザーからの問い合わせ一件が、FastAPI、vector database、LLM provider、社内API、承認待ちqueueへ広がるなら、共通のtrace idがないと全体像を追えません。
OpenTelemetryのGenAI semantic conventionsは、モデル呼び出し、agent/framework span、MCP、token usageなどを扱うための語彙を整理しています。公式ドキュメント上ではDevelopment扱いの項目があり、既存instrumentationでは安定化までの移行方針としてOTEL_SEMCONV_STABILITY_OPT_INが説明されています。ここは重要で、記事やサンプルコードを丸写しして固定属性に依存するより、バージョン更新を前提に薄いadapter層を作る方が安全です。
OpenTelemetryを使うときの実務上のコツは、すべてを一気に自動計測しようとしないことです。まずはagent.run、llm.generate、tool.execute、retrieval.searchの四種類を手でspan化します。次にtoken数、モデル名、tool名、retry回数、timeout、fallback有無を属性として揃えます。最後に必要な部分だけ自動instrumentationを足すと、traceが読みやすいまま育ちます。
特にMCPを使う場合、HTTP一発の監視だけでは足りません。MCPはJSON-RPCを土台にし、stdioやStreamable HTTPなどのtransportで動きます。OpenTelemetryのMCP semantic conventionsでは、HTTP conventionsだけではMCPのmessage exchangeを十分表せないという問題意識が示されています。AIエージェントでMCP serverを増やすなら、tool名、request id、transport、成功/失敗、durationを明示的に残す設計が必要です。
OpenTelemetryを選ぶべきケース
- 既存のSREチームがAPMやログ基盤をすでに運用している。
- モデルproviderやframeworkを将来変える可能性が高い。
- MCP server、社内API、RAG、ジョブqueueを横断して原因分析したい。
- 監査やセキュリティ上、保存項目を自社ルールで厳密に制御したい。
4. LangSmithを選ぶ理由
LangSmithは、LangChainが提供するLLMアプリケーション向けのobservability基盤です。公式ドキュメントでは、AIエージェントやLLMアプリのbuild、debug、deployに関わるplatformとして説明され、request tracing、output evaluation、prompt testing、deployment管理などが並んでいます。OpenTelemetryが共通語彙なら、LangSmithは開発者の作業画面に近い道具です。
LangSmithが向いているのは、「なぜこのagentは変なtoolを呼んだのか」「このprompt変更で品質は上がったのか」「失敗例だけを集めて評価セットにしたい」といった、LLMアプリ特有の改善サイクルです。一般的なAPMでもdurationやerrorは追えますが、prompt、intermediate step、evaluationを同じ文脈で見るにはLLM特化UIの方が速い場面があります。
一方で、すべてをLangSmithだけに寄せるのは慎重に判断した方がいいです。本番全体の障害対応では、DB、cache、外部API、queue、infra metricsも一緒に見たいからです。LangSmithでLLM runを深掘りし、OpenTelemetryで全体traceへつなぐ、という分担にすると、開発者とSREの両方が同じincidentを別角度から見られます。
LangSmith導入時に先に決めるべきなのは、保存する入力・出力の粒度です。顧客データを含むpromptやtool結果をそのまま送るのか、PIIをマスクするのか、失敗runだけ保存するのか。ここを曖昧にすると、デバッグは便利でもセキュリティレビューで止まります。便利さより先にデータ分類を決めるのが本番導入の近道です。
LangSmithを選ぶべきケース
- LangChainまたはLangGraphを主要frameworkとして使っている。
- prompt変更、evaluation、dataset化を開発チーム内で高速に回したい。
- LLM runの入力、出力、tool stepを非SREメンバーにも見せたい。
- 本番監視というより、開発・検証・品質改善の速度を上げたい。
5. Phoenixを選ぶ理由
Phoenixは、Arizeが公開しているOSS系のAI observability platformです。公式ドキュメントではOpenTelemetryを通じてAI applicationsをtraceし、LlamaIndex、LangChain、OpenAIなどとのfirst-class integrationsを持つと説明されています。LLM tracingは、retrieval、embedding generation、model invocation、response generationのような処理の流れをtimelineとして残すものです。
Phoenixの強みは、OpenTelemetryに寄せながらLLMアプリ向けの画面と評価機能を持てることです。ローカルや検証環境でagentのtraceを見たい、OSSで始めたい、特定ベンダーに寄せ切る前に観測設計を試したい、といった段階で相性が良いです。開発初期の「まず見える化したい」に対して、導入の心理的ハードルが低いのも利点です。
注意点は、PhoenixとArize AXを混同しないことです。Phoenixのドキュメント自体にも、PhoenixとArize AXは別のobservability productとして案内されています。チーム内で「Arizeを入れる」とだけ言うと、OSSのPhoenixなのか、商用のAXなのか、設定項目や認証が食い違います。導入メモには製品名、endpoint、認証方式を明記しましょう。
PhoenixはOpenTelemetry Protocolでtraceを受けられるため、将来的に別backendへ流す設計とも相性があります。ただし、OSS運用ではアップデート、保管容量、認証、ネットワーク境界、backupを自分たちで見る必要があります。小さく始められることと、運用責任が消えることは別です。
Phoenixを選ぶべきケース
- OSSでLLM traceを試したい、または検証環境を自社内に閉じたい。
- LangChain、LlamaIndex、OpenAIなど複数の経路を同じ画面で見たい。
- 評価やprompt experimentationも近い場所で扱いたい。
- 将来はOpenTelemetry互換の別backendへ流す可能性がある。
6. 実装パターン:まず3つだけ入れる
ここからは、ツール選定より先に入れるべき最小実装を示します。コードは概念を伝えるためのサンプルです。実際のprovider SDK、認証、マスキング、例外処理は自社のruntimeに合わせて調整してください。
パターンA:OpenTelemetryでagent runを囲む
from opentelemetry import trace
from opentelemetry.trace import Status, StatusCode
tracer = trace.get_tracer("agentlab.order_agent")
async def run_agent(user_id: str, question: str):
with tracer.start_as_current_span("agent.run") as span:
span.set_attribute("app.agent.name", "order-support-agent")
span.set_attribute("app.user.hash", hash_user(user_id))
span.set_attribute("app.input.length", len(question))
try:
docs = await retrieve_documents(question)
answer = await call_model(question, docs)
span.set_attribute("app.retrieval.count", len(docs))
span.set_attribute("app.output.length", len(answer))
return answer
except Exception as exc:
span.record_exception(exc)
span.set_status(Status(StatusCode.ERROR, str(exc)))
raise
最初は公式のGenAI属性をすべて埋めるより、自社で確実に守れる属性から始めます。user_idは直接保存せずhash化し、入力全文ではなく長さや分類だけを残す設計にしています。ここにtrace idが乗れば、Web APIからLLM呼び出し、tool実行までを一つの流れで追えます。
パターンB:モデル呼び出しspanを分ける
async def call_model(prompt: str, docs: list[str]):
with tracer.start_as_current_span("llm.generate") as span:
span.set_attribute("gen_ai.operation.name", "chat")
span.set_attribute("gen_ai.provider.name", "openai")
span.set_attribute("gen_ai.request.model", "gpt-4.1-mini")
span.set_attribute("gen_ai.request.stream", False)
span.set_attribute("app.prompt.template", "support_answer_v3")
span.set_attribute("app.context.doc_count", len(docs))
response = await llm_client.responses_create(
model="gpt-4.1-mini",
input=build_messages(prompt, docs),
)
usage = getattr(response, "usage", None)
if usage:
span.set_attribute("gen_ai.usage.input_tokens", usage.input_tokens)
span.set_attribute("gen_ai.usage.output_tokens", usage.output_tokens)
return response.output_text
OpenTelemetryのGenAI conventionsでは、gen_ai.operation.name、gen_ai.request.model、gen_ai.response.model、gen_ai.usage.input_tokens、gen_ai.usage.output_tokensなどの属性が定義されています。Development扱いの仕様を含むため、SDKやcollectorのバージョンで差分が出る可能性はあります。それでも、model名とtoken usageをspanへ寄せるだけで、コスト増や遅延の原因はかなり追いやすくなります。
パターンC:tool実行とリトライを見える化する
async def execute_tool(tool_name: str, payload: dict):
with tracer.start_as_current_span("tool.execute") as span:
span.set_attribute("gen_ai.tool.name", tool_name)
span.set_attribute("app.tool.payload_keys", sorted(payload.keys()))
for attempt in range(1, 4):
span.set_attribute("app.tool.attempt", attempt)
try:
result = await tool_registry[tool_name](payload)
span.set_attribute("app.tool.success", True)
span.set_attribute("app.tool.result_size", len(str(result)))
return result
except TimeoutError as exc:
span.add_event("tool.timeout", {"attempt": attempt})
if attempt == 3:
span.record_exception(exc)
span.set_attribute("app.tool.success", False)
raise
AIエージェントで事故りやすいのはtool実行です。モデルの返答が正しくても、toolのpayloadが古いschemaだったり、外部APIがtimeoutしたり、同じ操作を二重実行したりします。retry回数、idempotency key、timeout、fallbackをspanに残すと、品質問題とシステム障害を切り分けやすくなります。
パターンD:LangSmithやPhoenixへ送る前にマスキングする
import re
EMAIL = re.compile(r"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}")
PHONE = re.compile(r"0\d{1,4}-?\d{1,4}-?\d{3,4}")
def redact_text(text: str) -> str:
text = EMAIL.sub("[REDACTED_EMAIL]", text)
text = PHONE.sub("[REDACTED_PHONE]", text)
return text[:4000]
def safe_trace_payload(messages: list[dict]) -> list[dict]:
safe = []
for message in messages:
safe.append({
"role": message.get("role"),
"content": redact_text(str(message.get("content", ""))),
})
return safe
LLM特化ツールは便利ですが、promptとtool resultを見やすく保存できるぶん、情報管理の責任も増えます。まずマスキング関数を通し、保存上限を決め、全文保存が必要なrunだけ明示的にopt-inする設計にしましょう。監視のために秘密情報を増やすのは本末転倒です。
7. 選び方:3つの質問で決める
監視ツール選定で迷ったら、次の三つを順番に確認します。第一に、既存のAPMやSRE運用に接続する必要があるか。必要ならOpenTelemetryを外す理由はほぼありません。第二に、LLM runの中身を開発者が毎日見るか。見るならLangSmithやPhoenixのようなLLM特化UIが効きます。第三に、データをどこへ保存できるか。ここが決まらないままSaaSを入れると、後から止まります。
- 既存監視への統合が最優先なら、OpenTelemetryを基準にする。特定UIは後から足す。
- LangChain/LangGraph中心で、評価とprompt改善まで一気通貫にしたいなら、LangSmithを検討する。
- OSS・ローカル検証・OTLP互換を重視するなら、Phoenixで小さく始める。
- 本番で個人情報や顧客データを扱うなら、ツール選定より先にredactionとretentionを決める。
- MCP serverや社内toolが増える予定なら、tool spanとtrace context propagationを設計に入れる。
おすすめ構成は、開発初期はPhoenixまたはLangSmithでagentの挙動を可視化し、本番化の段階でOpenTelemetryを必ず挟む形です。こうしておくと、LLM専用画面の便利さを使いながら、システム全体の監視は標準的なtelemetryに寄せられます。
8. 失敗パターンと回避策
失敗1:traceを入れたのに原因が分からない
span名が全部「openai.call」や「chain.run」になっていると、画面は埋まっても分析できません。agent.run、retrieval.search、llm.generate、tool.execute、人間承認、fallbackなど、業務上の意味が分かる単位でspanを切りましょう。
失敗2:入力全文を保存してセキュリティで止まる
監視基盤はログ基盤と同じく、権限管理と保持期限が必要です。顧客名、メール、電話番号、契約情報、社内URL、API responseを保存するなら、マスキング、暗号化、閲覧権限、削除手順を先に用意します。
失敗3:token usageだけ見て品質を見ない
コスト監視は大事ですが、token数だけを最適化すると回答品質が落ちます。失敗run、ユーザー再質問、tool error、評価スコアを一緒に見ることで、安いが使えないagentを作る事故を避けられます。
失敗4:開発環境と本番環境のtrace設計が違う
開発ではLangSmith、本番では別APM、検証ではPhoenixという構成自体は問題ありません。ただしspan名、run id、user hash、tool名、prompt versionのルールが違うと比較できません。共通schemaをREADMEに書き、CIで最低限の属性を検査すると安定します。
失敗5:MCP toolをHTTP監視だけで済ませる
MCPはmessage exchangeの粒度が重要です。HTTP requestが200でも、内部のJSON-RPC tool callが失敗していることがあります。tool名、request id、paramsの分類、result size、error codeをdomain-specificに残す必要があります。
監視の失敗は、ツール選定よりも「何を記録するか」を決めていないことから起きます。最初の一週間で全機能を入れる必要はありません。まず四種類のspanと、十個前後の共通属性を定めるだけで、トラブルシュートの質は大きく変わります。
9. 導入チェックリスト
- agent.run、llm.generate、tool.execute、retrieval.searchのspan名を固定したか。
- prompt template version、model、token usage、tool name、retry countを取っているか。
- 入力・出力・tool resultの保存可否をデータ分類ごとに決めたか。
- OpenTelemetryのGenAI conventionsがDevelopment扱いであることを前提に、属性名変更に備えたadapterを置いたか。
- LangSmithやPhoenixへ送るデータにPIIが混ざらないようredactionを通したか。
- MCP serverを使う場合、tool call単位のtraceとerrorを残しているか。
- 失敗runを評価datasetへ回す運用を決めたか。
- 監視画面を見る人、incident時に判断する人、データ削除を担当する人を決めたか。
ここまで決めると、AIエージェント監視は「ツールを入れた」で終わらず、改善サイクルに乗ります。観測できるrunは評価でき、評価できるrunは改善できます。逆に、観測できないagentは、本番でユーザーから違和感を指摘されるまで問題に気づけません。
10. 公式情報・参考リンク
- OpenTelemetry GenAI Semantic Conventions
- OpenTelemetry GenAI spans
- OpenTelemetry MCP semantic conventions
- LangSmith Observability docs
- Phoenix tracing overview
- Phoenix tracing quickstart
なお、各サービスの料金、保存期間、細かなSDK仕様は変わります。この記事では固定の料金比較や非公開ベンチマーク風の数字は出していません。導入時は必ず公式ドキュメントと契約条件を確認してください。
関連記事・次に読む
監視設計は、コンテキスト設計、信頼性設計、ストリーミング応答の設計とセットで考えると破綻しにくくなります。特に長い会話履歴やRAGを扱うagentでは、何をcontextへ入れ、何をtraceへ残し、何を保存しないかを同時に決めるのが重要です。
まとめ
AIエージェント監視では、OpenTelemetry、LangSmith、Phoenixのどれか一つを選べば終わりではありません。OpenTelemetryは共通の観測基盤、LangSmithはLLM開発体験、PhoenixはOSS寄りのAI observabilityとして、それぞれ得意領域が違います。
まずはOpenTelemetry的なspan設計でagent.run、llm.generate、tool.execute、retrieval.searchを分け、model、token、tool、retry、errorを残しましょう。その上で、開発チームが毎日見るUIとしてLangSmithやPhoenixを選ぶと、導入後の運用がぶれにくくなります。
本番導入で最も大事なのは、traceを増やすことではなく、説明できるagentにすることです。ユーザーの一件の依頼に対して、どの情報を参照し、どのモデルが、どのtoolを、何回使い、どこで失敗したのかを追える状態を作る。これがAIエージェント監視の出発点です。
11. 小さく始める導入手順
最初の一日でやることは、agentの全機能を監視することではなく、代表的な一経路を最後まで追えるようにすることです。問い合わせ受付、検索、LLM生成、tool実行、回答返却までのhappy pathにtrace idを通します。この時点ではUIの美しさより、各spanに同じrun idが入っているかを確認します。
次に、失敗しやすい経路を一つ足します。検索結果がゼロ件だった場合、toolがtimeoutした場合、モデルがtool callを要求したがschema validationに落ちた場合などです。失敗経路のtraceが読めるようになると、運用メンバーはincident時にログを手探りしなくて済みます。
三日目以降に、評価と改善へつなげます。失敗runをdataset化し、prompt versionやmodel versionごとに比較できるようにします。LangSmithやPhoenixを使う場合も、この段階で「失敗例を集めて次の実験に回す」導線を作ると、監視が単なる後追いではなく開発速度を上げる仕組みになります。
最後に、定例レビューの指標を絞ります。平均レイテンシ、p95、error率、tool retry率、入力token、出力token、ユーザー再質問率、評価NG率のように、行動につながる指標だけを見るべきです。見ても誰も直さないグラフは、監視ではなく壁紙です。
この記事を読んで導入イメージが固まってきた方へ
UravationではAIエージェント導入の研修・コンサルを行っています。
