observability-llm-obs について
取り込まれた LLM とエージェントのデータを分析する Elastic のオブザーバビリティスキルです。データソースやフィールドを決めつけず、性能、トークン使用量、計測済みコスト、応答品質、トレースに基づく呼び出しチェーンを調査します。
主な機能
- 01利用可能な LLM データソースの特定
- 02環境ごとのフィールド名の確認
- 03レイテンシ、スループット、エラーの分析
- 04トークン数と計測済みコストの報告
- 05品質指標と呼び出しチェーンの調査
活用シーン
- 01モデル間の LLM 性能の比較
- 02トークン総数と利用可能なコストデータの確認
- 03障害やコンテンツフィルターイベントの調査
- 04トレースされたエージェントワークフローのボトルネック特定
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ elastic/ agent-skills --skill observability-llm-obsインストール後、Claude Code で/observability-llm-obsを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/esql-recipes.mdgenai-fields.mdintegrations.md詳細
LLM とエージェント型処理のオブザーバビリティ
Elastic に実際に取り込まれたデータだけを使って、LLM とエージェント型コンポーネントの監視に関する質問に回答します。それ以外のデータは使用しません。このスキルが回答する質問は、LLM のパフォーマンス、コストとトークン使用状況、応答品質、呼び出しチェーンまたはエージェント型ワークフローのオーケストレーションの 4 つです。
デプロイ環境では通常、APM/OTLP トレース、インテグレーションのメトリクスとログのいずれか、または両方など、1 つ以上の取り込み経路が使われます。どの経路が存在するかは調査によって確認するものであり、前提としてはなりません。両方が存在すると決めつけないでください。ES|QL の構文、コマンド、クエリパターンについては、elasticsearch-esql スキルを使用してください。LLM に特化しないサービスレベルのレイテンシとエラーのトリアージについては、observability-sre-triage スキルを使用してください。
環境設定
このスキルでは、elastic CLI を通じて Elasticsearch の操作を実行します。
elastic CLI がインストールされていない場合は、何のために必要なのかをユーザーに伝えてください。認証情報を推測したり、HTTP API を直接呼び出したり、ほかの回避策を試したりしないでください。
このスキルでは、HTTP 省略記法(例:GET /、GET /_cat/indices、GET /{index}/_mapping、
GET /{index}/_settings/index.mode、POST /_query)で操作を記載しています。このドキュメント末尾の
操作表では、それぞれの省略記法に対応する elastic CLI コマンドを示しています。HTTP API を直接呼び出すのではなく、必ず CLI を使用してください。
クラスターにアクセスできない場合の分析
上記の CLI チェックは、_クラスターへのクエリ実行_の条件であり、分析の条件ではありません。ユーザーが質問ですでに根拠(メトリクス値、件数、ステータスの理由、ログ行、アラートのペイロード、設定)を提示している場合は、その根拠から判断し、結論を提示してください。
ユーザーが提示していないデータが本当に必要な場合も、何をどのように確認するかを伝えてください。疑問を解決できる具体的なクエリ、インデックス、フィールドを示したうえで、CLI の設定を依頼してください。確認内容を明示した回答ならクラスターがなくても役に立ちますが、設定だけを求める回答では不十分です。
実施するタスク
- クエリを実行する前に、デプロイ環境で実際に使われている LLM の取り込み経路を特定する
- GenAI 属性の名前は一貫していないため、このデプロイ環境で使われている実際の LLM フィールド名を特定する
- モデル、サービス、または時間別に、LLM のレイテンシ、スループット、エラー率を報告する
- トークン使用状況を報告し、コストはコストフィールドが実際に存在する場合にのみ報告する
- 失敗、タイムアウト、終了理由、コンテンツフィルター、ガードレールイベントなど、応答品質を調査する
- エージェント型の呼び出しチェーンを再構成し、複数ステップのワークフローにおけるボトルネックのスパンを特定する
- LLM 関連データで定義された SLO やアラートルールと調査結果を関連付ける
出力の規律
このスキルを使用して生成されるすべての回答に適用されます。
- 最も根拠のある結論を明確に示します。 証拠が一方を示している場合は、そう述べます。慎重に聞こえるように確信度を下げないでください。明白な証拠に対して曖昧な表現を使うのは、謙虚さではなく欠点です。
- 確信度は結論で一度だけ述べます。 箇条書きごとに繰り返さないでください。
- 証拠の範囲を超えて推測しないでください。 原因が観測されていない場合は、回答に含めません。不明な点を述べたら、そこで止めてください。
- 存在しないものは、存在しないと報告します。 行が0件の場合、データが欠落しているか、収集されていないことを意味します。基盤となる状態が健全であることを意味するものではありません。特に、コストフィールドが存在しない場合は、コストは計測されていないと述べてください。トークン数に推測したトークン単価を掛けて、その積をコストの数値として提示してはいけません。
- 実際に見つかった数値を報告します。 クエリで返されたトークン数、レイテンシ、エラー率をそのまま記載してください。漠然とした表現に丸めたり、このスキルの例にある数値を流用したりしないでください。
- 余計な内容を加えないでください。 質問を言い換えたり、結果に影響しない限り実行したクエリを説明したり、要約をさらに要約したりしないでください。
- 所見で締めくくります。 「詳しく調べましょうか?」のような、最後に付け加える申し出は避けてください。実行可能な次の手順は、質問ではなく推奨事項として、推奨事項のリストに記載してください。
データの所在
| 取り込み経路 | インデックスパターン | 対応できる内容 |
|---|---|---|
| OTLP 経由の OTel / EDOT トレース | traces-*.otel-*、または汎用の traces-* | リクエストごとのレイテンシ、トークン数、モデル、終了理由、および呼び出しチェーン |
| Elastic APM エージェントのトレース | traces-apm*、または汎用の traces-* | リクエストごとのレイテンシと結果。SDK が追加する場合は GenAI 属性も |
| APM/OTel メトリクス | metrics-apm*、metrics-*.otel-* | サービスレベルの集計レートとレイテンシ |
| Elastic LLM 統合メトリクス | metrics-<integration>.*(例:metrics-aws_bedrock_agentcore.*) | トークン数、呼び出し回数、レイテンシ、場合によってはコストの集計 |
| Elastic LLM 統合ログ | logs-<integration>.* | プロンプト/応答の記録、ガードレールおよびコンテンツフィルターのイベント |
Elastic APM エージェントと OpenTelemetry のどちらが収集したデータであっても、トレースデータを見つけるには汎用の traces-* パターンを使用します。EDOT、OpenLLMetry、OpenLIT、または Langtrace から OTLP にエクスポートすることで計装できます。いずれも、LLM とエージェントのスパンをトレースデータストリームに取り込みます。
呼び出しチェーンを再構築できるのはトレースだけです。 エージェント型の呼び出しチェーンを再構築するには、trace.id、span.id、parent.id が唯一の手段です。統合メトリクスは事前に集計されているため、呼び出しチェーンを再構築できません。オーケストレーションに関する質問で、統合メトリクスしか存在しない場合は、利用可能なデータから呼び出しチェーンを再構築できないと述べてください。
参考資料: LLM とエージェント型 AI のオブザーバビリティ、 EDOT の LLM ユースケース、 Observability Labs — LLM Observability。
プロセス:利用可能なデータを特定する
以下を順番に実行します。ステップ 5 に進んで省略しないでください。
-
接続を確認し、バージョンを特定します。
GET /を呼び出します。これによって、利用できるクエリ言語の 機能が決まります。build_flavor: "serverless"の場合、TS、TRANGE、TBUCKETを含むすべての ES|QL 機能を 利用できます。それ以外の場合はversion.numberを使います。Stack ではTSは 9.2 でプレビュー、9.4 で GA となり、TRANGEは 9.3.0 で GA となるため、時系列の処理全体では Stack 9.4 を最低バージョンとし、それより古い場合は 下記のFROMとBUCKET(@timestamp, ...)に切り替えます。ここを誤ると、クエリの解析に失敗します。 -
どの取り込み経路があるかを特定します。 そのデプロイメントにトレースデータ、インテグレーションデータ、または その両方があるかどうかが決まり、回答できる質問の種類も変わります。
GET /_data_stream/<name>にtraces-*、metrics-*、logs-*を渡してデータストリームを一覧表示するか、GET /_resolve/index/<pattern>でパターンを解決します。トレースデータストリーム、metrics-apm*、および既知の LLM インテグレーションデータセットに一致するmetrics-*またはlogs-*を探します。どちらの経路にも LLM データが なければ、その旨を伝えて終了します。モデルプロバイダーに関する一般知識から回答してはいけません。 -
実際の LLM フィールド名を特定します。 ES|QL に記述する正確なフィールドパスを判断します。推測で決めることは できません。計装によって命名が異なり、
gen_ai.*、llm.*、インテグレーション固有の名前などがあります。また、 セマンティック規約自体も変化しており、古い計装ではgen_ai.system、新しいものではgen_ai.provider.nameが 出力されます。*gen_ai*、*llm*、*token*、*cost*などのフィールドパターンを指定してGET /_field_capsを使うか、GET /<index>/_mappingを読み取り、さらにドキュメントのサンプルで値が入力されていることを 確認します。属性のネストは取り込み経路によって異なります。OTel ネイティブのトレースデータストリームでは、 スパン属性はattributes.<name>(および多くの場合、ベア形式のパススルー<name>)として公開され、span.attributes.<name>ではありません。クエリを書く前に、どの形式が解決されるかを確認してください。属性カタログと 解決ルールについては、references/genai-fields.md を参照してください。 -
コストフィールドがあるかどうかを確認します。 コストは OpenTelemetry GenAI 仕様に含まれていません。一部の 計装は
llm.response.cost.usd_estimateのようなカスタム属性を追加し、一部のインテグレーションはコストメトリックを 公開しますが、どちらもないデプロイメントも多くあります。ステップ 3 で明示的に確認してください。存在しない場合は、 コストに関する質問への回答として、コストは計装されていないことを伝え、代わりにトークン数を報告して不足しているデータを 明示します。 -
質問ごとに、一貫したデータソースを 1 つだけ選びます。 APM トレースとインテグレーションメトリックの両方がある場合は、 どちらか一方を選び、回答全体でそのデータソースを使います。混在させると二重計上になり、同じ量に対して異なる 2 つの数値が 得られます。これは、インテグレーションがプロバイダー自身の会計データをポーリングする一方、トレースはクライアントが 観測した内容を記録するためです。質問の種類に応じて使い分けます。トレースはリクエスト単位の分析、呼び出しチェーン、 トレース階層が必要な内容に使います。インテグレーションメトリックは、長期間にわたるトークン数とコストの集計に 使います。回答のデータソースを明記してください。
-
関連性がありそうな場合は、アラートと SLO を確認します。 パフォーマンスの低下がすでに把握・追跡されているかを 判断します。
GET kbn:/api/alerting/rules/_findでルールを、GET kbn:/api/observability/slosで SLO を取得し、 LLM 関連のサービスまたはインテグレーションメトリックを対象とするものに絞り込みます。ステップ 3 で特定したフィールド名を 手がかりに、関連するルールを判断します。発火中のアラートや、違反または劣化ステータスの SLO は、パフォーマンスが低下して いる証拠です。なお、SLO API のsli.kql.customインジケーターは KQL を受け取ります。これは API の仕様であり、ほかの場面でも KQL を使うべきだという推奨ではありません。
ユースケースとクエリパターン
POST /_query でクエリを記述します。必ず時間範囲を指定し、存在する場合は service.name を追加して、結果を LIMIT で制限してください。
広い時間範囲を細かい粒度でスキャンするのではなく、傾向を把握するだけなら、粗いバケットを使用してください。
| 質問 | トレースのパス | インテグレーションのパス |
|---|---|---|
| レイテンシ、スループット、エラー率 | GenAI の操作またはモデル属性でフィルターし、バケットごとに COUNT(*)、AVG(span.duration.us) を計算し、event.outcome == "failure" で失敗を集計します | モデルのディメンションごとのリクエスト率、レイテンシ、エラーメトリクス |
| トークンとコスト | 時間、モデル、またはサービスごとに入力および出力トークン属性を SUM で集計します。コスト属性が存在する場合にのみ追加します | 時間とモデルごとに集計したトークンとコストのメトリクス |
| 応答の品質と安全性 | event.outcome、error.type、終了理由属性を確認します。プロンプトと応答は、記録されており、かつ編集されていない場合にのみ使用します | ガードレールによるブロック、コンテンツフィルターのイベント、ポリシー違反 |
| 呼び出しの連鎖とオーケストレーション | トレースのみ — trace.id でグループ化し、parent.id から span.id をたどって、スパン名または GenAI の操作ごとに集計します | 回答不可 — メトリクスは事前集計されています |
トレースのパスでは、スパンごとのレイテンシは duration(ナノ秒。OTel ネイティブのすべてのスパンに設定されています)、または span.duration.us(マイクロ秒。APM 互換フィールドで、多くのスパンでは null です。これでソートすると、最も遅いステップが知らないうちに除外されることがあります)から取得します。使用する前に、どちらに値が設定されているかを確認してください。最も遅い子スパンがボトルネックです。スパン名または GenAI の操作属性ごとに集計すると、ワークフロー内のステップ種別(検索、LLM 呼び出し、ツールの使用)の分布を確認できます。
OpenAI、Azure OpenAI、Azure AI Foundry、Amazon Bedrock、Bedrock AgentCore、GCP Vertex AI のインテグレーション固有のデータストリームとフィールド名については、references/integrations.md を参照してください。トレース階層をたどるクエリや時系列インテグレーションのパターンを含む、詳しいクエリ例については、references/esql-recipes.md を参照してください。
例
以下のフィールドパスは例示用です。何かを実行する前に、手順 3 で実際のパスを確認してください。このスキル独自の方法では、まずフィールド名を特定します。正しい階層は取り込み経路によって異なります。
「モデルごとのトークン消費量はどれくらいですか?」 — トークン属性が存在し、値を取得できることを確認してから、モデルと時間バケットごとに合計します。返された合計を報告します。
FROM traces-*
| WHERE @timestamp > NOW() - 24 hours AND attributes.gen_ai.request.model IS NOT NULL
| EVAL in_tok = TO_LONG(attributes.gen_ai.usage.input_tokens),
out_tok = TO_LONG(attributes.gen_ai.usage.output_tokens)
| STATS input_tokens = SUM(in_tok), output_tokens = SUM(out_tok)
BY hour = BUCKET(@timestamp, 1 hour), attributes.gen_ai.request.model
| SORT hour
| LIMIT 500
「LLM の費用はいくらですか?」 — クエリを実行する前に、GET /_field_caps で *cost* に一致するコストフィールドを探します。見つからない場合は、トークン使用量を報告し、このデプロイではコストが計測されていないことを明確に伝えます。解決するには、計装レイヤーでコスト属性を追加するか、コストを報告するインテグレーションを有効にします。想定した価格をトークン数に掛けてはいけません。
「最も遅いモデルはどれですか?また、エラーが発生していますか?」 — モデルごとにグループ化して、レイテンシーとエラー率を一度に確認します。
FROM traces-*
| WHERE @timestamp > NOW() - 24 hours AND attributes.gen_ai.request.model IS NOT NULL
| STATS request_count = COUNT(*),
failures = COUNT(*) WHERE event.outcome == "failure",
avg_duration_us = AVG(span.duration.us)
BY attributes.gen_ai.request.model
| EVAL error_rate = failures::double / request_count
| SORT avg_duration_us DESC
| LIMIT 100
「エージェントの動作が遅いのはなぜですか?」 — これにはトレースの階層が必要なため、トレースデータが必要です。LLM またはツールのスパンが複数含まれるトレースを見つけ、これらのスパンで費やされた時間が長い順に並べます。その後、最悪のトレースに trace.id で入り、ボトルネックとなっているスパンを特定します。WHERE で LLM スパンに絞り込んでいるため、以下の合計は LLM の所要時間であり、トレース全体の所要時間ではありません。実時間のレイテンシーと誤解されないよう、列名を明示します。
FROM traces-*
| WHERE @timestamp > NOW() - 3 hours AND attributes.gen_ai.operation.name IS NOT NULL
| STATS llm_span_count = COUNT(*), llm_duration_us = SUM(span.duration.us) BY trace.id
| WHERE llm_span_count > 1
| SORT llm_duration_us DESC
| LIMIT 50
「プロンプトがブロックされていますか?」 — トレース経路上の終了理由属性と error.type、またはインテグレーションのガードレールログイベントを確認します。コンテンツフィルターなどの終了理由は品質を示すシグナルであり、通信エラーではありません。
「すでにアラートが設定されていますか?」 — GET kbn:/api/alerting/rules/_find と GET kbn:/api/observability/slos を実行し、手順 2 で特定した LLM 関連サービスまたはインテグレーションのメトリクスに絞り込みます。
ガイドライン
- Elastic に取り込まれたデータのみを基に回答します。 他のベンダーの UI、コンソール、製品について説明したり、それらに依存したりしないでください。データが Elastic にない場合は、Elastic にないと回答します。
- クエリを実行する前に特定します。
GET /_field_caps、GET /<index>/_mapping、またはサンプルドキュメントを使って、取り込み経路(手順 2)とフィールド名(手順 3)を確認します。属性のパスを推測してはいけません。 - 質問ごとに一貫したデータソースを使います。 APM トレースとインテグレーションのメトリクスを、1 つの回答の中で混在させないでください。
- GenAI 仕様にコストは含まれていません。 コストフィールドがデータに存在する場合に限り、コストの数値を利用可能として扱います。
- チェーンを再構成できるのはトレースだけです。 エージェント型のオーケストレーションに関する質問で、インテグレーションのメトリクスしかない場合は、集計値から推測するのではなく、チェーンを再構成できないと伝えます。
- バージョンに応じて時系列構文を使い分けます。 Serverless または Stack 9.4+ では
TRANGEとTBUCKETを含むTSを使います。それより前のバージョンでは、BUCKET(@timestamp, ...)を含むFROMに切り替えます。バケットにエイリアスを付け(BY bucket = TBUCKET(1 hour))、そのエイリアスでソートします。 - 動的にマッピングされた属性では、型の競合が発生することがあります。 トークン属性は、ロールオーバー後に、あるバッキングインデックスでは
integer、別のインデックスではlongとしてマッピングされることがよくあります。その場合、ES|QL はフィールドを受け付けません。集計の前にEVALでTO_LONG(...)を使ってキャストします。 - Kibana UI に依存しません。 ES|QL と Elasticsearch API を優先し、SLO とアラートに限って Kibana API を使います。Kibana UI を開くようユーザーに指示してはいけません。
- ES|QL の構文とクエリパターンについては、elasticsearch-esql スキルを使用します。Stack 9.4+ と Serverless では
TSコマンドリファレンスが適用され、それ以外ではFROMコマンドリファレンスが適用されます。
Operations
| HTTP API (shorthand) | elastic CLI command |
|---|---|
GET / | elastic es info |
GET /_data_stream/<name> | elastic es indices get-data-stream --name '<name>' |
GET /_resolve/index/<pattern> | elastic es indices resolve-index --name '<pattern>' |
GET /<index>/_mapping | elastic es indices get-mapping --index '<index>' |
GET /_field_caps | elastic es field-caps --index '<index>' --fields '<fields>' |
POST /_query | elastic es esql query --format tsv --query '<esql>' |
GET kbn:/api/alerting/rules/_find | elastic kb alerting get-alerting-rules-find --filter '<filter>' |
GET kbn:/api/observability/slos | elastic kb slo find-slos-op --space-id '<space>' --kql-query '<kql>' |