Amazon Connect AIエージェントのオーケストレーションタイプで、ツールとしてフローモジュールを利用してみた
はじめに
Amazon ConnectのAIエージェント(オーケストレーションタイプ)では、エージェントが外部システムと連携するための「ツール」として、Connectフローモジュールが利用できます。
AI エージェントによる MCP ツールによる情報の取得とアクションの完了を有効にする - Amazon Connect
これまでフロー内でしか呼び出せなかったモジュールが、独立したツールという実行単位としてAIエージェントからも呼び出せるようになったことで、「従来のIVRフロー」と「生成AIによる対話フロー」の両方で同じビジネスロジック(Lambda連携など)を再利用できるという大きなメリットがあります。
今回は、顧客の「名前」と「電話番号」を元に予約状況を確認する処理をフローモジュールとして定義し、それをAIエージェントがツールとして利用して、顧客に予約内容を自然な対話で返答できるようにする構成を試してみます。
Lambda関数の作成
まずは、予約情報を検索するためのバックエンド処理としてLambda関数を作成します。
- 関数名:
SelfServiceOrchestrator-module-ReturnReservationMock - ランタイム: Pythonの最新バージョン
コンソールから作成すると自動でIAMロールが作成されます。今回はモックデータを使用するため、追加の権限付与は不要です。
import json
import logging
# ログ設定
logger = logging.getLogger()
logger.setLevel(logging.INFO)
# 擬似データ
MOCK_DATABASE = [
{
"phone_number": "09012345678",
"customer_name": "山田太郎",
"reservation_id": "RES-1001",
"date": "2026-03-15",
"time": "14:00",
"number_of_people": 4,
"status": "Confirmed"
},
{
"phone_number": "08098765432",
"customer_name": "鈴木花子",
"reservation_id": "RES-1002",
"date": "2026-03-16",
"time": "10:30",
"number_of_people": 2,
"status": "Pending"
}
]
def fix_mojibake(text):
if not text: return ""
text = str(text)
try:
return text.encode('iso-8859-1').decode('utf-8')
except (UnicodeEncodeError, UnicodeDecodeError):
return text
def normalize(text):
if not text: return ""
text = fix_mojibake(text)
return text.replace(" ", "").replace(" ", "").replace("-", "")
def lambda_handler(event, context):
logger.info("Received event: %s", json.dumps(event, ensure_ascii=False))
params = event.get('Details', {}).get('Parameters', {})
input_phone = params.get('phone_number') or event.get('phone_number')
input_name = params.get('customer_name') or event.get('customer_name')
logger.info(f"Input(Raw): Name={input_name}, Phone={input_phone}")
found_reservation = None
if input_phone and input_name:
target_phone = normalize(input_phone)
target_name = normalize(input_name)
logger.info(f"Input(Normalized): Name={target_name}, Phone={target_phone}")
for record in MOCK_DATABASE:
if record['phone_number'] == target_phone and record['customer_name'] == target_name:
found_reservation = record
break
# 結果の返却
if found_reservation:
logger.info("Reservation found: %s", found_reservation['reservation_id'])
# 【重要】Connectの属性に入れられるように、JSONを文字列化したもののみを返す
result_string = json.dumps(found_reservation, ensure_ascii=False)
return {
"json_result": result_string
}
else:
logger.info("Reservation not found.")
# 見つからなかった場合も json_result のみを返す
not_found_data = {
"status": "NotFound",
"message": "予約が見つかりませんでした。"
}
return {
"json_result": json.dumps(not_found_data, ensure_ascii=False)
}
処理内容について
このLambda関数は、フローモジュールから渡される入力パラメータ(phone_number と customer_name)を受け取り、内部のモックデータ(MOCK_DATABASE)と照合します。
一致する予約データがあればその情報を返し、なければ「見つからなかった」というステータスを返します。
ここでポイントとなるのが出力の形式です。ConnectのフローモジュールはJSONオブジェクトの返却をサポートしているため、検索結果をオブジェクトのまま渡すことも技術的には可能です。しかし、それを真面目にやろうとすると、フローモジュール側で複雑なJSONスキーマを定義したり、Connectフロー内で各プロパティを一つ一つマッピングしたりする手間が発生し、扱いづらくなる場合があります。
そのため、検索結果全体を json.dumps で文字列化し、json_result というキーに格納して返却するようにしています。これにより、フローモジュール側では単一の文字列プロパティとして結果を受け取るだけで済み、設定が圧倒的にシンプルになります。AIエージェントの裏側で動くLLMは、文字列化されたJSONであっても中身の構造を問題なく理解できるため、この方法を採用しています。
日本語の文字化け対策について
AIエージェントのツールを通してフローモジュールに日本語の値(例:「山田太郎」)を渡した場合、Lambda側で å±±ç°å¤ªé のように文字化けが発生することを確認しました。
そのため、上記のコードでは fix_mojibake 関数を用意し、iso-8859-1 でエンコードしてから utf-8 でデコードし直す変換処理を入れています。
フローモジュールの作成
次に、作成したLambdaを呼び出すフローモジュールを作成します。
Amazon Connectのコンソールから、[フローモジュールをツールとして作成] をクリックします。

入力パラメータの定義
モジュールでは、JSON構造の入出力パラメータを定義できます。
今回は名前と電話番号を元に予約確認をするため、入力として以下の2つを定義します。
- プロパティ名:
phone_number(タイプ:文字列、必須) - プロパティ名:
customer_name(タイプ:文字列、必須)

JSONスキーマエディターで確認すると以下のようになります。
{
"type": "object",
"properties": {
"phone_number": {
"type": "string"
},
"customer_name": {
"type": "string"
}
},
"required": [
"phone_number",
"customer_name"
]
}
出力パラメータの定義
出力内容も定義します。Lambdaで文字列化して返却した json_result の内容を、AIエージェントに渡すためのプロパティです。
- プロパティ名:
ReservationResult(タイプ:文字列、必須)

JSONスキーマは以下のようになります。
{
"type": "object",
"properties": {
"ReservationResult": {
"type": "string"
}
},
"required": [
"ReservationResult"
]
}
ツールの説明(Description)の記載
詳細タブから、モジュールの説明を記載します。この説明は、AIエージェントが「いつこのツールを選択すべきか」を判断するための重要なプロンプトとして機能するため、必ず明確に記載してください。

フローの構築
フローの全体像は以下の通りです。

1. [AWS Lambda 関数] ブロック
Lambdaに渡すパラメータを以下のように設定します。
- 宛先キー:
phone_number/ 値:名前空間「モジュール」、キー「入力」、phone_number - 宛先キー:
customer_name/ 値:名前空間「モジュール」、キー「入力」、customer_name

2. [コンタクト属性の設定] ブロック
Lambdaから返却された結果をモジュールの出力として設定します。
- 宛先キー:
ReservationResult(ユーザー定義済み) - 値:名前空間「外部」、キー「
json_result」

補足として、フローモジュールはバージョンとエイリアスを作成できます。エイリアス名を prod としてバージョン1と紐づける運用が推奨されますが、現時点ではAIエージェントのツールとして追加する際にエイリアスやバージョンの指定はできず、最新の公開された内容が利用されます。


セキュリティプロファイルの設定
AIエージェントがこのフローモジュールを実行できるように、AIエージェント用のセキュリティプロファイルに作成したフローモジュールを追加し、権限を付与します。

AIエージェントの作成
オーケストレーションタイプのAIエージェントを作成します。

ツール設定画面で、先ほど作成したフローモジュールを追加します。
ツールの説明には、フローモジュール側で記載した説明が自動で表示されます。

AIエージェントが適切にツールを使いこなし、自然な回答を生成できるように、以下の「指示テキスト」と「例」を設定します。
指示テキスト
顧客の名前と電話番号から予約情報を検索します。
実行結果として返される「日付(date)」「時間(time)」「人数(number_of_people)」を必ず顧客に伝えてください。
例1(良い例 - 予約が見つかった場合)
良い例 - 予約が見つかった場合:
返ってきたデータを具体的に読み上げている点が「良い」ポイントです。
<message>
山田太郎様ですね、確認いたしました。
2026年3月15日の14時から、4名様でご予約を承っております。
</message>
例2(良い例 - 予約が見つからなかった場合)
良い例 - 予約が見つからなかった場合:
Lambdaが「見つかりません」を返した際に、正直に伝えて再確認を促すパターンです。
<message>
申し訳ございません。いただいたお名前とお電話番号では、ご予約情報が見つかりませんでした。
恐れ入りますが、情報にお間違いがないかもう一度ご確認いただけますでしょうか?
</message>
例3(悪い例 - 避けるべきパターン)
悪い例 - 避けるべき:
現在発生している問題(中身を言わずに完了してしまう)を防ぐための例です。
<message>
ご予約を確認いたしました。完了しました。他にお手伝いできることはありますか?
</message>
指示テキストと例(プロンプト)の重要性について
AIエージェントはLLM(大規模言語モデル)をベースに動作しているため、ツールから返ってきたJSONデータ(今回の場合は ReservationResult の文字列)をどのように解釈し、顧客にどう伝えるかをプロンプトで制御する必要があります。
指示テキストで「必ず伝えるべき項目」を明示し、さらに例(Few-shotプロンプト)を用いて「成功時の自然な言い回し」「失敗時のフォロー」「避けるべきそっけない回答」を具体的に示すことで、エージェントの回答精度と接客の品質を向上させることができます。
ツールを定義後、ロケールを「日本」に設定し、先ほど設定したセキュリティプロファイルを指定して作成を完了します。

コールフローの作成
最後に、作成したAIエージェントを呼び出すコールフローを作成します。
AIエージェントを呼び出すと顧客との対話(ラリー)が開始されます。
予約確認ツール Reservation_confirmation が呼び出された場合はそのままエージェント内で対話が継続し、最終的にデフォルトツールの Complete(解決)や Escalate(オペレーター転送)が選択されたタイミングで分岐するフローです。
逆に言うと、Reservation_confirmation ツールで結果を返答した直後に、そのまま対話(ラリー)を終了させることはできません。 モジュールのツールはは、必ず顧客とのやり取りが継続し、AIエージェントがComplete(解決)や Escalate(オペレーター転送)と判断するまでブロック内に留まる仕様となっています。

[顧客の入力を取得する] ブロックで、作成したAIエージェントを指定します。

動作確認
実際にチャットでテストしてみます。
パターン1:情報を分けて伝える場合
BOT: お問い合わせ内容をお伝え下さい
Customer: 予約を確認したい
BOT: ご予約の確認を承ります。お名前とお電話番号をお伺いしてもよろしいでしょうか。
Customer: 山田太郎です。09012345678です。
BOT: 山田太郎様、お電話番号09012345678でご予約を確認いたします。少々お待ちください。
BOT: 山田太郎様、ご予約を確認いたしました。2026年3月15日の14時から4名様でご予約を承っております。ご予約番号はRES-1001でございます。他にお手伝いできることはございますか。
パターン2:一度にすべての情報を伝える場合
BOT: お問い合わせ内容をお伝え下さい
Customer: 予約を確認したい。山田、太郎です。090の1234の5678です。
BOT: 山田太郎様、ご予約の確認を承ります。お電話番号09012345678でお調べいたしますので、少々お待ちください。
BOT: 山田太郎様、ご予約を確認いたしました。2026年3月15日の14時から4名様でご予約を承っております。ご予約番号はRES-1001でございます。他にお手伝いできることはございますか。
どちらのパターンでも、AIエージェントが自然な対話の中で必要なパラメータ(名前、電話番号)を抽出し、フローモジュール(ツール)に渡して結果を返してくれました。
なお、バックエンドから取得した ReservationResult の中身は以下の通りです。想定どおりにデータが取得できていることが確認できます。
{
"phone_number": "09012345678",
"customer_name": "山田太郎",
"reservation_id": "RES-1001",
"date": "2026-03-15",
"time": "14:00",
"number_of_people": 4,
"status": "Confirmed"
}
まとめ
Amazon ConnectのフローモジュールをAIエージェントのツールとして利用することで、既存のIVRで使っていたLambda連携などのビジネスロジックを、生成AIの対話フローにも簡単に組み込むことができました。
日本語の文字化け対策など一部工夫が必要な点もありますが、開発のオーバーヘッドを減らしつつ、柔軟なカスタマーエクスペリエンスを提供できます。






