🚀

Amazon Bedrock AgentCore Observability では一体何が行われているのか

に公開

こんにちは! AWS でソリューションアーキテクトをしている Onishi です。

本ブログは AWS AI Agent ブログ祭り(Zenn: #awsaiagentblogfes, X: #AWS_AI_AGENT_ブログ祭り)の第 11 日目です。

2025/11/6 に開催された AWS 秋のオブザーバビリティ祭り 2025 〜最新アップデートと生成 AI × オブザーバビリティ〜 にて、「Amazon Bedrock AgentCore で実現!お手軽 AI エージェントオブザーバビリティ」というタイトルでお話ししてきました。

  • AI エージェントにオブザーバビリティが重要な理由
  • Amazon Bedrock AgentCore の包括的なダッシュボード (CloudWatch GenAI Observability) を通じて簡単にオブザーバビリティを獲得できること

をご紹介しました。

発表スライドはこちらにあるので興味がある方は是非ご一読ください。

Bedrock AgentCore では AI エージェントフレームワーク (LangGraph、LlamaIndex、Strands Agents など) からのテレメトリも、OpenTelemetry のゼロコード計装によって簡単に CloudWatch GenAI Observability に連携できます。
サクッと使う上では特に意識しなくても連携できるのですが、本記事ではこれがどのように連携されているのかを深ぼってみます。

チュートリアルの詳解 - Python ゼロコード計装の中身を知る

GitHub に公開している AWS 公式の Amazon Bedrock AgentCore samples のチュートリアルでは Bedrock AgentCore Starter ToolKit を使っており、ユーザーは何も意識せずとも CloudWatch GenAI Observability ダッシュボードで可視化できるようになっています。

実際連携されるまでに、何が行われているのでしょうか?

AgentCore Starter ToolKit で作成される Dockerfile

つい先日直接コードを AgentCore Runtime にデプロイできるアップデートがありましたが、2025/11/7 時点では AgentCore Starter ToolKit で作成される AgentCore Runtime は、ECR からコンテナイメージを pull する形で起動されます。

AgentCore Starter ToolKit を使うと、AgentCore Runtime の設定 (実行ロールやネットワーク設定) とコンテナイメージのビルド設定 (CodeBuild を使うか、ローカル環境でビルドするか) などを指定する .bedrock_agentcore.yaml とビルドするコンテナイメージの Dockerfile を生成し、それに基づいて AgentCore Runtime の作成までやってくれます。

2025 年 11 月 7 日現在で最新の AgentCore Starter ToolKit バージョン v0.1.5 で生成される Dockerfile を見てみると、observability_enabled が True (デフォルトで True) の場合には ADOT (AWS Distro for OpenTelemetry) の aws-opentelemetry-distro パッケージをインストールした上で、ゼロコード計装が設定されていることがわかります。

生成される Dockerfile の内容

ゼロコード計装の設定 (AgentCore Runtime の環境変数)

OpenTelemetry における Python のゼロコード計装はドキュメントにもある通り、設定プロパティを Python コードの実行時に CLI から渡すか、環境変数で設定でき、これによって取得される OpenTelemetry のテレメトリをどこにどのように送るかを指定できます。

上述のように AgentCore Starter ToolKit で生成される Dockerfile の CMD 命令箇所を見てみると、特に CLI から設定プロパティが渡されているわけではないので、AgentCore Runtime の実行環境ではゼロコード計装を行う際の環境変数が設定されているはずです。

{% if observability_enabled %}
CMD ["opentelemetry-instrument", "python", "-m", "{{ agent_module_path }}"]

AgentCore Runtime 以外でホスティングされる AI エージェントで AgentCore Observability を使用する場合のドキュメントを確認してみると、まさにこうした Python のゼロコード計装に関しての環境変数を明示的に指定する必要があると記載されています。

export AGENT_OBSERVABILITY_ENABLED=true # Activates the ADOT pipeline
export OTEL_PYTHON_DISTRO=aws_distro # Uses AWS Distro for OpenTelemetry
export OTEL_PYTHON_CONFIGURATOR=aws_configurator # Sets AWS configurator for ADOT SDK
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf # Configures export protocol
export  OTEL_EXPORTER_OTLP_LOGS_HEADERS=x-aws-log-group=<YOUR-LOG-GROUP>,x-aws-log-stream=<YOUR-LOG-STREAM>,x-aws- metric-namespace=<YOUR-NAMESPACE>
# Directs logs to CloudWatch groups
export OTEL_RESOURCE_ATTRIBUTES=service.name=<YOUR-AGENT-NAME> # Identifies your agent in observability data

そこで、試しに Bedrock AgentCore samples のチュートリアルの strands_claude.py にゼロコード計装に関する環境変数を取得するような変更を加え、AgentCore Runtime で実行しその結果のログを見てみると...

strands_claude.py の修正内容
from strands import Agent, tool
from strands_tools import calculator # Import the calculator tool
import argparse
import json
+ import os
from bedrock_agentcore.runtime import BedrockAgentCoreApp
from strands.models import BedrockModel

app = BedrockAgentCoreApp()

...<中略>...

@app.entrypoint
def strands_agent_bedrock(payload):
    """
    Invoke the agent with a payload
    """
    user_input = payload.get("prompt")
    print("User input:", user_input)
+     print("AGENT_OBSERVABILITY_ENABLED", os.getenv('AGENT_OBSERVABILITY_ENABLED'))
+     print("OTEL_PYTHON_DISTRO", os.getenv('OTEL_PYTHON_DISTRO'))
+     print("OTEL_PYTHON_CONFIGURATOR", os.getenv('OTEL_PYTHON_CONFIGURATOR'))
+     print("OTEL_EXPORTER_OTLP_PROTOCOL", os.getenv('OTEL_EXPORTER_OTLP_PROTOCOL'))
+     print("OTEL_EXPORTER_OTLP_LOGS_HEADERS", os.getenv('OTEL_EXPORTER_OTLP_LOGS_HEADERS'))
+     print("OTEL_RESOURCE_ATTRIBUTES", os.getenv('OTEL_RESOURCE_ATTRIBUTES'))
    response = agent(user_input)
    return response.message['content'][0]['text']

if __name__ == "__main__":
    app.run()

実行結果のログで以下のように出力されました。
AgentCore Runtime の場合、ユーザー側で特段指定せずとも ADOT によるゼロコード計装に関する環境変数設定がなされていることがわかります。

AGENT_OBSERVABILITY_ENABLED true
OTEL_PYTHON_DISTRO aws_distro
OTEL_PYTHON_CONFIGURATOR aws_configurator
OTEL_EXPORTER_OTLP_PROTOCOL http/protobuf
OTEL_EXPORTER_OTLP_LOGS_HEADERS x-aws-log-group=/aws/bedrock-agentcore/runtimes/<runtime の ID>,x-aws-log-stream=otel-rt-logs,x-aws-metric-namespace=bedrock-agentcore
OTEL_RESOURCE_ATTRIBUTES service.name=<runtime 名>.DEFAULT,aws.log.group.names=/aws/bedrock-agentcore/runtimes/<runtime の ID>,aws.log.stream.names=otel-rt-logs,deployment.environment.name=bedrock-agentcore:default,cloud.resource_id=<runtime の ARN>,cloud.platform=aws_bedrock_agentcore,cloud.provider=aws,cloud.region=<リージョン>

ADOT のゼロコード計装では何が起きているのか?

設定されている環境変数はわかりましたが、「結局なんでこの設定で AgentCore Observability として CloudWatch や X-Ray にメトリクス、ログ、トレースを送れているんだ?」という部分は未だわかりません。

ゼロコード計装にしても手動計装にしても、OpenTelemetry のテレメトリの送信先を指定するには

  • エクスポーターの形式の指定
    • OTEL_TRACES_EXPORTER
    • OTEL_METRICS_EXPORTER
    • OTEL_LOGS_EXPORTER
  • 送信先エンドポイントの指定
    • OTEL_EXPORTER_OTLP_TRACES_ENDPOINT
    • OTEL_EXPORTER_OTLP_METRICS_ENDPOINT
    • OTEL_EXPORTER_OTLP_LOGS_ENDPOINT

といった環境変数が設定されている必要があります。

ここでキーになっているのが、AGENT_OBSERVABILITY_ENABLED という環境変数です。
2025 年 11 月 7 日現在で最新の ADOT ゼロコード計装バージョン v0.12.1 で確認すると以下のような実装が確認できます。

環境変数の確認処理

AGENT_OBSERVABILITY_ENABLED が true となっている時、以下のように is_agent_observability_enabled() が true を返します。
https://github.com/aws-observability/aws-otel-python-instrumentation/blob/v0.12.1/aws-opentelemetry-distro/src/amazon/opentelemetry/distro/_utils.py#L36-L38

AGENT_OBSERVABILITY_ENABLED が true のとき、AI エージェントのオブザーバビリティを実現するための設定がなされます。

全貌はソースコードを見てもらえればと思いますが、ここではいくつか重要な設定項目について見ていきます。

1. テレメトリの送信先設定

エクスポーターには以下の環境変数が設定され

  • OTEL_TRACES_EXPORTER=otlp - ゼロコード計装で作成されたトレースを OTLP プロトコルで送信
  • OTEL_LOGS_EXPORTER=otlp - ゼロコード計装で作成されたログを OTLP プロトコルで送信
  • OTEL_METRICS_EXPORTER=awsemf - ゼロコード計装で作成されたメトリクスを EMF (Embedded Metric Format) で送信

OTLP エンドポイントには以下の環境変数が設定されます、

  • OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://xray.{region}.amazonaws.com/v1/traces
  • OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=https://logs.{region}.amazonaws.com/v1/logs
実装箇所
(補足) OpenTelemetry のテレメトリの送信方法

OpenTelemetry で生成したテレメトリをオブザーバビリティバックエンドへ送信する際には、ドキュメント にあるように大別して下記 3 種の方法があります。

  • No Collector: OpenTelemetry コレクターなしで SDK から直接バックエンドへ送信
  • Agent: OpenTelemetry コレクターをサイドカーや DaemonSet でアプリケーションと同じホスト上に置き、OpenTelemetry コレクター経由でバックエンドへ送信
  • Gateway: OpenTelemetry コレクターをスタンドアロンサービスとして実行し、OpenTelemetry コレクター経由でバックエンドへ送信

つまり、AgentCore Runtime での自動計装は AI エージェントの計装として OTEL_EXPORTER_OTLP_TRACES_ENDPOINTX-Ray の OTLP エンドポイント, OTEL_EXPORTER_OTLP_LOGS_ENDPOINTCloudWatch Logs の OTLP エンドポイント が設定されており、No Collector 型で直接 X-Ray, CloudWatch にテレメトリを送信していることがわかります。

2. LLM の入出力のキャプチャ設定を有効化

  • OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true - LLM の入出力メッセージを自動的にキャプチャする設定を有効化
実装箇所

OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT を有効化することで、Amazon Bedrock, OpenAI, Google Vertex AI など様々な LLM のプラットフォームでの入出力を取得できるよう計装が実装されています。(下記は Python の例)

https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/instrumentation-genai

Amazon Bedrock での LLM のやり取りに関しては、botocore (AWS サービスを Python から呼び出す低レベル API ライブラリ) の拡張として入出力のキャプチャが有効化できるようになっています。
ADOT のゼロコード計装では、これが自動的に取得されるようになっています。

実装箇所

3. トレースから LLM の入出力削除と GenAI Events への変換

キャプチャした LLM の入出力をイベントとして保存するかスパンの属性に保存にするかは議論がなされ、一度はイベントとして保存する方針に固まったものの、トレースを見る際に一緒に可視化できる便利さもあって実際にはスパンの属性として保存される例が多いようです。
一方で、これまでのトレースを保存するようなバックエンドでは、LLM の入出力のような大きなデータや機密データを含むやり取りを保存するような想定ではない場合が多いという課題もあります。
https://github.com/open-telemetry/semantic-conventions/issues/2010

ADOT のゼロコード計装ではどのような方法を採用しているかというと、上記計装 (OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT) で自動的にスパンの属性に含まれるようになった LLM の入出力を、スパン属性からは除去した上で、イベントとして抽出し CloudWatch Logs へログとして送信しています。
スパンと LLM の入出力の保存先は別れる形になりますが、同じスパン ID を持っているので、CloudWatch 側でトレースを可視化する時には同じ画面から確認ができるようになっています。

上記 3 の実装箇所

4. リソース属性の追加

テレメトリのリソース属性に aws.service.type が存在しない場合、その値として "gen_ai_agent" が設定されます。

上記 4 の実装箇所

X-Ray の OTLP エンドポイントにトレースを送信すると、CloudWatch Logs の aws/spans ロググループにトレースデータが保管されます。
これによって、従来の X-Ray では実現できなかったトレースの長期保管ができ、かつトレースの可視化も Transaction Search によって可能となっています。

X-Ray の OTLP エンドポイントや Transaction Search は、CloudWatch GenAI Observability のみならず CloudWatch の APM 機能である Application Signals でも活用され、同じロググループ (aws/spans) に Application Signals のトレースも保存されています。

CloudWatch GenAI Observability のダッシュボードでは AI エージェントに関するトレースだけを可視化できるようになっていますが、それはダッシュボードの検索条件にこのリソース属性 aws.service.type = "gen_ai_agent" を使って AI エージェントのトレースのみを特定しているからです。

まとめ

AgentCore Observability において AI エージェントのエンドツーエンドのオブザーバビリティがどのように実現されているかを解説してきました。

以下のような図にまとめられます。

  • AI エージェントに対して ADOT のゼロコード計装を用いることで X-Ray, CloudWatch に自動的にメトリクス、ログ、トレースが連携されること
  • AgentCore Runtime 上で AI エージェントをホスティングしたり、その他のホスティング環境でも AI エージェント用の ADOT に関する環境変数設定をすることで、AI エージェントのテレメトリに特化した設定になること
  • とりわけ重要な設定項目
    • No Collector 型で AI エージェントから直接 CloudWatch, X-Ray にテレメトリを送信していること
    • LLM の入出力をキャプチャした上で、スパンからの除去とイベントとしての抽出をしていること、それぞれ X-Ray と CloudWatch Logs の OTLP エンドポイントに送信された後、CloudWatch GenAI Observability ダッシュボードでは同一のスパン ID を相関付けて統合的に可視化できること
    • スパン属性に aws.service.type="gen_ai_agent" を付与することで CloudWatch GenAI Observability ダッシュボードで AI エージェントのテレメトリとして可視化できるようにしていること

を説明しました。
この情報が何かのお役に立てば幸いです!

アマゾン ウェブ サービス ジャパン (有志)

Discussion