Amazon Connect + Amazon Lex + Lambda を連携する
📅 2021-05-28
先日、ついにAmazon Lexが日本語に対応しました。 それに伴い、関連する記事も多く見られるようになった気がします。
ただ、まだ対応したてということもあり、基本的なBotの構築〜Amazon Connectとの連携といった、ごく初級的なものが多いように感じました。
というわけで、ここではもう一歩だけ踏み込んだ、Amazon Lex + Amazon Connect に、 + Lambda を加えた連携やデータの受け渡しについて解説しようと思います。
はじめに
解説していくにあたって、シナリオづけのために Amazon Connect + Amazon Lex + Lambda (+ Amazon SNS) を連携した自動問い合わせ受付システムを作成しました。
Amazon Connectに電話をかけるとAmazon Lexが自動応答を開始し、問い合わせが終了すると発信元の電話番号に完了通知SMSを送信する、といったシステムです。
また今回解説の対象となるのは、冒頭でも話した通り Amazon Connect + Amazon Lex + Lambda の連携 となります。
Amazon ConnectやLexの構築、SNSのトピック作成〜SMS送信といった部分は解説しませんので、ご注意ください。(調べればすぐに出てくると思います)
Amazon Connect と Amazon Lex の連携
まず、Amazon ConnectでAmazon Lexを利用するためには、インスタンスの設定画面から利用したいBotを追加する必要があります。
リージョンとBotを選択し、「+ Lexボットの追加」をクリックしましょう。
インスタンスにBotを追加できたので、実際にフロー上でAmazon Lexを使ってみましょう。 Lexボットの起動には、「顧客の入力を取得する」ブロックを使用します。
ブロックのメニューでは、テキスト形式や音声ファイルにて自動応答開始(Lexボットの起動)に伴うプロンプトを設定することができます。
また、下の方へスクロールすると起動するlexボットを選択することができます。
ここで本章の本題です。Amazon ConnectからAmazon Lexへ属性を渡す場合には、この画面で「セッション属性」を追加しましょう。 今回は発信元の電話番号にSMSを送信したいので、システムの顧客電話番号を指定します。
これで無事にAmazon ConnectとAmazon Lexを連携することができました。
Amazon Lex から Lambda への入力イベント
Amazon Connectから属性として発信元の電話番号を受け取ったので、次はその情報をLambdaへと入力します。 とはいっても、Amazon LexのFulfillmentという機能でLamdbaを起動すれば、Amazon Lexの持つ情報が入力イベントとして自動的に送信されます。
まず、Lambdaを作成しましょう。今回はランタイムに「Node.js 14.x」を選択しましたが、特に指定はありません。
次に、Amazon Lexで作成したIntentの設定画面にて、Fulfillment 「AWS Lambda function」の項目で作成したLambdaを指定します。 すぐ下のResponseという項目では、自動応答の最後に再生するアナウンスを指定します。
これで自動応答終了の際に、自動でLambdaを起動&イベント入力まで行うようになりました。あとはLambda上で入力されたイベントからデータを取得するだけです。
Amazon Lex から Lambda へ入力されるデータの形式は以下となってました。※設定によって多少異なります。
{
messageVersion: '1.0',
invocationSource: 'FulfillmentCodeHook',
userId: 'userId',
sessionAttributes: { phoneNumber: '+810000000000' },
requestAttributes: null,
bot: { name: 'botName', alias: 'botAlias', version: '1' },
outputDialogMode: 'Text',
currentIntent: {
name: 'intentName',
slots: { Date: '2021-05-12' },
slotDetails: { Date: [Object] },
confirmationStatus: 'Confirmed',
nluIntentConfidenceScore: 1
},
alternativeIntents: [
{
name: 'AMAZON.FallbackIntent',
slots: {},
slotDetails: {},
confirmationStatus: 'None',
nluIntentConfidenceScore: null
}
],
inputTranscript: 'lastInput',
recentIntentSummaryView: [
{
intentName: 'intentName',
checkpointLabel: null,
slots: [Object],
confirmationStatus: 'None',
dialogActionType: 'ConfirmIntent',
fulfillmentState: null,
slotToElicit: null
}
],
sentimentResponse: null,
kendraResponse: null
}
今回取得したいのはAmazon Connectから渡された顧客電話番号なので、4行目の sessionAttributesを指定します。
言語によって違うと思いますが、今回はnode.jsで書いているので event[‘sessionAttributes’][‘phonenumber’] として取得ができます。
Lambda から Amazon Lex へのレスポンス
Amazon Lexを正常に動作させるためには、Lambdaからレスポンスを返してあげる必要があります。
レスポンスは最大4つのフィールドで構成できます。詳細は以下です。
・sessionAttributes ⇨ セッション属性を返す ・recentIntentSummaryView ⇨ 1~3つの最近のインテントを設定できる ・activeContexts ⇨ 1つ以上の認識対象となるコンテキスト(文脈)を設定できる ・dialogAction ⇨ 次にくる一連のアクションを設定できる
このうち、レスポンスに必須な項目は dialogAction のみです。他3つの項目は省略可能となります。 今回は Amazon Lex を完了させたいだけなので、dialogAction で終了アクションのみを指定しました。
{
"dialogAction": {
"type": "Close",
"fulfillmentState": "Fulfilled"
}
}
※フィールドごとの詳細はこちらのAWS公式ドキュメントをご参照ください。↓ https://docs.aws.amazon.com/ja_jp/lex/latest/dg/lambda-input-response-format.html
終わりに
解説は以上となります。最後のLambdaからAmazon Lexへのレスポンスの部分に関しては、私自身完全に理解できているわけではないので、もっと詳しくなってからまた別の記事としてより詳細を解説できたらと思います。
最後に今回作成した Lambda のソースコードを貼っておきます。 ランタイムは 「Node.js 14.x」 です。(実際にSMSを送信するためにはトピックの作成が必要です。)
var AWS = require("aws-sdk");
var SNS = new AWS.SNS();
exports.handler = function(event, context, callback){
// Amazon Lex からセッション属性を取得
var phoneNumber = event['sessionAttributes']['phoneNumber'];
var message = "お問い合わせを受け付けました。";
// メッセージ情報を設定
var param = {
PhoneNumber: phoneNumber,
Message: message,
MessageAttributes: {
'AWS.SNS.SMS.SMSType': {
DataType: 'String',
StringValue: 'Promotional'
},
'AWS.SNS.SMS.SenderID': {
DataType: 'String',
StringValue: 'GeekFeed'
}
}
};
// SMSを送信する
SNS.publish(param, function(err, data){
if(err){
context.done(null, err);
} else {
// Amazon Lex へのレスポンスを定義
var response = {
"dialogAction": {
"type": "Close",
"fulfillmentState": "Fulfilled"
}
};
context.done(null, response);
}
});
};