「Season 2 Alexaスキル開発の基礎を学ぼう」を見ながら実際にコーヒーショップスキルを作ってみる(第二回)

はじめに

本投稿で書くAlexa道場の会は以下

  • Alexa道場 S02 EP07 カスタムインテントを追加しよう 〜 Episode 11 レスポンスビルダーを使いこなそう

何が分かったのかまとめてみた

  • Alexa道場 Season2では、「コーヒーショップスキルを作る」ことを目標にしている
  • 「コーヒーショップスキル」を作るためには、「対話モデル」を作成する必要がある
    • なぜなら、「コーヒー」とか「コーヒーを注文して」などの発話を受け付ける必要がある
    • 発話を受け付けるには「インテント」を定義する必要がある
    • 「コーヒー」・「コーヒーを注文して」は、標準ビルトインに含まれないので、カスタムインテントを追加する必要がある

  • カスタムインテントの追加方法
    • カスタムインテントの追加方法
      • ビルド > 対話モデル > インテント から、デフォルトで入っているHelloWorldIntentをクリックして編集画面に移動
      • インテント名を「OrderIntent」に修正し、サンプル発話に「ユーザがいいそうなこと」を入れる。具体的には、「コーヒーが欲しい」「コーヒーを注文して」を入力する
    • 入力したら「モデルを保存」を押して、「ビルドを実行」を押す

    リクエストの受け取り方(リクエストハンドラとレスポンスハンドラの実装方法)

    カスタムインテントを作成しただけでは、何も起こらない。

    カスタムインテントに対応するリクエストハンドラとレスポンスハンドラをコードエディタで実装することで、カスタムインテントに対応する動作を行うことができる

    • 例:コーヒーが欲しいなどと入力されたら「コーヒーの注文ありがとうございます。」とAlexaで返すプログラムコードを書く場合
      • コードエディタを開く
      • OrderIntentのHandlerを定義する
        • canHandleメソッドに、自分がそのIntentの処理対象かどうかを定義する
          • requestEnvelopeの内容をもとに、対象であればtrueを返す処理を定義する
        • handleメソッドに、Alexaに行わせたい動作を定義する
          • ResponseBuilderを生成し、返却する
      • 定義したHandlerをaddRequestHandlersの引数に追加する

    サンプルコード:

    Handler部分
    const OrderIntentHandler = {
        canHandle(handlerInput) {
            return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
                && Alexa.getIntentName(handlerInput.requestEnvelope) === 'OrderIntent';
        },
        handle(handlerInput) {
            return handlerInput.responseBuilder
                .speak('コーヒーの注文ありがとうございます。')
                //.reprompt('add a reprompt if you want to keep the session open for the user to respond')
                .getResponse();
        }
    };
    addRequestHandlers部分
    exports.handler = Alexa.SkillBuilders.custom()
        .addRequestHandlers(
            LaunchRequestHandler,
            OrderIntentHandler,
            HelpIntentHandler,
            CancelAndStopIntentHandler,
            FallbackIntentHandler,
            SessionEndedRequestHandler,
            IntentReflectorHandler)
        .addErrorHandlers(
            ErrorHandler)
        .withCustomUserAgent('sample/hello-world/v1.2')
        .lambda();
    
    コード全体
    /* *
     * This sample demonstrates handling intents from an Alexa skill using the Alexa Skills Kit SDK (v2).
     * Please visit https://alexa.design/cookbook for additional examples on implementing slots, dialog management,
     * session persistence, api calls, and more.
     * */
    const Alexa = require('ask-sdk-core');
    
    const LaunchRequestHandler = {
        canHandle(handlerInput) {
            return Alexa.getRequestType(handlerInput.requestEnvelope) === 'LaunchRequest';
        },
        handle(handlerInput) {
            const speakOutput = 'Welcome, you can say Hello or Help. Which would you like to try?';
    
            return handlerInput.responseBuilder
                .speak(speakOutput)
                .reprompt(speakOutput)
                .getResponse();
        }
    };
    
    const OrderIntentHandler = {
        canHandle(handlerInput) {
            return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
                && Alexa.getIntentName(handlerInput.requestEnvelope) === 'OrderIntent';
        },
        handle(handlerInput) {
            return handlerInput.responseBuilder
                .speak('コーヒーの注文ありがとうございます。')
                //.reprompt('add a reprompt if you want to keep the session open for the user to respond')
                .getResponse();
        }
    };
    
    const HelpIntentHandler = {
        canHandle(handlerInput) {
            return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
                && Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.HelpIntent';
        },
        handle(handlerInput) {
            const speakOutput = 'You can say hello to me! How can I help?';
    
            return handlerInput.responseBuilder
                .speak(speakOutput)
                .reprompt(speakOutput)
                .getResponse();
        }
    };
    
    const CancelAndStopIntentHandler = {
        canHandle(handlerInput) {
            return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
                && (Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.CancelIntent'
                    || Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.StopIntent');
        },
        handle(handlerInput) {
            const speakOutput = 'Goodbye!';
    
            return handlerInput.responseBuilder
                .speak(speakOutput)
                .getResponse();
        }
    };
    /* *
     * FallbackIntent triggers when a customer says something that doesn’t map to any intents in your skill
     * It must also be defined in the language model (if the locale supports it)
     * This handler can be safely added but will be ingnored in locales that do not support it yet 
     * */
    const FallbackIntentHandler = {
        canHandle(handlerInput) {
            return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
                && Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.FallbackIntent';
        },
        handle(handlerInput) {
            const speakOutput = 'Sorry, I don\'t know about that. Please try again.';
    
            return handlerInput.responseBuilder
                .speak(speakOutput)
                .reprompt(speakOutput)
                .getResponse();
        }
    };
    /* *
     * SessionEndedRequest notifies that a session was ended. This handler will be triggered when a currently open 
     * session is closed for one of the following reasons: 1) The user says "exit" or "quit". 2) The user does not 
     * respond or says something that does not match an intent defined in your voice model. 3) An error occurs 
     * */
    const SessionEndedRequestHandler = {
        canHandle(handlerInput) {
            return Alexa.getRequestType(handlerInput.requestEnvelope) === 'SessionEndedRequest';
        },
        handle(handlerInput) {
            console.log(`~~~~ Session ended: ${JSON.stringify(handlerInput.requestEnvelope)}`);
            // Any cleanup logic goes here.
            return handlerInput.responseBuilder.getResponse(); // notice we send an empty response
        }
    };
    /* *
     * The intent reflector is used for interaction model testing and debugging.
     * It will simply repeat the intent the user said. You can create custom handlers for your intents 
     * by defining them above, then also adding them to the request handler chain below 
     * */
    const IntentReflectorHandler = {
        canHandle(handlerInput) {
            return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest';
        },
        handle(handlerInput) {
            const intentName = Alexa.getIntentName(handlerInput.requestEnvelope);
            const speakOutput = `You just triggered ${intentName}`;
    
            return handlerInput.responseBuilder
                .speak(speakOutput)
                //.reprompt('add a reprompt if you want to keep the session open for the user to respond')
                .getResponse();
        }
    };
    /**
     * Generic error handling to capture any syntax or routing errors. If you receive an error
     * stating the request handler chain is not found, you have not implemented a handler for
     * the intent being invoked or included it in the skill builder below 
     * */
    const ErrorHandler = {
        canHandle() {
            return true;
        },
        handle(handlerInput, error) {
            const speakOutput = 'Sorry, I had trouble doing what you asked. Please try again.';
            console.log(`~~~~ Error handled: ${JSON.stringify(error)}`);
    
            return handlerInput.responseBuilder
                .speak(speakOutput)
                .reprompt(speakOutput)
                .getResponse();
        }
    };
    
    /**
     * This handler acts as the entry point for your skill, routing all request and response
     * payloads to the handlers above. Make sure any new handlers or interceptors you've
     * defined are included below. The order matters - they're processed top to bottom 
     * */
    exports.handler = Alexa.SkillBuilders.custom()
        .addRequestHandlers(
            LaunchRequestHandler,
            OrderIntentHandler,
            HelpIntentHandler,
            CancelAndStopIntentHandler,
            FallbackIntentHandler,
            SessionEndedRequestHandler,
            IntentReflectorHandler)
        .addErrorHandlers(
            ErrorHandler)
        .withCustomUserAgent('sample/hello-world/v1.2')
        .lambda();

    最後に

    思ったこと。

    • コードエディタを使って、Alexaに行わせる動作を変更できることと、どこを変更したら、Alexaの動作がどう変わるか、をざっくり抑えられればOK
    • 一からコードを書くと大変なので、似た処理をコピーして実装するとよい

    参考

    • Alexa Skills Kit: Webinars https://developer.amazon.com/ja-JP/alexa/alexa-skills-kit/get-deeper/webinars
    • 応答のビルド — ASK SDK for Node.js ドキュメント https://ask-sdk-for-nodejs.readthedocs.io/ja/latest/Building-Response.html

    コメントを残す

    メールアドレスが公開されることはありません。 * が付いている欄は必須項目です