[Amazon Lex] バリデーションのLambdaから動的に生成したレスポンスカードを返してみる

2019.03.28

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

1 はじめに

こんにんちは、AIソリューション部の平内(SIN)です。

Amazon Lex(以下、Lex)では、レスポンスに画像やボタンを含んだカードを返すことができます。

今回は、このようなレスポンスカードをLambdaで動的に生成して返す要領を確認してみました。

なお、Lambdaの実装では、ライブラリとしてLex SDKを使用しています。

[Amazon Lex] Alexa SDK V2 みたいに書けるSDKを雑に作ってみました

Lex SDKは、Lexのコードを、Alexa SDK Ver2のように書けるので、既にAlexaの開発に関わっている方には、Lex開発のハードルがかなり低くなるのでは無いかと思います。また、TypeScriptでも書けますので、慣れない、Lexのオブジェクトが.(ドット)を打つだけで表示できて超便利です。(ステマ)

2 コンソールから設定する場合

レスポンスカードは、コードで生成しなくても、AWSコンソールから簡単に追加できます。

コンソールから設定する場合は、下記のように、当該スロットのSettingsを開きます。

カードの情報は、次のようなUIで追加することになります。

しかし、この要領では、表示する画像やメッセージ、そして、ボタンの種類や数は、ボットの設計時に決定され固定となってしまいます。

3 コードから設定する場合

(1) バリデーションLambda

コードで実装する場合は、initialization and varidation code hookで指定したLambdaの中で行ないます。

(2) ElicitSlot

Lambdaのレスポンスで、dialogActiontype が、ElicitSlotである時、指定されたスロットを取得するモードとなり、このレスポンスにResponseCardのオブジェクトを含めることができます。

Lambda Function Input Event and Response Format より

(3) Lex SDK

Lex SDKでは、ResponseBuildergetElicitSlotResponse()メソッドで、ElicitSlotを返すレスポンスを返すことができます。

class ResponseBuilder {
getElicitSlotResponse(sessionAttributes, intentName, slots, slotToElicit, message, responseCard) {
return {
sessionAttributes: sessionAttributes,
dialogAction: {
type: DialogActionType.ElicitSlot,
intentName: intentName,
slots: slots,
slotToElicit: slotToElicit,
message: message,
responseCard: responseCard
}
};
}
・・・略・・・

このため、下記のように、InvocationSourceが、DialogCodeHookであり、かつ、Flowerスロットに、まだ値が入っていない時に、カードを返すように実装しています。

if (h.source === Lex.InvocationSource.DialogCodeHook) {
if(!flowerType) {
const responseCard: Lex.ResponseCard = {
genericAttachments:[
{
title: "TITLE",
imageUrl: imageUrl,
buttons:[...],
}
]
}
return h.responseBuilder.getElicitSlotResponse(
...
responseCard)
}
return h.responseBuilder
.getDelegateResponse(h.attributes, h.slots)

4 実装コード

実装した全てのコードは、下記のようになりました。簡略化のため、エラーハンドは省略されています。

import * as Lex from 'lex-sdk';

let bot: Lex.Bot;
declare var exports: any;
exports.handler = async function (event: Lex.IntentRequest, context: any) {
console.log(JSON.stringify(event));
if (!bot) {
bot = Lex.BotBuilder()
.addRequestHandlers(
OrderIntentHandler,OrderIntentHandler)
.create();
}
return bot.invoke(event, context);
}

const OrderIntentHandler: Lex.RequestHandler = {
canHandle(h: Lex.HandlerInput) {
return (h.intentName == 'OrderFlowers')
},
handle(h: Lex.HandlerInput) {

const flowerType = h.slots['FlowerType'];
const date = h.slots['PickupDate'];
const time = h.slots['PickupTime'];

if (h.source === Lex.InvocationSource.DialogCodeHook) {

if(!flowerType) {
const message = { contentType: Lex.ContentType.PlainText, content: `What type of flowers would you like to order?` };
const imageUrl = 'https://d1eju4ngjniac3.cloudfront.net/OrderFlower.png';
const responseCard: Lex.ResponseCard = {
version: "001",
contentType : Lex.CardContentType.Generic,
genericAttachments:[
{
title: "TITLE",
subTitle: "SubTitle",
imageUrl: imageUrl,
attachmentLinkUrl: imageUrl,
buttons:[
{text:'roses',value:'roses'},
{text:'tulips',value:'tulips'},
{text:'lilies',value:'lilies'},
],
}
]
}
return h.responseBuilder
.getElicitSlotResponse(h.attributes,h.intentName,h.slots,'FlowerType', message, responseCard)
}
return h.responseBuilder
.getDelegateResponse(h.attributes, h.slots)

} else { // FulfillmentCodeHook
const message = { contentType: Lex.ContentType.PlainText, content: `Thanks, your order for ${flowerType} has been placed and will be ready for pickup by ${time} on ${date}` };
return h.responseBuilder
.getCloseResponse(
h.attributes,
Lex.FulfillmentState.Fulfilled,
message)
}
}
}

5 最後に

今回は、レスポンスカードをLambdaで動的に生成して返す要領を確認してみました。 カードを使用すると、明確に選択内容をユーザーに示すことができ、また、ボタンでのレスポンスも可能になります。

UX向上に非常に有効なレスポンスカードをコードから生成することで、状況に応じたきめ細かいユーザー体験が可能になるかも知れません。

Lex SDKを使用すると、レスポンスカードも簡単に記述できました。(すいません、しつこくステマです)

画像は、下記のものを利用させて頂きました。

パブリックドメインQ:著作権フリー画像素材集


弊社ではAmazon Connectのキャンペーンを行なっております。

3月に引き続き、4月も「無料Amazon Connectハンズオンセミナー」を開催致します。導入を検討されておられる方は、是非、お申し込み下さい。

また音声を中心とした各種ソリューションの開発支援も行なっております。