はじめに
はじめまして、26新卒のしょうです。
このたびコンタクトセンター部門への仮配属が決まりました。
「とりあえずやってみよう」ということで、Amazon Connectを使ったコンタクトセンターシステムを個人で作ってみました。
シリーズで書いていく予定で、今回は第1弾。
「電話をかけたらAIが応答する」 ところまでをLambda + Bedrock + DynamoDBで作ったのでまとめます。
構成図
電話をかけるとAmazon Connectが受けて、コンタクトフローでLambdaを呼び出し、BedrockのAIが回答を生成、最後にDynamoDBへ保存する流れです。
コンタクトフローはこんな感じです。
- 音声の設定(日本語 / Kazuha)
- 音声プロンプトの再生(受付案内)
- Lambda呼び出し
- 音声プロンプトの再生(AI回答の読み上げ)
今回はユーザーの発話に関係なく、Lambda内のテストデータをBedrockに送っています。発話認識(Lex連携)は次回以降に実装予定です。
実装手順
1. IAMユーザー作成
まず開発用のIAMユーザーを作るところから。SAA勉強中なので最小権限は意識したいところです。
今回の権限はこんな感じです。
| ポリシー | 用途 |
|---|---|
| AmazonBedrockFullAccess | Bedrock APIの呼び出し |
| AmazonConnect_FullAccess | Connectの操作 |
| AmazonDynamoDBFullAccess | テーブルの作成・読み書き |
| AWSLambdaFullAccess | Lambda関数の作成・更新 |
| ConnectDevIAMPolicy(カスタム) | LambdaへのIAMロール付与 |
IAMFullAccessは権限が広すぎるので、必要な4つのアクションだけのカスタムポリシーを作りました。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"iam:PassRole",
"iam:CreateRole",
"iam:AttachRolePolicy",
"iam:GetRole"
],
"Resource": "*"
}
]
}
2. Lambda関数作成
今回の構成の核になるLambda関数です。ランタイムはPython 3.13、リージョンは東京(ap-northeast-1)で作りました。
3. Bedrock設定
モデルはClaude Haiku 4.5を使います。初回利用時はAnthropicのユースケース申請が必要です。申請後、数分で反映されます。
モデルIDにクセがあります。詳しくは詰まったところに書きました。
4. DynamoDB作成
対応履歴を保存するテーブルを作ります。
| 項目 | 値 |
|---|---|
| テーブル名 | ContactHistory |
| パーティションキー | contact_id(文字列) |
| ソートキー | timestamp(文字列) |
5. Lambdaコード実装
Connectからintentとutteranceを受け取って、Bedrockに投げてAI回答を生成、DynamoDBに保存する流れです。
import json
import boto3
from datetime import datetime, timezone
bedrock = boto3.client("bedrock-runtime", region_name="ap-northeast-1")
dynamo = boto3.resource("dynamodb", region_name="ap-northeast-1")
table = dynamo.Table("ContactHistory")
SYSTEM_PROMPT = """あなたは株式会社〇〇のコンタクトセンターAIアシスタントです。
以下のルールを必ず守ってください:
- 回答は3文以内
- 丁寧語(です・ます調)を使う
- 金額・日程の確約は絶対にしない
- 社内規定にない内容は「担当者に確認します」と回答する"""
USER_PROMPTS = {
"return": "【返品の問い合わせ】\nお客様: {utterance}",
"delivery": "【配送状況の問い合わせ】\nお客様: {utterance}",
"default": "【一般問い合わせ】\nお客様: {utterance}"
}
def lambda_handler(event, context):
params = event.get("Details", {}).get("Parameters", {})
intent = params.get("intent", "default")
utterance = params.get("utterance", "お問い合わせありがとうございます")
contact_id = event.get("Details", {}).get("ContactData", {}).get("ContactId", "test-001")
template = USER_PROMPTS.get(intent, USER_PROMPTS["default"])
user_msg = template.format(utterance=utterance)
response = bedrock.invoke_model(
modelId="jp.anthropic.claude-haiku-4-5-20251001-v1:0",
body=json.dumps({
"anthropic_version": "bedrock-2023-05-31",
"max_tokens": 256,
"system": SYSTEM_PROMPT,
"messages": [{"role": "user", "content": user_msg}]
})
)
result = json.loads(response["body"].read())
ai_response = result["content"][0]["text"]
table.put_item(Item={
"contact_id": contact_id,
"timestamp": datetime.now(timezone.utc).isoformat(),
"intent": intent,
"utterance": utterance,
"ai_response": ai_response
})
return {"contact_id": contact_id, "intent": intent, "ai_response": ai_response}
6. Amazon Connectインスタンス作成
インスタンス作成後、「問い合わせフロー」の設定からLambda関数を登録します。これを忘れるとコンタクトフローからLambdaを呼べないので注意です。
7. コンタクトフロー作成
音声設定 → プロンプト再生 → Lambda呼び出し → AI回答読み上げ → 切断の流れです。
8. 電話番号取得
本来は日本の電話番号を取得したいところですが、Amazon Connectで日本の番号を取得するにはAWSサポートケースへの申請が必要です。個人アカウントでは審査が通らないケースもあるとのことで、現在申請中です。承認され次第、日本番号に切り替え予定です。それまでの間はアメリカの番号を取得して動作確認を行っています。テストの際は国際電話になるため、電話代が少し心配です笑
詰まったところ
LambdaのIAMロールに権限が足りない
LambdaのIAMロールにBedrockとDynamoDBの権限を別途付与する必要がありました。でも開発ユーザー自身にIAMの権限を付与していなかったので、ロールの設定をしようとしたらアクセス拒否に。
ルートユーザーと開発ユーザーを何度も行き来する羽目になりました笑
設計の時点で「Lambdaが使う権限」と「開発ユーザーが操作する権限」を整理しておくべきでした。
BedrockのモデルIDがLegacy
最初に指定したClaude 3 HaikuのモデルIDがLegacy扱いになっていて使えませんでした。Lambda経由のエラーは原因特定がしんどくてデバッグに苦労しました。
さらに新しいモデルはクロスリージョン推論プロファイルを使う必要があって、モデルIDのプレフィックスにjp.が必要です。これは沼でした。
# NG
anthropic.claude-haiku-4-5-20251001-v1:0
# OK
jp.anthropic.claude-haiku-4-5-20251001-v1:0
まとめ
Lambda + Bedrock + DynamoDB + Amazon Connectを繋いで、実際に電話をかけてAIが応答するところまで作れました。
現時点ではユーザーの発話は認識されておらず、テストデータをもとにAIが返答するだけですが、コンタクトセンターの骨格部分は動いています。
次回はLexを使った発話認識とintent判定の実装をまとめる予定です。
仲間を募集しています!
ARIではエンジニア・ITコンサルタント・PM職全方位で仲間を募集しております。
カジュアル面談、随時受付中です!
ご興味ある方はこちらをご覧ください。

