Tech カテゴリのイメージ

LLMアプリの可観測性とは?ログ・メトリクス・トレースで失敗箇所をつなぐ

LLMアプリの回答が遅いとき、原因はモデルとは限らない。待ち行列、RAGの検索、再ランキング、モデル呼び出し、道具、データベース保存、ストリーミングのどこかで時間を使っている。最終エラーだけを記録しても、どの段階で何が起きたかは分からない。

可観測性は、システムが外へ出すログ、メトリクス、トレースから、内部の状態を調べられるようにする設計である。LLMアプリでは、一つの利用者操作から派生した検索、複数回のモデル呼び出し、道具実行、再試行を同じ作業としてつなぐことが中心になる。

ログ・メトリクス・トレースは役割が違う

三つは同じ情報の保存形式ではない。

ログ

一つの出来事を記録する。要求の受付、モデルAPIの失敗、道具の拒否、Schema検証エラーなど、個別事象の詳細を調べるのに向く。

メトリクス

多数の出来事を集計した数値である。要求数、成功率、遅延分布、トークン数、再試行率、費用などを時系列で監視し、SLOやアラートに使う。

トレース

一つの利用者操作が、複数のサービスと処理段階を通る流れを表す。各処理区間をスパンとして記録し、親子関係と時間順をつなぐ。

メトリクスで「遅延が悪化した」と気付き、トレースで遅い区間を探し、ログで個別エラーの詳細を確認する。この順で相互に移動できる識別子を持たせる。

利用者の一つの仕事をトレースの起点にする

ブラウザからの一HTTP要求だけを起点にすると、ストリーミング後の保存、非同期評価、Webhook、再開したバッチが別の処理に見える。先にアプリ独自の作業IDを発行し、利用者が達成しようとした一つの仕事へ付ける。

例えばRAG回答なら、次を同じトレースまたは関連付けられたトレースとして扱う。

  1. 要求受付と認証
  2. 質問の前処理
  3. 埋め込みと検索
  4. 再ランキング
  5. モデルへの回答生成要求
  6. 出力検証
  7. ストリーム完了
  8. 履歴保存
  9. 後から行う品質評価

同期処理の境界を越えるときは、作業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の遅延しきい値を超えた処理
  • 再試行、代替モデル、縮退経路を使った処理
  • 高額なトークンや多数の道具を使った処理
  • ガードレールや承認で停止した処理
  • 新しい構成版の限定公開

結果を見てから保存を決める尾部サンプリングは有効だが、決定までスパンを保持する基盤が必要になる。早い段階で捨てる場合も、件数、失敗率、遅延、費用のメトリクスは全体から集計する。

サンプリングされなかった要求を成功扱いしない。詳細トレースがないことと、処理結果が不明なことを区別する。

テレメトリ障害で本処理を止めない

監視基盤が遅いとき、トレース送信を同期的に待って利用者の回答まで遅くしてはいけない。テレメトリはバッファへ送り、上限を超えたら重要度に応じて間引く。

ただし、捨てた件数はメトリクスとして残す。監視データが欠けているのに「異常なし」と判断しないためである。

重要操作の監査ログは可観測性データより強い保証が必要になる。通常トレースを落としても、監査記録は失わない別経路にする。

各サービスの時計がずれると、スパンの順序や所要時間が不自然になる。時刻同期を行い、可能なら親子関係と単調時計による所要時間も使う。

トレースから評価データへ戻す

可観測性は障害調査だけで終わらない。失敗、遅延、高額、利用者修正が多いトレースから、個人情報を除いた代表例を選び、回帰評価へ追加する。

このとき、本番のプロンプト全文をそのまま評価環境へ複製しない。再現に必要な構造、構成版、失敗分類を残し、利用許可と匿名化を確認した入力だけを採用する。

評価結果も元の構成版へ結び付ける。現在のモデルで直ったからといって、発生時の原因を別の版へ上書きしない。

最初に作る最小のトレース

初期実装では、一つの作業について次が分かればよい。

  1. いつ、どの機能と構成版で始まったか
  2. 検索、モデル、道具、保存のどこに時間を使ったか
  3. 何回再試行し、どの経路へ分岐したか
  4. 技術上と業務上の最終状態は何か
  5. トークン数、道具回数、概算費用はいくらか
  6. 提供者request IDは何か
  7. 品質評価が後からどう結び付いたか

最初から本文の完全保存や全関数の計装を行わない。実際の障害調査で答えられなかった質問を基に、必要なスパンと属性を増やす。

LLMアプリの可観測性は、プロンプトと回答を大量に保存することではない。利用者の一つの仕事をtrace IDと作業IDでつなぎ、検索、モデル、道具、保存を分け、構造情報だけで失敗箇所を絞れるようにする設計である。メトリクスで異常を見つけ、トレースで経路を追い、ログで詳細を確認できれば、品質、遅延、費用の変化を同じ実行へ結び付けられる。

参照資料

生成AI・機械学習講座の一覧へ