LLMアプリの可観測性とは?ログ・メトリクス・トレースで失敗箇所をつなぐ
LLMアプリの回答が遅いとき、原因はモデルとは限らない。待ち行列、RAGの検索、再ランキング、モデル呼び出し、道具、データベース保存、ストリーミングのどこかで時間を使っている。最終エラーだけを記録しても、どの段階で何が起きたかは分からない。
可観測性は、システムが外へ出すログ、メトリクス、トレースから、内部の状態を調べられるようにする設計である。LLMアプリでは、一つの利用者操作から派生した検索、複数回のモデル呼び出し、道具実行、再試行を同じ作業としてつなぐことが中心になる。
ログ・メトリクス・トレースは役割が違う
三つは同じ情報の保存形式ではない。
ログ
一つの出来事を記録する。要求の受付、モデルAPIの失敗、道具の拒否、Schema検証エラーなど、個別事象の詳細を調べるのに向く。
メトリクス
多数の出来事を集計した数値である。要求数、成功率、遅延分布、トークン数、再試行率、費用などを時系列で監視し、SLOやアラートに使う。
トレース
一つの利用者操作が、複数のサービスと処理段階を通る流れを表す。各処理区間をスパンとして記録し、親子関係と時間順をつなぐ。
メトリクスで「遅延が悪化した」と気付き、トレースで遅い区間を探し、ログで個別エラーの詳細を確認する。この順で相互に移動できる識別子を持たせる。
利用者の一つの仕事をトレースの起点にする
ブラウザからの一HTTP要求だけを起点にすると、ストリーミング後の保存、非同期評価、Webhook、再開したバッチが別の処理に見える。先にアプリ独自の作業IDを発行し、利用者が達成しようとした一つの仕事へ付ける。
例えばRAG回答なら、次を同じトレースまたは関連付けられたトレースとして扱う。
- 要求受付と認証
- 質問の前処理
- 埋め込みと検索
- 再ランキング
- モデルへの回答生成要求
- 出力検証
- ストリーム完了
- 履歴保存
- 後から行う品質評価
同期処理の境界を越えるときは、作業IDとトレースの関連をキューのメッセージへ引き継ぐ。再試行では同じ作業IDを保ち、試行ごとに別のスパンや試行番号を付ける。
trace IDと提供者のrequest IDを混同しない
trace IDは、自分のシステム全体で一連の処理を結び付ける識別子である。モデル提供者が返すrequest IDは、提供者側の一回のAPI要求を調べる識別子である。
一つのトレースに複数のモデル要求があれば、request IDも複数になる。反対に、同じ業務を再開した場合は別トレースになっても、同じ作業IDへ結び付くことがある。
OpenAIの公式SDK資料は、本番でrequest IDをログへ残すことを推奨している。成功時と失敗時の両方で取得し、自分のスパンへ属性として関連付ける。request IDを顧客ID、重複防止キー、業務の正本には使わない。
W3C Trace Contextでサービス間をつなぐ
W3C Trace Contextは、分散トレーシングの文脈をHTTPサービス間で伝えるため、traceparent と tracestate の形式を標準化している。異なる言語や監視製品でも、同じトレースを引き継ぎやすくなる。
受け取ったトレース文脈をバックエンド、検索サービス、道具サービスへ伝える。ただし、外部から受け取った値を無条件に信頼しない。形式を検証し、サンプリング指示やベンダー固有情報が負荷増加や情報漏えいにつながらないようにする。
W3C仕様は、trace IDやtracestateへ個人情報や機密情報を入れないよう求めている。識別子は関連付けだけに使い、利用者名、メールアドレス、質問内容を埋め込まない。
スパンは失敗箇所を分けられる粒度にする
すべての関数をスパンにすると量が増え、重要な流れが見えなくなる。利用者体験、外部依存、再試行、費用の境界になる処理を選ぶ。
LLMアプリでは次が候補になる。
- 入力検査と権限確認
- キュー待機
- 埋め込み作成
- ベクトル検索と再ランキング
- モデル応答の各試行
- Tool Callingの提案と実行
- 外部APIやデータベース
- 出力Schemaとガードレールの検証
- ストリームの最初の表示と完了
- 非同期結果の取り込み
モデル呼び出しの中で、入力作成、API待機、出力検証を分けると、プロンプト処理ではなくネットワークや検証が遅いことを見つけやすい。
スパンに本文を入れなくても調査できる
プロンプトと回答全文を記録すれば調査しやすく見えるが、個人情報、社内文書、認証情報、道具の結果が監視基盤へ複製される。閲覧権限と保持期間も本番データとは別に増える。
まず本文を保存せず、次の構造情報を記録する。
- モデルとサービス階層
- プロンプト、道具定義、Schema、ルーティング規則の版
- 入力、キャッシュ済み入力、出力、推論のトークン数
- 検索件数、文書版、スコアの分布
- 道具名、許可判定、成功状態、所要時間
- 応答形式、終了理由、検証結果
- 再試行番号、エラー分類、提供者request ID
- 最初の応答と全体完了の時間
本文が必要な調査では、目的、対象、保持期間、閲覧者を限定した別の保存経路を使う。通常のトレース属性として常時記録しない。
高カーディナリティな値をメトリクスへ入れない
trace ID、request ID、利用者ID、質問文は値の種類が非常に多い。これらをメトリクスのラベルにすると、時系列数が急増し、監視基盤の費用と検索負荷が大きくなる。
個別識別子はログとトレースへ置く。メトリクスは、機能名、環境、モデル区分、状態、エラー分類、構成版など、種類が管理された値へ絞る。
モデル名やエラー文字列も無制限に受け入れず、既知の分類へ正規化する。生の例外文はログに残し、メトリクスには timeout、rate_limit、schema_error など有限の分類を使う。
失敗をHTTP状態だけで決めない
モデルAPIが成功しても、出力が不完全、Schemaに不一致、道具が拒否、利用者が取消、根拠が不足なら、業務として失敗の場合がある。反対に、第一候補のモデルが失敗して代替経路で完了したなら、最終結果は成功でも内部には障害があった。
スパンには技術的な状態と業務上の状態を分けて記録する。
- 技術状態: API成功、タイムアウト、取消、通信失敗
- 検証状態: 形式合格、ガードレール拒否、出力不足
- 業務状態: 完了、部分完了、人へ引き継ぎ、失敗
- 品質状態: 未評価、合格、不合格、判定不能
最上位スパンが成功でも、内部スパンの代替実行や再試行率を監視する。見かけ上の成功率だけでは劣化の前兆を見落とす。
Tool Callingでは提案と実行を分ける
モデルが道具の呼び出しを提案したことと、アプリが引数を検証し、許可し、実行し、外部状態が変わったことは別の出来事である。
トレース上でも、モデル生成、引数検証、承認待ち、道具実行、結果取得、最終回答を別スパンまたはイベントにする。これにより、モデルが誤った道具を選んだのか、権限で止まったのか、外部APIが失敗したのかを区別できる。
道具の引数や結果には機密情報が含まれやすい。全文を属性へ入れず、Schema版、引数の存在、対象種別、安全な内部ID、結果状態を記録する。
トレースは監査ログの代わりではない。金銭、削除、権限変更などの重要操作には、改ざん防止、長期保持、主体と承認を満たす別の監査記録を用意する。
生成AI向け属性は標準の成熟度を確認する
OpenTelemetryは、スパン、メトリクス、ログへ共通の意味を持たせるSemantic Conventionsを定めている。生成AI向けにも、モデル名、操作、トークン利用量、道具種別などの属性が整備されている。
共通名を使えば、言語や提供者が変わってもダッシュボードと集計を再利用しやすい。一方、生成AI向け仕様には移動中または安定前の項目が含まれ得る。採用した仕様版を固定し、更新時に属性名と意味の差分を確認する。
提供者固有の詳細を無理に共通属性へ押し込まない。標準属性と、名前空間を分けた独自属性を使い分ける。
サンプリングで費用と調査可能性を両立する
すべての詳細トレースを長期間保存すると、監視費用とプライバシー上の複製が増える。通常要求は一定割合で保存し、重要なトレースを優先して残す。
優先候補は次である。
- エラーまたはタイムアウト
- SLOの遅延しきい値を超えた処理
- 再試行、代替モデル、縮退経路を使った処理
- 高額なトークンや多数の道具を使った処理
- ガードレールや承認で停止した処理
- 新しい構成版の限定公開
結果を見てから保存を決める尾部サンプリングは有効だが、決定までスパンを保持する基盤が必要になる。早い段階で捨てる場合も、件数、失敗率、遅延、費用のメトリクスは全体から集計する。
サンプリングされなかった要求を成功扱いしない。詳細トレースがないことと、処理結果が不明なことを区別する。
テレメトリ障害で本処理を止めない
監視基盤が遅いとき、トレース送信を同期的に待って利用者の回答まで遅くしてはいけない。テレメトリはバッファへ送り、上限を超えたら重要度に応じて間引く。
ただし、捨てた件数はメトリクスとして残す。監視データが欠けているのに「異常なし」と判断しないためである。
重要操作の監査ログは可観測性データより強い保証が必要になる。通常トレースを落としても、監査記録は失わない別経路にする。
各サービスの時計がずれると、スパンの順序や所要時間が不自然になる。時刻同期を行い、可能なら親子関係と単調時計による所要時間も使う。
トレースから評価データへ戻す
可観測性は障害調査だけで終わらない。失敗、遅延、高額、利用者修正が多いトレースから、個人情報を除いた代表例を選び、回帰評価へ追加する。
このとき、本番のプロンプト全文をそのまま評価環境へ複製しない。再現に必要な構造、構成版、失敗分類を残し、利用許可と匿名化を確認した入力だけを採用する。
評価結果も元の構成版へ結び付ける。現在のモデルで直ったからといって、発生時の原因を別の版へ上書きしない。
最初に作る最小のトレース
初期実装では、一つの作業について次が分かればよい。
- いつ、どの機能と構成版で始まったか
- 検索、モデル、道具、保存のどこに時間を使ったか
- 何回再試行し、どの経路へ分岐したか
- 技術上と業務上の最終状態は何か
- トークン数、道具回数、概算費用はいくらか
- 提供者request IDは何か
- 品質評価が後からどう結び付いたか
最初から本文の完全保存や全関数の計装を行わない。実際の障害調査で答えられなかった質問を基に、必要なスパンと属性を増やす。
LLMアプリの可観測性は、プロンプトと回答を大量に保存することではない。利用者の一つの仕事をtrace IDと作業IDでつなぎ、検索、モデル、道具、保存を分け、構造情報だけで失敗箇所を絞れるようにする設計である。メトリクスで異常を見つけ、トレースで経路を追い、ログで詳細を確認できれば、品質、遅延、費用の変化を同じ実行へ結び付けられる。