はじめに
こんにちは、アプリケーションサービス本部ディベロップメントサービス3課の北出です。
前回の記事では Amazon Bedrock Agents を使って天気エージェントを作りました。今回は同じ天気エージェントを Amazon Bedrock AgentCore(以下 AgentCore)で作り直してみます。
AgentCoreは「自分で書いたコードを本番で動かすための基盤」で、Bedrock Agentsとは設計思想が異なります。実際に触ってみて、何が違うのか、どういう場面で使うべきかを体感したかったのが動機です。
今回体験するAgentCore特有の機能は以下の通りです。
Runtime: サーバーレスなエージェント実行環境
Memory: 会話の文脈保持
Gateway: 外部APIをMCPツールとして接続
Observability: OpenTelemetry互換のトレース
今回作るもの
前回と同じ「天気を教えてくれるエージェント」ですが、AgentCoreの機能を活用します。
ユーザー
「ニセコの天気教えて」
│
▼
AgentCore Runtime(Strands Agents + Claude Sonnet 4)
│
├── Gateway経由: geocode_location(地名→緯度経度)
│ └── Lambda → Open-Meteo Geocoding API
│
└── @tool: get_weather(緯度経度→天気)
└── Open-Meteo Weather API
│
▼
Memory に会話を保存
│
▼
ユーザーに回答
前回のBedrock Agentsとの大きな違い:
コードベースで開発(
@toolデコレータでツール定義)ローカルで動作確認してからデプロイ
Gatewayで外部APIをMCPツール化
トレースで推論の各ステップを可視化
検証環境
Node.js 24
Python 3.14
Docker
AWS CLI
AWS CDK(
npm install -g aws-cdk)Bedrock モデルアクセス(Claude Sonnet 4)
手順
まずは Gateway 経由の「地名→緯度経度」の機能はなく、前回作った Bedrock Agents と同等のエージェントを AgentCore で作ります。
1. AgentCore CLIのインストール
npm install -g @aws/agentcore
AgentCore CLI はプロジェクトの作成からデプロイ、呼び出しまでを一貫して行えるツールです。
2. プロジェクト作成
agentcore create \ --name WeatherAgent \ --framework Strands \ --protocol HTTP \ --model-provider Bedrock \ --memory shortTerm \ --build Container
--memory shortTerm で会話の文脈を保持するMemoryが自動設定されます。--build Container でDockerコンテナベースのデプロイになります。
CodeZipデプロイ(デフォルト)も選べますが、OpenTelemetry等の重い依存を含む場合はContainerの方が初期化タイムアウトを回避できて安定します。
生成されるプロジェクト構造:
WeatherAgent/ ├── agentcore/ # AgentCoreの設定・インフラ定義 │ ├── agentcore.json # プロジェクト設定(Memory設定含む) │ ├── aws-targets.json # デプロイ先リージョン │ └── .env.local # ローカル環境変数 ├── app/ │ └── WeatherAgent/ # エージェントのアプリケーション │ ├── main.py # エージェントのエントリポイント │ ├── Dockerfile # コンテナビルド定義 │ └── pyproject.toml # Python依存関係 └── README.md
3. エージェントコードの作成
app/WeatherAgent/main.py にエージェントのロジックを書きます。
import json import urllib.request from strands import Agent, tool from bedrock_agentcore.runtime import BedrockAgentCoreApp app = BedrockAgentCoreApp() WEATHER_CODES = { 0: "快晴", 1: "おおむね晴れ", 2: "一部曇り", 3: "曇り", 45: "霧", 48: "着氷性の霧", 51: "弱い霧雨", 53: "霧雨", 55: "強い霧雨", 61: "弱い雨", 63: "雨", 65: "強い雨", 71: "弱い雪", 73: "雪", 75: "強い雪", 80: "弱いにわか雨", 81: "にわか雨", 82: "激しいにわか雨", 95: "雷雨", 96: "雹を伴う雷雨", 99: "激しい雹を伴う雷雨", } @tool def get_weather(latitude: str, longitude: str) -> str: """指定された緯度と経度の現在の天気情報を取得します。 Args: latitude: 緯度(例: 35.6762) longitude: 経度(例: 139.6503) """ url = ( f"https://api.open-meteo.com/v1/forecast?" f"latitude={latitude}&longitude={longitude}" f"¤t=temperature_2m,weather_code,wind_speed_10m,relative_humidity_2m" f"&timezone=auto" ) req = urllib.request.Request(url) with urllib.request.urlopen(req) as res: data = json.loads(res.read().decode()) current = data.get("current", {}) temperature = current.get("temperature_2m", "不明") weather_code = current.get("weather_code", 0) wind_speed = current.get("wind_speed_10m", "不明") humidity = current.get("relative_humidity_2m", "不明") weather_desc = WEATHER_CODES.get(weather_code, "不明") return ( f"現在の天気: {weather_desc}\n" f"気温: {temperature}°C\n" f"湿度: {humidity}%\n" f"風速: {wind_speed} km/h" ) SYSTEM_PROMPT = """あなたは天気情報を提供するアシスタントです。 ユーザーから場所を聞かれたら、その場所の緯度・経度を推定してget_weatherツールで天気情報を取得してください。 主要都市の緯度・経度: - 東京: 緯度 35.6762, 経度 139.6503 - 大阪: 緯度 34.6937, 経度 135.5023 - 名古屋: 緯度 35.1815, 経度 136.9066 - 札幌: 緯度 43.0618, 経度 141.3545 - 福岡: 緯度 33.5904, 経度 130.4017 回答のルール: 1. 必ず日本語で回答する 2. 天気情報を分かりやすくフォーマットして表示する 3. 場所が曖昧な場合はユーザーに確認する 4. 天気に基づいたアドバイス(傘が必要か等)も添える 5. 前の会話で聞いた場所を覚えておき、「さっきの場所は?」と聞かれたら答える """ _agents = {} @app.entrypoint def handle(payload: dict, session_id: str = None): prompt = payload.get("prompt", "") session_id = session_id or "default" if session_id not in _agents: from memory.session import get_memory_session_manager session_manager = get_memory_session_manager(session_id=session_id, actor_id="user") _agents[session_id] = Agent( model="apac.anthropic.claude-sonnet-4-20250514-v1:0", system_prompt=SYSTEM_PROMPT, tools=[get_weather], session_manager=session_manager, ) agent = _agents[session_id] result = agent(prompt) return {"response": str(result)} if __name__ == "__main__": app.run()
コードのポイント:
@toolデコレータでツールを定義。docstringがツールの説明文、型ヒントがパラメータ定義になる_agents辞書でセッションごとにAgentをキャッシュ。handle関数内で毎回新規作成すると前の会話を忘れてしまうsession_managerでAgentCore Memoryと連携し、同一セッション内の会話履歴を自動保持if __name__ == "__main__": app.run()はContainerデプロイで必須(HTTPサーバーを起動する)
注意: この時点ではGateway未接続のため、プロンプトに主要都市の緯度経度をハードコードしています。手順9でGatewayを追加し、任意の地名に対応できるようにします。
4. Dockerfileの作成
Containerデプロイには Dockerfile が必要です。
FROM public.ecr.aws/docker/library/python:3.14-slim WORKDIR /app COPY . . RUN pip install --no-cache-dir . EXPOSE 8080 CMD ["opentelemetry-instrument", "python", "main.py"]
CMD で opentelemetry-instrument を使うのがポイントです。これによりトレースが自動的にCloudWatchに送信されます(CodeZipデプロイでは自動設定されますが、Containerでは明示的に指定が必要)。
5. ローカルテスト
cd WeatherAgent agentcore dev
ブラウザでAgent Inspectorが開き、ローカルでエージェントの動作確認ができます。Bedrock Agentsではコンソール上でしかテストできませんでしたが、AgentCoreではデプロイ前にローカルで試行錯誤できるのが大きな利点です。

会話を見ると、Memoryも機能していることがわかります。
6. デプロイ
agentcore deploy
AWS Cloud Development Kit(AWS CDK)ベースで AWS CloudFormation スタックが作成され、Runtime・Memory・Gatewayなどのリソースがプロビジョニングされます。
7. 呼び出しと動作確認
agentcore invoke "東京の天気を教えて"
マネジメントコンソールのサンドボックスで動作確認することもできます。

8. Observability(トレース確認)
CloudWatchの生成 AI オブザーバビリティダッシュボードでトレースを確認できます。
トレースを見るには、アカウントで1回だけ CloudWatch トランザクション検索 を有効化する必要があります。
- CloudWatch コンソール →「Application Signals (APM)」→「Transaction search」
- 「Enable Transaction Search」→ スパンの取り込みを有効化
- インデックスするトレースの割合を設定(テスト中は100%推奨)
有効化後、以下の手順でトレースを確認します。
- CloudWatch コンソール →「生成 AI オブザーバビリティ」→「Bedrock AgentCore」
- 「Traces」タブ → 対象のトレースをクリック
トレースを見ると、各処理でかかっている時間や、get_weather tool が実行されていることがわかります。 また、処理時間の約7割がLLMの推論に費やされており、ツール実行やその他の操作は軽量であることがわかります。

9. Gateway でジオコーディングAPIを接続
ここまでは大体前回の記事の Amazon Bedrock Agents でやったことと同じで、エージェントはプロンプトに主要都市の緯度経度をハードコードしていました。
次に、Gatewayを使って任意の地名に対応できるようにします。
ただし、無理に Gateway を使う必要はなく、@tool を追加することでも十分対応できますが、理解のために Gateway で実装します。
Gatewayは、既存のAPIやLambda関数をMCP互換のツールに変換し、エージェントから統一的に呼び出せるようにするサービスです。
まず、ジオコーディング用のLambda関数を作成します。
関数名:
geocode-functionランタイム: Python 3.14
タイムアウト: 30秒
import json import urllib.request import urllib.parse def lambda_handler(event, context): """地名から緯度経度を取得する(Open-Meteo Geocoding API)""" tool_use = event.get("toolUse", {}) tool_input = tool_use.get("input", {}) location_name = tool_input.get("location", "Tokyo") url = f"https://geocoding-api.open-meteo.com/v1/search?name={urllib.parse.quote(location_name)}&count=1&language=ja" req = urllib.request.Request(url) with urllib.request.urlopen(req) as res: data = json.loads(res.read().decode()) results = data.get("results", []) if not results: return { "content": [{"text": f"「{location_name}」の位置情報が見つかりませんでした。"}] } place = results[0] return { "content": [ { "text": json.dumps({ "name": place.get("name", location_name), "latitude": place.get("latitude"), "longitude": place.get("longitude"), "country": place.get("country", ""), "admin1": place.get("admin1", "") }, ensure_ascii=False) } ] }
次に、プロジェクトルートに tools.json(ツールスキーマ)を作成します。
[ { "name": "geocode_location", "description": "地名や都市名から緯度と経度を取得します。天気を調べる前に、ユーザーが指定した場所の座標を取得するために使います。", "inputSchema": { "type": "object", "properties": { "location": { "type": "string", "description": "検索する地名(例: 渋谷、横浜、Paris)" } }, "required": ["location"] } } ]
Gatewayを作成し、Lambda関数をターゲットとして登録します。
agentcore add gateway --name WeatherGateway --authorizer-type NONE --runtimes WeatherAgent agentcore add gateway-target \ --name GeocodingTarget \ --type lambda-function-arn \ --lambda-arn <Lambda ARN> \ --tool-schema-file tools.json \ --gateway WeatherGateway
次に、エージェントのコードを更新してからデプロイします。
app/WeatherAgent/main.py を以下のように更新します。変更点は3つです。
変更1: import追加とGATEWAY_URL定義(ファイル先頭)
import json import os import urllib.request from strands import Agent, tool from strands.tools.mcp.mcp_client import MCPClient from mcp.client.streamable_http import streamablehttp_client from bedrock_agentcore.runtime import BedrockAgentCoreApp app = BedrockAgentCoreApp() GATEWAY_URL = os.getenv("GATEWAY_WEATHERGATEWAY_URL", "<デプロイ後に確認したURL>")
変更2: SYSTEM_PROMPTの更新(ハードコード緯度経度を削除し、geocode_locationの使用を指示)
SYSTEM_PROMPT = """あなたは天気情報を提供するアシスタントです。 ユーザーから場所を聞かれたら: 1. 必ず最初に geocode_location ツールで地名から緯度・経度を取得すること(自分の知識で緯度経度を推定してはいけない) 2. 次に get_weather ツールで天気情報を取得する 回答のルール: 1. 必ず日本語で回答する 2. 天気情報を分かりやすくフォーマットして表示する 3. 場所が曖昧な場合はユーザーに確認する 4. 天気に基づいたアドバイス(傘が必要か等)も添える 5. 前の会話で聞いた場所を覚えておき、「さっきの場所は?」と聞かれたら答える """
変更3: get_gateway_tools()関数の追加と、handle()内でGatewayツールを渡す
def get_gateway_tools(): """Gateway経由のツールを取得する""" if not GATEWAY_URL: return [] mcp_client = MCPClient(lambda: streamablehttp_client(GATEWAY_URL)) mcp_client.__enter__() return mcp_client.list_tools_sync() _agents = {} @app.entrypoint def handle(payload: dict, session_id: str = None): prompt = payload.get("prompt", "") session_id = session_id or "default" if session_id not in _agents: from memory.session import get_memory_session_manager session_manager = get_memory_session_manager(session_id=session_id, actor_id="user") gateway_tools = get_gateway_tools() _agents[session_id] = Agent( model="apac.anthropic.claude-sonnet-4-20250514-v1:0", system_prompt=SYSTEM_PROMPT, tools=[get_weather] + gateway_tools, # ← Gatewayツールを追加 session_manager=session_manager, ) agent = _agents[session_id] result = agent(prompt) return {"response": str(result)}
注意: Gateway URLはデプロイ後にしか確定しないため、2回デプロイが必要です。1回目でGatewayを作成し、
agentcore statusでURLを確認してコードに設定し、2回目で反映します。ドキュメントを見てもGatewayのデプロイとGateway URLの受け渡し方法のベストプラクティスはわかりませんでした。Gatewayとエージェントは別チームが管理する想定で、同時にデプロイする設計思想ではないかもしれません。
コードを更新したらデプロイします。
# 1回目: Gateway作成 + コード反映 agentcore deploy # Gateway URLを確認 aws cloudformation describe-stacks \ --stack-name AgentCore-WeatherAgent-default \ --region ap-northeast-1 \ --query "Stacks[0].Outputs[?contains(OutputKey,'GatewayUrl')].OutputValue" \ --output text # 確認したURLを main.py の GATEWAY_URL に設定してから2回目のデプロイ agentcore deploy
これで「ニセコの天気を教えて」のような任意の地名にも対応できるようになりました。
質問後にトレースを見ると、API に対する POST リクエストがあり、 Gateway が実行されていることがわかります。

@tool と Gateway の使い分け
今回のエージェントでは2種類のツール定義を使いました。
| @tool(コード内定義) | Gateway経由 | |
|---|---|---|
| 今回の例 | get_weather | geocode_location |
| 向いているケース | そのエージェント専用のロジック | 複数エージェントで共有したいツール |
| 変更時 | コード修正 + 再デプロイ | Gateway設定変更のみ |
| 管理者 | エージェント開発者 | プラットフォームチーム |
Bedrock Agents との比較
同じ天気エージェントを両方で作ってみた結果の比較です。
| 観点 | Bedrock Agents | AgentCore |
|---|---|---|
| 開発方法 | コンソールGUI | コード + CLI |
| ツール定義 | アクショングループ(GUI設定 + Lambda) | @tool デコレータ or Gateway(MCP) |
| ローカルテスト | 不可 | agentcore dev で可能 |
| トレース | コンソールで確認 | OpenTelemetry互換(CloudWatch GenAI Observability) |
| 構築難易度 | 低い | 高い. |
ハマったポイント
実際に触ってみていくつかハマった点があったので共有します。
CodeZipデプロイの初期化タイムアウト
デフォルトのCodeZipデプロイでは、OpenTelemetryの初期化が重く30秒の制限を超えてしまいました。Containerデプロイに切り替えることで解決しました。
AgentCoreを使うときは基本的にContainerデプロイが良いと思います。
Containerデプロイでのトレース設定
Containerデプロイの場合、Dockerfileの CMD で opentelemetry-instrument を明示的に指定する必要があります。
# NG: トレースが送信されない CMD ["python", "main.py"] # OK: トレースが送信される CMD ["opentelemetry-instrument", "python", "main.py"]
opentelemetry-instrument は aws-opentelemetry-distro パッケージに含まれるコマンドで、pyproject.toml の依存関係に含まれていれば pip install 時に自動でインストールされます。CodeZipデプロイではCLIが自動設定してくれるため、ドキュメントを読み飛ばすとハマります。
参考: Enabling observability in agent code for AgentCore-hosted agents
まとめ
Amazon Bedrock AgentCore で天気エージェントを作成し、Runtime・Memory・Gateway・Observabilityの4つの機能を体験しました。
Runtime: Dockerコンテナをサーバーレスでデプロイ。セッション分離されたmicroVMで実行
Memory: 会話の文脈を自動保持。コード数行で設定可能
Gateway: 外部APIをMCPツールとして接続。エージェントのコード変更なしでツール追加可能
Observability: 推論の各ステップをトレースで可視化。ボトルネック分析に有用
Bedrock Agentsが「設定だけで素早くエージェントを作る」サービスなのに対し、AgentCoreは「自分のコードを本番品質で運用する」ためのプラットフォームです。軽く試すならBedrock Agents、本番運用はAgentCoreという使い分けが良さそうです。