こんにちは。エンジニアの岩間です。
Amazon Connect アドベントカレンダー 2024、23日目の記事です!
クラスメソッドさん、クラウドビルダーズさんとギークフィードのメンバーによるAmazon Connect盛りだくさんのカレンダーです。
ぜひ他の記事もチェックしてみてください。
近年、AI技術の進展により、顧客体験を向上させるソリューションが多様化しています。
特に会話型AIは、顧客との対話を自然に行い、適切な提案をリアルタイムで行えることから、さまざまなビジネス領域で注目されています。
Amazon Lexは、AWSが提供する自然言語理解 (NLU) を活用した会話型インターフェース構築サービスであり、チャットボットや音声アシスタントを容易に実現できます。
また、AWSにはAmazon Personalizeのように高度なパーソナライズレコメンドを実現するサービスも存在しますが、顧客のニーズに即した「タイミング」と「内容」を提案することが、競争優位性を高める鍵となってきていると感じております。
そこで今回は、会話型AIの一例として「お花の注文電話」を想定し、Amazon LexとAmazon Connectを活用して、シーン別に最適なお花をレコメンドする仕組みを構築してみましたので、その実装手順とポイントをご紹介します。
目次
やりたいこと
今回の実装では、Amazon LexとAmazon Connectを活用し、電話注文を通じてお客様に最適なお花をレコメンドする仕組みを構築してみます。
会話イメージ
以下の画像の通り、お客様が電話音声を通じて「誕生日」「記念日」「感謝」などの用途(Occasion)を伝えると、システムが用途に応じた最適な花をレコメンドする流れを想定しています。
構成図
構成図に示す通り、システムの流れは次のようになっています。
- Amazon Connect:
電話注文のエントリーポイントとして、顧客からの通話を受け付けます。Amazon Connectのフロー内にAmazon Lexを組み込み、会話を通じて顧客の用途(Occasion)をヒアリングします。 - Amazon Lex:
会話型AIを担う部分です。お客様のリクエストを自然言語で理解し、「誕生日」「記念日」などの用途を抽出します。 - AWS Lambda:
Lexから受け取った用途をもとに、バックエンドロジックを処理します。OccasionをキーにDynamoDBにクエリを行い、花の情報(在庫状況、価格、説明)を取得します。 - Amazon DynamoDB:
花のデータベースとして利用します。在庫状況、価格、説明を用途ごとに管理し、適切なレコメンド情報を返します。 - 応答生成:
取得したデータをもとに、在庫状況に応じて提案内容を変更します。例えば、在庫がない場合は代替提案も行える仕組みです。
実装
それでは早速実装していきましょう。
LambdaとDynamoDB作成(CDK)
まずは、AWS CDKでLambdaとDynamoDBをデプロイしていきます。
※CDKはバージョン2.160.0、言語はTypeScriptで作成していきます。
1. CDK用のディレクトリを作成しその配下で、
cdk init sample-app --language typescript
cdk bootstrap
を実行します。
2. lib/flower-suggestion-stack.tsに以下の記述を追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
import { Duration, Stack, StackProps } from 'aws-cdk-lib'; import { Construct } from 'constructs'; import { NodejsFunction} from "aws-cdk-lib/aws-lambda-nodejs"; import { Runtime } from "aws-cdk-lib/aws-lambda"; import { Table, AttributeType } from 'aws-cdk-lib/aws-dynamodb'; export class FlowerSuggestionStack extends Stack { constructor(scope: Construct, id: string, props?: StackProps) { super(scope, id, props); // DynamoDBテーブルを作成 const table = new Table(this, 'Flowers', { partitionKey: { name: 'Occasion', type: AttributeType.STRING }, tableName: 'Flowers', }); // Lambdaを作成 const lambdaFunction = new NodejsFunction( this, 'flowerSuggestionFunction', { runtime: Runtime.NODEJS_18_X, entry: "lambda/flower-suggestion.ts", handler: "handler", environment: { DYNAMODB_TABLE_NAME: table.tableName, }, timeout: Duration.seconds(600), memorySize: 150, }, ); // Lambda関数にDynamoDBへのアクセス権を付与 table.grantReadWriteData(lambdaFunction); } } |
3. 次にlambda/flower-suggestion.tsを作成し、以下の記述を追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
import { DynamoDB } from 'aws-sdk'; // DynamoDBクライアントの初期化 const dynamoDBClient = new DynamoDB.DocumentClient(); // Lambdaハンドラ関数 exports.handler = async (event: any) => { console.log("Received event: ", JSON.stringify(event, null, 2)); // イベントからOccasionTypeスロットの値を取得 const occasion = event.interpretations?.[0]?.intent?.slots?.OccasionType?.value?.resolvedValues[0] || null; if (occasion) { console.log("Occasion Type:", occasion); } else { console.log("Occasion Type is not provided."); } console.log("occasionType: ", occasion); // DynamoDBから該当するOccasionの花情報を取得するためのパラメータ設定 const params = { TableName: process.env.DYNAMODB_TABLE_NAME || '', Key: { Occasion: occasion } }; // FlowerTypeスロットがすでに入力されている場合、注文を確定して処理を終了 if (occasion != null && event.interpretations?.[0]?.intent?.slots?.FlowerType != null) { return closeIntent( event.interpretations?.[0]?.intent?.name, "ご注文いただきありがとうございました。" ); } try { // DynamoDBからデータ取得 const data = await dynamoDBClient.get(params).promise(); if (data.Item) { // DynamoDBに該当アイテムが存在する場合、花の情報を取得 const { FlowerType, Description, Price, Availability } = data.Item; // 在庫がない場合はユーザーに別の提案を促す if (!Availability) { return elicitSlotResponse( event.interpretations?.[0]?.intent?.name, occasion, '', `申し訳ありませんが、${FlowerType}は現在在庫切れです。他の花をご検討いただけますか?` ); } // 在庫がある場合、おすすめの花とその詳細をユーザーに提案 const message = `おすすめの花は${FlowerType}です。${Description}。価格は${Price}円です。注文しますか?`; return elicitSlotResponse( event.interpretations?.[0]?.intent?.name, occasion, FlowerType, message ); } else { // DynamoDBに該当するデータが存在しない場合の処理 return elicitSlotResponse( event.interpretations?.[0]?.intent?.name, occasion, '', "申し訳ありませんが、その用途に合った花が見つかりませんでした。別の種類の花をご検討いただけますか?" ); } } catch (error) { // DynamoDBからデータ取得時にエラーが発生した場合の処理 console.error("DynamoDB query error:", error); return elicitSlotResponse( event.interpretations?.[0]?.intent?.name, occasion, '', "システムエラーが発生しました。後ほど再度お試しください。" ); } }; // 次のスロットを収集するための応答を生成するヘルパー関数 const elicitSlotResponse = (intentName: any, occasionType: any, flowerType: any, message: string) => { return { messages: [{ 'contentType': 'PlainText', 'content': message }], sessionState: { dialogAction: { type: 'ConfirmIntent', // ユーザーに意図確認を促す }, intent: { name: intentName, slots: { FlowerType: { // FlowerTypeスロットに値を設定 shape: "Scalar", value: { originalValue: flowerType, resolvedValues: [flowerType], interpretedValue: flowerType } }, OccasionType: { // OccasionTypeスロットに値を設定 shape: "Scalar", value: { originalValue: occasionType, resolvedValues: [occasionType], interpretedValue: occasionType } }, }, state: "InProgress" // 対話の状態をInProgressに設定 } } }; } // 意図を閉じて、ユーザーにメッセージを送信するヘルパー関数 const closeIntent = (intentName: any, message: string) => { return { messages: [{ 'contentType': 'PlainText', 'content': message }], sessionState: { dialogAction: { type: 'Close', // 対話を完了させる fulfillmentState: "Fulfilled", // 意図が完了した状態を示す }, intent: { name: intentName, state: 'Fulfilled' // 対話の状態をFulfilledに設定 } } }; }; |
4. cdk deployコマンドを実行して問題なくデプロイできたらOKです!
DynamoDBにデータ追加
次はDynamoDBに花情報のデータを追加していきます。
AWSコンソールでDynamoDBの画面にいき左メニューの「項目を探索」からデータ追加が行えるので、下記の画像のようなデータを追加していきましょう。
Lex設定
次はLexの設定をしていきます。
AWSコンソールでLexのページにいきます。
Bot作成
1. まずはBotを作成します。Lexのページから「Create bot」をクリックします。
2. 設定画面に遷移するので、下の画像のように設定します。
3. 言語設定は日本語にします。
スロットタイプ作成
次はスロットタイプ作成です。
2. モーダルが表示されるので、「FlowerType」と入力し保存します。
3. Slot type nameが保存できたら、次は以下の画像のように編集していきます。
4. 同じ要領でOccasionTypeも登録します。
Lambda設定
次はBotの中でLambdaを扱うための設定をします。
1. 左メニューの「Alias」をクリックし、言語:Japaneseをクリックします。
(これまた画面パス迷子になりがちなので注意です!)
2. 以下のような画面になるので、CDKで作成したLambda関数を設定して保存します。
インテント作成
次にインテントを設定します。
1. 左メニューの「Intents」をクリックすると下の画像のような画面が開くので、「Add empty intent」をクリックします。
2. インテント名を入力し追加します。
3. インテントの中身は以下の画像のように設定しました。
4. Slotsの中身はこうなっています。
5. OccasionTypeのslotではAdvanced optionsのボタンから、Lambdaが実行されるように設定しています。
6. インテントが設定できたら、画面右上の「Build」を実行して、「Test」します。
Amazon Connect設定
そろそろ実装も終盤に差し掛かってきました。最後にAmazon Connect側での設定を行います。
Amazon ConnectでLexを扱うための設定
Amazon ConnectインスタンスにLexのBotを追加します。
1. Amazon Connect画面からインスタンンスエイリアス名をクリックし、左メニューから「問い合わせフロー」のページにいきます。
2. Amazon Lexのセクションにて先ほど作成したLexのボットとエイリアスを選択し、追加します。
フロー設定
Amazon Connectのフローに作成したLexを組み込んでいきます。
※Amazon Connectインスタンス作成や通話するまでの設定はこちらの記事で紹介しております
今回は、下の画像のように設定してみました。
「顧客の入力を取得する」ブロックにてLexの設定をしています。
お疲れ様でした!これで設定は完了です。
最後にテストコールを行い、想定通りの動作になれば成功です!
まとめ
今回は、「[会話型AI活用]Amazon LexとConnectでシーン別にお花をリコメンドする仕組みを作ってみた」というテーマで、電話注文におけるシーン別のお花提案システムを紹介しました。
今回はDynamoDBに保存された固定データをベースにお花をレコメンドする形ですが、Amazon Bedrockを利用して大規模言語モデル(LLM)にAPIを投げてみるのも高度なパーソナライズができそうで良さそうだと思いました。
会話型AIの活用により、ビジネスにおける顧客体験をよりスマートに向上させることができます。
ぜひ、皆さんもシーンに応じたAIシステムの構築に挑戦してみてください!
ここまでお読みいただき、ありがとうございました!
- [会話型AI活用]Amazon LexとConnectでシーン別にお花をレコメンドする仕組みを作ってみた - 2024-12-23
- FreePBXで留守電メッセージをメール通知するまでの設定手順 - 2024-12-16
- Amazon Connect最初の一歩!シンプルなコールセンターを構築してみた - 2024-12-06
- 電子工作で自作した植物の自動水やり機から土壌データをAWS IoT Coreに連携してみた - 2024-09-13
- 【ESP8266 NodeMCU】電子工作で植物の自動水やり機を作ってみた - 2024-08-14