Bedrock AgentCore実装運用|Strands×マルチエージェント設計

目次

1. Vol1 の振り返りと Vol2 実装・運用レイヤの全体像

Vol1基盤からVol2実装運用レイヤへの全体像
図1: AgentCore 基盤コンポーネント(Vol1)の上に実装・協調・デプロイ・運用(Vol2)を積む全体像
この記事の要点

  • Vol1 で扱った基盤コンポーネント(Runtime/Memory/Gateway/Identity/Observability)の上に、実装フレームワーク・協調・デプロイ・運用を積む実戦編
  • AWS製OSSの Strands Agents SDK(2025年5月リリース・model-driven)でエージェントを実装し、supervisor-worker と A2A プロトコルでマルチエージェント協調を構成
  • AWS CDK による本番デプロイ・カナリアリリース・CI/CD、Observability とコスト最適化まで一気通貫
対象読者

  • Vol1 で AgentCore の基盤を理解し、実際にエージェントを実装・本番運用したいエンジニア
  • 複数エージェントの協調(supervisor-worker / A2A)を設計するアーキテクト
  • エージェントの CI/CD・カナリア・コスト最適化を整備する運用担当

1-1. 本記事のゴール

本記事は AWS Bedrock AgentCore 本番運用シリーズの第2巻として、Vol1 で学んだ基盤コンポーネントを前提に「どのように実装し、協調させ、本番環境で安定運用するか」を実戦的に解説する。

具体的な到達目標は次の3点だ。まず、AWS 公式のオープンソース SDK である Strands Agents SDK を使い、最小限のコードでエージェントを実装できるようになること。次に、supervisor-worker アーキテクチャと A2A(Agent-to-Agent)プロトコルによるマルチエージェント協調の設計指針を理解すること。そして AWS CDK を用いた本番デプロイ・カナリアリリース・Observability の整備まで、ソフトウェアエンジニアリングの視点で整合性のある運用体制を構築できるようになることだ。

単一のプロトタイプエージェントを動かすだけなら数行のコードで十分だ。しかし、本番サービスとして利用者に提供し続けるには、デプロイ・監視・コスト・品質保証のすべての観点を整える必要がある。AI エージェントを「動かす」から「育て運用し続ける」フェーズに移行した今、Strands SDK・マルチエージェント協調・本番デプロイの3本柱を理解することが、エージェント開発者に求められる次の水準だ。本記事はその全領域をカバーし、AgentCore を使った本番運用の完全なロードマップを提供する。

なお、Vol1(基盤編)と Vol2(実装運用編)の二部作は、各章が独立して参照できる設計になっている。Vol1 を熟読していなくても、本記事の §1-2(Vol1 振り返り)を読めば最低限の前提知識を確認できる。ただし AgentCore Runtime の microVM 分離特性や Memory の短期/長期設計を深く理解していると、後続の §2〜§4 の設計判断がより腑に落ちる。不明な点があれば Vol1 に立ち戻ることを推奨する。

1-2. Vol1 の要点振り返り

Vol1 では AgentCore が提供する基盤コンポーネントを詳細に解説した。Vol2 を理解するために、ここで各コンポーネントの要点を簡潔に振り返っておく。

Runtime は microVM ベースのサーバーレス実行環境で、ユーザーセッションごとに独立した CPU・メモリ・ファイルシステムを確保する。最大8時間のセッション継続をサポートし、エージェントが長時間の推論ループを実行しても他ユーザーに影響を与えない分離保証を提供する。コンテナではなく microVM を採用することで、カーネルレベルの強いセキュリティ分離とエージェント専用のネットワーク・プロセス空間を実現している。

セッション開始時の cold start は通常数秒以内に完了し、インタラクティブなユーザー体験に耐えられる設計になっている。1ユーザーが同時に複数のセッションを起動することも可能で、並列ワークフローをサポートする。

Memory はマネージドなコンテキスト保持システムで、短期メモリ(会話内の文脈維持)と長期メモリ(ユーザープロファイルや過去のインタラクション蓄積)を統合管理する。データベース・キャッシュ・ベクターストアの構築・管理が不要で、エージェントが複数セッションを跨いだパーソナライズを実現できる。短期メモリは DynamoDB、長期メモリは S3 + ベクターインデックスで実装されており、エージェントコードから透過的に利用できる。

Gateway は既存 API・Lambda 関数・MCP サーバーを統一インターフェースでエージェントにツールとして公開する仕組みだ。OpenAPI / Smithy 仕様・Lambda 関数・コンテナイメージ(ECR)を接続でき、エンドユーザー検証とバックエンドへの委譲接続という2層の認証方式を内包する。

Identity は AgentCore が各エージェントに固有のアイデンティティを付与し、Okta / Microsoft Entra ID / Amazon Cognito などのコーポレート IdP と統合する仕組みだ。エンドユーザーは自分が権限を持つエージェントにのみアクセスでき、エージェントが誤って権限外のシステムにアクセスするリスクを排除する。

Observability は CloudWatch ダッシュボードと OTEL 互換テレメトリによって、セッション数・レイテンシ・セッション継続時間・トークン使用量・エラー率を一元監視する。複雑な推論ループのデバッグには分散トレースが有効で、どのツール呼び出しがレイテンシスパイクを引き起こしたかを特定できる。これら5つのコンポーネントが協調して初めて「信頼できる基盤」が完成し、Vol2 の実装・運用レイヤを安全に積み上げられる。

コンポーネント間の連携を例示すると、Strands エージェント(Vol2 §2)は Runtime 上で動作し、会話コンテキストを Memory に永続化する。エージェントが外部サービスを呼び出す際は Gateway 経由でツールを実行し、実行者の識別は Identity が担保する。エージェントの動作全体は Observability によって可視化される。Vol1 でこの連携を理解していると、Vol2 のアーキテクチャ設計の判断根拠が明快になる。

1-3. Vol2 で扱う実装・運用レイヤ

Vol1 と Vol2 の棲み分けを一言で表すなら、「基盤(インフラレイヤ)」vs「実装・運用(アプリケーションレイヤ)」だ。

Vol1 が扱うのは AgentCore 自体のコンポーネント設計、すなわちエージェントが動くための「箱と配管」だ。それに対し Vol2 が扱うのは、その箱の中でエージェントをどう実装し、複数のエージェントをどう協調させ、本番トラフィックに耐えるようにどうデプロイ・運用するか、という「実装者・運用者の関心事」だ。言い換えると、Vol1 の読者は「AgentCore に何ができるか」を理解した状態であり、Vol2 を読み終えた読者は「AgentCore を使って何を、どう作り、どう動かし続けるか」を実行できる状態になる。

A2A プロトコルの採用により、Strands・OpenAI Agents SDK・LangGraph・Google ADK など異なるフレームワーク間でのエージェント協調が標準化されつつある。Vol2 で学ぶマルチエージェント協調の設計原則は、Strands SDK を超えてエージェントエコシステム全体に通用する普遍的な知識だ。

Vol2 が扱う5つの実装・運用領域は以下のとおりだ。

  • Strands Agents SDK(§2): AWS製OSSで model-driven にエージェントを実装する方法。プロンプトとツールリストだけでエージェントが動く設計哲学と、他フレームワークとの違いを解説する。
  • マルチエージェント協調(§3): routing supervisor と domain worker による supervisor-worker アーキテクチャ、および A2A プロトコルによって異なるフレームワーク間でエージェントが協調する仕組みを扱う。
  • 本番デプロイ(§4): CDK による GitOps ワークフロー、traffic splitting カナリアリリース、CI/CD パイプラインへの回帰ブロック統合を解説する。
  • ツール統合実戦(§5): Gateway 経由の API/Lambda/MCP 統合と2層認証(エンドユーザー検証/バックエンド委譲)の実装パターンを解説する。
  • 運用・コスト最適化(§6): Observability の整備、トークンコストとレイテンシの管理戦略、Guardrails 連携を扱う。

これら5領域を縦断することで、Vol1 の基盤知識がシステム全体として有機的につながる。

5領域を学ぶ順序には意味がある。まず Strands SDK(§2)でエージェントの実装単位を理解し、次に supervisor-worker と A2A(§3)で複数エージェントの協調方法を学ぶ。実装イメージが固まった段階で、CDK による本番デプロイとカナリアリリース(§4)に取り組む。ツール統合(§5)と運用・コスト管理(§6)は、単一エージェントの場合も複数エージェントの場合も共通して必要な横断スキルだ。統合パターン(§7)は設計フェーズで参照し、詰まりポイント(§8)は実装・デバッグ中に都度参照する使い方が効果的だ。

1-4. 前提環境と必要知識

本記事のコード例を手元で動かすには以下の環境が必要だ。

AWS 側の準備事項

  • 有効な AWS アカウントと AgentCore が利用可能なリージョン(us-east-1・us-west-2 が主要対応リージョン。最新情報は AWS ドキュメントで確認されたい)
  • Amazon Bedrock のモデルアクセス許可(Claude 3.7 Sonnet を推奨・AWS コンソールの Bedrock モデルアクセス画面で有効化が必要)
  • AgentCore Runtime / Memory / Gateway を操作する IAM ロールとポリシー(最小権限設計を推奨)
  • AWS CDK v2 のインストール(npm install -g aws-cdk)と対象アカウント・リージョンでの CDK Bootstrap 済み環境

ローカル開発環境

  • Python 3.11 以上(Strands SDK は 3.11+ を推奨)
  • pip install strands-agents でインストール可能な Strands Agents SDK(PyPI 公開済み・2026年6月時点で 1400万件超のダウンロード実績)
  • AWS CLI v2(認証情報の設定済み。aws configure またはインスタンスプロファイルで解決)

前提知識

  • Vol1 の内容(Runtime・Memory・Gateway・Identity の概念理解)
  • Python の型ヒントとデコレータの基礎(@tool デコレータを理解するために必要)
  • AWS IAM の基礎(最小権限設計と委譲の概念を理解するために必要)
  • IaC の基礎(AWS CDK または CloudFormation の概念理解)

本記事のコード例は概念説明を目的としたスニペットであり、本番適用時には IAM の最小権限設計・例外処理・テスト・モニタリングを別途整備する必要がある点に注意されたい。

コスト管理: AgentCore Runtime・Bedrock モデル・Memory ストレージはリクエスト数・セッション数・トークン使用量に応じて課金される。本番トラフィックを流す前に AWS Cost Explorer と Budgets アラートを設定し、予期しない高額請求を防ぐ対策を取ることを強く推奨する。学習目的の場合は少量のテストリクエストから始め、コスト感覚を掴んでからスケールアップするのが安全だ。

1-5. 本記事の構成

本記事は §2 から §8 の7章構成で、Vol2 の全実装・運用領域をカバーする。各章は独立して参照できるが、§2→§3→§4 の順で読むことで実装から本番運用への流れを体系的に理解できる。

§2 Strands Agents SDK — AWS が2025年5月にオープンソースとして公開した model-driven エージェント SDK の基本構造(@tool デコレータ・エージェントループ・モデル選択)を解説し、LangGraph や CrewAI との設計哲学の違いを比較する。初めて Strands を試す読者はここから手を動かすことを推奨する。

§3 マルチエージェント協調 — routing supervisor と domain-specific worker による supervisor-worker アーキテクチャ、および A2A(Agent-to-Agent)プロトコルによって Strands・OpenAI Agents SDK・LangGraph など異なるフレームワーク間でエージェントが協調する仕組みを詳細に解説する。

§4 本番デプロイパターン — AWS CDK によるエージェントのバージョン管理・GitOps ワークフロー・traffic splitting カナリアリリース・CI/CD パイプラインへの回帰ブロック統合を扱う。インフラ変更とエージェントロジック変更を同一リポジトリで管理する実践例を示す。

§5 ツール統合の実戦 — AgentCore Gateway 経由で既存システムをエージェントのツールに変換する実装パターンと、エンドユーザー検証・バックエンド委譲の2層認証設計を解説する。

§6 運用・コスト最適化 — CloudWatch + OTEL テレメトリによる Observability の整備、トークンコストとレイテンシの管理戦略、Guardrails 連携によるコンテンツフィルタリングの実装を扱う。

§7 実戦統合パターンと移行 — これまでの章を横断した本番統合パターンと、既存の単一エージェント(Bedrock Agents / 単体 Strands)からマルチエージェント構成への移行戦略を示す。

§8 詰まりポイント・アンチパターンとまとめ — 実装・運用でよくはまる7つ以上のポイント・アンチパターンと正解パターン・本シリーズ全体のまとめと次巻予告で締めくくる。

読み方のガイド: Strands SDK を初めて触る場合は §2 から順に読み進めることを推奨する。すでに Strands の基礎を知っており、マルチエージェント協調に移りたい場合は §3 から、本番デプロイに集中したい場合は §4 から入るのが効率的だ。詰まったときや設計レビューの際には §8 を先に参照してアンチパターンを確認するのも有効な使い方だ。各章の末尾には ep-box でそのセクションの要点をまとめているため、流し読みの際には ep-box だけを拾い読みしても全体像を掴める。

前提記事: AgentCore 本番運用 Vol1(Runtime/Memory/Gateway/Identity)→

それでは §2 から、Strands Agents SDK によるエージェント実装の詳細に入っていこう。

本章(§1)では Vol1 の基盤知識の確認と Vol2 の全体像を示した。


2. Strands Agents SDK — エージェント実装

Strands Agents SDKのエージェントループ
図2: Strands Agents SDK の model-driven エージェントループとツール定義

2-1. Strands Agents SDK とは

Strands Agents SDK は AWS が2025年5月にオープンソースとして公開した Python 製のエージェント SDK だ。GitHub リポジトリは strands-agents 組織で管理され、PyPI から pip install strands-agents で即座にインストールできる。公開から約1年で 1400万件超のダウンロードを記録し、Amazon Q Developer・AWS Glue・VPC Reachability Analyzer など AWS 内部の複数プロダクトが本番利用している実績を持つ。

Strands の設計思想はひとことで言えば「最小抽象でモデルの能力を最大限に活かす」だ。従来のエージェントフレームワークは開発者がエージェントの実行グラフや状態遷移を明示的に設計する必要があったが、Strands はプロンプトとツールリストを渡すだけで残りのループ制御をモデルに委ねる。その結果、LangGraph で60行必要だったエージェントロジックが Strands では3行に圧縮された事例も報告されている。

2025年12月には TypeScript サポートがプレビューとして追加され、Python 以外のエコシステムへの展開も進んでいる。本記事では Python 版の実装例を中心に解説する。

2-2. model-driven アプローチの設計哲学

エージェントフレームワークのアーキテクチャには大きく2つの流派がある。graph-driven(グラフ駆動)と model-driven(モデル駆動)だ。

graph-driven アプローチの代表例が LangGraph だ。開発者が DAG(有向非巡回グラフ)でノードとエッジを明示的に定義し、エージェントの実行フローを事前に設計する。実行の決定論性が高く、複雑なビジネスロジックを正確に制御できる一方、設計コストが高く、グラフ構造の変更に伴うリファクタリングが重くなりやすい。

model-driven アプローチの代表例が Strands だ。開発者はプロンプト(エージェントへの指示)とツールリスト(エージェントが呼び出せる関数群)だけを定義し、「次に何をするか」の判断はすべてモデルが行う。ReAct パターン(推論と行動を交互に繰り返すループ)が暗黙的に実装されており、開発者がループ制御コードを書く必要がない。

from strands import Agent, tool

@tool
def get_weather(city: str) -> str:
 """指定都市の天気を取得する"""
 return f"{city}は晴れ、気温22度です"

agent = Agent(
 system_prompt="あなたは天気アシスタントです",
 tools=[get_weather]
)

response = agent("東京の天気を教えて")

上記3行(@tool デコレータ・Agent 定義・agent(...) 呼び出し)がエージェントの全体を構成する。モデルは会話コンテキストを読み、get_weather ツールをいつ呼ぶかを自律的に判断し、ツール結果を組み込んで最終回答を生成する。

model-driven アプローチはモデルの推論能力に依存するため、「決まった順序で必ず A→B→C を実行しなければならない」という厳密な制御が必要なユースケースでは graph-driven が優位だ。一方、「ユーザーの質問に応じて柔軟にツールを組み合わせて回答する」という汎用エージェントには model-driven が圧倒的にシンプルだ。

2-3. インストールと初期セットアップ

pip install strands-agents

Bedrock を使う場合は追加の依存パッケージが必要だ。

pip install "strands-agents[bedrock]"

AWS 認証情報は通常の AWS SDK と同様、環境変数・AWS プロファイル・インスタンスプロファイルのいずれかで解決される。ローカル開発では aws configure でデフォルトプロファイルを設定しておくのが最も簡単だ。

from strands import Agent
from strands.models import BedrockModel

model = BedrockModel(
 model_id="us.anthropic.claude-sonnet-4-5",
 region_name="us-east-1"
)

agent = Agent(
 model=model,
 system_prompt="あなたはAWSのエキスパートです"
)

モデルを明示しない場合、Strands はデフォルトで Amazon Bedrock の Claude 3.7 Sonnet を使用する。ローカル開発中は Ollama などを使ったローカルモデルで動作確認してから Bedrock に切り替えるワークフローも有効だ。

2-4. @tool デコレータによるツール定義

Strands の中核機能のひとつが @tool デコレータだ。任意の Python 関数にこのデコレータを付与するだけで、その関数をエージェントが呼び出せるツールに変換できる。

from strands import tool

@tool
def search_aws_docs(query: str, service: str = "bedrock") -> str:
 """AWS ドキュメントを検索して関連情報を返す。

 Args:
  query: 検索クエリ文字列
  service: 対象 AWS サービス名(デフォルト: bedrock)

 Returns:
  関連ドキュメントの要約テキスト
 """
 # 実際の実装では検索サービスを呼び出す
 return f"{service} の {query} に関するドキュメントを返します"

@tool デコレータは以下を自動的に処理する。

  • JSON スキーマ生成: Python の型ヒント(strintlist[str] など)を、モデルが読み取れる JSON スキーマに変換する。これによりモデルは正しい引数形式でツールを呼び出せる。
  • ツール説明の抽出: 関数の docstring をツールの説明として使用する。モデルはこの説明を読んで「いつこのツールを呼ぶべきか」を判断する。したがって、docstring は「このツールが何をするか」を明確に記述することが重要だ。
  • 呼び出しハンドリング: モデルがツール呼び出しを要求したとき、Strands が自動的に関数を実行し、結果をモデルに返す。

型ヒントを適切に書くことで、複雑なスキーマも自動生成される。

from typing import Optional
from strands import tool

@tool
def create_s3_presigned_url(
 bucket: str,
 key: str,
 expiration_seconds: int = 3600,
 content_type: Optional[str] = None
) -> dict:
 """S3 オブジェクトの署名付き URL を生成する。

 Args:
  bucket: S3 バケット名
  key: オブジェクトキー(パス)
  expiration_seconds: URL の有効期限(秒)。デフォルト 3600秒。
  content_type: Content-Type ヘッダー(任意)

 Returns:
  url と expiration を含む辞書
 """
 import boto3
 s3 = boto3.client("s3")
 url = s3.generate_presigned_url(
  "get_object",
  Params={"Bucket": bucket, "Key": key},
  ExpiresIn=expiration_seconds
 )
 return {"url": url, "expiration": expiration_seconds}

2-5. エージェントループの仕組み

Strands のエージェントループは ReAct(Reason + Act)パターンを暗黙的に実装している。具体的には以下のサイクルを繰り返す。

  1. 入力受付: ユーザーのメッセージと現在の会話履歴をモデルに渡す。
  2. モデルによる推論: モデルは次のアクションを判断する。「最終回答を生成する」または「ツールを呼び出す」のいずれかだ。
  3. ツール実行: ツール呼び出しが要求された場合、Strands が対応する Python 関数を実行し、結果を会話履歴に追加する。
  4. 結果の組み込み: ツール実行結果を受け取ったモデルが次のアクションを再度判断する(ステップ2に戻る)。
  5. 終了: モデルが最終回答を生成したとき、ループが終了し応答が返される。

このループはモデルが「もう十分な情報が揃った」と判断するまで継続する。単純なクエリでは1ステップで終わるが、複数のツールを組み合わせる複雑なタスクでは数十ステップのループが発生することもある。

ループの最大ステップ数は max_steps パラメータで制御できる。本番環境では無限ループを防ぐために必ず上限を設定することを推奨する。

agent = Agent(
 model=model,
 tools=[search_aws_docs, create_s3_presigned_url],
 max_steps=10
)

2-6. モデル選択 — BedrockModel / AnthropicModel / LiteLLMModel

Strands はモデルアグノスティックな設計で、複数のモデルプロバイダに対応している。主要な3つのモデルクラスを解説する。

BedrockModel は Amazon Bedrock 経由でモデルを利用するクラスで、AWS 環境での本番利用に最も適している。

from strands.models import BedrockModel

model = BedrockModel(
 model_id="us.anthropic.claude-sonnet-4-5",
 region_name="us-east-1",
 streaming=True,
 temperature=0.0
)

AWS IAM による認証が自動的に処理され、VPC 内からのプライベートアクセスも可能だ。Bedrock のクロスリージョン推論(Cross-Region Inference)にも対応しており、us. プレフィックスを付けることでリージョン間の負荷分散が有効になる。

AnthropicModel は Anthropic API に直接アクセスするクラスで、Bedrock を経由しない場合に使用する。

from strands.models import AnthropicModel
import os

model = AnthropicModel(
 model_id="claude-sonnet-4-5-20251001",
 api_key=os.environ["ANTHROPIC_API_KEY"]
)

ローカル開発や Bedrock が利用できないリージョンでのテストに便利だ。本番環境では Bedrock 経由の利用を推奨する(AWS の監査・コスト管理・セキュリティ統制が活用できるため)。

LiteLLMModel は LiteLLM ライブラリを介して OpenAI・Google Gemini・Ollama・llama.cpp など多数のモデルプロバイダに対応するクラスだ。

from strands.models import LiteLLMModel

model = LiteLLMModel(
 model_id="ollama/llama3.2",
 base_url="http://localhost:11434"
)

ローカルモデルで動作検証してから本番 Bedrock に切り替える開発フローや、マルチプロバイダでのベンチマーク比較に活用できる。

クラス主な用途認証方式本番推奨度
BedrockModelAWS 本番環境IAM ロール
AnthropicModelAnthropic 直接利用API キー△(開発向き)
LiteLLMModelマルチプロバイダ / ローカルプロバイダ依存△(開発向き)

2-7. マルチツールエージェントの実装例

複数のツールを組み合わせた実用的なエージェントの実装例を示す。以下は AWS リソースの状態を調査して報告するエージェントだ。

from strands import Agent, tool
from strands.models import BedrockModel
import boto3

@tool
def list_ec2_instances(region: str = "ap-northeast-1") -> list:
 """指定リージョンの EC2 インスタンス一覧を取得する"""
 ec2 = boto3.client("ec2", region_name=region)
 response = ec2.describe_instances()
 instances = []
 for reservation in response["Reservations"]:
  for inst in reservation["Instances"]:
instances.append({
 "InstanceId": inst["InstanceId"],
 "State": inst["State"]["Name"],
 "InstanceType": inst["InstanceType"]
})
 return instances

@tool
def get_cloudwatch_metric(
 namespace: str,
 metric_name: str,
 dimension_name: str,
 dimension_value: str
) -> dict:
 """CloudWatch メトリクスの直近1時間の平均値を取得する"""
 import datetime
 cw = boto3.client("cloudwatch")
 end = datetime.datetime.utcnow()
 start = end - datetime.timedelta(hours=1)
 response = cw.get_metric_statistics(
  Namespace=namespace,
  MetricName=metric_name,
  Dimensions=[{"Name": dimension_name, "Value": dimension_value}],
  StartTime=start,
  EndTime=end,
  Period=3600,
  Statistics=["Average"]
 )
 if response["Datapoints"]:
  dp = response["Datapoints"][0]
  return {"average": dp["Average"], "unit": dp["Unit"]}
 return {"average": None, "unit": "None"}

model = BedrockModel(model_id="us.anthropic.claude-sonnet-4-5")

ops_agent = Agent(
 model=model,
 system_prompt=(
  "あなたは AWS 運用エージェントです。"
  "提供されたツールを使って AWS リソースの状態を調査し、簡潔にレポートしてください。"
 ),
 tools=[list_ec2_instances, get_cloudwatch_metric]
)

result = ops_agent(
 "ap-northeast-1 の EC2 インスタンス一覧を確認し、"
 "running 状態のインスタンスがあれば CPU 使用率も確認してください"
)
print(result)

このエージェントはユーザーの指示に応じて list_ec2_instancesget_cloudwatch_metric を自律的に組み合わせて実行する。どのツールをどの順序で呼ぶかはモデルが判断するため、コード側でフローを設計する必要がない。

2-8. MCP ツールの統合

Strands は Model Context Protocol(MCP)をネイティブサポートしており、数千の公開 MCP サーバーを即座にツールとして利用できる。

from strands import Agent
from strands.tools.mcp import MCPClient

mcp_client = MCPClient(
 command="npx",
 args=["-y", "@modelcontextprotocol/server-filesystem", "/tmp/workspace"]
)

with mcp_client:
 agent = Agent(
  tools=mcp_client.list_tools_sync()
 )
 result = agent("workspace ディレクトリ内のファイル一覧を表示して")

AgentCore Gateway と MCP を組み合わせることで、既存のエンタープライズシステムを MCP サーバーとして公開し、Strands エージェントのツールとして使用する構成が可能だ。Gateway が認証・レート制限・ログ収集を担当するため、エージェントコードは業務ロジックに集中できる。

2-9. LangGraph / CrewAI との比較

Strands の設計哲学を理解するために、主要な競合フレームワークとの比較を整理する。

観点Strands AgentsLangGraphCrewAI
アプローチmodel-driven(モデルがフロー制御)graph-driven(開発者がグラフ設計)role-based(エージェントに役割を付与)
実装複雑度低(プロンプト+ツールのみ)高(DAG の明示的設計が必要)中(役割と Crew の定義)
フロー制御モデルが自律判断開発者が事前設計Crew コーディネータが管理
ユースケース汎用エージェント・AWS 統合厳密な状態機械が必要な業務ロジック役割分担型のチームエージェント
AWS 統合ネイティブ(Bedrock モデル・IAM)追加設定が必要追加設定が必要
初期化速度高速(LangGraph 比で約10倍)低速(グラフ初期化コストあり)中程度

Strands が優位なケース: ユーザーの多様な質問に柔軟に答える汎用エージェント、AWS サービスを多数組み合わせるオペレーション自動化、短期間でプロトタイプを構築したい場合。

LangGraph が優位なケース: 承認フローや条件分岐など「必ずこの順序で実行しなければならない」ビジネスロジックが存在する場合、決定論的な実行が監査・コンプライアンス上必須の場合。

CrewAI が優位なケース: 研究・執筆・コードレビューなど役割を持つ複数エージェントの協働が適している場合。LangGraph の状態機械のノードとして CrewAI の Crew を組み込むハイブリッド構成も実用的だ。

AWS 環境で AgentCore を使う場合、Strands は Bedrock との統合・IAM 認証・OTEL 互換テレメトリが最初から整っており、立ち上げコストが最も低い。まず Strands で動かし、「どうしても決定論的なフロー制御が必要」という要件が出てきたときに LangGraph の採用を検討するという判断が合理的だ。

2-10. AWS 環境での推奨プラクティス

Strands を AWS 本番環境で運用する際の推奨プラクティスをまとめる。

モデル選択: 本番環境では BedrockModel を使用し、クロスリージョン推論(us. プレフィックス)を有効化する。これにより単一リージョンの障害がエージェントサービスに影響しにくくなる。

ループ上限: max_steps を必ず設定する。設定しないと複雑なタスクで無限ループが発生しトークンコストが際限なく膨らむ。本番では10〜20ステップを上限の目安とし、超過した場合はエラーとして記録する。

ツール設計: @tool の docstring はモデルが読む仕様書だ。「何をするか」「どんな引数か」「何を返すか」を明確に書くことで、モデルが適切なタイミングでツールを呼び出せる。曖昧な docstring は不要なツール呼び出しやエラーの原因になる。

エラーハンドリング: ツール関数内で発生した例外は適切にキャッチし、エラーメッセージを文字列として返すことを推奨する。例外を伝播させるとエージェントループが停止するが、エラーメッセージを返すとモデルがリカバリ判断を行える。

可観測性: Strands は OTEL 互換のテレメトリを出力するため、AgentCore Observability や AWS X-Ray との統合が容易だ。本番デプロイ時は必ずトレースを有効化し、ツール呼び出しのレイテンシとトークン使用量を監視する。


3. マルチエージェント協調 — Supervisor/Worker と A2A

supervisor-workerとA2Aによるマルチエージェント協調
図3: routing supervisor と domain worker による A2A マルチエージェント協調

3-1. Supervisor-Worker アーキテクチャ

AgentCore のマルチエージェント設計の核心は supervisor-worker パターン だ。1 体の supervisor(オーケストレーター)が最上位タスクを受け取り、専門化した複数の worker エージェントへ分解・委譲する。Strands SDK では worker エージェントを @tool デコレーターでラップするだけで、supervisor が通常のツール呼び出しと同じインターフェースで委譲できる。

from strands import Agent, tool

# --- Worker エージェント定義 ---
_research_agent = Agent(
 system_prompt="あなたはリサーチ専門エージェントです。Web 調査・情報収集を担当します。",
 tools=[web_search, python_repl],
)

_summary_agent = Agent(
 system_prompt="あなたは要約専門エージェントです。受け取った情報を簡潔にまとめます。",
 tools=[file_write],
)

# --- Worker を tool として公開 ---
@tool
def research_worker(query: str) -> str:
 """リサーチ専門エージェントに検索タスクを委譲する"""
 return str(_research_agent(query))

@tool
def summary_worker(content: str) -> str:
 """要約専門エージェントにまとめタスクを委譲する"""
 return str(_summary_agent(content))

# --- Supervisor が自律的に委譲を判断 ---
supervisor = Agent(
 system_prompt="あなたはオーケストレーターです。ユーザーの依頼を分析し、リサーチ→要約の順に委譲してください。",
 tools=[research_worker, summary_worker],
)

supervisor が「どの worker に何を頼むか」を自律的に判断する。LLM が呼び出し順序・並列化の可否をコンテキストから推論するのが Strands の model-driven の特長だ。

アーキテクチャの 3 つの利点:

  • 関心分離: ビジネスロジックを worker 単位で分離できるため、個別テスト・個別デプロイが可能
  • 失敗の封じ込め: worker が例外を返しても supervisor は代替処理を継続できる
  • スケールの独立性: worker のインスタンス数を supervisor と非連動でスケールできる

3-2. タスク分解戦略 — 順次・並列・条件分岐

supervisor が worker に仕事を渡す際、3 つの分解戦略を使い分ける。

戦略説明典型ユースケース
順次(Sequential)A → B → C と直列に実行前段の出力を次段が必要とする場合
並列(Parallel)A / B / C を同時起動独立した調査タスクをまとめて投げる場合
条件分岐(Conditional)前段の結果に応じて後段 worker を切り替えエラー時リトライ・フォールバック先の切り替え

Strands では LLM が暗黙的に並列化を判断するが、明示的に並列化したい場合は asyncio.gather() で worker tool を並行呼び出しすることもできる。並列化の判断ロジックをコードに書かずにモデルに任せることで、仕様変更時の修正箇所が最小化される。

3-3. A2A(Agent-to-Agent)プロトコル — 2025 年 11 月対応

2025 年 11 月、AWS は AgentCore Runtime に A2A(Agent-to-Agent)プロトコル のサポートを正式追加した。これにより、フレームワークをまたいだエージェント間通信が標準化された。

対応フレームワーク(2025 年 11 月時点):
– Strands Agents SDK(AWS 製 OSS)
– OpenAI Agents SDK
– LangGraph(LangChain)
– Google ADK(Agent Development Kit)
– Claude Agents SDK(Anthropic)

A2A プロトコルの特長は、これら異なる SDK で構築されたエージェントが context・capability・reasoning を共通の検証可能な形式で共有 できる点だ。組織内に複数のフレームワークが混在していても、A2A を経由すれば統一インターフェースで連携できる。

A2A の技術仕様:

項目仕様
通信方式JSON-RPC 2.0 over HTTP/S または SSE(Server-Sent Events)
エージェント識別Agent Card(JSON メタデータ)で identity・capability・endpoint を宣言
タスク管理Task Object に一意 ID とライフサイクルを付与
動的発見Agent Card のクエリで peer の capability を確認してから委譲

Agent Card はエージェントが公開する JSON 仕様書だ。他エージェントはこのカードを読んで「何を依頼できるか」を動的に判断する。

{
  "name": "research-agent",
  "version": "1.2.0",
  "capabilities": ["web_search", "document_analysis"],
  "endpoint": "https://<runtime-id>.bedrock-agentcore.us-east-1.amazonaws.com/a2a",
  "auth": { "type": "iam_sigv4" }
}

各 worker は AgentCore Runtime 上で Agent Card を公開し、supervisor が起動時にカードを収集して委譲候補リストを構築する。新 worker を追加する際、supervisor 側のコード変更は不要だ。

3-4. 疎結合とスケール設計 — A2A がもたらす 3 つのメリット

① 動的な追加・削除

稼働中のマルチエージェントシステムへ新しい worker を追加する際、supervisor 側の変更ゼロでよい。新 worker が Agent Card を公開するだけで、supervisor は次回から候補として認識する。ビジネス要件の追加に際し既存エージェントへの影響がない。

② 単一障害の封じ込め(Per-Agent Failure Isolation)

worker が応答不能になっても A2A の Task Object にエラーステータスが記録されるだけで、supervisor と他 worker は動作し続ける。失敗した Task Object を supervisor がリトライ判断するかはポリシー次第だが、デフォルトで障害の波及が封じられる。

③ フレームワーク混在の許容

既存の LangGraph エージェントと新規の Strands エージェントを同一ワークフローに混在させられる。組織の技術スタックが分散していても統一インターフェースで結線できるため、段階的なマイグレーションが可能だ。

§3 まとめ: マルチエージェント協調の勘所

  • Strands の supervisor-worker は「worker を @tool でラップするだけ」— 最小の学習コストで実現可能
  • タスク分解は順次・並列・条件分岐の 3 戦略を場面で使い分ける
  • A2A(2025 年 11 月対応)でフレームワークをまたいだ相互運用が可能になった
  • Agent Card による動的発見が疎結合とスケール設計を支える

4. 本番デプロイパターン — CDK/CI/CD/カナリア

CDKによる本番デプロイとカナリアリリース
図4: AWS CDK による GitOps デプロイと traffic splitting カナリアリリース

4-1. CDK によるエージェントの Infrastructure-as-Code 定義

AWS CDK を使うと、AgentCore のエージェントを Lambda や ECS と同じ感覚で IaC として定義できる。モデル選択・ツール設定・セッションポリシー・IAM 権限をすべてコードで表現し、Git でバージョン管理する。

from aws_cdk import Stack
from aws_cdk.aws_bedrock import CfnAgent, CfnAgentAlias

class AgentCoreStack(Stack):
 def __init__(self, scope, id, **kwargs):
  super().__init__(scope, id, **kwargs)

  # エージェント本体の定義
  agent = CfnAgent(
self, "ResearchAgent",
agent_name="research-agent-prod",
foundation_model="anthropic.claude-3-5-sonnet-20241022-v2:0",
instruction="あなたはリサーチ専門エージェントです...",
idle_session_ttl_in_seconds=1800,
  )

  # バージョン管理とエイリアス(カナリア用)
  alias = CfnAgentAlias(
self, "ProdAlias",
agent_id=agent.attr_agent_id,
agent_alias_name="prod",
routing_configuration=[
 {"agentVersion": "3", "agentVersionPercentage": 90},
 {"agentVersion": "4", "agentVersionPercentage": 10},
],
  )

エージェントの定義変更は cdk deploy 一コマンドで再現可能・監査可能なデプロイが実行され、CI/CD パイプラインに組み込むことで人手のデプロイ操作を排除できる。

4-2. GitOps + CI/CD パイプライン

AgentCore のエージェントデプロイは GitOps ワークフローに乗せるのが本番標準だ。

パイプラインの主要ステージ:

① プルリクエスト
 └─ ユニットテスト / プロンプト評価スイート(Bedrock Evaluations)
 └─ 品質スコアが閾値を下回ったらデプロイブロック

② マージ → main
 └─ CDK Synth(差分確認)→ CDK Deploy(ステージング環境)
 └─ 統合テスト / 回帰テスト(E2E 評価)

③ 本番デプロイ承認
 └─ カナリアリリース(10% → 50% → 100%)
 └─ 各ステップでメトリクス監視・閾値逸脱で自動ロールバック

④ デプロイ完了
 └─ Observability ダッシュボードで token 使用量 / レイテンシ追跡

評価スイートの単調増加ルール: 本番でバグが見つかったら必ずそのケースをテストスイートに追加する。これにより CI/CD が回帰防壁として機能し続ける。

高重要度ケースの回帰ブロック: ハルシネーション・有害出力・セキュリティ違反に相当するケースが回帰した場合、CI がデプロイを自動ブロックする。人の承認なしに本番へ昇格しない。

4-3. Traffic Splitting カナリアリリース

AgentCore エイリアスの routing_configuration を使うと、エージェント変種間でトラフィックを段階的に分割できる。

デプロイ進行の例(v3 → v4 段階移行):

フェーズv3v4判断基準
カナリア開始90%10%エラー率 < 1% / P95 レイテンシ < 5s を 15 分維持
中間フェーズ50%50%同上を 30 分維持
全量移行0%100%問題なし
ロールバック100%0%上記メトリクスが閾値超過した瞬間に自動実行

ロールバックの実行: CloudWatch Alarm が閾値超過を検知すると、CDK パイプラインの rollback ステージが routing_configuration を旧バージョン 100% に戻す。API 一呼び出しで完結するため MTTR(平均復旧時間)が短い。

# ロールバック時の設定更新(AWS SDK 経由)
import boto3
client = boto3.client("bedrock-agent")

client.update_agent_alias(
 agentId="XXXXXXXXXX",
 agentAliasId="YYYYYYYYYY",
 agentAliasName="prod",
 routingConfiguration=[
  {"agentVersion": "3", "agentVersionPercentage": 100}
 ],
)

4-4. バージョニング戦略の選択

戦略概要使いどき
カナリア旧バージョンを残しつつ段階的にトラフィック移行プロンプト変更・モデル変更など品質リスクがある変更
ブルーグリーン旧(Blue)と新(Green)を並行稼働し一括切り替え大規模アーキテクチャ変更・ダウンタイムゼロが必要な場合
ローリングインスタンスを順次入れ替えステートレスな worker エージェントの水平スケール更新

AgentCore では カナリアが推奨デフォルト だ。エージェントは確定的でないため、わずかなプロンプト変更でも挙動が大きく変わることがある。段階的な移行と監視の組み合わせがリスク低減に最も効果的だ。

§4 まとめ: 本番デプロイの要点

  • CDK でエージェントを IaC 化し、Lambda/ECS と同じ GitOps パイプラインで管理する
  • 評価スイートを CI に組み込み、高重要度ケースの回帰でデプロイ自動ブロック
  • Traffic Splitting カナリアで 10% → 50% → 100% と段階的にリリースし、メトリクス逸脱で即時ロールバック
  • プロンプト・モデル変更はカナリア一択。ブルーグリーンはアーキテクチャ刷新時に限定

5. ツール統合の実戦 — Gateway/API/Lambda/MCP

5-1. AgentCore Gateway によるツール統合

AgentCore Gateway は、既存の REST API / Lambda 関数 / OpenAPI 仕様 / MCP サーバーを MCP 互換ツールに変換するプロキシレイヤーです。エージェントは「どのバックエンドか」を意識せず、同一の MCP プロトコルでツールを呼び出せます。

サポートするバックエンドタイプ:

タイプ用途変換方式
REST API既存 Web APIOpenAPI/Smithy から自動生成
Lambda 関数サーバーレス処理関数名+ペイロードで直接呼出
MCP サーバー外部 MCP 対応サービスプロトコルブリッジ
from strands import Agent
from strands_tools import AgentCoreGatewayTool

# Gateway 経由でツールを取得
tool = AgentCoreGatewayTool(
 gateway_id="gw-xxxxxxxx",
 tool_name="customer-lookup"
)

agent = Agent(tools=[tool])
result = agent("顧客 ID: C-12345 の注文履歴を調べて")

Gateway を経由することで、Lambda の更新・API のエンドポイント変更があっても、エージェント側のコードを変更せずに対応できます。ツールの定義はGateway側で一元管理されるため、複数エージェントが同じツールセットを参照する場合の保守コストが大幅に下がります。

5-2. 接続管理とバックエンド委譲パターン

Gateway では inbound / outbound の 2 段階で接続を管理します。

Inbound 側の検証: エージェントランタイムが呼び出し元の操作権限を確認します。IAM ロールや OAuth スコープを用い、どのエージェントがどのツールを呼び出せるかをポリシーで制御します。

Outbound 側の接続確立: バックエンド API への接続をエージェントが代理で行います。秘密情報は Secrets Manager や OAuth クライアントクレデンシャルフローで取得し、エージェントのコード内には保持しません。この分離により、バックエンドの認証方式を変更しても、エージェントのコードには影響が及びません。

sequenceDiagram
 Agent->>Gateway: ツールリクエスト
 Gateway->>API/Lambda: MCP変換後呼出
 API/Lambda-->>Gateway: レスポンス
 Gateway-->>Agent: ツール結果
Gateway経由ツール統合フローと認可委譲
図5: AgentCore Gateway — API/Lambda/MCP統合フローと認可委譲パターン

5-3. エラーハンドリング戦略

ツール呼び出しは必ず失敗する前提で設計します。Strands SDK のエージェントループはツール呼び出し結果をモデルに返す構造のため、エラー情報もツール結果として返すことでモデルが次の行動を判断できます。

リトライ: 一時的なネットワークエラーや流量制限(throttling)には指数バックオフ付きリトライを適用します。Strands SDK の @tool デコレータ内でリトライロジックを実装するか、Lambda の組み込みリトライを活用します。

フォールバック: プライマリツールが失敗した場合に代替ツールを呼び出す戦略をプロンプトで明示します。例: 「リアルタイム API が失敗した場合はキャッシュデータを参照すること」。

サーキットブレーカー: バックエンドが連続で失敗している場合、一定時間ツール呼び出しを停止してバックエンドの回復を待ちます。CloudWatch アラームと連携して、エラー率が閾値を超えたらツールを一時停止する実装が有効です。

import time
from functools import wraps
from strands import tool

def with_retry(max_attempts=3, backoff_base=1.0):
 def decorator(fn):
  @wraps(fn)
  def wrapper(*args, **kwargs):
for attempt in range(max_attempts):
 try:
  return fn(*args, **kwargs)
 except Exception as e:
  if attempt == max_attempts - 1:
return {"error": str(e), "retried": max_attempts}
  time.sleep(backoff_base * (2 ** attempt))
  return wrapper
 return decorator

@tool
@with_retry(max_attempts=3)
def fetch_customer_data(customer_id: str) -> dict:
 """顧客データを取得する"""
 # 実際の API 呼び出し
 ...

5-4. ツール選択戦略

エージェントが複数ツールを持つ場合、モデルはツール名・説明・パラメータスキーマからどれを使うかを推論します。ツール選択の精度を高めるには:

  1. ツール名は動詞+目的語形式: search_orders / get_inventory のように目的が明確な命名にします。
  2. 説明に使用条件を明記: 「注文履歴を日付範囲で検索する場合に使用」のように条件を書きます。曖昧な説明はモデルの誤選択を招きます。
  3. ツール数は絞り込む: 1 エージェントに 20 本以上のツールを持たせると選択精度が落ちます。supervisor-worker で分割し、各 worker に 5〜10 本程度に絞るのが実績のあるパターンです。
  4. パラメータにバリデーションを: JSON Schema の enumpattern で入力を制約すると、モデルが無効な値を渡すリスクを減らせます。
from strands import tool
from typing import Literal

@tool
def search_orders(
 customer_id: str,
 status: Literal["pending", "shipped", "delivered", "cancelled"],
 limit: int = 10
) -> list:
 """
 指定した顧客の注文を状態でフィルタリングして検索する。
 注文の状態確認・配送追跡に使用すること。
 """
 ...

6. 運用・コスト最適化

6-1. Observability — CloudWatch と OTEL

AgentCore は CloudWatch との統合と OTEL(OpenTelemetry)互換テレメトリを提供します。エージェントのセッションごとに以下のメトリクスが自動収集されます:

メトリクス説明活用例
session_countセッション数(並列実行数)スケーリング指標
session_latencyエンドツーエンドのレイテンシSLO 管理
tool_call_countツール呼び出し回数コスト推計
token_usage入出力トークン数月次コスト計算
error_rateエラー率信頼性監視

分散トレースにより、複雑なマルチエージェント推論ループのどのステップで遅延が発生しているかを把握できます。単一クエリが再帰的なツール呼び出しや複数エージェントのサブタスクを経由する場合、従来のログ監視では原因特定が困難です。OTEL トレースで span を確認することで、どのツール呼び出しで遅延・失敗が起きているかをピンポイントで特定できます。

graph LR
 Agent-->|トレース|CloudWatch
 Agent-->|メトリクス|OTEL
 CloudWatch-->Dashboard
 Guardrails-->Agent
運用・コスト最適化 Observability/Guardrails連携
図6: 運用・コスト最適化 — CloudWatch/OTEL Observabilityとコスト管理

6-2. コスト管理 — 消費量の追跡と削減

マルチエージェントシステムでは、消費量が単一エージェントと比べて予測しづらくなります。コスト管理の主な手法:

プロンプトキャッシュの活用: Bedrock のプロンプトキャッシュを使うと、同一のシステムプロンプト・コンテキスト部分を再利用してコストを削減できます。supervisor から worker への指示に含まれる共通部分をキャッシュポイントに設定することで、反復的な消費を抑制します。

入出力の最適化:
– コンテキスト圧縮: 長い会話履歴は要約して渡すことで入力量を削減
– ストリーミング: 出力を逐次受け取ることでユーザー体験は向上するが、生成量自体は変わらない点に注意
– 短絡評価: ルールベースで回答できる質問はエージェントに渡さず前段でフィルタリング

# プロンプトキャッシュの設定例 (Bedrock Converse API)
model_config = {
 "modelId": "anthropic.claude-3-5-sonnet-20241022-v2:0",
 "inferenceConfig": {"maxTokens": 4096},
 "system": [
  {
"text": long_system_prompt,
"cachePoint": {"type": "default"}  # キャッシュ有効化
  }
 ]
}

6-3. レイテンシ最適化

エージェントのレスポンスタイムに影響する主な要因と対策:

要因典型的な遅延対策
モデル推論1〜10 秒ストリーミング・より小さいモデルに委譲
ツール呼び出し0.1〜5 秒非同期並列実行・キャッシュ
エージェント間通信0.5〜3 秒A2A 接続の事前確立
メモリ検索0.1〜2 秒TTL 付きキャッシュ

並列ツール呼び出し: Strands SDK はモデルが複数ツールを同時呼び出しする parallel tool use に対応しています。依存関係のないツール(例: 在庫確認と価格取得)を並列実行することで、シーケンシャル実行と比べてレイテンシを大幅に削減できます。

ストリーミング応答: エージェントの最終回答をストリーミングで返すと、ユーザーは生成開始から最初のトークンを受け取れるため、体感レイテンシが改善されます。

from strands import Agent

agent = Agent(model="us.anthropic.claude-3-5-sonnet-20241022-v2:0")

# ストリーミングで応答を受け取る
for chunk in agent.stream("マルチエージェントのコスト最適化戦略を教えて"):
 print(chunk, end="", flush=True)

6-4. Guardrails 連携

AgentCore Guardrails は、エージェントの応答に対してコンテンツフィルタリングと PII 保護を適用します。

コンテンツフィルタリング: 不適切コンテンツ・プロンプトインジェクション攻撃をモデルの応答前にブロックします。Guardrails が拒否した場合、対応する span には action=BLOCKEDcategory が記録されるため、どのポリシーに違反したかをトレースで確認できます。

PII 保護: 個人情報(氏名・メールアドレス・クレジットカード番号など)を自動でマスクまたは匿名化します。エージェントが顧客データを扱う業務アプリケーションで特に重要です。

Guardrails の段階的な適用:
– エントリポイント: ユーザーの入力に適用(プロンプトインジェクション対策)
– エグジットポイント: エージェントの最終応答に適用(PII・有害コンテンツ除去)
– ツール応答: バックエンド API からのデータにも適用可能(データ漏洩防止)

6-5. コスト試算例

マルチエージェントシステムの月次コスト概算(参考値):

構成想定リクエスト数/月推定コスト(USD)
単一エージェント(Claude 3.5 Sonnet)10,000約 150〜300
supervisor + worker×3 構成10,000約 500〜1,000
プロンプトキャッシュ適用後10,000約 300〜600

※ 生成量・ツール呼び出し回数・モデル選択により大きく変動します。実際の構成では Bedrock Cost Explorer でセッションごとのコストを追跡することを推奨します。


7. 実戦統合パターンと移行

7-1. Strands × AgentCore Runtime の組み合わせパターン

Strands Agents SDK は実装フレームワーク、AgentCore Runtime は実行インフラという役割分担が成立する。この2つを組み合わせる際、設計上の「結合点」を意識することが本番品質に直結する。

最もシンプルな構成は、Strands で定義したエージェントを AgentCore Runtime 上にデプロイする シングルエージェント + ランタイム管理 パターンだ。Strands の @tool デコレータでツールを定義し、Agent クラスでエージェントロジックを記述する。AgentCore Runtime がマイクロVM分離・セッション管理・タイムアウト(最大8時間)を担う。

from strands import Agent, tool
from strands.models import BedrockModel

@tool
def search_knowledge_base(query: str) -> str:
 """社内ナレッジベースを検索する"""
 # 実装省略
 return result

model = BedrockModel(
 model_id="anthropic.claude-sonnet-4-5",
 region_name="us-east-1"
)

agent = Agent(
 model=model,
 tools=[search_knowledge_base],
 system_prompt="あなたは社内情報を検索・回答するエージェントです。"
)

AgentCore Runtime へのデプロイ時、エージェントコードは Lambda 関数または ECS タスクとしてパッケージングし、CDK スタックで管理する。Runtime が提供するセッション分離により、複数ユーザーが同時にエージェントを利用しても互いの状態が干渉しない。

Strands × Gateway の組み合わせ では、エージェントが呼び出すツールを Gateway 経由で統一管理できる。社内 REST API を OpenAPI 仕様書から MCP ツールに変換し、Strands の @tool 定義なしに動的にツールをロードする構成が可能だ。Gateway が認可委譲(OAuth / IAM)を吸収するため、エージェントコード側の認証ロジックを排除できる。

Strands × Memory の組み合わせ では、エージェントが AgentCore Memory から会話履歴・ユーザープロファイルを取得し、コンテキストに注入する。短期メモリ(DynamoDB)は同一セッション内の文脈保持、長期メモリ(S3)はユーザーの学習・蓄積知識の保持に使い分ける。

Strands × Observability の組み合わせ では、Strands のエージェントループが自動的にトレースを生成し、CloudWatch / OTEL へ送信する。各ツール呼び出しがスパンとして記録されるため、どのツールがどれだけのレイテンシとトークンを消費しているかを計測できる。

7-2. 既存 Bedrock Agents からの段階的移行

既存の フルマネージド Bedrock Agents(コンソール設定型)から Strands + AgentCore Runtime(モジュラー実装)への移行は、一括切替ではなく段階的に進めるのが安全だ。

フェーズ1 — 現状整理と並行稼働

まず既存 Bedrock Agent のアクショングループ(Lambda)・ナレッジベース・システムプロンプトを棚卸しする。Strands でも同等のツール・プロンプトを定義し、同じユーザーリクエストに対して両実装を並行稼働させる。A/B テストではなく、同一リクエストを両者に流してレスポンス差分を計測する。

品質評価スイートをこの段階で構築する。具体的には、「期待する回答」と「実際の回答」の類似度を自動評価するテストケースを20件以上用意し、Strands 実装が旧実装と同等以上のスコアを継続して達成することを確認してから次フェーズへ進む。

フェーズ2 — モジュラー実装への段階移行

トラフィックを徐々に Strands 実装へ切り替える(5% → 20% → 50% → 100%)。AgentCore の traffic splitting 機能を用いると、単一エンドポイントで比率を制御できる。品質評価スイートがフェーズ1で作成した回帰ケースでグリーンになり続ける場合のみ次の比率へ進む。

各段階でのモニタリング指標: エラーレート・平均レイテンシ・ユーザーが問い合わせを繰り返す率(一発解決率)。いずれかが旧実装対比で悪化した場合は即時ロールバックする判断基準を事前に定義しておく。

フェーズ3 — 旧実装廃止と最適化

100% 移行後、旧 Bedrock Agent リソースを削除。Strands × AgentCore Runtime の設定をコードベース(CDK スタック)に集約し、IaC 管理下に置く。レスポンスレイテンシ・トークン使用量のベースラインを計測し、モデル選択・プロンプト最適化のサイクルを開始する。

7-3. マルチエージェント本番事例 — カスタマーサポート自動化

マルチエージェント構成の典型的な本番事例として、カスタマーサポート自動化システム が挙げられる。実際のアーキテクチャは以下のような supervisor-worker 構成をとる。

ユーザー問い合わせ
↓
[Routing Supervisor]
  - 問い合わせカテゴリを分類
  - 適切な Worker へルーティング
↙↓↘
[Tech Support]  [Billing]  [FAQ]
  Worker  Worker  Worker
↘↓↙
  [Memory / Knowledge Base]

Supervisor エージェントが問い合わせを分類し、技術サポート・請求・FAQ の各 Worker エージェントへ A2A プロトコルで委譲する。Worker はそれぞれ専用のナレッジベースとツールを持ち、処理結果を Supervisor へ返す。Supervisor が最終レスポンスを合成してユーザーへ返答する構成だ。

この構成の本番運用において重要な点が3つある。

コンテキスト伝播の設計: ユーザーのセッション情報(過去の問い合わせ履歴・認証情報・優先度)を Supervisor から Worker へ明示的に渡すスキーマを定義する。暗黙的な状態共有は Worker 間の競合を生む。Worker が必要とする情報の最小セットを事前定義し、それ以外は Memory から動的取得させる設計が保守性に優れる。

Worker 障害の封じ込め: 特定 Worker(例: 外部 API タイムアウトで Tech Support Worker が応答不能)が落ちても、Supervisor は他の Worker への切り替えかエラーレスポンスの返却を選択できる設計にする。Circuit Breaker パターンの実装が有効だ。

コスト配分の可視化: Worker ごとのトークン使用量・レイテンシを CloudWatch カスタムメトリクスで分離計測し、高コスト Worker の最適化につなげる。FAQ Worker は軽量モデル(Haiku 系)に切り替えるだけでコストが大幅に削減できるケースが多い。

7-4. 段階導入戦略 — 単一→複数→フル協調

マルチエージェント化は最初から全部を作ろうとすると失敗する。推奨する3段階の導入戦略を示す。

Stage 1 — 単一エージェント + AgentCore Runtime

最初のシステムは必ず単一エージェントで立ち上げる。Runtime のセッション管理・Observability・ツール統合の基盤を安定させることを優先する。この段階で品質評価スイート(回帰テストケース)を構築し、後段の分割を安全に進めるための基準を確立する。

Stage 2 — 機能分割による複数エージェント化

単一エージェントが「何でも屋」になってトークン使用量が膨らんだタイミングで、機能ドメインごとに Worker エージェントへ分割する。最初に分割するのは「頻繁に使われ・独立性が高い」機能から。ログ検索・データ取得など副作用のないツールを持つ Worker から始めると安全だ。

Stage 3 — フル協調システム

Worker 間の A2A 通信が必要になるユースケース(例: 契約照会 Worker の結果を使って請求計算 Worker が処理する連鎖処理)で A2A プロトコルを導入する。全 Worker を立ち上げてから A2A を繋ぐのではなく、1つの Worker ペアから実証して横展開する。

7-5. 失敗パターンと回避策

エージェント間デッドロック

Supervisor が Worker-A に委譲し、Worker-A が Worker-B に問い合わせ、Worker-B がさらに Supervisor に問い合わせる循環参照が発生すると、全エージェントが応答待ちでデッドロックする。回避策は有向非巡回グラフ(DAG)として通信フローを設計し、双方向通信が生じうる箇所に明示的な最大ホップ数制限(例: max_depth=3)を設ける。

コンテキスト肥大化

Supervisor が会話履歴・Worker からの返答・エラーログを無制限に蓄積すると、数ターンでコンテキストウィンドウが枯渇する。Memory コンポーネント(短期: DynamoDB、長期: S3)を活用し、現在の推論に必要な情報のみをワーキングコンテキストに保持するサマリゼーション戦略を実装する。具体的には、会話が5ターンを超えたら直近3ターン + サマリに圧縮するロジックを Supervisor に組み込む。

Worker スケールアウト時のセッション継続性

高負荷時に Worker インスタンスが追加された場合、既存セッションの文脈が新インスタンスで引き継げないと会話が途切れる。AgentCore の Memory コンポーネントをセッションストアとして共有することで、インスタンス間の透過的な引き継ぎを実現する。インスタンス固有のローカル状態は持たない設計(ステートレス Worker)が基本原則だ。


8. 詰まりポイント・アンチパターンとまとめ

8-1. 詰まりポイント7選

本番環境でエンジニアが実際に詰まりやすいポイントを、具体的なエラーや設計ミスとともに解説する。


詰まり1: Strands SDK のインストール・依存関係トラブル

Strands は 2025年5月にリリースされた新しいライブラリのため、他のパッケージとの依存競合が起きやすい。特に boto3 のバージョン要件が厳しく、既存プロジェクトで boto3==1.26.x を使っている場合、Strands の要求する boto3>=1.34.x と衝突してインストールが失敗する。

症状例:

ERROR: pip's dependency resolver does not currently take into account
all the packages that are installed. This behaviour is the source of
the following dependency conflicts.
strands-agents 0.1.x requires boto3>=1.34.0, but you have boto3 1.26.x.

解決策: プロジェクトを仮想環境(venv / Poetry)で分離し、依存ツリーを新規から構築する。Lambda 等のデプロイ環境では requirements.txt にバージョン固定してからビルドレイヤを作成する。本番デプロイ前に pip check で依存競合がないことを確認するステップを CI に組み込む。


詰まり2: マルチエージェントのコンテキスト共有失敗

A2A プロトコルでエージェント間を連携させる際、Supervisor が Worker に渡すコンテキストが正しく伝わらないケースが頻発する。典型的な原因は2つある。

まず、A2A のメッセージスキーマは厳密な JSON 構造を要求するが、Strands のエージェントが返す文字列をそのまま次エージェントに渡すとパースエラーになる。Worker が str 型で返した結果を、Supervisor 側で json.loads() せずに次の Worker へ渡すケースが多い。

次に、context_id の設計ミスだ。同一ユーザーセッションの複数 Worker 呼び出しで同じ context_id を使用しないと、Memory が別セッションとして記録してしまう。セッション識別子を最初のリクエスト受信時に生成し、全 Worker 呼び出しに一貫して渡す設計が必要だ。


詰まり3: AgentCore Runtime タイムアウト設計の誤り

AgentCore Runtime のセッション上限は8時間だが、デフォルトのアイドルタイムアウト(ユーザー入力待ち)は設定により短縮される。「8時間使えると思っていたら1時間でセッションが切れた」というトラブルの原因は、インタラクティブタイムアウトの設定漏れが多い。

また、長時間処理(外部データベースの一括処理・大量ドキュメントの要約)をエージェントに実行させると、処理中に Lambda の15分制限に引っかかる。長時間処理は SQS → ECS タスク(タイムアウト上限なし)に分離し、エージェントはキューへの投入と結果取得のみを担当する設計が正解だ。


詰まり4: Gateway MCP 変換でのツール署名不一致

Gateway が OpenAPI 仕様から MCP ツールを自動生成する際、API のレスポンス型が object ではなく array を返す場合に Strands のツール定義と型不一致が発生する。

症状: ValidationError: Expected object, got list のようなエラーが本番リクエストでランダムに発生する。

根本原因は OpenAPI 仕様書の responses[200].content.application/json.schematype: array になっているのに、Gateway 生成のラッパーが object を期待する型定義を出力するケース。Gateway コンソールで「ツールのスキーマプレビュー」を確認し、array 型のレスポンスには手動でラッパーオブジェクト({"items": [...]} 形式)を追加する。


詰まり5: 委譲トークンの期限切れハンドリング漏れ

AgentCore Identity が発行する委譲トークン(短命 STS 認証情報)は有効期間が1時間と短い。長時間セッション内でツールを繰り返し呼び出す場合、最初の呼び出しでは成功するが、1時間後の呼び出しで ExpiredTokenException が発生する。

解決策: ツール実装側でリトライロジックとトークン更新処理を組み込む。Strands の @tool デコレータ内でキャッチした botocore.exceptions.ClientError のエラーコードが ExpiredTokenException の場合、AgentCore の認証情報リフレッシュ処理を呼び出してから再試行する。

import botocore.exceptions
from strands import tool

@tool
def call_backend_api(payload: dict) -> dict:
 """バックエンド API を呼び出す"""
 for attempt in range(2):
  try:
return _call_api(payload)
  except botocore.exceptions.ClientError as e:
if e.response["Error"]["Code"] == "ExpiredTokenException" and attempt == 0:
 _refresh_credentials()
 continue
raise

詰まり6: カナリアリリース時のセッション継続性問題

traffic splitting でカナリア(新バージョン5%)を有効化した際、既存セッション(旧バージョンで会話中のユーザー)が次のリクエストでカナリアへルーティングされると、会話の文脈(バージョン固有のシステムプロンプト・ツールセット)が変わって回答品質が劣化する。

この問題の回避策は セッションアフィニティ の設定だ。AgentCore の traffic splitting でセッション ID ベースのスティッキーセッションを有効化し、既存セッションは元のバージョンで処理し続ける。新規セッションのみカナリア比率でルーティングする設定にすることで、既存ユーザーへの影響を遮断できる。セッションが終了した後、次回の新規セッション開始時に自然とカナリアへ移行する流れになる。


詰まり7: Observability メトリクスの過剰収集によるコスト増大

全エージェントのリクエスト・レスポンスをそのまま CloudWatch Logs に記録すると、トークン数が多い会話では1レスポンスで数十KB のログが生成される。日次 100万リクエスト規模になるとログコストが月数十万円に膨らむケースがある。

解決策は3段階のロギング戦略だ。

  1. サマリメトリクスのみ本番常時記録: トークン数・レイテンシ・エラー有無の数値のみを CloudWatch Metrics として保存(コスト極小)。
  2. サンプリングログ: 全リクエストの1%(設定可能)のみプロンプト・レスポンス全文を S3 に保存。品質分析に使用。
  3. エラー時の全文記録: エラーが発生したリクエストのみ自動的に全文ログを CloudWatch Logs Insights 対象に書き込む。デバッグに不可欠な情報を確実に残す。

この3段階戦略で、フル記録対比で80〜90%のログコスト削減が期待できる。

8-2. アンチパターン → 正解パターン


アンチ1: 全エージェントに同じモデル・ツールセットを与える

アンチパターン: 全ての Worker エージェントに Claude Opus(最高精度・高コスト)と全ツールセットを割り当てる。「性能は高いほど良い」という発想で全エージェントを最強構成にしてしまう。

正解: 役割に応じてモデルと最小権限ツールセットを使い分ける。Routing Supervisor は高精度モデル(Opus 系)で分類精度を確保し、FAQ Worker は低コストモデル(Haiku 系)でコスト最適化する。ツールも各 Worker が必要なツールのみを付与し、権限のスコープを最小化する。これによりコストが平均30〜50%削減される実績がある。


アンチ2: エージェント間通信を HTTP API で自前実装する

アンチパターン: Supervisor から Worker への委譲を REST API(requests.post("http://worker-a-endpoint/invoke", ...))で自前実装する。シンプルに見えるが、認証・リトライ・タイムアウト管理・ログ相関をすべて自前で実装する必要がある。

正解: A2A プロトコル / AgentCore Gateway 経由で標準化する。A2A はコンテキスト伝播・認証・トレーシングを組み込みで提供する。自前実装ではなく標準プロトコルを使うことで、将来的に異なるフレームワーク(LangGraph / OpenAI Agents SDK)の Worker との接続も設定変更のみで対応できる。


アンチ3: Strands SDK をフレームワーク非依存の代替として使う

アンチパターン: 「Strands は AWS に縛られないので既存 LangChain プロジェクトをそのまま Strands に移植する」という発想で採用する。Strands の BedrockModel 以外のモデルプロバイダー対応を前提にした設計をしてしまう。

正解: AgentCore をインフラ、Strands を AWS エコシステム向けの実装フレームワークとして明確に分離する。Strands は Bedrock モデル・AWS ツールとの親和性が最も高く、他クラウドへの移植性は設計目標ではない。AWS 環境に最適化した実装として割り切って使うことが成功の鍵だ。マルチクラウド対応が要件なら LangChain / LangGraph の選択を検討する。


アンチ4: エラーハンドリングなしでツールチェーンを設計する

アンチパターン: tool_A → tool_B → tool_C と複数ツールを連鎖させる設計で、各ツールが失敗した場合の処理を考慮しない。tool_B が None や空リストを返した場合に tool_C が AttributeError で落ちる。

正解: 各ツールのレスポンスに success: bool / error_code: str / payload: dict のスキーマを統一し、エージェントが各ステップで結果を検証してから次ツールを呼び出す設計にする。さらに、ツールチェーンの途中失敗時に部分的な結果をユーザーに伝える「部分成功レスポンス」を設計に含めると UX が大幅に向上する。


アンチ5: カナリア判定をリクエスト数のみで行う

アンチパターン: 「エラーレート 1% 未満ならカナリア成功」という単純な閾値でカナリア判定を行う。エラーレートは正常でも回答品質が劣化(例: 以前は3ステップで完了した処理が5ステップ必要になった)するケースを見逃す。

正解: 評価指標を多層化する。エラーレート(技術的正確性)に加えて、①平均ターン数(効率性)、②ユーザーが問い合わせを繰り返す率(一発解決率)、③レスポンスレイテンシ(応答速度)を合わせてカナリア判定条件に含める。複数指標が全てグリーンの場合のみ次の比率へ進む。


アンチ6: デプロイ設定をコンソール操作で管理する

アンチパターン: AgentCore の設定(モデル選択・ツールセット・メモリ設定・Guardrails)をコンソールで手動変更し、変更履歴を残さない。「動けばよい」という判断で IaC 管理を省略する。

正解: すべての設定を CDK スタックで管理し、変更は必ず Pull Request → CI/CD 経由でデプロイする。コンソールは「確認」に使い、「変更」には使わないルールを徹底する。誰がいつ何を変えたかが git ログで追跡できる状態が本番運用の前提条件だ。コンソールで変更した設定は次回の CDK デプロイで上書きされてしまうという事故も防げる。

8-3. まとめ — Vol1・Vol2 の学習パスと本番設計の始め方

Vol1・Vol2 の二部作学習パス

Vol1(基盤)では AgentCore の5つのコンポーネント(Runtime / Memory / Gateway / Identity / Observability)を理解し、それぞれの役割・設定・制限を把握する。Vol2(本記事)では Strands SDK でエージェントを実装し、supervisor-worker + A2A でマルチエージェント協調を構成する。CDK でデプロイし、カナリアリリース・CI/CD・Observability・コスト最適化まで整備する。

両者を組み合わせることで、「単なる実験」ではなく「本番システムとして設計されたエージェント」を作れる状態になる。

フレームワーク選択指針

要件推奨
AWS 環境で素早く実装したいStrands + AgentCore
グラフ構造を人が細かく制御したいLangGraph
ビジネスチーム向けの高速プロトタイプCrewAI
マルチクラウド・プロバイダー非依存LangChain

本番運用チェックリスト

  • [ ] CDK スタックでエージェント設定を IaC 管理している
  • [ ] 品質評価スイート(回帰テストケース)を10件以上構築している
  • [ ] カナリアリリースの判定指標が多層化されている(エラーレート + 効率性 + 品質)
  • [ ] Worker ごとのトークン使用量・レイテンシを CloudWatch で分離計測している
  • [ ] Memory コンポーネントのサマリゼーション戦略を実装している(コンテキスト肥大化防止)
  • [ ] 委譲トークンのリフレッシュロジックをツール実装に組み込んでいる
  • [ ] A2A 通信の有向非巡回グラフ設計(最大ホップ数制限)を実施している
  • [ ] セッションアフィニティを有効化してカナリア移行時の文脈切れを防止している

next step — 本番システム設計の始め方

  1. まず単一エージェント + AgentCore Runtime で立ち上げる(Stage 1)
  2. Observability を整備し、ボトルネック(高コストツール・高レイテンシパス)を計測する
  3. ドメイン分割の境界を決め、Worker エージェントへ段階的に切り出す(Stage 2)
  4. 品質評価スイートがグリーンを維持する範囲で A2A 協調を追加する(Stage 3)

AgentCore × Strands で作るエージェントの設計原則

本記事を通じて共通して現れる設計原則をまとめると以下の通りだ。

  • 単一責任: 各 Worker エージェントは単一ドメインに特化させる。多目的エージェントはコスト・品質・保守性の全面で劣る。
  • ステートレス設計: Worker インスタンスは状態を持たず、全ての状態は Memory コンポーネントまたは呼び出し元の Supervisor が管理する。これによりスケールアウト・障害復旧が透明になる。
  • 明示的な契約: エージェント間(Supervisor ↔ Worker)の入出力スキーマを事前に定義する。暗黙的な文字列のやり取りは型エラーの温床になる。
  • 計測ファースト: 最適化の前に必ず計測する。トークン使用量・レイテンシ・エラーレートを Worker 単位で把握してから最適化に着手する。
  • 段階的進化: 単一エージェントから始め、計測の裏付けがある範囲でのみマルチエージェント化を進める。最初から複雑な構成は作らない。

これらの原則は AWS の Well-Architected Framework のエージェント版として捉えることができ、スケーラビリティ・信頼性・コスト効率の全てに寄与する。

シリーズ案内 — AWS Bedrock AgentCore 本番運用

  • Vol1: 基盤コンポーネント — Runtime のマイクロVM分離・Memory の短期/長期設計・Gateway のMCP変換・Identity の委譲認証・Observabilityの構成を解説
  • Vol2(本記事): 実装・協調・デプロイ・運用 — Strands SDK によるエージェント実装・マルチエージェント協調・CDK本番デプロイ・カナリアリリース・詰まりポイント
  • Vol3: 可観測性・評価・コスト統制・Identityガバナンス — AgentCore Observability×CloudWatch・Evaluations・per-invocationコスト統制・OAuthガバナンス・スケーリング設計

参考リンク: Strands Agents SDK(GitHub) / AgentCore 公式ドキュメント

前編: AgentCore 本番運用 Vol1(Runtime/Memory/Gateway/Identity)を読む →

次編: AgentCore 本番運用 Vol3(可観測性・評価・コスト統制・Identityガバナンス)を読む →

関連:Bedrock本番運用Vol3(Guardrails高度化・Prompt management・Nova生成)を読む

生成AIアプリ本番運用 LLMOps統合編 Vol1 — 評価・可観測性・コスト統制・ガードレール横断運用 →