第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-instrumentはaws-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/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.tomlはstrands-agentsbedrock-agentcoreともにバージョン非固定。書籍のコーディングスタイル方針(バージョンは==で固定)が cloudwatch 側だけ守られておらず、実際に解決されるバージョンはlangfuse/pyproject.tomlのbedrock-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 送信に切り替えると安全。
記事参照¶
- 書籍 第11章「運用状況を可視化する『オブザーバビリティ』」。第5章ハンズオンへの差分ファイル。
- 全体像:
../../lectures/agentcore_basics/STUDY_NOTES.md§3-7「Observability(可観測性)」。 - 関連 lecture:
../../lectures/langfuse_basics/(Langfuse SDK の trace/span/generation 階層をそのまま踏襲する対応関係)。
作成: 2026-07-17 / 最終更新: 2026-07-17