初めてのスキル開発



初めてのスキル開発

このガイドでは、ASK SDK v2 for Node.jsを使ったスキル開発の手順を説明します。

前提条件

  • Amazon開発者アカウント。Alexaスキルの作成と設定に必要です。
  • アマゾンウェブサービス(AWS)アカウント。このガイドでは、AWS Lambdaでスキルをホスティングする手順を確認できます。
  • インストールされたSDKに依存関係のあるNPMプロジェクト。ASK SDK v2 for Node.jsのセットアップを参照してください。このサンプルスキルには、標準SDK配布パッケージが必要です。依存関係をカスタマイズしている場合は、コアSDKのサポートモジュールをインストールする必要があります。

スキルコードを記述する

以下のセクションでは、ハンドラーごとのスキルコードの記述について説明します。

依存関係をインポートする

index.js(下記のセクションでTypeScript.Sameを使用する場合は、index.ts)というファイルを作成し、以下のコードを貼り付けます。

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

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

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

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

リクエストハンドラーの追加

まず、スキルで受信するさまざまなタイプのリクエストを処理するのに必要なリクエストハンドラーを作成します。

LaunchRequestハンドラー

以下は、スキルがLaunchRequestを受信した時に呼び出されるハンドラーを設定するコードのサンプルです。特定のインテントなしでスキルが呼び出された場合、LaunchRequestイベントが発生します。

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

const LaunchRequestHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'LaunchRequest';
  },
  handle(handlerInput) {
    const speechText = 'ようこそ、アレクサスキルキットへ。こんにちは、と言ってみてください。';

    return handlerInput.responseBuilder
      .speak(speechText)
      .reprompt(speechText)
      .withSimpleCard('ハローワールド', speechText)
      .getResponse();
  }
};

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

const LaunchRequestHandler : RequestHandler = {
  canHandle(handlerInput : HandlerInput) : boolean {
    return handlerInput.requestEnvelope.request.type === 'LaunchRequest';
  },
  handle(handlerInput : HandlerInput) : Response {
    const speechText = 'ようこそ、アレクサスキルキットへ。こんにちは、と言ってみてください。';

    return handlerInput.responseBuilder
      .speak(speechText)
      .reprompt(speechText)
      .withSimpleCard('ハローワールド', speechText)
      .getResponse();
  },
};

受信したリクエストがLaunchRequestの場合、canHandle関数はtrueを返します。handle関数は、基本的なあいさつの応答を生成して返します。

HelloWorldIntentハンドラー

以下は、スキルがHelloWorldIntentを受信した時に呼び出されるハンドラーを設定するコードのサンプルです。

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

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

const HelloWorldIntentHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'IntentRequest'
      && handlerInput.requestEnvelope.request.intent.name === 'HelloWorldIntent';
  },
  handle(handlerInput) {
    const speechText = 'こんにちは';

    return handlerInput.responseBuilder
      .speak(speechText)
      .withSimpleCard('ハローワールド', speechText)
      .getResponse();
  }
};

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

const HelloWorldIntentHandler : RequestHandler = {
  canHandle(handlerInput : HandlerInput) : boolean {
    return handlerInput.requestEnvelope.request.type === 'IntentRequest'
      && handlerInput.requestEnvelope.request.intent.name === 'HelloWorldIntent';
  },
  handle(handlerInput : HandlerInput) : Response {
    const speechText = 'こんにちは';

    return handlerInput.responseBuilder
      .speak(speechText)
      .withSimpleCard('ハローワールド', speechText)
      .getResponse();
  },
};

canHandle関数は受信するリクエストがIntentRequestかどうかを検出し、インテント名がHelloWorldIntentの場合にtrueを返します。handle関数は、基本的な「こんにちは」という応答を生成して返します。

HelpIntentハンドラー

以下は、スキルがビルトインインテントAMAZON.HelpIntentを受信した時に呼び出されるハンドラーを設定するコードのサンプルです。

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

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

const HelpIntentHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'IntentRequest'
      && handlerInput.requestEnvelope.request.intent.name === 'AMAZON.HelpIntent';
  },
  handle(handlerInput) {
    const speechText = 'こんにちは、と言ってみてください。';

    return handlerInput.responseBuilder
      .speak(speechText)
      .reprompt(speechText)
      .withSimpleCard('ハローワールド', speechText)
      .getResponse();
  }
};

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

const HelpIntentHandler : RequestHandler = {
  canHandle(handlerInput : HandlerInput) : boolean {
    return handlerInput.requestEnvelope.request.type === 'IntentRequest'
      && handlerInput.requestEnvelope.request.intent.name === 'AMAZON.HelpIntent';
  },
  handle(handlerInput : HandlerInput) : Response {
    const speechText = 'こんにちは、と言ってみてください。';

    return handlerInput.responseBuilder
      .speak(speechText)
      .reprompt(speechText)
      .withSimpleCard('ハローワールド', speechText)
      .getResponse();
  },
};

さきほどのハンドラー同様、このハンドラーはIntentRequestを想定されるインテント名と照合します。基本のヘルプ手順が返されます。

CancelAndStopIntentハンドラー

CancelAndStopIntentハンドラーもビルトインインテントによって呼び出されるため、HelpIntentハンドラーに似ています。以下は、1つのハンドラーを使ってAmazon.CancelIntentAmazon.StopIntentという2つのインテントに応答している例です。

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

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

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

    return handlerInput.responseBuilder
      .speak(speechText)
      .withSimpleCard('ハローワールド', speechText)
      .withShouldEndSession(true)
      .getResponse();
  }
};

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

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

    return handlerInput.responseBuilder
      .speak(speechText)
      .withSimpleCard('ハローワールド', speechText)
      .withShouldEndSession(true)      
      .getResponse();
  },
};

両方のインテントに対する応答は同じであるため、1つのハンドラーにすることで重複するコードを減らせます。

SessionEndedRequestハンドラー

SessionEndedRequestを受信した後は音声、カード、ディレクティブを使った応答を返すことはできませんが、クリーンアップロジックを追加するにはSessionEndedRequestHandlerが最適な場所です。

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

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

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

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

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

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

エラーハンドラーの追加

ASK SDK v2 for Node.jsはエラー処理が簡単で、スムーズなユーザーエクスペリエンスを実現するスキルが作成しやすくなります。エラーハンドラーは、未処理のリクエストやAPIサービスのタイムアウトなどのエラー処理ロジックを組み込むのに最適です。以下の例では、catch allエラーハンドラーをスキルに追加して、すべてのエラーに対してスキルが意味のあるメッセージを返すようにしています。

以下のコードを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ハンドラー関数は、作成したリクエストハンドラーを使用して設定されたSDKのSkillインスタンスを作成します。

以下のコードをindex.jsファイルの、前述のセクションの後に貼り付けます。

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

let skill;

exports.handler = async function (event, context) {
  console.log(`REQUEST++++${JSON.stringify(event)}`);
  if (!skill) {
    skill = Alexa.SkillBuilders.custom()
      .addRequestHandlers(
        LaunchRequestHandler,
        HelloWorldIntentHandler,
        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,
        HelloWorldIntentHandler,
        HelpIntentHandler,
        CancelAndStopIntentHandler,
        SessionEndedRequestHandler,
      )
      .addErrorHandlers(ErrorHandler)
      .create();
  }

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

  return response;
};

関数は、SkillBuilders.customビルダーを使用してSDKインスタンスを作成します。addRequestHandlersビルダー関数はリクエストハンドラーを登録します。関数は、Lambdaハンドラー関数としてエクスポートされます。

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

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

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

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

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

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

スキルのコードが完成したら、スキルパッケージを作成できます。スキルをAWS Lambdaにアップロードするには、スキルファイルとnode_modulesフォルダーを含むzipファイルを作成します。すべてのプロジェクトファイルは、プロジェクトフォルダではなく、ファイルを直接圧縮するようにしてください。

スキルをAWS Lambdaにアップロードする

スキルに適切なロールでAWS Lambda関数を作成する手順については、カスタムスキルをAWS Lambda関数としてホスティングするを参照してください。関数作成時には、一から作成オプションを選択し、ランタイムとしてNode.js 10.xを選択します。

AWS Lambda関数を作成し、トリガーとして「Alexa Skills Kit」を設定したら、前のステップで作成した.zipファイルをアップロードします。その際、ハンドラーはデフォルトのindex.handlerのままにします。最後に、AWS Lambda関数のARNをコピーします。このARNはAmazon開発者コンソールでスキルを設定する際に必要となります。

スキルの設定とテストを行う

スキルコードをAWS Lambdaにアップロードしたら、Alexaのスキルを設定できます。新しいスキルを作成するには、

  1. Alexa Skills Kit開発者コンソールに移動してログインします。
  2. 右上のスキルの作成ボタンをクリックします。
  3. スキル名として「HelloWorld」と入力します。
  4. カスタムスキルを選択してからスキルを作成をクリックします。

次に、スキルの対話モデルを定義します。サイドバーの呼び出し名を選択し、スキルの呼び出し名に「ハローワールド」を入力します。

次に、HelloWorldIntentというインテントを対話モデルに追加します。対話モデルのインテントセクションにある追加ボタンをクリックします。カスタムインテントを作成を選択した状態で、インテント名として「HelloWorldIntent」を入力し、インテントを作成します。インテントの詳細ページで、ユーザーがこのインテントを呼び出すのに使用できるサンプル発話をいくつか追加します。この例では、以下のサンプル発話を追加しましたが、これ以外でもかまいません。

こんにちはと言って
ハローワールドと言って
こんにちは
ハイと言って
ハイワールドと言って
ハイ
ごきげんいかが

AMAZON.CancelIntentAMAZON.HelpIntentAMAZON.StopIntentはAlexaのビルトインインテントのため、サンプル発話を追加する必要はありません。

開発者コンソールでは、スキルモデル全体をJSON形式で編集することもできます。サイドバーでJSONエディターを選択します。この例では、以下のJSONスキーマを使用できます。

{
  "interactionModel": {
    "languageModel": {
      "invocationName": "ハローワールド",
      "intents": [
        {
          "name": "AMAZON.CancelIntent",
          "samples": []
        },
        {
          "name": "AMAZON.HelpIntent",
          "samples": []
        },
        {
          "name": "AMAZON.StopIntent",
          "samples": []
        },
        {
          "name": "HelloWorldIntent",
          "slots": [],
          "samples": [
            "ごきげんいかが",
            "ハイ",
            "ハイワールドと言って",
            "ハイと言って",
            "こんにちは",
            "ハローワールドと言って",
            "こんにちはと言って"
          ]
        }
      ],
      "types": []
    }
  }
}

対話モデルの編集が完了したら、モデルを保存してビルドします。

次に、スキルのエンドポイントを設定します。エンドポイントAWS LambdaのARNを選択し、さきほど作成した関数のARNを貼り付けます。残りの設定は、デフォルト値のままで構いません。エンドポイントを保存をクリックします。

この時点で、スキルをテストできるようになります。上部にあるメニューでテストをクリックし、テストページに移動します。このスキルでは、テストは有効になっていますオプションがONになっていることを確認します。テストページを使って、テキストや音声でリクエストをシミュレーションできます。

呼び出し名と、さきほど設定したサンプル発話のうち1つを使います。たとえば、「アレクサ、ハローワールドを開いてこんにちはと言って」と言うと、スキルは「こんにちは」と答えるはずです。また、(スマートフォンやhttps://alexa.amazon.comで)Alexaアプリを開くと、スキル一覧が表示されます。ここから、Alexa搭載デバイスでテストできるように、アカウントでスキルを有効にすることができます。

この時点で、さまざまなインテントや、スキルコードに対応するリクエストハンドラーを試してみてください。一通りのテストが完了したら、スキルの認定を申請して世界中のAlexaユーザーに公開するプロセスに進むことができます。