AI エージェントの「中で何が起きたか」を見える化する

BLOG

誰の、どんな問題を解決する記事か

AI エージェントを PoC から運用に進めると、次のような場面に必ず出会います。

  • 回答は返ってきたが、なぜ 30 秒もかかったのか分からない
  • 検索したはずなのに、答えが実データと合わない
  • トークンの消費が想定より多い。Agent の作り方の問題か、使い方の問題か判断できない

この記事は、Elastic Agent Builder を使っていて、こうした「回答の裏側」を調べたいエンジニアのために書きました。この分野は Agent Observability(エージェントの可観測性) と呼ばれ、Splunk など他社も同じ名前で製品を出し始めています。求められていることは共通です。処理の流れを追って原因を突き止めること、トークンとコストを追うこと、異常を検知することです。

Elastic 9.5 では、Agent Builder の実行を OpenTelemetry(OTel)形式で記録する Agent Observability and Monitoring が Technical Preview として発表されました。LLM 呼び出し、ツール呼び出し、処理時間、トークン使用量を Elasticsearch に保存し、1 回の回答から環境全体の傾向まで確認できます。

この記事では、Kibana の標準サンプルデータ(Sample eCommerce orders)を使い、Agent Builder に質問して本物のトレースを作りました。その結果をもとに、個別の調査 → 全体の監視 → 自動通知の順で説明します。

  1. トレースの基本構造(会話、ラウンド、スパン)
  2. 4 つの調査シナリオを View Trace で読む
  3. 環境全体を Overview ダッシュボード で見る
  4. 実務で使う ES|QL 3 本と、最初に作るアラート 2 つ
  5. 本番導入前に決めるプライバシー設定と権限

トレースは普通の Elasticsearch データです。Discover や ES|QL の知識がそのまま使えます。

検証環境: Elastic Cloud Serverless(検証日: 2026 年 9 月 16 日)。トレースに記録された Kibana のバージョンは 9.6.0 でした。Serverless は機能が順次提供されるため、画面や項目名がスタック版と異なる場合があります。 Agent Observability and Monitoring は、Elastic 9.5(2026 年 8 月 4 日発表)で Technical Preview として発表されました。

トレースの基本構造

Agent Builder のトレースは、会話 > ラウンド > スパンの 3 段でできています。

段意味ID
会話(conversation)1 つのチャット画面gen_ai.conversation.id
ラウンド(round)メッセージを 1 回送って、回答が 1 回返るまでtrace_id(1 ラウンド = 1 トレース)
スパン(span)ラウンドの中の 1 つの処理span_id

スパンの種類

スパンは主に 4 種類です。種類は attributes.elastic.inference.span.kind という属性に入っています。

スパン名の形種類何を表すか主に確認できること
invoke_agent <Agent 名>CHAINラウンド全体(一番外側の箱)全体時間、成否
invoke_agent <Agent 名>AGENTAgent 1 回分の実行Agent 単位の時間、成否
chat <モデル名>LLMLLM へのリクエストモデル、プロバイダー、入力・出力トークン、時間
execute_tool <ツール名>TOOL検索や ES|QL 実行などのツールどのツールか、時間、成功・失敗

invoke_agent は CHAIN と AGENT の 2 つの意味で使われます。区別するときは span.kind を見ます。このほかに generate_title(会話タイトルの生成)と generate_esql(自然言語から ES|QL を作る処理)というスパンも出てきます。どちらも中で LLM を呼びます。

どこに、どんな名前で保存されるか

トレースは traces-agent_builder.otel-<Space ID> というデータストリームに保存されます。Default Space なら traces-agent_builder.otel-default です。隠しインデックスではなく、普通のデータストリームなので、Discover、Lens、Dashboard、ES|QL、アラートルールがそのまま使えます。

属性の多くは、OpenTelemetry の GenAI セマンティック規約(gen_ai.*)に沿った名前です。今回の検証で確認できた主なものを挙げます。

属性意味実測の例
gen_ai.operation.name処理の種類chat、execute_tool、invoke_agent
gen_ai.request.model / gen_ai.response.modelモデル名anthropic-claude-5-sonnet
gen_ai.provider.nameプロバイダー名elastic
gen_ai.usage.input_tokens / output_tokens入力・出力トークン数(long 型)17,154 / 84
gen_ai.conversation.id会話の ID(既定では匿名化されたハッシュ)399b6330a91f4697
gen_ai.tool.name / gen_ai.tool.type / gen_ai.tool.call.idツール名、種類、呼び出し IDplatform.core.execute_esql、extension
gen_ai.input.messages / gen_ai.output.messages入出力メッセージプライバシー設定オフのため []

Elasticsearch の OTel データストリームでは、これらは attributes.gen_ai.request.model のように attributes. の下に入ります。スパン名は name フィールドで、span.name はその別名(alias)です。時間は duration にナノ秒で入ります。

ただし、すべてが標準というわけではありません。gen_ai.* の規約はまだ試験段階です(Elastic の公式ドキュメントでは Experimental、OTel のレジストリでは Development と表記されています)。また、attributes.elastic.inference.span.kind と CHAIN、AGENT、LLM、TOOL という分類は、elastic.* で始まる Elastic 独自の拡張です。つまり、保存形式は OTel、属性名は OTel の規約、スパンの分類は Elastic 独自、という 3 層になっています。

準備

トレース収集の設定を確認する

Stack Management → GenAI Settings → Agent Builder Traces を開きます。

  • Collect conversation traces(agentBuilder:tracing:enabled)は既定でオンです。オフになっていると View Trace アイコンが表示されません。
  • Advanced privacy settings は既定ですべてオフです。今回はオフのまま進めます。理由は第 8 章で説明します。
  • 同じ画面の Install Dashboard から、[Elastic] Agent Builder Overview ダッシュボードを導入します。ダッシュボードは自動では入りません。Space ごとに導入が必要です。

サンプルデータを入れる

Integrations を開き、Sample Data と検索して Sample eCommerce orders を Install data します。インデックス名は kibana_sample_data_ecommerce です。件数を Dev Tools で確認しておきます。

GET kibana_sample_data_ecommerce/_count

結果は 4,675 件でした。後で Agent の回答と比べるために、この数字を控えておきます。

四つの調査シナリオ

ここからが本題です。カスタム Agent やカスタムツールは作らず、標準の Elastic AI Agent だけを使います。モデルはチャット画面の既定のまま(表示は「Anthropic Claude Sonnet 5」)です。目的は回答の内容を評価することではなく、質問の種類を変えると、トレースがどう変わるかを比べることです。

シナリオ調べること使う質問
1ツールを使わない場合と使う場合の比較短い回答だけの質問と、件数を実データで調べる質問
2複数ツールを使った処理の遅延調査2 段階の集計
3ツールエラーの調査存在しないインデックスの検索
4長い会話のトークン増加調査同じ会話で 3 回質問

各質問の後、回答の下にある View Trace アイコンをクリックします。フライアウトのヘッダーに Trace ID、スパン数、合計時間が表示され、その下にウォーターフォールが並びます。各行にスパンの種類と時間、LLM 呼び出しの行には入力・出力トークン数が出ます。

シナリオ 1:ツールを使わない場合と使う場合の比較

まず基準値を取ります。ツールを使わない短い回答です。

「トレース基本テスト成功」とだけ回答してください。ツールは使用しないでください。

次に、ツールを 1 回使わせます。

kibana_sample_data_ecommerce のドキュメント数を、Elasticsearch の実データを検索して確認してください。推測では答えず、必ず利用可能なツールを使ってください。最終回答には件数と、検索に成功したかどうかだけを書いてください。

項目ツールなしツール 1 回
スパン数(View Trace)46
合計時間4,579 ms6,307 ms
回答を作る LLM 呼び出し1 回(4,168 ms)2 回(3,369 ms と 2,188 ms)
ツール呼び出し01 回(platform.core.execute_esql、31 ms)
入力/出力トークン(本体)17,154 / 8417,581 / 16
ステータスOkOk

この比較から 3 つのことが分かります。

1 つ目は、短い質問でも入力トークンが約 17,000 あることです。入力トークンは画面に入力した文章だけではありません。Agent のシステム指示、利用できるツールやスキルの定義も LLM への入力に含まれます。この約 17,000 トークンは、いわば Agent の「基本料金」に近い部分です。この値は Agent の設定、有効なツールやスキルの数、製品のバージョンによって変わります。

2 つ目は、ツールの前後で LLM が呼ばれることです。今回は、1 回目の LLM 呼び出しで Agent が「どのツールを、どんな引数で使うか」を決め、ツールを実行した後、2 回目で結果を読んで回答を作りました。Agent が複数のツールをまとめて選んだり、結果を見てやり直したりすると、回数は増えます。時間の内訳を見ると、ツールの実行は 31 ms、LLM 呼び出しは合わせて 5,557 ms です。この質問では、時間のほぼ全部が LLM 待ちでした。

3 つ目は、頼んでいない generate_title スパンがあることです。新しい会話の 1 ラウンド目には、会話のタイトルを自動で付ける処理が走ります。この処理は、回答を作るモデルとは別の軽いモデル(google-gemini-3.5-flash-lite、入力 249 / 出力 19 トークン)を使っていました。2 ラウンド目以降には出ません。

この軽いモデルは fast model と呼ばれます。Agent Builder は、会話タイトルの生成や ES|QL の生成のように深い推論がいらない処理を、既定のモデル(main model)ではなく fast model に自動で回します。回答を速くして、トークンのコストを下げるためです。Elastic Cloud Serverless と Cloud Hosted では、fast model は Elastic Managed LLMs であらかじめ用意されています。この振り分けはオフにできません。どのモデルが使われるかは環境によって違うので、実際のモデル名はトレースで確認してください。

シナリオ 2:複数ツールを使った処理の遅延調査

kibana_sample_data_ecommerce で、最も注文数が多い商品カテゴリ(category)を調べてください。次に、そのカテゴリの注文の平均金額(taxful_total_price の平均)も調べてください。2 つの結果を、それぞれどのクエリで確認したかと一緒に答えてください。

回答は「Men’s Clothing(2,024 件)」と「平均 60.97(対象 908 件)」でした。

トレースは 18 スパン、34,707 ms です。シナリオ 1 より質問が複雑になっただけで、スパン数は 6 から 18 に、時間は 6 秒から 35 秒に増えました。内訳は、回答を作る chat が 6 回、ツールが 5 回(attachments.read、get_index_mapping、generate_esql、execute_esql 2 回)、そして generate_esql の中の chat が 2 回です。

ツールの中で LLM が呼ばれている

ツリーを見ると、execute_tool platform.core.generate_esql(2,553 ms)の下に generate_esql スパンがあり、その下に chat google-gemini-3.5-flash-lite が 2 つ入っています。「自然言語を ES|QL に変える」という仕事には、通常 LLM などの生成処理が必要です。Agent 本体(claude-sonnet)は「今は ES|QL が必要だ」と判断してツールを呼ぶだけで、実際のクエリ作成はツールの中でfast model(今回はgoogle-gemini-3.5-flash-lite)に任せています。

つまり、ツールには 2 種類あります。ここでの「決定的」とは、同じ入力に対して基本的に同じ処理を行うという意味です。

ツールの種類例時間
決定的な処理を行うツールexecute_esql(27 ms)、get_index_mapping(15 ms)Elasticsearch に問い合わせて結果を返す。速い
内部で LLM を呼ぶツールgenerate_esql(2,553 ms)変換や生成のために LLM を呼ぶ。遅い

利用者の画面にはtool: platform.core.generate_esql ranと 1 行出るだけです。その中で別のモデルが 2 回動いていたことは、トレースを見ないと分かりません。「ツールが遅い」と思っていたら、実は中の LLM が遅かった、という切り分けができます。

入力トークンはラウンドの中でも増える

このラウンドの chat の入力トークンは、17,233 → 17,522 → 18,552 → 18,929 → 19,446 → 20,019 と増え続けました。ツールの結果が返るたびに、それが次の LLM 入力に追加されるためです。

なお、この実験では 1 回目の送信で画面に「Reasoning error: TypeError: network error」と表示されましたが、2回目に実行したら1回目と2回目の回答が無事に表示されました。問題は Agent の処理の外側(ブラウザへの配信経路のどこか)で起きたと考えられます。トレースは、記録された範囲で「Agent が何をしたか」を示しますが、「利用者の画面に何が起きたか」は範囲外です。

同じ質問をもう一度送ると

同じ会話で、同じ質問をもう一度送ってみました。今度は 3 スパン、9,915 ms、LLM 呼び出しは 1 回だけです。ツールは使っていません。Agent は「前回と同じ内容のご質問ですね」と答え、会話履歴から回答を作りました。入力トークンは 20,669 です。

「検索せずに履歴から答えた」ことが、ツールスパンが無いという形ではっきり見えます。回答が期待と違うときに、まず確認するべきポイントです。

シナリオ 3:ツールエラーの調査

Observability では、正常な処理だけでなく、失敗した処理を見つけられることが重要です。存在しないインデックスを指定して、読み取りクエリを 1 回だけ実行させます。

利用可能な ES|QL 実行ツールで、次のクエリを変更せずに 1 回実行し、結果またはエラーを説明してください。
FROM trace-test-missing-index-v1
| LIMIT 1
別のインデックスには置き換えないでください。

Agent はクエリをそのまま実行し、verification_exception: Unknown index [trace-test-missing-index-v1] というエラーを受け取りました。そして、インデックスが存在しないことが原因だと利用者に説明しました。

トレースは 7 スパン、14,610 ms です。execute_tool platform.core.execute_esql(14 ms)の status.code は Error でした。一方、ラウンド全体を表すルートの invoke_agent は Ok です。

ここで注目したいのは、ツールの失敗と会話の失敗は別の概念だという点です。ツールが失敗しても、Agent がそれを受け止めて説明できれば、会話としては成功です。監視するときは両方を見ます。ツールのエラー率は「壊れやすいツール」を見つけるため、Agent 実行のエラー率は「Agent が正常終了できなかった割合」を見るためです。

注意が 2 つあります。公式ドキュメントによると、execute_tool スパンの status.code が Error になるのは、主に引数やスキーマの検証エラーのときです。ツールの中でエラーを捕まえて、通常の結果として Agent に返した場合は Error になりません。今回の Unknown index は Elasticsearch が返した実行エラーでしたが Error として記録されました。ツールの実装によって扱いが違うので、status.code だけですべてのツール失敗を拾えるとは考えないほうが安全です。もう 1 つ、今回の設定(Include tool call details オフ)では、エラーの本文(Unknown index … という文字列)はトレースに保存されません。残るのは status.code = Error という事実だけです。

シナリオ 4:長い会話のトークン増加調査

新しい会話を 1 つ作り、その中で 3 回続けて質問しました。

kibana_sample_data_ecommerceにはどんなフィールドがありますか。主なものを10個だけ挙げてください。
そのうち、金額に関係するフィールドはどれですか。
その金額フィールドの中で、合計が一番大きいものはどれですか。実データで確認してください。

ラウンド最初の chat の入力トークンツールラウンド全体の時間(ES|QL で集計)
117,167get_index_mapping14,667 ms
218,595なし(履歴から回答)9,913 ms
319,095generate_esql、execute_esql24,925 ms

入力トークンが 17,167 → 18,595 → 19,095 と、ラウンドを重ねるごとに増えています。ラウンドを重ねると、過去の会話のコンテキストが入力に加わるためです。なお、公式ドキュメントによると、Agent Builder は長い会話を圧縮(compaction)します。そのため、常に全履歴がそのまま送られるわけではありません。

シナリオ 2 の「ツール結果で増える」と合わせると、今回の実験で入力トークンが増えた主な理由は 2 つでした。会話が長くなること、そして 1 ラウンドの中でツールを何度も使うことです。このほかにも、システム指示の変更、有効なツールやスキルの変更、添付ファイル、会話の圧縮、モデルや製品バージョンの変更で入力トークンは変わります。トークンの監視では、1 回の呼び出しだけでなく、会話単位の合計を見ることが大切です。

小さな注意:View Trace はすぐに開かない

2 ラウンド目の直後に View Trace を開くと、「No spans found」と表示されました。少し待って開き直すと、3 スパンが表示されました。トレースの書き込みは非同期です。特に、ラウンド全体を表すルートの invoke_agent は、Agent の処理が終わった後に閉じられて書き込まれます。回答直後に開くと、このスパンや、終わったばかりのスパンがまだ入っていないことがあります。数字を記録するときは、少し待ってから ES|QL で数え直すのが確実です。

ダッシュボードで環境全体を見る

1 件の回答を詳しく調べるなら View Trace、複数の会話をまとめて監視するなら [Elastic] Agent Builder Overview ダッシュボードを使います。

ダッシュボードは 4 つのセクションに分かれています。今回の検証(会話ラウンド 10 回、約 19 分)の値を並べます。

セクション確認できること今回の値
Token Usage & Cost入出力トークン合計、LLM リクエスト数、モデル別・プロバイダー別の内訳入力 571,179、出力 11,067、リクエスト 40(sonnet 27、gemini 13)
Conversation Volume & Latency会話ラウンド数、平均・p95・最大の処理時間10 ラウンド、平均 20.75 秒、p95 48.50 秒、最大 59.79 秒
Agent ExecutionAgent 別の実行回数と処理時間elastic-ai-agent 10 回、平均 17.37 秒
Tool Call Frequency & Errorsツール呼び出し数、エラー数、成功率、平均時間、ツール別の内訳17 回、エラー 1、成功率 94.1%、平均 2.78 秒

読み取れることが 3 つあります。

  • 入力トークン 571,179 に対して、出力は 11,067 です。入力が出力の 50 倍以上あります。コストを考えるなら、入力側を見る必要があります。
  • Conversation(ラウンド全体)の平均 20.75 秒と Agent Execution の平均 17.37 秒には約 3.4 秒の差があります。ラウンドには、Agent の実行のほかに、タイトル生成や回答の保存といった処理が含まれるためです。
  • ツールの成功率 94.1% は黄色で表示されています。ダッシュボードの定義では、90% 未満が赤、90〜99% が黄、99% 以上が緑です。シナリオ 3 で意図的に起こした 1 回のエラーが、そのまま反映されています。
  • リクエスト 40 回のうち gemini の 13 回は、fast model に回ったタイトル生成と ES|QL 生成です。モデル別に見ると、どの処理が既定のモデルを使い、どの処理が fast model を使ったかが分かれます。

このダッシュボードは、すべてのパネルが ES|QL で作られています。データストリーム名が traces-agent_builder.otel-default と直接書かれているため、Space ごとに導入する必要があります。ダッシュボードは Elastic が管理する読み取り専用(Managed)です。自社向けに変えたいときは、複製してから編集します。パネルのクエリは、自分のダッシュボードを作るときの手本になります。

小さなコツを 1 つ。データが見えないときは、まず Time Picker を広げてください。既定の Last 15 minutes のままだと、少し前の会話は範囲外になります。

※セルフマネージドなど fast model がない環境では、タイトル生成や ES|QL 生成も既定のモデルで動きます。そのため、トレースに出るモデル名やトークン数は、この記事の値と違うことがあります。

実務で使う ES|QL 3 本

トレースは普通のデータストリームです。ここでは、調査でよく使う 3 本のクエリと、その実測結果を紹介します。

ラウンド(トレース)ごとの集計

FROM traces-agent_builder.otel-default
  | WHERE @timestamp > NOW() - 3 hours
  | STATS
      spans = COUNT(*),
      llm_calls = COUNT(*) WHERE `span.name` LIKE "chat *",
      tool_calls = COUNT(*) WHERE `span.name` LIKE "execute_tool *",
      errors = COUNT(*) WHERE status.code == "Error",
      input_tokens = SUM(TO_LONG(attributes.gen_ai.usage.input_tokens)),
      output_tokens = SUM(TO_LONG(attributes.gen_ai.usage.output_tokens)),
      total_ms = MAX(duration) / 1000000,
      started = MIN(@timestamp)
      BY trace_id
  | SORT started ASC

この 1 本のクエリで、ラウンドごとの違いが一目で分かります。

ポイントは 3 つです。span.name は name の別名で、公式ドキュメントに合わせてバッククォートで囲っています。duration の単位はナノ秒なので、1,000,000 で割ってミリ秒にしています。トークン数は公式ドキュメントに合わせて TO_LONG() で包んでいます。今回の環境では long 型だったので直接 SUM() もできましたが、型の違いに備えて公式例と同じ書き方にしています。

ツール別の呼び出し回数とエラー数

FROM traces-agent_builder.otel-default
  | WHERE @timestamp > NOW() - 3 hours AND `span.name` LIKE "execute_tool *"
  | EVAL duration_ms = duration / 1000000
  | STATS calls = COUNT(*),
    errors = COUNT(*) WHERE status.code == "Error",
    avg_ms = AVG(duration_ms)
  BY `span.name`
| SORT calls DESC

モデル別のトークン使用量

FROM traces-agent_builder.otel-default
  | WHERE @timestamp > NOW() - 3 hours AND `span.name` LIKE "chat *"
  | STATS requests = COUNT(*),
    input_tokens = SUM(TO_LONG(attributes.gen_ai.usage.input_tokens)),
    output_tokens = SUM(TO_LONG(attributes.gen_ai.usage.output_tokens))
  BY attributes.gen_ai.request.model, attributes.gen_ai.provider.name

最初に作るアラート 2 つ

ES|QL が書けるということは、アラートも作れるということです。Stack Management → Rules → Create rule で Elasticsearch query ルールを選び、クエリ言語に ES|QL を指定します。公式ドキュメントには 4 つの例がありますが、最初に必要なのは次の 2 つです。

会話ごとのトークン上限

1 つの会話の入出力トークン合計が上限を超えたら通知します。公式ドキュメントの例では 256,000 です。「Create an alert for each row」を選ぶと、会話ごとに 1 件のアラートになります。

FROM traces-agent_builder.otel-default
  | WHERE `span.name` LIKE "chat *"
  | STATS input_tokens = SUM(TO_LONG(attributes.gen_ai.usage.input_tokens)),
    output_tokens = SUM(TO_LONG(attributes.gen_ai.usage.output_tokens))
    BY attributes.gen_ai.conversation.id
  | EVAL total_tokens = input_tokens + output_tokens
  | WHERE total_tokens > 256000

ツールの失敗

特定のツールの失敗が 5 回を超えたら通知します。シナリオ 3 のエラーは、この条件で拾えました。ただし、シナリオ 3 で書いたとおり、ツールの中で捕まえて通常の結果として返されたエラーは status.code に出ません。このアラートは一部のツール失敗を見逃す可能性があります。

FROM traces-agent_builder.otel-default
| WHERE `span.name` LIKE "execute_tool *" AND status.code == "Error"
| STATS failures = COUNT(*) BY `span.name`
| WHERE failures > 5

作るときの注意点と、応用の例

公式の例を少し変えるだけで、運用に合わせた検知も作れます。たとえば、6-1 のクエリの BY を trace_id にして llm_calls = COUNT(*) を加えれば、「1 ラウンドで LLM 呼び出しが 15 回を超えた」という暴走の検知になります。また、今回の環境では attributes.user.hash(匿名化された利用者の識別子)がトレースに入っていたので、これで集計すれば「利用者ごとのトークン上限」も作れます。ただし、このフィールドは執筆時点の公式ドキュメントには載っていないため、使う前に

GET traces-agent_builder.otel-default/_field_caps?fields=attributes.user.*

で自分の環境を確認してください。

もう 1 つ、Elastic Workflows(決まった手順を自動で実行する機能)と組み合わせる使い方があります。Workflow から夜間に Agent を何十回も呼ぶような運用では、誰も画面を見ていません。トークンの使いすぎやツールの連続失敗をアラートで知らせる仕組みが必要になります。上の 2 つのアラートはそのまま使えます。Workflow ツールのスパン名や、Workflow から呼び出した Agent の記録単位は今回は検証していないので、次回の記事で確認します。

閾値は、ダッシュボードで 1 週間ほど傾向を見てから決めます。今回の検証では 19 分で約 58 万トークンでした。

プライバシー設定とアクセス権

Agent Builder のトレース収集は既定で有効です。ただし、既定で保存するのは処理時間、モデル名、トークン数、ステータスといった構造的なメタデータです。会話の内容を保存する Advanced privacy settings は、管理者が明示的にオンにしない限りオフです。

今回の環境では、Advanced privacy settings に 7 つの項目がありました。

設定オンにすると保存されるもの主なリスク
Include user prompts in traces利用者の質問個人情報、顧客情報
Include LLM responses in tracesAgent の回答検索結果や機密情報の再保存
Include tool call details in tracesツールの引数と結果クエリ、取得文書、外部 API の結果
Include system prompt in tracesAgent のシステム指示内部ルールやプロンプト設計の露出
Include real tool, agent, and conversation names in tracesカスタム Agent・ツールの実名、会話タイトル内部構成の特定
Include real conversation and workflow IDs in traces実際の ID会話や利用者との関連付け
Include user data in traces実際のユーザー ID とユーザー名利用者の特定

執筆時点の公式ドキュメントとは 2 か所で違いがありました。5 番目の項目は、公式ドキュメントでは「Include real tool and agent names in traces」で、会話の名前については書かれていません。7 番目の「Include user data in traces」は、公式ドキュメント(6 項目)にはありません。画面の説明では、既定では実際のユーザー ID とユーザー名を省き、相関用の安定したハッシュ(user.hash)だけを残すとありました。どちらも Serverless の画面が先行している可能性があります。自分の環境の画面で確認してください。

匿名化の仕組みも押さえておきます。カスタムのツール、Agent、Workflow の名前は custom という文字に置き換わります。ID は安定したハッシュに置き換わるので、実名が分からなくても同じ会話のスパンをグループ化できます。Elastic 組み込みのツールと Agent は、常に実名で記録されます。

今回の検証では、7 項目すべてオフでした。この状態でも、処理時間、モデル、トークン数、ステータスは確認できました。一方、スパンの詳細では gen_ai.input.messages が [](空)になっています。これは不具合ではなく、設定が反映された結果です。

トレースと会話履歴は別の保存先

トレース側で利用者の入力を保存しなくても、Agent Builder の会話履歴には本文が残ります。個人情報を扱う場合は、トレースの設定だけでなく、会話の保持・共有・削除の方針も別に決める必要があります。

アクセス権は「インデックス単位」

Agent Builder のトレースは、traces-agent_builder.otel-<Space ID> というデータストリームに保存されます。通常の閲覧権限だけでは、「自分の会話のトレースだけを読む」ようには自動で分離されません。そのため、トレースを調査する担当者を決め、データへの権限をロールで管理できます。

Elastic Cloud Serverless では、Admin and settings → Custom roles → Create role を開きます。Index privileges の対象に traces-agent_builder.otel-*、権限に read と view_index_metadata を指定します。Discover やダッシュボードで調査する担当者には、Kibana 側で対象 Space の Discover: Read と Dashboard: Read も付けます。Agent Builder を利用させる必要がなければ、その機能の権限は None で構いません。

このロールを追加しても、担当者が別のロールから持っている広い権限は消えません。実際の閲覧範囲は、割り当て済みのロールを合わせて確認してください。また、トレースの生データにはプロジェクト名や ID などが含まれるため、社外に共有する際は確認とマスクが必要です。

参考資料

本記事は、以下の Elastic 公式ドキュメントと OpenTelemetry の仕様に基づいています。