チュートリアル: ASK SDK for Node.jsでAlexaスキル開発を始める

チュートリアル: ASK SDK for Node.jsでAlexaスキル開発を始める

このチュートリアルでは、 Alexa Skills Kit(ASK)Software Development Kit(SDK)v2 for Node.jsを使ったスキル開発の手順を説明します。以下は、サンプルスキルのフローです。

ユーザー: アレクサ、天気を教えて
Alexa: 天気は晴れです

まず、空のスキルプロジェクトを使ってリクエストハンドラーを追加してから、スキルパッケージを作成します。次に、AWSマネジメントコンソールで対応するAmazon Web Services(AWS)Lambda関数を作成し、ASK開発者コンソールを使ってスキルの設定を行います。設定したら、開発者コンソールのシミュレーターかAlexa搭載デバイスを使ってスキルを実行します。

前提条件

このチュートリアルを開始するには、次のアカウントとツールを準備する必要があります。

  • Amazon開発者アカウント – Alexaスキルを作成して設定するにはAmazon開発者アカウントが必要です。アカウントを作成する方法の詳細については、Amazon開発者アカウントの作成を参照してください。(注:日本のAlexaスキル開発者はAmazon.co.jpでアカウントを作成してから、同じアカウントで開発者ポータルにログインしてください)
  • AWSアカウント – AWS LambdaでスキルコードをホストするにはAWSアカウントが必要です。AWSアカウントを作成するには、AWS無料利用枠を参照してください。
  • Node Package Manager(NPM) – NPMのインストール方法については、NPMドキュメントのGetting started(英語)を参照してください。
  • ASK SDK v2 for Node.js – SDKのインストール方法については、ASK SDKのセットアップを参照してください。このチュートリアルを開始するには、以下のいずれかをインストールする必要があります。
    • 標準SDK配布パッケージ
    • コアSDKのサポートモジュール(依存関係をカスタマイズする場合)

ASK SDK for Node.jsを使ったスキル作成手順

以下の手順でスキルを作成します。

  1. 空のスキルプロジェクトを作成
  2. 依存関係をインポート
  3. リクエストハンドラーを追加
  4. スキルパッケージを作成
  5. AWS Lambda関数を作成
  6. スキルの作成と設定
  7. スキルのテスト

ステップ1: 空のスキルプロジェクトを作成

以下の手順に従い、NPMを使って空のスキルを作成します。

空のスキルプロジェクトを作成する

  1. コンピューターにスキル用のフォルダを作成します。
  2. コマンドプロンプトを開き、スキルフォルダに移動します。
  3. コマンドプロンプトに、npm initと入力します。
  4. すべてのプロンプトに対してEnterキーを押します。
    スキルコードフォルダに、package.jsonというファイルが作成されました。
  5. スキルコードフォルダで、npm install --save ask-sdkと入力します。
    このコマンドを実行すると、node_modulesフォルダが作成されます。
  6. スキルフォルダに、index.jsという空のファイルを作成します。

ステップ2: 依存関係を読み込む

コードがASK SDK for Node.jsに依存するよう指定するには、依存関係としてSDKを読み込む必要があります。

依存関係を読み込む

  • 以下のコードをindex.jsファイルに貼り付けます。

クリップボードにコピーされました。

const Alexa = require('ask-sdk-core');

クリップボードにコピーされました。

import {
  ErrorHandler,
  HandlerInput,
  RequestHandler,
  SkillBuilders,
} from 'ask-sdk-core';
import {
  Response,
  SessionEndedRequest,
} from 'ask-sdk-model';

ステップ3: リクエストハンドラーを追加

リクエストハンドラーは、スキルで受信するさまざまなタイプのリクエストを処理します。以下のセクションは、リクエストハンドラーのサンプルコードです。スキルコードに貼り付けて使用できます。

LaunchRequestハンドラー

ユーザーが特定のインテントなしでスキルを呼び出すと、AlexaはスキルにLaunchRequestを送信します。以下のコードでは、スキルがLaunchRequestを受信したときにAlexaが呼び出すハンドラーを設定しています。

LaunchRequestハンドラーを追加する

  • 以下のコードを、index.jsファイルの前回追加したrequire行の後に貼り付けます。

クリップボードにコピーされました。

const LaunchRequestHandler = {
  canHandle(handlerInput) {
    return Alexa.getRequestType(handlerInput.requestEnvelope) === 'LaunchRequest';
  },
  handle(handlerInput) {
    const speechText = '開発キットの天気ボットにようこそ。天気のことは私に聞いてください。';

    return handlerInput.responseBuilder
      .speak(speechText)
      .reprompt(speechText)
      .withSimpleCard('開発キットの天気ボットにようこそ。天気のことは私に聞いてください。', speechText)
      .getResponse();
  }
};

クリップボードにコピーされました。

const LaunchRequestHandler : RequestHandler = {
  canHandle(handlerInput : HandlerInput) : boolean {
    const request = handlerInput.requestEnvelope.request;
    return request.type === 'LaunchRequest';        
  },
  handle(handlerInput : HandlerInput) : Response {
    const speechText = '開発キットの天気ボットにようこそ。天気のことは私に聞いてください。';

    return handlerInput.responseBuilder
      .speak(speechText)
      .reprompt(speechText)
      .withSimpleCard('開発キットの天気ボットにようこそ。天気のことは私に聞いてください。', speechText)
      .getResponse();
  },
};

コードでは、受信したリクエストがLaunchRequestの場合、canHandle()関数はtrueを返します。handle()関数は、Alexaへの応答を生成して返します。

AskWeatherIntentハンドラー

以下のコードでは、ユーザーが天気をたずねたときにAlexaが呼び出すハンドラーを設定しています。

AskWeatherIntentハンドラーを追加する

  • 以下のコードを、index.jsファイルの前回のハンドラーの後に貼り付けます。

クリップボードにコピーされました。

const AskWeatherIntentHandler = {
  canHandle(handlerInput) {
    return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
      && Alexa.getIntentName(handlerInput.requestEnvelope) === 'AskWeatherIntent';
  },
  handle(handlerInput) {
    const speechText = '今日の天気は晴れです。';

    return handlerInput.responseBuilder
      .speak(speechText)
      .withSimpleCard('今日の天気は晴れです。', speechText)
      .getResponse();
  }
};

クリップボードにコピーされました。

const AskWeatherIntentHandler : RequestHandler = {
  canHandle(handlerInput : HandlerInput) : boolean {
    const request = handlerInput.requestEnvelope.request;  
    return request.type === 'IntentRequest'
      && request.intent.name === 'AskWeatherIntent';
  },
  handle(handlerInput : HandlerInput) : Response {
    const speechText = '今日の天気は晴れです。';

    return handlerInput.responseBuilder
      .speak(speechText)
      .withSimpleCard('今日の天気は晴れです。', speechText)
      .getResponse();
  },
};

コードでは、canHandle()関数が受信するリクエストがIntentRequestかどうかを検出し、インテント名がAskWeatherIntentの場合にtrueを返します。handle()関数は、応答と天気を読み上げるAlexa音声を返します。

HelpIntentハンドラー

以下のコードは、スキルがビルトインインテントAMAZON.HelpIntentを受信したときにAlexaが呼び出すハンドラーを設定します。ユーザーがビルトインインテントをトリガーする方法については、標準ビルトインインテントを参照してください。

HelpIntentハンドラーを追加する

  • 以下のコードを、index.jsファイルの前回のハンドラーの後に貼り付けます。

クリップボードにコピーされました。

const HelpIntentHandler = {
  canHandle(handlerInput) {
    return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
      && Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.HelpIntent';
  },
  handle(handlerInput) {
    const speechText = '天気のことは私に聞いてください。';

    return handlerInput.responseBuilder
      .speak(speechText)
      .reprompt(speechText)
      .withSimpleCard('天気のことは私に聞いてください。', speechText)
      .getResponse();
  }
};

クリップボードにコピーされました。

const HelpIntentHandler : RequestHandler = {
  canHandle(handlerInput : HandlerInput) : boolean {
    const request = handlerInput.requestEnvelope.request;    
    return request.type === 'IntentRequest'
      && request.intent.name === 'AMAZON.HelpIntent';
  },
  handle(handlerInput : HandlerInput) : Response {
    const speechText = '天気のことは私に聞いてください。';

    return handlerInput.responseBuilder
      .speak(speechText)
      .reprompt(speechText)
      .withSimpleCard('天気のことは私に聞いてください。', speechText)
      .getResponse();
  },
};

先ほどのハンドラー同様、HelpIntentハンドラーはIntentRequestを想定されるインテント名と照合します。ハンドラーは、指定された提案をAlexaが読み上げる応答を返します。

CancelAndStopIntentハンドラー

1つのハンドラーで2つの異なるインテントに応答することができます。以下は、1つのハンドラーを使ってAMAZON.CancelIntentAMAZON.StopIntentという2つのビルトインインテントに応答している例です。両方のインテントに対する応答は同じであるため、1つのハンドラーを使うことで重複するコードを減らせます。

CancelAndStopIntentハンドラーを追加する

  • 以下のコードを、index.jsファイルの前回のハンドラーの後に貼り付けます。

クリップボードにコピーされました。

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 speechText = 'さようなら。';

    return handlerInput.responseBuilder
      .speak(speechText)
      .withSimpleCard('さようなら。', speechText)
      .withShouldEndSession(true)
      .getResponse();
  }
};

クリップボードにコピーされました。

const CancelAndStopIntentHandler : RequestHandler = {
  canHandle(handlerInput : HandlerInput) : boolean {
    const request = handlerInput.requestEnvelope.request;
    return request.type === 'IntentRequest'
      && (request.intent.name === 'AMAZON.CancelIntent'
         || request.intent.name === 'AMAZON.StopIntent');
  },
  handle(handlerInput : HandlerInput) : Response {
    const speechText = 'さようなら。';

    return handlerInput.responseBuilder
      .speak(speechText)
      .withSimpleCard('さようなら。', speechText)
      .withShouldEndSession(true)      
      .getResponse();
  },
};

SessionEndedRequestハンドラー

スキルはSessionEndedRequestを受け取った後に応答を返すことはできませんが、クリーンアップロジックを含むハンドラーを提供することはできます。以下は、SessionEndedRequestのハンドラーを作成する方法の例です。

SessionEndedRequestハンドラーを追加する

  • 以下のコードを、index.jsファイルの前回のハンドラーの後に貼り付けます。

クリップボードにコピーされました。

const SessionEndedRequestHandler = {
  canHandle(handlerInput) {
    return Alexa.getRequestType(handlerInput.requestEnvelope) === 'SessionEndedRequest';
  },
  handle(handlerInput) {
    // クリーンアップロジックをここに追加します。
    return handlerInput.responseBuilder.getResponse();
  }
};

クリップボードにコピーされました。

const SessionEndedRequestHandler : RequestHandler = {
  canHandle(handlerInput : HandlerInput) : boolean {
    const request = handlerInput.requestEnvelope.request;    
    return request.type === 'SessionEndedRequest';
  },
  handle(handlerInput : HandlerInput) : Response {
    console.log(`セッションが終了しました。理由:${(handlerInput.requestEnvelope.request as SessionEndedRequest).reason}`);

    return handlerInput.responseBuilder.getResponse();
  },
};

Errorハンドラー

エラー処理ロジックをエラーハンドラーに追加できます。たとえば、未処理のリクエストやAPIサービスのタイムアウトなどを処理するロジックを追加できます。以下のコードでは、スキルがすべてのエラーに対して意味のあるメッセージを返すよう、スキルにエラーハンドラーを追加しています。

エラーハンドラーを追加する

  • 以下のコードを、index.jsファイルの前回のハンドラーの後に貼り付けます。

クリップボードにコピーされました。

const ErrorHandler = {
  canHandle() {
    return true;
  },
  handle(handlerInput, error) {
    console.log(`処理されたエラー: ${error.message}`);

    return handlerInput.responseBuilder
      .speak('すみません。コマンドを理解できませんでした。もう一度言ってください。')
      .reprompt('すみません。コマンドを理解できませんでした。もう一度言ってください。')
      .getResponse();
  }
};

クリップボードにコピーされました。

const ErrorHandler : ErrorHandler = {
  canHandle(handlerInput : HandlerInput, error : Error ) : boolean {
    return true;
  },
  handle(handlerInput : HandlerInput, error : Error) : Response {
    console.log(`処理されたエラー:${error.message}`);

    return handlerInput.responseBuilder
      .speak('すみません。コマンドを理解できませんでした。もう一度言ってください。')
      .reprompt('すみません。コマンドを理解できませんでした。もう一度言ってください。')
      .getResponse();
  }
};

Lambdaハンドラー

Lambdaハンドラーは、AWS Lambda関数のエントリーポイントとなります。以下は、スキルが受信するすべてのリクエストのルーティングを行うLambdaハンドラー関数のコードです。

Lambdaハンドラーを追加する

  • 以下のコードを、index.jsファイルの前回のハンドラーの後に貼り付けます。

クリップボードにコピーされました。

let skill;

exports.handler = async function (event, context) {
  console.log(`REQUEST++++${JSON.stringify(event)}`);
  if (!skill) {
    skill = Alexa.SkillBuilders.custom()
      .addRequestHandlers(
        LaunchRequestHandler,
        AskWeatherIntentHandler,
        HelpIntentHandler,
        CancelAndStopIntentHandler,
        SessionEndedRequestHandler,
      )
      .addErrorHandlers(ErrorHandler)
      .create();
  }

  const response = await skill.invoke(event, context);
  console.log(`RESPONSE++++${JSON.stringify(response)}`);

  return response;
};

クリップボードにコピーされました。

let skill;

exports.handler = async (event, context) => {
  console.log(`REQUEST++++${JSON.stringify(event)}`);
  if (!skill) {
    skill = SkillBuilders.custom()
      .addRequestHandlers(
        LaunchRequestHandler,
        AskWeatherIntentHandler,
        HelpIntentHandler,
        CancelAndStopIntentHandler,
        SessionEndedRequestHandler,
      )
      .addErrorHandlers(ErrorHandler)
      .create();
  }

  const response = await skill.invoke(event, context);
  console.log(`RESPONSE++++${JSON.stringify(response)}`);

  return response;
};

Lambdaハンドラー関数は、SkillBuilders.custom()ビルダー関数を使ってSDKのSkillインスタンスを作成します。addRequestHandlers()ビルダー関数は、先ほどのステップで作成したリクエストハンドラーを登録します。コードは、関数をLambdaハンドラー関数としてエクスポートします。

また、ASK SDK v2 for Node.jsで提供されるlambdaビルダー関数を使い、Skillのインスタンスを呼び出して応答を返すLambdaハンドラー関数を作成することもできます。以下は、lambdaビルダー関数を使用する方法の例です。

クリップボードにコピーされました。

exports.handler = Alexa.SkillBuilders.custom()
  .addRequestHandlers(
    LaunchRequestHandler,
    AskWeatherIntentHandler,
    HelpIntentHandler,
    CancelAndStopIntentHandler,
    SessionEndedRequestHandler)
  .addErrorHandlers(ErrorHandler)
  .lambda();

クリップボードにコピーされました。

exports.handler = SkillBuilders.custom()
  .addRequestHandlers(
    LaunchRequestHandler,
    AskWeatherIntentHandler,
    HelpIntentHandler,
    CancelAndStopIntentHandler,
    SessionEndedRequestHandler,
  )
  .addErrorHandlers(ErrorHandler)
  .lambda();

ステップ4: スキルパッケージを作成

スキルコードが完成したら、後続のステップでAWS Lambdaにアップロードするスキルパッケージを作成します。

スキルパッケージを作成する

  • スキルコードフォルダのコンテンツを含むzipファイルを作成します。
    zipファイルには、index.jspackage.jsonnode_modulesフォルダが含まれます。

ステップ5: AWS Lambda関数を作成

先ほどのステップで、Lambda関数のコードを作成しました。AWSコンソールで、対応するLambda関数も作成する必要があります。作成したら、スキルコードをLambda関数にアップロードします。

AWS Lambda関数を作成する

  1. AWS Lambdaコンソールにサインインします。
  2. コンソールの右上隅にあるリージョンのドロップダウンリストをクリックし、米国東部(バージニア北部)などAlexaスキルで利用可能なAWSリージョンを1つ選択します。
    Alexaスキルで利用可能なリージョンの一覧については、AWS Lambda関数に最適なリージョンを選択するを参照してください。
  3. まだLambda関数がない場合は、今すぐ始めるをクリックします。
  4. 1つ以上のLambda関数がある場合は、関数の作成をクリックします。
  5. 一から作成を選択します。
  6. 関数名に、Lambda関数の名前を入力します。
  7. ランタイムで、Node.js 10.xを選択します。
  8. アクセス権限で、デフォルトの実行ロールの変更を展開し、AWSポリシーテンプレートから新しいロールを作成を選択します。
  9. ロール名に、ロールの名前を入力します。
  10. ポリシーテンプレートリストで、シンプルなマイクロサービスのアクセス権限を選択します。
  11. ページ下部にある関数の作成をクリックします。
  12. 関数の概要をクリックし、トリガーを追加をクリックします。
  13. トリガーの設定で、Alexa Skills Kitを選択します。
  14. スキルID検証無効を選択します。
  15. 追加をクリックします。
  16. ページの中ほどにあるコードタブをクリックします。
  17. 右側のアップロード元をクリックし、.zipファイルを選択します。
  18. ステップ4: スキルパッケージを作成で作成したzipファイルをアップロードします。
  19. Lambda関数ページの右上にあるARNをコピーをクリックし、後でアクセスできる場所にARNを貼り付けます。
    ARNは、次のステップでASK開発者コンソールからスキルを設定するときに必要です。

ステップ6: スキルの作成と設定

スキルコードをAWS Lambdaに追加したら、ASK開発者コンソールでスキルを作成し、設定します。

Alexaスキルを作成して設定する

  1. ASK開発者コンソールにサインインします。
  2. スキルの作成をクリックします。
  3. スキル名を入力します。
  4. スキルに追加するモデルを選択カスタムを選択します。
  5. スキルのバックエンドリソースをホスティングする方法を選択で、ユーザー定義のプロビジョニングを選択します。
  6. スキルを作成をクリックします。
  7. スキルに追加するテンプレートを選択で、スクラッチで作成を選択します。
  8. 右上にある選択をクリックします。
  9. 左側の呼び出しをクリックします。
  10. スキルの呼び出し名開発キットの天気ボットと入力し、モデルを保存をクリックします。
    スキルを起動するには、「アレクサ、開発キットの天気ボットを開いて」と呼びかけます。
  11. 左側で対話モデルを展開し、インテントをクリックしてインテントを追加をクリックします。
  12. カスタムインテントを作成AskWeatherIntentと入力し、カスタムインテントを作成をクリックします。
  13. サンプル発話で、ユーザーが天気をたずねるときに言う可能性のあるサンプル発話をいくつか追加します。エントリーごとにプラス記号+)をクリックするか、Enterを押します。
    次のようなサンプル発話を追加できますが、これ以外に追加してもかまいません。
    どんな天気
    天気を教えて
    外の天気はどう
    外の天気を教えて
    
  14. 画面上部のモデルを保存をクリックします。
  15. 画面上部のモデルをビルドをクリックします。
  16. 左側でアセットを展開し、エンドポイントをクリックします。
  17. デフォルトの地域ステップ5: AWS Lambda関数を作成でコピーしたAWS Lambda ARNで置き換えます。
  18. 画面上部のエンドポイントを保存をクリックします。

ステップ7: スキルをテストする

スキルのテストには、開発者コンソールのAlexaシミュレーターを使うこともできますし、EchoデバイスなどのAlexa搭載デバイスを使うこともできます。スキルのテストの詳細については、スキルのテストとデバッグを参照してください。

開発者コンソールでスキルをテストする

  1. 開発者コンソールの上部で、テストタブをクリックします。
  2. 左上にあるこのスキルでは、テストは無効になっていますで、開発中を選択します。
  3. Alexaシミュレーターの入力またはマイクを長押しで発話フィールドをクリックし、開発キットの天気ボットを開いてと入力してEnterを押します。
  4. 「天気を教えて」と入力するか、マイクのアイコンを押しながら話しかけます。
    Alexaは「天気は晴れです」と答えるはずです。

EchoデバイスなどのAlexa搭載デバイスを使ってスキルをテストすることもできます。使用するデバイスは、スキルを作成したものと同じAmazonアカウントに登録する必要があります。

Alexa搭載デバイスを使ってスキルをテストする

  1. Alexa搭載デバイスに「アレクサ、開発キットの天気ボットを開いて」と話しかけます。
  2. Alexaがスキルを起動したら、「天気を教えて」とたずねます。
    Alexaがスキルを見つけられない場合、Alexa搭載デバイスがスキルの作成に使用したものと同じAmazonアカウントに登録されていることを確認してください。