本記事はAgentOps連載の第3弾です。第1弾(入門・ライフサイクル概要)で「エージェント運用(AgentOps)とは何か」の全体像を、第2弾(評価・ゴールデンデータセット設計)で「何で品質を測るか」を固めました。今回はその続きとして、本番で”今まさに何が起きているか”を見え続けさせる——すなわちオブザーバビリティ(Observability/可観測性)の設計に踏み込みます。
はじめに——「作れる」の次に来る壁は「見え続ける」
エージェントは、作った瞬間がゴールではありません。むしろ本番に出してからが本番です。ところが多くの現場で、エージェントの運用は今も「落ちてから調べる」——障害やクレームが起きて初めてログを掘り返す、という後追い型にとどまっています。
第2弾で「何で測るか(評価データセット)」を固めても、それはあくまでオフライン(事前)の話です。本番稼働中に品質がじわじわ劣化していないか、ループに陥って暴走していないか、トークン消費が異常に膨らんでいないか——こうした「今この瞬間」の状態が見えなければ、気づいたときには手遅れになります。
モニタリング(Monitoring)とオブザーバビリティ(Observability)は違います。モニタリングは「あらかじめ決めた指標が閾値を超えたか」を監視すること。オブザーバビリティは「システムが出力する信号(トレース・メトリクス・ログ)から、事前に想定していなかった問い——なぜ遅い?なぜ失敗した?どのツール呼び出しが原因?——にも後から答えられる状態」を指します。エージェントは非決定的でマルチステップだからこそ、後者が決定的に重要になります。
幸い、潮目は変わりつつあります。OpenTelemetryのGenAI(生成AI)セマンティック規約が実用段階に入り、エージェントのマルチステップな軌跡(トラジェクトリ)を標準化されたスパンとしてトレースできるようになってきました。ベンダー独自の計装に縛られず、業界標準の「共通語彙」でエージェントを観測できる——これが、いま計装設計を見直すべき理由です。
本記事では、次の4点に集中して解説します。
- 何を計装するか——エージェントのトラジェクトリを1本のトレースに束ねる考え方
- どうスパンに落とすか——OpenTelemetry GenAI規約準拠のスパン設計
- 何を測るか——エージェント固有のSLI/SLO(タスク成功率・ツールエラー率・ループ/ステップ数・トークン予算消費 ほか)
- いつ気づくか——バーンレート(Burn Rate)とマルチウィンドウ・アラート設計
前提——「エージェントのトラジェクトリ」とは何か
従来のWebアプリのオブザーバビリティは、比較的シンプルでした。「リクエストが来て、DBを叩いて、レスポンスを返す」——このリニアな流れを1本のトレースにすればよかったのです。
エージェントは違います。1つのユーザー要求(タスク)に対して、エージェントは「考える→ツールを呼ぶ→結果を観察する→また考える」を何度も繰り返します。この一連の実行過程をトラジェクトリ(Trajectory/軌跡)と呼びます。トラジェクトリは次のような入れ子構造を持ちます。
| 階層 | 意味 | OTel上の対応(例) |
|---|---|---|
| タスク(Task/Session) | ユーザーの1要求全体。トレース全体のルート | ルートスパン(invoke_agent) |
| ステップ(Step/Turn) | 「思考→行動→観察」の1周 | 子スパン |
| LLM呼び出し | 推論エンドポイントへの1リクエスト | chat スパン |
| ツール呼び出し | 検索・API・コード実行など | execute_tool スパン |
ポイントは、これらをバラバラのログにしないことです。1つのタスクに紐づくすべてのLLM呼び出しとツール呼び出しを、共通のトレースID(trace_id)でひとつのトレースに束ねる——これがエージェント・オブザーバビリティの出発点です。1本のトレースを開けば、そのタスクが「どう考え、何を呼び、どこで詰まったか」を時系列で追える。これが「落ちてから調べる」を卒業する第一歩になります。
何を計装するか——トラジェクトリを1本のトレースに束ねる
コンテキスト伝播(Context Propagation)が生命線
トラジェクトリを1本に束ねる技術的な要は、コンテキスト伝播です。ルートスパンで発行した trace_id を、子ステップ・LLM呼び出し・ツール呼び出しへと確実に受け渡していく仕組みを指します。ここが切れると、トレースが分断され、「同じタスクの出来事」が別々のトレースに散らばってしまいます。
特に注意が必要なのは次の3ケースです。
- 非同期・並列実行:複数ツールを並行呼び出しする場合、各非同期タスクにコンテキストを明示的に引き継ぐ必要があります。
- マルチエージェント(Agent-to-Agent):エージェントが別のエージェントを呼ぶ場合、
traceparentヘッダを伝播させ、親子関係(span link)を保ちます。 - バックグラウンド/非同期トリガー:キューやWebhook経由で起動する処理は、トリガー元のコンテキストを引き継ぐか、少なくとも span link で因果を残します。
「意味のある単位」でスパンを切る
計装で陥りがちな失敗は、細かすぎる計装(過剰)と粗すぎる計装(不足)の両極です。原則は「後からデバッグする自分が、その粒度で欲しいか」。実務では次の粒度を推奨します。
- タスク全体で1スパン(ルート)
- 推論ステップ(思考の1周)で1スパン
- 各LLM呼び出しで1スパン(モデル名・トークン数・レイテンシを属性に)
- 各ツール呼び出しで1スパン(ツール名・入力サイズ・成否・レイテンシを属性に)
- リトライ/ガードレール発火など「例外的分岐」はイベント(span event)で記録
OpenTelemetry GenAI規約準拠のスパン設計
ここが本記事の技術的な中核です。独自命名でスパンや属性を作ると、後でツールを乗り換えるたびにダッシュボードを作り直す羽目になります。最初からOpenTelemetryのGenAIセマンティック規約に沿って属性を付けておけば、対応バックエンド(Grafana Tempo、Jaeger、Langfuse、Arize Phoenix など)をまたいで同じ語彙で観測できます。
注意:OpenTelemetryのGenAI規約は多くの部分がまだ「Development(開発中)」ステータスで、属性名やイベント表現は今後変更される可能性があります。だからこそ「独自命名で固めきる」よりも、規約に追随する薄い計装層(後述)を挟むのが賢明です。以下は2026年時点の代表的な属性・命名の考え方として押さえてください。
スパンの命名と操作種別(gen_ai.operation.name)
GenAI規約では、スパン名を汎用の関数名ではなく「操作の種類 + 対象」で表現します。エージェント運用で登場する主な操作は次の通りです。
| 操作(operation.name) | 意味 | スパン名の例 |
|---|---|---|
create_agent | エージェントの生成・初期化 | create_agent researcher |
invoke_agent | エージェントへのタスク投入(ルート) | invoke_agent researcher |
chat | LLMへの対話(推論)呼び出し | chat gpt-4.1 |
execute_tool | ツール(関数・API)の実行 | execute_tool web_search |
主要な属性(Attributes)
スパンには、後から集計・絞り込みできるよう構造化した属性を付与します。GenAI規約でよく使う属性を挙げます。
| 属性キー | 内容 |
|---|---|
gen_ai.system | プロバイダ/基盤(例:openai, anthropic, ollama) |
gen_ai.operation.name | 操作種別(chat / execute_tool / invoke_agent など) |
gen_ai.request.model | 要求したモデル名 |
gen_ai.response.model | 実際に応答したモデル名 |
gen_ai.usage.input_tokens | 入力トークン数 |
gen_ai.usage.output_tokens | 出力トークン数 |
gen_ai.agent.name / gen_ai.agent.id | エージェント名・ID |
gen_ai.tool.name / gen_ai.tool.call.id | ツール名・呼び出しID |
gen_ai.response.finish_reasons | 停止理由(stop / length / tool_calls など) |
スパン構造のイメージ
1つのタスクは、次のような入れ子のトレースになります(擬似表現)。
Trace (trace_id: 7f3a...)
└─ invoke_agent researcher [gen_ai.operation.name=invoke_agent]
├─ chat gpt-4.1 [input_tokens=1,240 / output_tokens=180]
├─ execute_tool web_search [gen_ai.tool.name=web_search / status=OK]
├─ chat gpt-4.1 [input_tokens=2,010 / output_tokens=95]
├─ execute_tool db_query [status=ERROR / retry=1] ← ここで失敗
└─ chat gpt-4.1 [finish_reason=stop]
このように束ねておけば、「db_query の失敗が全体のレイテンシとリトライを押し上げていた」といった因果を、1本のトレースを開くだけで読み解けます。
プロンプト/出力本文の扱い(プライバシー)
スパンにはプロンプトや応答の全文(コンテンツ)を載せることもできますが、機微情報・個人情報が含まれるため、本番では既定でオフか、マスキング/サンプリングを前提にします。GenAI規約でも、コンテンツの記録はオプトイン扱いが基本です。「デバッグに必要な最小限だけ、明示的に許可した環境で」——この原則を崩さないでください。
エージェント固有のSLI/SLO設計
トレースが揃ったら、次は「良い/悪いを何で判定するか」です。ここで従来のWebサービスの指標(可用性・レイテンシ・エラー率)をそのまま持ち込むと、エージェント特有の失敗——「動いてはいるが仕事を達成していない」「無限にループしている」「コストが爆発している」——を取りこぼします。
SLI(Service Level Indicator)は「測る指標」、SLO(Service Level Objective)は「その指標の目標値」。例:SLI=タスク成功率、SLO=直近28日で95%以上。
エージェント固有のSLI一覧
| SLI | 測る内容 | SLO目標の例 |
|---|---|---|
| オンライン・タスク成功率 | 本番で「意図した成果」に到達した割合(明示フィードバック/後述の判定で算出) | ≥ 95%(28日) |
| ツール呼び出しエラー率 | execute_toolスパンのうち status=ERROR の割合 | ≤ 2% |
| ループ/ステップ数 | 1タスクあたりの推論ステップ数。異常に多い=ループの疑い | p95 ≤ 8ステップ |
| コンテキスト窓利用率 | 入力トークン ÷ モデル上限。高止まり=切り詰め・劣化の兆候 | p95 ≤ 70% |
| タスク単価(Cost per Task) | 1タスクあたりのトークン課金額。異常=暴走・非効率 | p95 ≤ 目標額 |
| エンドツーエンド・レイテンシ | タスク投入から完了までの時間 | p95 ≤ 目標秒数 |
| ガードレール発火率 | 安全フィルタ・出力検証が発火した割合 | 監視(急変を検知) |
「成功」をどう判定するか——ここが最難関
タスク成功率は最重要SLIですが、本番では「正解ラベル」がありません。ここで効いてくるのが、第2弾(評価)で作ったオンライン評価シグナルです。本番の成功判定には、次を組み合わせます。
- 明示フィードバック:👍/👎、再試行、途中離脱などのユーザー行動
- ヒューリスティック:ツール成功で完了したか、エラーで終わったか、ステップ上限で打ち切られたか
- LLM-as-a-Judge(オンライン評価):本番出力の一部をサンプリングし、評価用モデルで合否を推定
重要なのは、これらの判定結果をトレース(trace_id)に紐づけて記録することです。そうすれば「失敗したタスクのトレースだけを開いて原因を掘る」という運用ができます。②の評価シグナルと③の本番テレメトリが1本の線でつながる——これが連載の核心のひとつです。
バーンレートとアラート設計——「いつ叩き起こすか」
SLOを決めたら、次は「破りそうになったら、どう気づくか」です。ここで単純な閾値アラート(例:エラー率が5%を超えたら通知)を使うと、ノイズだらけで誰も見なくなるか、気づくのが遅すぎるかのどちらかに転びます。そこでエラーバジェット(Error Budget)とバーンレート(Burn Rate)の考え方を導入します。
エラーバジェットとは「SLOが許す失敗の総量」。SLOがタスク成功率99%なら、残り1%が”予算”です。バーンレートは、その予算を消費する速度。バーンレート1=「その速度なら期間ちょうどで使い切る」、バーンレート14.4=「猛烈な速さで燃やしている(=重大障害)」を意味します。
マルチウィンドウ・マルチバーンレート・アラート
Google SREのプラクティスに倣い、「速い燃焼は短時間で・遅い燃焼は長時間で」検知する多段構成にします。エージェント運用に当てはめた設計例が次の表です(SLO=タスク成功率99%/30日を想定)。
| 深刻度 | バーンレート | 観測ウィンドウ | 意味 | アクション |
|---|---|---|---|---|
| 緊急(Page) | 14.4× | 1時間/5分 | 2%の予算を1時間で消費=重大障害 | 即時呼び出し(オンコール) |
| 高(Page) | 6× | 6時間/30分 | 5%を6時間で消費=進行中の劣化 | 呼び出し |
| 中(Ticket) | 1× | 3日/6時間 | じわじわ燃焼=要調査 | チケット起票(営業時間内) |
「短ウィンドウでも燃えている」ことを確認してから発報する(ロング+ショートの二重条件)ことで、一時的なスパイクによる誤報を抑えつつ、本物の障害には素早く反応できます。
エージェントならではのアラート
成功率・レイテンシに加えて、エージェント特有の「暴走・浪費」系アラートを必ず入れてください。
- ループ検知:1タスクのステップ数がp99や上限に張り付いたら発報(無限ループ・思考の堂々巡り)
- トークン予算アラート:タスク単価または時間あたりトークン消費が急増したら発報(「財布枯渇攻撃(Denial of Wallet)」対策とも直結)
- ツールエラー急増:特定ツールのエラー率スパイク(外部API障害の早期検知)
- ガードレール発火の急変:安全フィルタの発火率が跳ねたら、攻撃または劣化のサイン
実装スタック——どう組むか
計装からダッシュボードまで、標準的な構成は次の3層です。ベンダーロックインを避けたいなら、OpenTelemetry Collectorを中継点に置くのが定石です。
- 計装層(Instrumentation):アプリ側でスパンを生成。手書きのOTel SDKに加え、フレームワーク(LangGraph、CrewAI ほか)向けの自動計装ライブラリ(OpenLLMetry / OpenInference など)を併用。
- 収集・転送層(Collector):OpenTelemetry Collectorで受け、サンプリング・マスキング・整形をしてからバックエンドへ振り分け。ここを挟むと、規約変更やバックエンド乗り換えの影響を1か所に閉じ込められます。
- 可視化・分析層(Backend):トレースはGrafana Tempo/Jaeger、メトリクスはPrometheus+Grafana、GenAI特化ならLangfuse/Arize Phoenixなど。SLO・バーンレートのダッシュボードとアラートをここに構築。
ローカル/オンプレでエージェントを動かす場合の監視スタック構築そのものについては、別記事「ローカルLLM Observability(監視スタック構築)」で詳説しています。本記事はその上に載る「エージェント運用としての観測規律」——何を計装し、どのSLI/SLOで運用するか——に焦点を当てています。
連載の中での位置づけ(差別化)
本サイトには観測・運用に関する記事が複数ありますが、それぞれ扱う”層”が違います。混同しないよう整理します。
| 記事 | 扱う層 | 問い |
|---|---|---|
| セマンティック・テレメトリ/意図トレース | 意味論の深掘り(Decision Provenance・反実仮想ログ) | なぜそう判断したか |
| ローカルLLM Observability | 監視スタックの構築 | 何で監視するか(基盤) |
| 本記事(AgentOps③) | エージェント運用のオブザーバビリティ規律 | 何を計装し・何で測り・いつ気づくか |
本記事(③)は、②(評価)のオンライン評価シグナルを本番テレメトリに接続する”結節点”です。さらに、「セマンティック・テレメトリ/意図トレース」で扱う意味論的な深掘り(なぜそうしたか)と、「インシデント対応」で扱う障害発生後の初動へと接続することで、「連載ハブ→各論」の導線が完成します。まず観測して異常に気づき(③)、なぜかを掘り下げ(意図トレース)、対処する(インシデント対応)——という運用の流れです。
Q&A
Q1. モニタリングは既にやっています。オブザーバビリティを別に用意する必要は?
A. あります。既存のモニタリングは「決めた閾値を超えたか」までしか教えてくれません。エージェントの障害は「なぜ失敗したか」がステップの中に埋もれているため、後から任意の問いに答えられるトレースが不可欠です。両者は排他ではなく、トレース(可観測性)の上にSLO監視(モニタリング)を載せる関係です。
Q2. OpenTelemetryのGenAI規約はまだ開発中と聞きました。今から採用して大丈夫?
A. 「独自命名で固めきる」よりは確実に安全です。規約は変わり得ますが、業界が収束しつつある共通語彙に沿っておけば、将来の移行コストは最小化できます。変更の影響をOpenTelemetry Collectorや薄い計装ラッパーに閉じ込める設計にしておけば、規約の更新にも追随しやすくなります。
Q3. タスク成功率は、正解がない本番でどう測るのですか?
A. 単一の完璧な指標はありません。明示フィードバック(👍/👎・再試行)、ヒューリスティック(正常終了か打ち切りか)、LLM-as-a-Judgeによるサンプリング評価を組み合わせ、それぞれの判定をtrace_idに紐づけて記録します。第2弾の評価設計と地続きにするのがコツです。
Q4. プロンプトや出力を全部スパンに残せば、デバッグが楽では?
A. 楽になりますが、機微情報・個人情報の漏えいリスクとストレージコストが跳ね上がります。本番では既定でコンテンツ記録をオフにし、マスキングやサンプリング(例:エラー時のみ全文保存)で必要最小限にとどめるのが安全です。
Q5. アラートが多すぎて疲弊しています。減らすには?
A. 閾値アラートをバーンレート・アラートに置き換えるのが最短です。「今この瞬間の数値」ではなく「エラーバジェットを危険な速度で消費しているか」で発報することで、一時的スパイクの誤報が激減し、本当に対処すべき事象だけが残ります。ロング+ショートの二重ウィンドウ条件も効果的です。
まとめ
エージェント・オブザーバビリティの要点は、次の3つに集約されます。
- トラジェクトリを1本のトレースに束ねる。1タスクのLLM呼び出しとツール呼び出しを共通trace_idで束ね、「どう考え・何を呼び・どこで詰まったか」を1本で追える状態を作る。
- OpenTelemetry GenAI規約に沿って計装する。独自命名を避け、標準の操作種別・属性で計装すれば、バックエンドをまたいで同じ語彙で観測でき、乗り換えコストを抑えられる。
- エージェント固有のSLI/SLOをバーンレートで守る。タスク成功率・ツールエラー率・ループ/ステップ数・トークン予算・コンテキスト窓利用率・タスク単価を測り、マルチウィンドウ・バーンレート・アラートで「暴走・浪費・劣化」に素早く気づく。
「落ちてから調べる」から「見え続ける」へ。エージェントは本番に出した瞬間から、非決定的に振る舞い続ける——この前提に立てば、オブザーバビリティは”あとで足す運用機能”ではなく、”設計に織り込む規律”だと分かります。次回(第4弾)は、ここで観測した異常をどうインシデント対応につなぐか——本番での「対処」の設計に進みます。

コメント