こんにちは、YAMALEX駿です。
YAMALEX は Acroquest 社内で発足した、会社の未来の技術を創る、機械学習がメインテーマのデータサイエンスチームです。
(詳細はリンク先をご覧ください。)
AIエージェントの開発において、本番環境での動作を監視し、パフォーマンスを測定・デバッグすることは非常に重要です。
Amazon Bedrock AgentCore Observability を活用することで、エージェントの動作を詳細に追跡し、問題を迅速に特定できるようになります。
本記事では、Strands Agentsフレームワークを使用して、OpenTelemetryによる自動計装を実装し、CloudWatchでエージェントの動作を可視化する方法について解説します。
AgentCore Runtimeを使うと自動で有効になる機能ではありますが、Runtimeを使わないときでも、 Strands Agentsを使ってOpenTelemetryを有効にするだけで、すべての情報が一元管理できるのは大きなメリットです。
1. はじめに
1.1. Amazon Bedrock AgentCore Observabilityとは
Amazon Bedrock AgentCore Observability は、AIエージェントのパフォーマンスを本番環境で追跡、デバッグ、監視するための機能です。
主な機能:
| No | 項目 | 説明 |
|---|---|---|
| 1 | 詳細なワークフロー可視化 | エージェントの実行パス、中間出力、パフォーマンスのボトルネックや障害を検査できます |
| 2 | リアルタイムダッシュボード | Amazon CloudWatchを活用したダッシュボードで、セッション数、レイテンシー、実行時間、トークン使用量、エラー率などの主要メトリクスをリアルタイムで確認できます |
| 3 | OpenTelemetry互換 | 標準化されたOpenTelemetry(OTEL)形式でテレメトリーデータを出力するため、既存の監視・可観測性スタックと容易に統合できます |
| 4 | 自動収集 | デフォルトでエージェント、ゲートウェイリソース、メモリリソースの主要メトリクスを自動的に出力します |
| 5 | カスタム計装 | エージェントコードを計装することで、追加のスパンやトレースデータ、カスタムメトリクス、ログを提供できます |
すべてのメトリクス、スパン、ログはAmazon CloudWatchに保存され、統合的に管理できます。
1.2. OpenTelemetryとは
OpenTelemetry(OTel)は、アプリケーションから以下の3種類のテレメトリーデータを収集・送信する統一的な方法を提供するオープンソースのフレームワークです。
| No | 項目 | 説明 |
|---|---|---|
| 1 | トレース(Traces) | リクエストの流れを追跡し、各処理ステップの実行時間や依存関係を記録 |
| 2 | メトリクス(Metrics) | システムのパフォーマンス指標(レイテンシー、エラー率、リソース使用量など)を数値化して収集 |
| 3 | ログ(Logs) | イベントやエラーの詳細情報を構造化されたメッセージとして記録 |
OpenTelemetryの最大の利点はベンダー中立性です。
一度コードにインストゥルメント(計測コード)を組み込めば、AWS X-Ray、Datadog、New Relic、Jaeger、Prometheusなど、任意のモニタリングサービスにデータを送信できます。
監視ツールを変更する際も、アプリケーションコードを書き換える必要がありません。
これにより、ベンダーロックインを避け、柔軟な監視基盤を構築できます。
1.3. Strands Agentsとは
Strands Agentsは、本番環境に対応した自律的なAIエージェントを構築するためのオープンソースSDKです。
簡単な使い方についてはこちらの記事をご参照ください。
Strands AgentsはOpenTelemetry(OTEL)に標準対応しており、エージェントの動作を完全に可視化できます。
デフォルトではOpenTelemetryは無効化されていますが、簡単な環境変数の設定とコマンド実行により、
LLM呼び出し、ツール実行、意思決定プロセスをすべて分散トレースとして自動的に記録し、CloudWatchと統合できます。
今回は Strands Agents、OpenTelemetry、AgentCore Observability を組み合わせて、どのような情報が記録されるのか、どのように可視化されるのかを実際に確認していきます。
2. 構成
2.1. アーキテクチャ概要
本記事では、以下のようなアーキテクチャを構築します:
2.2. 主要コンポーネント
1. Strands Agents
- AIエージェントのロジックを実装するフレームワーク
- OpenTelemetryトレースを自動的に出力可能
- モデルファーストのアプローチで本番環境対応のエージェントを構築
2. AWS Distro for OpenTelemetry (ADOT)
3. Amazon CloudWatch
- CloudWatch Logs: 生のトレース情報(送信したプロンプト、使用したモデル、実行結果など)を保存
- Observability Dashboard: エージェント実行データの可視化、トレース分析、カスタムスパンメトリクス、エラー分析などを提供
2.3. セットアップ手順
2.3.1. CloudWatch Transaction Search の有効化
まず、CloudWatch Transaction Searchを有効にする必要があります。これはAWSアカウントごとに一度だけ実施する設定です。
CloudWatchコンソールでの有効化手順
- CloudWatchコンソールを開く
- ナビゲーションペインで「Application Signals (APM)」を展開し、「Transaction search」を選択
- 「Enable Transaction Search」を選択
- 「ingest spans as structured logs」のチェックボックスを選択
- (オプション)「X-Ray trace indexing」でインデックス化する割合を変更(デフォルトは1%、必要に応じて増やすことでより詳細な分析が可能)
- 「Save」を選択
2.3.2. エージェントコードの計装
Strands Agentsを使ったエージェントでOpenTelemetryを有効にするには、以下の手順を実施します:
1. 依存関係の追加
以下のパッケージをインストールします。
pip install strands-agents strands-agents-tools aws-opentelemetry-distro>=0.10.0
2. エージェントコードの作成
以下は、今回の検証用に作成した、AWS操作を実行できるエージェントの実装例です。
class AWSAgent: """AWSの操作、ドキュメンテーション検索を行うエージェント""" def __init__(self): # Bedrock Claude モデルの設定 model = BedrockModel( model_id='us.anthropic.claude-sonnet-4-5-20250929-v1:0', streaming=True, additional_request_fields={ 'thinking': {'type': 'enabled', 'budget_tokens': 4000}, 'anthropic_beta': ['interleaved-thinking-2025-05-14'], }, region_name='us-west-2', cache_prompt='default', cache_tools='default', ) # エージェントのシステムプロンプト prompt = dedent("""\ use use_aws tool to perform user requests. use us-west-2 if not otherwise specified always get verification from user before executing write operations. reask user until you get a concrete go sign. AWS Knowledge MCP can help you with detailed knowledge of AWS. """) # AWS Knowledge MCPクライアントの初期化 aws_mcp = MCPClient(lambda: streamablehttp_client('https://knowledge-mcp.global.api.aws')) aws_mcp.__enter__() weakref.finalize(self, aws_mcp.__exit__, None, None, None) # エージェントの初期化 self.__agent = Agent( model=model, system_prompt=prompt, tools=[*aws_mcp.list_tools_sync(), use_aws, current_time, sleep], ) def __call__(self, prompt): """エージェントを同期実行する""" return self.__agent(prompt) class AWSAgentGUI: """エージェントと会話するためのGUI""" # Tkinterで実装しました。詳細略 def set_session_context(session_id): """ OpenTelemetry用のセッションコンテキストを設定する この関数は、トレースとログをセッションIDで関連付けるために使用します。 baggageを使用することで、すべての子スパンに自動的にセッションIDが伝播されます。 Args: session_id (str): 一意のセッション識別子 Returns: token: コンテキストトークン(後でdetachするために使用) """ ctx = baggage.set_baggage('session.id', session_id) token = context.attach(ctx) logger.info("session id '%s' attached to telemetry context", session_id) return token def main(): """メイン関数""" # 一意のセッションIDを生成 session_id = str(uuid.uuid4()) context_token = set_session_context(session_id) try: agent = AWSAgent() root = tk.Tk() _ = AWSAgentGUI(root, agent) root.mainloop() except Exception as e: logger.exception('Application error') messagebox.showerror('エラー', f'アプリケーションエラー: {e!s}') finally: # セッション終了時にコンテキストをデタッチ context.detach(context_token) logger.info("session context for '%s' detached", session_id)
コードのポイント:
set_session_context(): OpenTelemetryのbaggageを使用してセッションIDを設定し、すべてのトレースに紐付けます
3. OpenTelemetry自動計装でエージェントを実行
今回はOpenTelemetryがどのようなものなのかを確認する、というのも込みで、 Agent Core Runtimeを使わず、ローカルから実行する手順を示します。
以下の手順でOpenTelemetryを有効にしてエージェントを実行します:
ステップ1: 環境変数を設定
プロジェクトのルートディレクトリに .env ファイルを作成し、以下の環境変数を設定します:
# OpenTelemetry基本設定
OTEL_PYTHON_DISTRO=aws_distro
OTEL_PYTHON_CONFIGURATOR=aws_configurator
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
OTEL_TRACES_EXPORTER=otlp
# CloudWatch Logs設定
# ${LOG_GROUP_NAME}と${SERVICE_NAME}は実際の値に置き換えてください
OTEL_EXPORTER_OTLP_LOGS_HEADERS=x-aws-log-group=${LOG_GROUP_NAME},x-aws-log-stream=default,x-aws-metric-namespace=bedrock-agentcore
OTEL_RESOURCE_ATTRIBUTES=service.name=${SERVICE_NAME}
AGENT_OBSERVABILITY_ENABLED=true
ステップ2: OpenTelemetryを有効にしてエージェントを実行
以下のコマンドでエージェントを起動します:
opentelemetry-instrument python my_agent.py
このコマンドにより、OpenTelemetryが自動的に以下を実行します:
- Pythonコードの自動計装
- トレース、メトリクス、ログの収集
- CloudWatchへのデータ送信
特別なコード変更は不要で、opentelemetry-instrumentコマンドでラップするだけで自動計装が有効になります。
2.4. 可観測性の重要な概念
AgentCore Observabilityでは、以下の3つの階層的な概念でエージェントの動作を理解します:
1. セッション (Sessions)
- ユーザーとエージェント間の完全な対話コンテキストを表現
- 複数のやり取り(トレース)にわたって状態とコンテキストを維持
- ユーザーエンゲージメントパターンの高レベルビューを提供
- 例:ユーザーがエージェントとの会話を開始してから終了するまでの全体
2. トレース (Traces)
- 単一のリクエスト-レスポンスサイクルの詳細な記録
- 内部処理ステップ、外部サービス呼び出し、意思決定ポイント、リソース使用率を含む実行パス全体をキャプチャ
- 例:ユーザーからの1つの質問に対するエージェントの処理全体
3. スパン (Spans)
- エージェントの実行フロー内の個別の測定可能な作業単位
- 各スパンは以下の情報を持ちます:
- 例:単一のLLM API呼び出し、単一のツール実行
階層構造:
セッション ├─ トレース1(ユーザー質問1への応答) │ ├─ スパン1(LLM呼び出し) │ ├─ スパン2(ツール実行) │ └─ スパン3(LLM呼び出し) ├─ トレース2(ユーザー質問2への応答) │ ├─ スパン1(LLM呼び出し) │ └─ スパン2(ツール実行) └─ ...
この階層構造により、マクロ(セッション全体)からミクロ(個々のスパン)まで、様々な粒度でエージェントの動作を分析できます。
3. 試してみた
OpenTelemetryの自動計装コマンドを使って、エージェントを起動し、各画面で何が見られるのかを確認していきます。
3.1. CloudWatch Logs
Strands Agentsのエージェントから送られるトレース情報は、CloudWatch Logsに自動的に蓄積されていきます。
CloudWatch Logsで確認できる情報:
| No | 項目 | 説明 |
|---|---|---|
| 1 | 生のトレース情報 | すべてのスパンとトレースの詳細データ |
| 2 | 送信したプロンプト | エージェントに送信されたユーザーのクエリ |
| 3 | 使用したモデル | どのLLMモデルが使用されたか(モデルIDと設定) |
| 4 | モデルのレスポンス | LLMからの応答内容(ツール呼び出しの決定を含む) |
| 5 | ツールの実行結果 | 各ツールが返した結果データ |
| 6 | 相関ID | ログを関連するトレースにリンクするためのトレースIDとスパンID |
| 7 | パフォーマンスメトリクス | 実行時間、トークン数などの数値データ |
CloudWatch Logsでは、2種類のログが出力されます:
3.1.1. 標準ログ(stdout/stderr出力)
アプリケーションの通常のログ出力がここに記録されます。
このログを確認することで、ユーザーとエージェントがどのようなやり取りをしていたか、 どのようなツールが使われたか、どのような意思決定が行われたかが分かります。
ログの例:
{ "resource": { "attributes": { "aws.local.service": "agentic-travel-strands", "aws.service.type": "gen_ai_agent", "telemetry.sdk.language": "python", "service.name": "agentic-travel-strands", "telemetry.sdk.version": "1.33.1", "telemetry.auto.version": "0.10.1-aws", "telemetry.sdk.name": "opentelemetry" } }, "scope": { "name": "opentelemetry.instrumentation.botocore.bedrock-runtime", "schemaUrl": "https://opentelemetry.io/schemas/1.30.0" }, "timeUnixNano": 1759771827044236800, "observedTimeUnixNano": 1759771827044239300, "severityNumber": 9, "severityText": "", "body": { "content": [ { "text": "strandsagentsがopentelemetryに対応していることについて、ブログのセクションに入れるための説明文を書いてください" } ] }, "attributes": { "event.name": "gen_ai.user.message", "gen_ai.system": "aws.bedrock" }, "flags": 1, "traceId": "68e3fca0ce125547f5150168eb68a4ae", "spanId": "08dd5d31d25cc3ba" }
ログから分かること:
- ユーザーが送信したメッセージ内容(
body.content) - 使用されているサービス(
aws.bedrock) - トレースID(
68e3fca0ce125547f5150168eb68a4ae)で他のログと関連付け可能
3.1.2. OTEL構造化ログ(自動収集された詳細ログ)
OpenTelemetryによって自動的に収集される詳細な操作情報を含むログです。
このログを分析することで、エージェントのレスポンス速度、消費したトークン数などを詳細に検証することが可能です。
どの処理に想定より時間がかかっているかが分かれば、エージェントのパフォーマンス改善につなげることができます。
ログの例:
{ "_aws": { "Timestamp": 1759771830666, "CloudWatchMetrics": [ { "Namespace": "bedrock-agentcore", "Metrics": [ { "Name": "gen_ai.client.token.usage" } ], "Dimensions": [ [ "gen_ai.system", "server.address", "server.port", "gen_ai.request.model", "gen_ai.operation.name", "gen_ai.token.type" ] ] } ] }, "Version": "1", "otel.resource.telemetry.sdk.language": "python", "otel.resource.telemetry.sdk.name": "opentelemetry", "otel.resource.telemetry.sdk.version": "1.33.1", "otel.resource.service.name": "agentic-travel-strands", "otel.resource.telemetry.auto.version": "0.10.1-aws", "otel.resource.aws.local.service": "agentic-travel-strands", "otel.resource.aws.service.type": "gen_ai_agent", "gen_ai.client.token.usage": { "Values": [ 31893.142877534214, 37114.843191131615 ], "Counts": [ 1, 1 ], "Count": 2, "Sum": 68759, "Min": 31826, "Max": 36933 }, "gen_ai.system": "aws.bedrock", "server.address": "bedrock-runtime.us-west-2.amazonaws.com", "server.port": "443", "gen_ai.request.model": "us.anthropic.claude-sonnet-4-5-20250929-v1:0", "gen_ai.operation.name": "chat", "gen_ai.token.type": "input" }
ログから分かること:
- 使用されたトークン数(
gen_ai.client.token.usage): 合計68,759トークン(入力トークン) - 使用モデル(
gen_ai.request.model) - エンドポイント情報(
server.address,server.port) - CloudWatchメトリクスへの自動送信設定
これらのログには、以下のような情報も含まれます:
3.2. Observabilityダッシュボード
CloudWatchのGenerative AI Observabilityページでは、CloudWatch Logsに蓄積されたログ情報が自動的に集計され、視覚的に分かりやすく可視化されます。
ダッシュボードで確認できる主要な情報:
| No | 項目 | 説明 |
|---|---|---|
| 1 | 使用されたエージェント数 | 期間内にアクティブだったエージェントの総数 |
| 2 | セッション数 | ユーザーセッションの総数 |
| 3 | トレース数 | 実行されたトレースの総数 |
| 4 | 平均レイテンシー | リクエストからレスポンスまでの平均時間 |
| 5 | トークン使用量 | LLMで消費されたトークンの総数と内訳 |
| 6 | エラー率 | エラーが発生したリクエストの割合 |
| 7 | 成功率 | 正常に完了したリクエストの割合 |
| 8 | カスタムメトリクス | エージェントコードから出力されたカスタムメトリクス |

このダッシュボードでは、複数のエージェントを横断的に監視し、パフォーマンス比較や異常検知を行うことができます。
3.2.1. セッションビュー
セッションビューで確認できる情報:
| No | 項目 | 説明 |
|---|---|---|
| 1 | エラー数 | そのセッションで発生したエラーの総数(エージェント内部で発生してエージェント側で処理したエラーも含む) |
| 2 | 平均レイテンシー | セッション内のトレースの平均レイテンシー |
| 3 | トレース一覧 | セッション内のすべてのトレースのリスト |
| 4 | トークン使用量 | セッション全体で消費したトークン数 |
| 5 | 実行時間の推移 | 各トレースの実行時間の時系列変化 |

セッションビューを活用することで、特定のユーザーとの会話全体を俯瞰し、ユーザーエクスペリエンスを評価できます。 例えば、会話が進むにつれてレイテンシーが増加していないか、エラーが頻発していないかなどを確認できます。
3.2.2. トレースビュー
トレースを開くと、そのトレース内で発生したイベントや呼び出し関係を詳細に確認することができます。
ビジュアルで表現されたタイムラインを見ることも、CloudWatch Logsに出力された生のデータを参照することも可能です。
トレースビューで確認できる情報:
| No | 項目 | 説明 |
|---|---|---|
| 1 | 呼び出し関係 | どのコンポーネントがどの順序で呼び出されたかの視覚的な表現 |
| 2 | 引数と出力 | 各呼び出しに渡された引数と返された出力の内容(プロンプト、レスポンスなど) |
| 3 | 使用されたモデル | 各処理でどのLLMモデルが使用されたか |
| 4 | 消費トークン数 | 各モデル呼び出しで消費された入力トークン数と出力トークン数 |
| 5 | 実行時間 | 各スパンの開始時刻、終了時刻、経過時間 |
| 6 | トレース可視化 | リクエストの実行パスの視覚的な表現(ウォーターフォール図) |
| 7 | エラー情報 | スパンレベルでのエラーメッセージとスタックトレース |

タイムラインビューでは、各スパンがどれだけの時間を消費しているかが視覚的に分かります。
これにより、パフォーマンスのボトルネックとなっている処理を特定できます

依存関係ビューでは、スパン間の親子関係が階層的に表示されます。
これにより、エージェントがどのような順序でLLM呼び出しやツール実行を行っているかが一目瞭然です。
活用例:
- ビジュアル的に何が呼ばれていて、それぞれにどれくらいの時間がかかっているのかが分かるのがとても便利です
- 特定のツール呼び出しが遅い場合、そのツールの最適化を検討できます
- LLM呼び出しが多い場合、エージェントロジックの見直しやキャッシュの活用を検討できます
- エラーが発生したスパンを特定し、原因を調査できます
4. まとめ
Amazon Bedrock AgentCore ObservabilityとOpenTelemetryを組み合わせることで、AIエージェントの動作を包括的に可視化できます。
本記事でカバーした内容:
| No | 項目 | 説明 |
|---|---|---|
| 1 | AgentCore Observabilityの概要 | AIエージェントのパフォーマンスを監視・デバッグするための機能 |
| 2 | OpenTelemetryの役割 | ベンダーニュートラルな可観測性フレームワークとしての標準化されたテレメトリーデータ収集 |
| 3 | Strands Agentsの特徴 | モデルファーストのアプローチで本番環境対応のAIエージェントを構築できるオープンソースSDK |
| 4 | セットアップ方法 | CloudWatch Transaction Searchの有効化、環境変数の設定、ADOTの統合による簡単なセットアップ |
| 5 | 可観測性の実践 | CloudWatch Logsでの生データ確認とObservabilityダッシュボードでの多層的な可視化(セッション、トレース、スパン) |
得られるメリット:
| No | 項目 | 説明 |
|---|---|---|
| 1 | 開発効率の向上 | エージェントの動作を詳細に追跡できることで、デバッグ時間を大幅に削減し、問題の根本原因を迅速に特定 |
| 2 | 本番環境の安定性 | リアルタイムでパフォーマンスとエラー率を監視し、問題を早期に検出して対処 |
| 3 | コスト最適化 | トークン使用量を詳細に可視化し、LLMのコストを継続的に監視・最適化(キャッシュ活用の効果測定など) |
| 4 | ユーザー体験の改善 | レイテンシーやエラー率を監視し、ユーザー体験を継続的に改善するためのデータドリブンな意思決定 |
| 5 | 標準化された可観測性 | OpenTelemetry標準に準拠することで、既存のツールやプラットフォームとの統合が容易 |
Strands Agentsを使えば、opentelemetry-instrumentコマンドでエージェントを起動するだけで、すべてのトレース、メトリクス、ログが自動的にCloudWatchに送信され、一元管理できます。
コードの大幅な変更は不要で、環境変数の設定とコマンドラインオプションだけで完全な可観測性が実現します。
また、今回は扱いませんでしたが、Bedrock Agent Core Runtime上でStrandsAgentsを起動すると、 自動でOpenTelemetryを使ったCloudWatchへの収集が有効になっているため、 追加の設定は不要で、Runtimeにデプロイするだけで、Observabilityの機能をフルに活用できるようになります。 この手軽さと強力な可視化機能により、本番環境でAIエージェントを安心して運用できるようになります。
今後の展望:
AIエージェントの開発において、可観測性は必須の要素となっています。 特に本番環境では、ユーザーからのフィードバックを待つのではなく、プロアクティブに問題を検知し対処することが重要です。 Amazon Bedrock AgentCore Observabilityを活用して、より信頼性の高いAIエージェントシステムを構築しましょう。
さらに、カスタムメトリクスを追加することで、ビジネス固有のKPIを監視することも可能です。 例えば、特定のツールの使用頻度、ユーザーの満足度スコア、エージェントの応答品質などを追跡できます。
ぜひ、本記事の内容を参考に、ご自身のAIエージェントプロジェクトに可観測性を導入してみてください。
Acroquest Technologyでは、キャリア採用を行っています。
- Azure OpenAI/Amazon Bedrock等を使った生成AIソリューションの開発
- ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
- マイクロサービス、DevOps、最新のOSSやクラウドサービスを利用する開発プロジェクト
- 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長
少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。
