[日本語Alexa] Alexa-SDK Ver2(その5) ダイアログモード

ダイアログモードのリクエストでは、dialogStateプロパティによって、Dialog.Delegateディレクティブを返す必要があります。 Ver2では、レスポンスを作成するresponseBuilderには、addDelegateDirective()などの専用ヘルパー関数が用意されています。
2018.04.27

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

1 はじめに

本記事は、Alexa SDK for Node.js Ver2入門と題して、入門用という位置付けで、Alexa SDKの使い方を、順に紹介しているものです。(対象は、Node.js用のみです。Java用には触れておりません)

その5では、ダイアログモードの実装方法について見ていきたいと思います。

2 ダイアログモード

最初に今回のサンプルを作成するにあたり準備したスキルを紹介しておきます。

設定したインテントは一つ(OrderIntent)のみです。スロットは、飲み物の種類(drink)と数(amount)の2つで、どちらも入力が必須となっています。

飲み物スロット(drink)のAlexa の音声プロンプトユーザーの発話の設定です。

数スロット(amount)のAlexa の音声プロンプトユーザーの発話の設定です。

これにより、以下のように動作することを想定しています。

3 最も簡単なダイアログモード

下記は、ダイアログモードのインテントであるOrderIntentを処理しているハンドラの例です。

const OrderIntentHandler = {
    canHandle(handlerInput) {
        return handlerInput.requestEnvelope.request.type === 'IntentRequest' &&
             handlerInput.requestEnvelope.request.intent.name === 'OrderIntent';
    },
    handle(handlerInput) {
        const dialogState = handlerInput.requestEnvelope.request.dialogState;
        if (dialogState !== 'COMPLETED') {
            return handlerInput.responseBuilder
            .addDelegateDirective()
            .getResponse();
        } else {
            return handlerInput.responseBuilder
            .speak('ご注文ありがとうございます')
            .getResponse();
        }

    }
};

ダイアログモードのリクエストには、dialogStateプロパティが含まれており、その値は、STARTEDIN_PROGRESS及び、COMPLETEDの3種類となっています。

そして、ダイアログが完了していない状態である STARTED、及びIN_PROGRESSでは、 Dialog.Delegateディレクティブを返す必要があります。

サンプルでは、handler()の中で、dialogStateプロパティを確認し、COMPLETEDで無ければ、addDelegateDirective()でレスポンスを作成しています。

addDelegateDirective()は、オプションでパラメータにIntentを設定でき、これでLambdaからスロットの内容を上書きすることも可能です。

4 ステートによるハンドラ分岐

次は、先程のサンプルと動きは全く同じですが、2つのハンドラに分けて書いた例を紹介します。

Alexa SDKのVer2では、canHandler()を使用して、開発者が独自にルーティング処理をするため、このような書き方も可能になります。

通常、COMPLETEDになる前の処理は、スロット値を集めることが目的であり、COMPLETED後は、集められたスロット値を使用する処理です。この2つは、その目的が大きく違うため、このように分けて記述出来ることは有効かもしれません。

const OrderProgressIntentHandler = {
    canHandle(handlerInput) {
        return handlerInput.requestEnvelope.request.type === 'IntentRequest' &&
             handlerInput.requestEnvelope.request.intent.name === 'OrderIntent' &&
            (handlerInput.requestEnvelope.request.dialogState === 'STARTED' || 
             handlerInput.requestEnvelope.request.dialogState === 'IN_PROGRESS');
    },
    handle(handlerInput) {
        // スロット値を集めるための処理
        return handlerInput.responseBuilder
            .addDelegateDirective()
            .getResponse();
    }
};

const OrderCompletedIntentHandler = {
    canHandle(handlerInput) {
        return handlerInput.requestEnvelope.request.type === 'IntentRequest' &&
        handlerInput.requestEnvelope.request.intent.name === 'OrderIntent' &&
        handlerInput.requestEnvelope.request.dialogState === 'COMPLETED';
    },
    handle(handlerInput) {
        // スロット値を使用する処理
        return handlerInput.responseBuilder
            .speak('ご注文ありがとうございます')
            .getResponse();
    }
};

5 その他のダイアログディレクティブ

ダイアログが完了していない状態では、上記で使用したDialog.Delegateディレクティブ以外にも、下記のものがあります。

  • Dialog.ElicitSlot(特定のスロット値についてユーザーに尋ねる)
  • Dialog.ConfirmSlot(特定のスロットの値を確認する)
  • Dialog.ConfirmIntent(インテントに提供したすべての情報をユーザーに確認する)

そして、それぞれResponseBuilderのヘルパー関数として用意されています。

addElicitSlotDirective(slotToElicit: string, updatedIntent?: Intent): this;
addConfirmSlotDirective(slotToConfirm: string, updatedIntent?: Intent): this;
addConfirmIntentDirective(updatedIntent?: Intent): this;

下記は、先のサンプルのうち数量のスロット(amount)のみを「必須」でない設定しても、その値の催促をLambda側で行うようにした例です。

const OrderProgressIntentHandler = {
    canHandle(handlerInput) {
        return handlerInput.requestEnvelope.request.type === 'IntentRequest' &&
             handlerInput.requestEnvelope.request.intent.name === 'OrderIntent' &&
            (handlerInput.requestEnvelope.request.dialogState === 'STARTED' || 
             handlerInput.requestEnvelope.request.dialogState === 'IN_PROGRESS');
    },
    handle(handlerInput) {
        const intent = handlerInput.requestEnvelope.request.intent;
        if (intent.slots.amount.value === undefined) {
            const slotToElicit = 'amount';
            return handlerInput.responseBuilder
            .speak('いくつになさいますか')
            .addElicitSlotDirective(slotToElicit)
            .getResponse();
        } else {
            return handlerInput.responseBuilder
            .addDelegateDirective()
            .getResponse();
        }
    }
};

6 最後に

今回は、Ver2におけるダイアログモードの使用方法を確認してみました。

個人的に魅力を感じているのは、COMPLETED前後の処理を別のハンドラに分けて書くことができるようになったことです。 目的の異なる2つの実装を、別々に記述できるようになって非常に心地よく感じています。

7 参考リンク


Alexa Skills Kit SDK for Node.js
Now Available: Version 2 of the ASK Software Development Kit for Node.js
GitHub repository for the SDK v2 for Node.js
[日本語Alexa] Alexa SDK for Node.js Ver2入門(その1)はじめの一歩
[日本語Alexa] Alexa SDK for Node.js Ver2入門(その2)ハンドラの登録
[日本語Alexa] Alexa SDK for Node.js Ver2入門(その3)レスポンスの作成
[日本語Alexa] Alexa-SDK Ver2(その4)スキル属性
[日本語Alexa] Alexa-SDK Ver2(その5)ダイアログモード
[日本語Alexa] Alexa-SDK Ver2(その6) 所在地情報
[日本語Alexa] Alexa-SDK Ver2(その7) ディスプレイ表示