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

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ハンズオンセミナー」を開催致します。導入を検討されておられる方は、是非、お申し込み下さい。

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

コメントは受け付けていません。