コンテンツにスキップ

8.4.4 ログと監視

  • ログ、指標、追跡がそれぞれ何の問題を解決するのか理解する
  • 構造化ログのフィールド設計を学ぶ
  • LLM システムで特に監視すべき指標を理解する
  • 最小限のログ + 監視の例を読めるようになる

可観測性は、次の言葉を分けて考えると理解しやすくなります。

用語何に答えるかLLM アプリでの例
logある瞬間に何が起きたか検索開始、モデル呼び出し失敗、エクスポート完了
metric全体の傾向はどうかエラー率、P95 レイテンシ、平均 token コスト
trace1件のリクエストはどの経路を通ったかAPI -> 検索 -> モデル -> テンプレート描画 -> 返却
P95 / P99遅い 5% または 1% のリクエストはどれくらい遅いか平均は悪くないのに、たまに遅いと感じる原因を見る
observability外側からシステムの状態を理解できるかログ、指標、トレース、ダッシュボード、アラートの組み合わせ

まずはこう覚えると十分です。ログは個別イベント、指標は集計値、トレースは1件のリクエストの複数ステップをつなぐものです。


ログと監視は、「何が起きたか -> 全体の様子はどうか -> 1件のリクエストは何を通ったか」という順で理解すると分かりやすいです。

flowchart LR
A["ログ"] --> B["単発イベントを記録する"]
B --> C["指標"]
C --> D["全体の傾向を見る"]
D --> E["トレース"]
E --> F["1件のリクエストの流れを再現する"]

この節で本当に解決したいのは次のことです。

  • 問題が起きたとき、まずどの層を見るべきか
  • なぜログ、指標、トレース のどれかひとつでも欠けると、原因調査が難しくなるのか

なぜこの話がとても重要なのか?

Section titled “なぜこの話がとても重要なのか?”

LLM システムの障害は、普通の API より見えにくい

Section titled “LLM システムの障害は、普通の API より見えにくい”

普通の API のエラーは、たいてい分かりやすいです。

  • 500
  • タイムアウト
  • パラメータの間違い

でも LLM システムには、次のような“やわらかい障害”があります。

  • 回答の質が下がる
  • 検索結果がずれる
  • token コストが急増する
  • 特定の場面でだけ失敗する

だから観測できる仕組みがないと、システムはよくこうなります。

まだ動いているように見えるのに、実はもう半分壊れている。

ログと監視は、何を解決するのか?

Section titled “ログと監視は、何を解決するのか?”

ざっくり 3 層に分けて考えられます。

  • ログ:何が起きたか
  • 指標:どれくらい起きているか、どれくらい速いか、どれくらい高いか
  • 追跡:1件のリクエストがどの手順を通ったか

可観測性は、次のように考えるとイメージしやすいです。

  • システムにメーターパネル、ドライブレコーダー、整備記録を付ける

これがないと、システムが壊れても言えるのはせいぜいこうです。

  • なんとなく変だ

でも、これがあれば分かるようになります。

  • どこから異常が始まったか
  • それは一時的か、ずっと続いているか
  • 1件だけの問題か、システム全体の問題か

まずログを見る:いちばん基本で、いちばん壊されやすい

Section titled “まずログを見る:いちばん基本で、いちばん壊されやすい”

ただ1行の文字列を出すよりも、たとえばこういう出力です。

print("request received")

それより価値が高いのは、次のような構造化されたフィールドを記録することです。

  • request_id
  • user_id
  • stage
  • レイテンシ_ms
  • model_name
log = {
"trace_id": "trace_001",
"stage": "retrieval",
"query": "返金ポリシーは何ですか",
"latency_ms": 120,
"top_k": 3
}
print(log)

想定出力:

{'trace_id': 'trace_001', 'stage': 'retrieval', 'query': '返金ポリシーは何ですか', 'latency_ms': 120, 'top_k': 3}

このログのいちばん大きな利点は、

あとからフィールドごとに検索したり、集計したりできることです。文字列を人間が目で読むだけではありません。


LLM システムでよく見る指標には、次のようなものがあります。

  • リクエスト数
  • エラー率
  • 平均レイテンシ
  • P95 / P99 レイテンシ
  • token 使用量
  • ツール呼び出し回数
  • 検索ヒット率
requests = [
{"latency_ms": 800, "tokens": 600, "ok": True},
{"latency_ms": 1200, "tokens": 750, "ok": True},
{"latency_ms": 3000, "tokens": 900, "ok": False}
]
avg_latency = sum(r["latency_ms"] for r in requests) / len(requests)
error_rate = sum(not r["ok"] for r in requests) / len(requests)
avg_tokens = sum(r["tokens"] for r in requests) / len(requests)
print("avg_latency_ms =", avg_latency)
print("error_rate =", error_rate)
print("avg_tokens =", avg_tokens)

想定出力:

avg_latency_ms = 1666.6666666666667
error_rate = 0.3333333333333333
avg_tokens = 750.0

これが、監視ダッシュボードの最小の形です。

初学者がまず覚えるとよい指標表

Section titled “初学者がまず覚えるとよい指標表”
指標何に答えているか
リクエスト数システムは忙しいか
エラー率システムはよく失敗しているか
平均 / P95 レイテンシユーザーは待たされすぎていないか
token 使用量コストが異常ではないか
検索ヒット率RAG の流れが悪くなっていないか
ツール呼び出し成功率Agent の実行層は安定しているか

この表は、初心者にとても向いています。
「指標はたくさんある」という話を、理解しやすい問いに戻してくれるからです。

ログ、指標、トレース の可観測性図


追跡(トレース):1件のリクエストは、何をたどったのか?

Section titled “追跡(トレース):1件のリクエストは、何をたどったのか?”

なぜ LLM システムでは トレース が特に重要なのか?

Section titled “なぜ LLM システムでは トレース が特に重要なのか?”

1件のリクエストが、1つのモジュールだけを通るとは限らないからです。たとえば次のような流れがあります。

  1. API 受付
  2. 検索
  3. ツール呼び出し
  4. モデル生成
  5. 後処理

もし最終的な回答が間違っていたら、知りたいのは次の点です。

  • 検索が間違っていたのか
  • モデル生成が間違っていたのか
  • それともツール層が落ちたのか
trace = [
{"trace_id": "trace_001", "stage": "api_in", "latency_ms": 20},
{"trace_id": "trace_001", "stage": "retrieval", "latency_ms": 120},
{"trace_id": "trace_001", "stage": "llm_generate", "latency_ms": 850},
{"trace_id": "trace_001", "stage": "response_out", "latency_ms": 15}
]
for item in trace:
print(item)

想定出力では、同じ trace_id で各 stage の latency が並びます。

stagelatency_ms
api_in20
retrieval120
llm_generate850
response_out15

trace の核心は、

同じリクエストの「完全な旅の記録」を見られることです。

はじめて本番障害を調べるときの、いちばん安全な順番

Section titled “はじめて本番障害を調べるときの、いちばん安全な順番”

通常は、次の順で見るのが安定しています。

  1. まず指標に全体的な異常があるかを見る
  2. 次にログで、どの種類のリクエストが失敗しているかを見る
  3. 最後に トレース をたどって、全体の流れを確認する

この順番のほうが、いきなり大量のログを読むよりずっと原因を見つけやすいです。


実際に近い、最小の観測ループ

Section titled “実際に近い、最小の観測ループ”
import time
def timed_stage(name, fn, *args, **kwargs):
start = time.time()
result = fn(*args, **kwargs)
latency_ms = int((time.time() - start) * 1000)
log = {
"trace_id": "trace_demo_001",
"stage": name,
"latency_ms": latency_ms
}
print(log)
return result
def fake_retrieve(query):
time.sleep(0.1)
return ["返金ポリシー"]
def fake_llm(docs):
time.sleep(0.2)
return f"{docs} に基づいて回答を生成する"
docs = timed_stage("retrieval", fake_retrieve, "返金ポリシーは何ですか")
answer = timed_stage("llm_generate", fake_llm, docs)
print(answer)

出力例です。実際の latency_ms は少し変わることがあります。

Terminal window
{'trace_id': 'trace_demo_001', 'stage': 'retrieval', 'latency_ms': 100}
{'trace_id': 'trace_demo_001', 'stage': 'llm_generate', 'latency_ms': 200}
['返金ポリシー'] に基づいて回答を生成する

この例は小さいですが、すでに次の重要な項目が入っています。

  • トレース_id
  • stage
  • レイテンシ

LLM システムで、追加で特に監視すべきもの

Section titled “LLM システムで、追加で特に監視すべきもの”

普通の API と比べて、LLM システムでは次の項目もよく監視します。

これは、次のことに直結するからです。

  • いくらお金がかかっているか
  • prompt がどんどん長くなっていないか

たとえば次のようなものです。

  • top-1 が当たっているか
  • 検索結果が空の割合

たとえば次のようなものです。

  • ツール呼び出し成功率
  • パラメータ検証の失敗率
  • リトライ率

たとえば次のようなものです。

  • ユーザーの追加質問率
  • ユーザーの修正率
  • 低評価率

こうした指標はオフライン評価の代わりにはなりませんが、とても重要です。


なぜアラートは「サービスが落ちたか」だけを見てはいけないのか?

Section titled “なぜアラートは「サービスが落ちたか」だけを見てはいけないのか?”

LLM システムの多くの問題は、直接 500 にはならない

Section titled “LLM システムの多くの問題は、直接 500 にはならない”

たとえば次のような問題があります。

  • 回答品質が落ち続ける
  • token 使用量が急に2倍になる
  • 検索ヒット率が大きく落ちる

こうした問題では、システムはまだ「動いている」かもしれません。
でも、業務としてはもう明らかに悪化しています。

だからアラートは 2 層に分けるのがよい

Section titled “だからアラートは 2 層に分けるのがよい”
  • 基本可用性アラート

    • エラー率
    • タイムアウト率
  • 業務品質アラート

    • 検索ヒット率の低下
    • 平均 token 数の異常増加
    • ユーザーのネガティブ反応の増加

初学者がまず覚えるとよいアラートの分け方

Section titled “初学者がまず覚えるとよいアラートの分け方”
アラートの種類典型例
可用性アラートエラー率が高い、タイムアウト率が高い
コストアラートtoken が急増する、呼び出し回数が異常
品質アラート検索ヒット率が下がる、ユーザーの追加質問率が上がる

この表は、LLM システムの「壊れ方」は1種類ではない、と教えてくれます。


「ナレッジベース駆動の SOP 文書アシスタント」なら、最初に何を監視すべき?

Section titled “「ナレッジベース駆動の SOP 文書アシスタント」なら、最初に何を監視すべき?”

この種のシステムは、普通の質問応答よりも「見た目は大丈夫そうなのに、実はずれている」問題が起きやすいです。

最初に作るときは、特に次のフィールドを追うとよいです。

監視ポイント何を見ているか
retrieved_countポリシー、ケース、チェックリスト根拠を取得できたか
case_count処理済みケースを添付できたか
policy_coverage必要なポリシーセクションが揃っているか
export_successWord の出力に成功したか
schema_valid構造化結果が SOP テンプレート要件を満たしているか

最小のログオブジェクトは、たとえばこう書けます。

log = {
"trace_id": "trace_001",
"topic": "返金エスカレーション SOP",
"retrieved_count": 5,
"case_count": 2,
"policy_coverage": "complete",
"schema_valid": True,
"export_success": True,
}
print(log)

想定出力:

{'trace_id': 'trace_001', 'topic': '返金エスカレーション SOP', 'retrieved_count': 5, 'case_count': 2, 'policy_coverage': 'complete', 'schema_valid': True, 'export_success': True}

この例は初心者にとても向いています。
なぜなら、この種のプロジェクトの監視ポイントが、

  • モデルが速いかどうか
  • だけではなく、
  • ちゃんと根拠を取れているか
  • SOP セクションが形になっているか
  • 文書を書き出せているか

まで含むと分かるからです。


とても実用的なログフィールドの一覧

Section titled “とても実用的なログフィールドの一覧”

LLM サービスを作るなら、実用的なフィールドはだいたい次のとおりです。

フィールド役割
トレース_id全体の流れをつなぐ
user_id / session_idユーザーや会話を特定する
stage今どの工程にいるか
レイテンシ_msその工程にどれくらいかかったか
model_nameどのモデルを使ったか
prompt_tokens / completion_tokensコスト分析
tool_nameどのツールを呼んだか
retrieval_topk検索設定
error_code失敗の種類

すべてのログに全部入れる必要はありませんが、設計の出発点としてはとても使いやすい一覧です。


文字列だけを記録して、フィールドを記録しない

Section titled “文字列だけを記録して、フィールドを記録しない”

あとで集計しづらくなります。

成功だけを記録して、失敗を記録しない

Section titled “成功だけを記録して、失敗を記録しない”

これだと、問題の切り分けがとてもつらくなります。

問題が起きても、1本の流れを最後まで追えません。

システム可用性だけを監視して、業務品質を監視しない

Section titled “システム可用性だけを監視して、業務品質を監視しない”

LLM プロジェクトでは、これは特に起こりやすい落とし穴です。


このページを終えたら、この証拠カードを残します。

サービス契約
エンドポイント、入力スキーマ、出力スキーマ、エラースキーマ
実行シグナル
レイテンシ、スループット、ログ、ヘルスチェック、またはコンテナ状態
可観測性
request id、trace id、構造化ログ、または metric
失敗確認
タイムアウト、リトライの連鎖、ログ不足、デプロイ不一致
運用アクション
バックオフ、キュー、アラート、段階展開、またはロールバック

この節でいちばん大事なのは、「ログの出し方を覚えること」ではなく、次を理解することです。

ログ、指標、トレース が一緒になって、システムの可観測性を作る。これが、本番に出した LLM サービスを本当に運用できるかどうかを決める。

観測できなければ、多くの障害は推測するしかありません。
観測できれば、システムは初めて保守可能になります。

これをプロジェクトやシステム設計として見せるなら、何を見せるべきか

Section titled “これをプロジェクトやシステム設計として見せるなら、何を見せるべきか”

見せる価値が高いのは、たいてい次のようなものです。

  • 「ログシステムをつないだ」こと
  • ではなく、
  1. 1件のリクエストの トレース
  2. 主要な指標のセット
  3. 典型的なエラーをどう特定したか
  4. 品質アラートと可用性アラートをどう分けたか

こうすると、見る人には次のことが伝わりやすくなります。

  • あなたが理解しているのは、可観測性の閉ループである
  • 単にログを print できるだけではない

  1. 本節の timed_stage()error_code フィールドを追加してみましょう。
  2. 検索フェーズ専用の、自分のログ構造を設計してみましょう。
  3. 考えてみましょう。サービスのエラー率は変わっていないのに、ユーザーの追加質問率が急に上がったら、普通は何を意味するでしょうか?
  4. 自分の言葉で説明しましょう。なぜ LLM システムのアラートは、500 とタイムアウトだけを見ていてはいけないのでしょうか。
参考実装と解説
  1. error_code があると、生のメッセージ文字列ではなく失敗タイプごとに集計できます。
  2. 検索ログには trace_id、query、rewritten query、filters、top_k、候補 ID、score、選ばれた citations、latency、user role/permission 結果を含めます。
  3. 追加質問率の上昇は、回答が不明確、引用不足、信頼度不足、またはタスク未完了を示すことがあります。システムエラーでなくても品質問題です。
  4. LLM の失敗には、検索不良、hallucination、policy ミス、tool 誤用、高コスト、満足度低下もあります。500 と timeout だけでは見逃します。