コンテンツにスキップ

第11章 オブザーバビリティ(OTelトレース) — 学習メモ

書籍「Amazon Bedrock AgentCore実践入門」第11章のサンプルコード(このフォルダ)を読み解いた個人学習メモ。 AgentCore 全体像は ../../lectures/agentcore_basics/STUDY_NOTES.md 参照。 実行検証は伴わない。コードに現れた API 名だけ断定し、読めない挙動は「〜と推測」で明示する。 本章は第5章ハンズオンで作成したプロジェクト(handson/app/MyAgent/)への差分ファイル群であり、単体では完結しない。

一言で

エージェントの実行を OpenTelemetry(OTel)トレースとして計装し、CloudWatch(コード変更ゼロ・自動計装)か Langfuse(コードで明示的に OTLP エクスポータを設定)のどちらかに送る、という2つの経路を対比できる章。

全体像

flowchart TB
    Agent["Strands Agent 実行<br>main.py"]
    subgraph CW["CloudWatch経路 (cloudwatch/)"]
        ADOT["opentelemetry-instrument<br>python main.py<br>(ADOT自動計装)"]
    end
    subgraph LF["Langfuse経路 (langfuse/)"]
        ST["main.py内<br>StrandsTelemetry<br>.setup_otlp_exporter()"]
    end
    Agent -.->|"コンテナ起動コマンドを差し替え"| ADOT
    Agent -.->|"コード内で計装を追加"| ST
    ADOT -->|"OTLP"| CloudWatchLogs["Amazon CloudWatch"]
    ST -->|"OTLP<br>Basic認証ヘッダー"| Langfuse["Langfuse"]

2経路は同時に使うものではなく択一cloudwatch/langfuse/ はそれぞれ独立した差し替えファイル一式)。どちらも「第5章で作った Runtime プロジェクトの main.py / Dockerfile / pyproject.toml を差し替える」という同じ適用方法を取る。

使用ライブラリ・原理

  • Langfuse 経路: strands.telemetry.StrandsTelemetry.setup_otlp_exporter(endpoint=, headers=) を呼ぶだけで、Strands 内部の OTel 計装が指定エンドポイントへトレースを送るようになる。Langfuse は OTLP(OpenTelemetry Protocol)の受け口を公開しているため、認証ヘッダー(Basic 認証: base64(public_key:secret_key))さえ付与すれば送信できる。
  • CloudWatch 経路: コードは一切変更せず、コンテナの起動コマンドを python main.py から opentelemetry-instrument python main.py に変えるだけ。opentelemetry-instrumentaws-opentelemetry-distro(ADOT = AWS Distro for OpenTelemetry)が提供する CLI ラッパーで、対応ライブラリへ実行時に計装を動的注入する自動計装(auto-instrumentation)の仕組み。
  • 対比の要点: Langfuse は「エクスポータをコードで明示」、CloudWatch は「起動コマンドで自動計装」。AgentCore(Runtime)の視点では、どちらも同じ OTel トレースの出口を差し替えているに過ぎない。

ファイル別の役割

ファイル 役割
README.md 差分ファイルの適用先・依存追加コマンドの案内
langfuse/main.py 第5章 handson/app/MyAgent/main.py の置き換え版。StrandsTelemetry で Langfuse への OTLP 送信を追加したエントリポイント
langfuse/Dockerfile 第5章 Dockerfile の置き換え版。起動コマンドは通常の python main.py(計装はコード側で完結するため自動計装は不要)
langfuse/pyproject.toml strands-agents[otel]==1.38.0 + bedrock-agentcore==1.6.4 に依存を差し替え
cloudwatch/Dockerfile 起動コマンドを opentelemetry-instrument python main.py に変えるだけの Dockerfile(main.py 自体は無変更で流用)
cloudwatch/pyproject.toml strands-agents bedrock-agentcore aws-opentelemetry-distro に依存を差し替え(バージョン非固定)

中心コードの読み解き

# chapter11/langfuse/main.py:1-25
import base64
import os

from strands import Agent
from strands.telemetry import StrandsTelemetry           # ①
from bedrock_agentcore import BedrockAgentCoreApp

LANGFUSE_PUBLIC_KEY = os.environ["LANGFUSE_PUBLIC_KEY"]  # ②
LANGFUSE_SECRET_KEY = os.environ["LANGFUSE_SECRET_KEY"]
LANGFUSE_HOST = os.environ["LANGFUSE_HOST"]

auth = base64.b64encode(                                   # ③
    f"{LANGFUSE_PUBLIC_KEY}:{LANGFUSE_SECRET_KEY}".encode()
).decode()

StrandsTelemetry().setup_otlp_exporter(                     # ④
    endpoint=f"{LANGFUSE_HOST}/api/public/otel/v1/traces",
    headers={
        "Authorization": f"Basic {auth}",
        "x-langfuse-ingestion-version": "4",                # ⑤
    },
)
# やってること なぜ
langfuse/main.py:5 Strands 本体の telemetry サブモジュールから StrandsTelemetry をインポート Strands の OTel 計装を制御するエントリポイントクラス
langfuse/main.py:9-11 Langfuse の public/secret key と host を環境変数から取得 資格情報をコードにハードコードしない原則どおり
langfuse/main.py:14-16 public:secret を base64 エンコードして Basic 認証トークンを作る OTLP エンドポイントは Authorization: Basic ヘッダーで認証する仕様のため
langfuse/main.py:19-25 setup_otlp_exporter にエンドポイント・ヘッダーを渡す この1回の呼び出しだけで、以降の Agent() 実行が自動的にトレース送信対象になる(内部で OTel の TracerProvider が差し替わると推測)
langfuse/main.py:23 x-langfuse-ingestion-version: "4" を付与 Langfuse v4 の ingestion API 互換性のための識別ヘッダー

Dockerfile 対比

# chapter11/langfuse/Dockerfile:19-20
# 通常のpythonコマンドで起動(自動計装を無効化)
CMD ["python", "main.py"]
# chapter11/cloudwatch/Dockerfile:19-20
# OTelのPython SDK経由でアプリを起動(自動計装を有効化)
CMD ["opentelemetry-instrument", "python", "main.py"]
観点 Langfuse 経路 CloudWatch 経路
コード変更 main.py に計装コードを追加 main.py は無変更
起動コマンド python main.py opentelemetry-instrument python main.py
計装方式 明示的(コードで OTLP エクスポータを設定) 自動(ADOT が実行時にライブラリへ計装を注入)
送信先の認証 Basic 認証ヘッダーをコードで組み立て AWS 側の権限に委譲(コードに認証情報なし)
依存バージョン strands-agents[otel]==1.38.0 bedrock-agentcore==1.6.4 strands-agents bedrock-agentcore aws-opentelemetry-distro(すべて非固定)

学んだこと(要点)

  • 「差分ファイル章」というこの本独特の構成: 第11章は単体で動くプロジェクトではなく、第5章で作った Runtime プロジェクトへ部分的にファイルを差し込む前提。README にも「章内でハンズオンは実施しません」と明記されている。
  • OTel の「エクスポータをコードで明示する方式」と「自動計装で差し替える方式」を1つの章で対比できるのが本章の価値。CloudWatch 側は「アプリのコードを一切知らなくても計装できる」自動計装の強さを示す実例になっている。
  • AgentCore の Observability 部品は「OTel の出口を用意するだけ」で、受け皿(Langfuse・CloudWatch)は差し替え可能な部品として扱われている。

落とし穴・現代版に移植するなら

  • バージョン記述の齟齬がある: cloudwatch/pyproject.tomlstrands-agents bedrock-agentcore ともにバージョン非固定。書籍のコーディングスタイル方針(バージョンは == で固定)が cloudwatch 側だけ守られておらず、実際に解決されるバージョンは langfuse/pyproject.tomlbedrock-agentcore==1.6.4 と異なる可能性がある(uv.lock を確認すれば分かるが未検証)。
  • Langfuse 側の LANGFUSE_PUBLIC_KEY 等は AgentCore Runtime へのデプロイ時に環境変数として渡す前提。ローカルで単に uv run main.py すると KeyError になる。
  • 現代版に移植するなら、.env.op + op run パターンで Langfuse キーを注入し、ローカル実行時はまず setup_console_exporter()lectures/agentcore_basics で確認済みの別 API)で手元確認してから OTLP 送信に切り替えると安全。

記事参照


作成: 2026-07-17 / 最終更新: 2026-07-17