![[Amazon Bedrock] AgentCoreをVPCにデプロイして自然言語でAuroraに問い合わせてみました](https://images.ctfassets.net/ct0aopd36mqt/7M0d5bjsd0K4Et30cVFvB6/5b2095750cc8bf73f04f63ed0d4b3546/AgentCore2.png?w=3840&fm=webp)
[Amazon Bedrock] AgentCoreをVPCにデプロイして自然言語でAuroraに問い合わせてみました
1 はじめに
製造ビジネステクノロジー部の平内(SIN)です。
「先月の売上トップ5は?」——こんな質問を投げるだけで、AI エージェントが SQL を生成し、データベースに問い合わせて、結果を日本語で返してくれます。
$ agentcore invoke '{"prompt": "売上トップ5の商品は?"}'
売上トップ5の商品をお調べしました!
1. スマートウォッチ(電子機器)— 売上合計:1,339,200円
2. カシミヤセーター(衣類)— 売上合計:790,000円
3. ダウンベスト(衣類)— 売上合計:752,000円
4. 黒毛和牛すき焼きセット(食品)— 売上合計:704,000円
5. リネンシャツ(衣類)— 売上合計:470,820円
$ agentcore invoke '{"prompt": "先月の食品の売上状況をまとめて"}'
## 先月(2026年3月)の食品売上状況まとめ
### 📊 全体概況
- **総売上金額**: 426,420円
- **総販売数量**: 84個
- **販売商品数**: 6商品
- **注文件数**: 23件
- **平均単価**: 5,011円
### 🏆 商品別売上ランキング
1. **黒毛和牛すき焼きセット** - 192,000円
- 販売数量: 15個
- 注文件数: 5件
- 平均単価: 12,800円
2. **魚沼産コシヒカリ 5kg** - 75,620円
- 販売数量: 19個
- 注文件数: 6件
- 平均単価: 3,980円
3. **有機抹茶パウダー** - 66,960円
- 販売数量: 27個
- 注文件数: 8件
- 平均単価: 2,480円
### 💡 特徴
- 高単価商品の「黒毛和牛すき焼きセット」が売上No.1
- 「有機抹茶パウダー」は最も多くの数量が販売された人気商品
- 全商品が売上を記録しており、食品カテゴリ全体で好調な売上
本記事では、AgentCore を VPC に配置して、同じ VPC 内の Aurora Serverless v2 に自然言語で問い合わせる Text-to-SQL エージェントを作ってみました。
ソースコードは GitHub で公開しています。
2 AgentCore から Aurora へのアクセス
AgentCoreをデプロイするモードによって、Auroraへのアクセス方法が変わります。
| PUBLIC | VPC | |
|---|---|---|
| Aurora へのアクセス | Data API(AWS API 経由) | VPC 内直接通信 |
| DB 接続方式 | boto3 rds-data クライアント | PostgreSQL ドライバ(psycopg2) |
| セキュリティ | IAM ポリシーで制御 | SG でネットワークレベル制御 |
| レイテンシ | 数十〜数百ミリ秒 | 数ミリ秒 |
| 結果サイズ | 1 MB 制限 | 制限なし |
| 構築の手軽さ | 簡単(VPC 設定不要) | VPC / VPC Endpoint の設定が必要 |
検証やプロトタイプなら PUBLIC + Data API の方が手軽そうですが、本番運用では、以下のような理由から VPC + 直接接続がオススメかも知れないということで、今回は、こちらを採用してみました。
- セキュリティ: VPC内に閉じ、Security Group でアクセス元を限定できる
- Data API の制限: 大量データの集計やバッチ処理では 1MB 制限が問題になるかも知れません
- アーキテクチャの一貫性: 通常のアプリからのアクセスと統一できる(Private Subnetからのみの接続)
3 構成
AgentCore 及び、Aurora Serverless v2は、VPC(Private Subnet)に配置されており、必要なサービスへは、VPC Endpoint経由で接続されています。
Aurora Serverless v2 は、最小ACU が0となっていて、アクセスがなければ利用費ゼロですが、最初のクエリにはコールドスタート(数十秒)が発生します。
なお、未使用でも、VPC Endpointの利用費は発生するのでご注意ください。

4 インフラ構築
(1) CDK デプロイ
VPC、Aurora、VPC Endpoint などのインフラは CDK(TypeScript)で構築しています。
git clone https://github.com/furuya02/agentcore-vpc-text-to-sql.git
cd agentcore-vpc-text-to-sql
cd cdk
npm install
npx cdk deploy
デプロイ完了時に表示される、CDK Outputs の SubnetIds、AgentCoreSecurityGroupId、SecretArn は後のステップで利用するのでコピーしておいてください。
Outputs:
text-to-sql-stack.SubnetIds = subnet-xxx,subnet-yyy
text-to-sql-stack.AgentCoreSecurityGroupId = sg-zzz
text-to-sql-stack.SecretArn = arn:aws:secretsmanager:ap-northeast-1:...
text-to-sql-stack.ClusterArn = arn:aws:cloudformation:...
CDK の主なリソースは以下のとおりです。
| リソース | 設定 |
|---|---|
| VPC | Private Isolated Subnet 、NAT Gateway なし |
| VPC Endpoint | bedrock-runtime, logs, secretsmanager の 3 つ |
| Aurora Serverless v2 | PostgreSQL 16.4、最小 ACU: 0、最大 ACU: 1 |
| Security Group | AgentCore SG → Aurora SG の TCP 5432 のみ許可 |
(2) サンプルデータ
Aurora の Data API を使って、サンプルデータを投入します。EC サイトの注文データを想定した 4 テーブル構成です。
cd cdk
chmod +x scripts/seed-data.sh
./scripts/seed-data.sh
| テーブル | 件数 | 内容 |
|---|---|---|
| customers | 50 | 顧客(氏名、メール、都道府県) |
| products | 30 | 商品(5 カテゴリ x 6 商品) |
| orders | 200 | 注文(過去 90 日分) |
| order_items | 500 | 注文明細(各注文に平均 2.5 明細) |
5 エージェントコードの実装
(1) 全体構成
エージェントのエントリポイントは agent/texttosql/app/texttosql/main.py です。
処理の流れは以下です。
- ユーザーのプロンプトを Bedrock に送信(
converseAPI) - Claude が
tool_useでlist_tablesを要求 → テーブル構造を取得して結果を返送 - Claude が SQL を生成し
tool_useでexecute_queryを要求 → SQL を実行して結果を返送 - Claude が最終応答テキストを返却(
stop_reason: end_turn)
(2) DB 認証情報の取得
DB の認証情報は Secrets Manager から取得しています。CDK が Aurora クラスター作成時に自動生成したシークレットを、環境変数 DB_SECRET_ARN で参照しています。
_db = None
def db_config():
global _db
if _db: return _db
s = json.loads(boto3.client("secretsmanager", region_name="ap-northeast-1")
.get_secret_value(SecretId=os.environ["DB_SECRET_ARN"])["SecretString"])
_db = dict(host=s["host"], port=int(s.get("port", 5432)),
dbname=os.environ.get("DB_NAME", "ecommerce"),
user=s["username"], password=s["password"])
return _db
(3) ツールの実装
2 つのツールを実装しています。
list_tables は information_schema からテーブル構造を取得します。execute_query は SELECT 文のみ実行を許可し、SQL インジェクションのリスクを軽減しています。
def list_tables(_):
conn = psycopg2.connect(**db_config())
cur = conn.cursor()
cur.execute("SELECT table_name, column_name, data_type FROM information_schema.columns "
"WHERE table_schema='public' ORDER BY table_name, ordinal_position")
# ... 省略(テーブル名: カラム名 (型) の形式で整形)
def execute_query(inp):
sql = inp.get("sql", "")
if not sql.strip().upper().startswith("SELECT"):
return "エラー: SELECT文のみ実行可能です"
# ... 省略(psycopg2 で SQL を実行し、結果をテキストで返却)
(4) Tool Loop の実装
boto3 の converse API を使って、ツール呼び出しのループを実装しています。stop_reason が tool_use の間はツールを実行して結果を返し、end_turn で最終応答を返却します。
def run(client, prompt):
msgs = [{"role": "user", "content": [{"text": prompt}]}]
for _ in range(10):
r = client.converse(modelId=MODEL_ID, system=[{"text": SYSTEM}],
messages=msgs, inferenceConfig={"maxTokens": 2000}, toolConfig=TOOL_CONFIG)
c = r["output"]["message"]["content"]
msgs.append({"role": "assistant", "content": c})
if r["stopReason"] == "end_turn":
return next((b["text"] for b in c if "text" in b), "")
msgs.append({"role": "user", "content": [
{"toolResult": {"toolUseId": b["toolUse"]["toolUseId"],
"content": [{"text": TOOLS[b["toolUse"]["name"]](b["toolUse"].get("input", {}))}]}}
for b in c if "toolUse" in b]})
return ""
詳細なコードは GitHub リポジトリの main.py をご覧ください。
(5) Inference Profile ID について
ap-northeast-1 では、一部のモデルでオンデマンド呼び出しが使えないため、APAC Inference Profile ID を使用しています。
MODEL_ID = "apac.anthropic.claude-sonnet-4-20250514-v1:0"
6 AgentCore デプロイ
(1) プロジェクトの作成
agentcore CLI(v0.8)でプロジェクトを作成します。これにより agentcore.json や CDK 定義などの雛形が生成されます。
cd agent
agentcore create \
--name texttosql \
--framework Strands \
--model-provider Bedrock \
--memory none \
--network-mode VPC \
--subnets "(SubnetIds の値1),(SubnetIds の値2)" \
--security-groups "(AgentCoreSecurityGroupId の値)" \
--skip-python-setup
生成された main.py を、前章で実装したエージェントコードに差し替えます。
(2) 環境変数の設定
agent/texttosql/agentcore/agentcore.json の envVars に環境変数を追加します。
"envVars": [
{ "name": "DB_SECRET_ARN", "value": "(SecretArn の値)" },
{ "name": "DB_NAME", "value": "ecommerce" }
]
(3) デプロイ
cd agent/texttosql
agentcore deploy
(4) IAM 権限の追加
agentcore deploy で自動作成される実行ロールには、Bedrock の呼び出し権限は含まれていますが、Secrets Manager へのアクセス権限は含まれていません。エージェントコードから Secrets Manager を呼び出すため、手動で権限を追加します。
aws iam put-role-policy \
--role-name "(実行ロール名)" \
--policy-name "SecretsManagerReadAccess" \
--policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": "secretsmanager:GetSecretValue",
"Resource": "(SecretArn)*"
}]
}'
7 動作確認
(1) テーブル一覧の取得
$ agentcore invoke '{"prompt": "テーブル一覧を教えて"}'
ECサイトのデータベースには以下の4つのテーブルがあります:
1. customers(顧客テーブル)
- customer_id:顧客ID(整数)
- name:顧客名(文字列)
- email:メールアドレス(文字列)
- prefecture:都道府県(文字列)
- created_at:登録日時(タイムスタンプ)
...(省略)
エージェントが list_tables ツールを呼び出し、Aurora に接続してスキーマ情報を取得できています。VPC Endpoint 経由で Bedrock API と Secrets Manager にアクセスし、VPC 内で Aurora と直接通信しています。
(2) 売上の集計クエリ
$ agentcore invoke '{"prompt": "先月の売上トップ3の商品を教えて"}'
先月の売上トップ3の商品をお答えします:
**1位:スマートウォッチ**
- 売上金額:297,600円
- 販売数量:12個
**2位:ダウンベスト**
- 売上金額:263,200円
- 販売数量:14個
**3位:Bluetoothスピーカー**
- 売上金額:256,000円
- 販売数量:20個
list_tables でテーブル構造を確認した後、execute_query で集計 SQL(JOIN + GROUP BY + ORDER BY)を生成・実行しています。1 回の invoke で Bedrock の Converse API を 3 回呼び出しています(テーブル構造確認 → SQL 実行 → 結果整形)。
(3) 生成された SQL の確認
今回のエージェントコードにはログ出力を入れていません。しかし、Bedrock のモデル呼び出しログを有効化していると、Converse API のリクエスト/レスポンスに tool_use の内容(生成された SQL)が含まれるので、そちらで確認が可能です。

- 生成された SQL
SELECT
p.name AS 商品名,
SUM(oi.quantity * oi.unit_price) AS 売上金額,
SUM(oi.quantity) AS 販売数量
FROM order_items oi
JOIN orders o ON oi.order_id = o.order_id
JOIN products p ON oi.product_id = p.product_id
WHERE DATE_TRUNC('month', o.order_date) = DATE_TRUNC('month', CURRENT_DATE
- INTERVAL '1 month')
GROUP BY p.product_id, p.name
ORDER BY 売上金額 DESC
LIMIT 3
-
3 回の呼び出しの流れ:
Turn 入力 出力 レイテンシ 1 ユーザープロンプト tool_use: list_tables 6,825ms 2 テーブル構造(tool_result) tool_use: execute_query + 上記 SQL 9,238ms 3 SQL 結果(tool_result) 最終回答テキスト(end_turn) 6,367ms
8 ハマりどころ
作業中、いくつかハマったポイントがありましたので、共有させてください。
(1) Strands SDK で応答が返らなかった
当初、Strands Agents SDK でエージェントを実装していたのですが、私の環境では VPC モードで 2 回目以降の Bedrock API 呼び出しの応答が返ってきませんでした。1 回目の呼び出し(テーブル構造の確認まで)は成功するのですが、ツール結果を含む 2 回目の呼び出しで応答が返らない状態でした。
設定の問題かもしれませんが、切り分けの結果、converse を直接呼び出す方式では問題なく動作できたため、今回はこちらを採用しました。
(2) AgentCore の名前にハイフンが使えない
agentcore create でプロジェクト名に text-to-sql を指定したところ、エラーになりました。AgentCore のプロジェクト名には英数字のみ使用可能(ハイフン・アンダースコア不可)です。texttosql に変更しました。
(3) 実行ロールに Secrets Manager の権限が必要
agentcore deploy で自動作成される IAM 実行ロールには、Secrets Manager へのアクセス権限が含まれていません。aws iam put-role-policy で secretsmanager:GetSecretValue の権限を追加する必要がありました。
(4) ENI の解放遅延によるスタック削除失敗
agentcore destroy → cdk destroy の順で実行したところ、CDK スタックの削除が失敗しました。AgentCore が VPC 内に作成した ENI が、destroy 後もすぐには解放されず、ENI が Security Group に紐付いているため、CDK が SG を削除出来ない状態です。数時間後に、削除できましたが、注意が必要そうです。
9 最後に
今回は、AgentCore を VPC モードで Aurora Serverless v2 に接続する Text-to-SQL エージェントを構築しました。
VPC Endpoint の設定や ENI の解放遅延など、PUBLIC モードにはないハマりどころがありましたが、
AgentCore の VPC モードは、普段のアプリケーションと同じように Private Subnet から DB に直接接続できるので、既存のネットワーク構成にそのまま載せることも出来そうだなと感じました。








