Alexa Skills Kit for Node.js はじめの一歩

2017.06.22

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

この記事は、2018/01/03 日本語対応も含め、最新版にリメイクされました。是非、こちらをご参照下さい。

1 はじめに

Alexa Skills Kit for Node.js (以下、Alexa SDK) は、Amazon のAlexaチームによって作成されたスキル作成用のSDKです。

Announcing the Alexa Skills Kit for Node.js

AlexaのSkill開発では、「SessionAttributesの扱い」「永続性」「レスポンスの組み立て」「動作のモデリング」などに多大な労力が必要ですが、このSDKを利用することで、その殆どを任せることができ、開発者は、ロジックに集中すことができるようになります。

Alexa SDKの機能は、次のように列挙されています。

  • NPMパッケージとして提供される
  • Eventを利用してレスポンスが構築される
  • 新しいセッションや未知のイベントも処理できる
  • 定義した「ステート(状態)」に応じてインテントを処理できる
  • DynamoDBでデータ処理が可能
  • すべての音声出力は、SSMLでラップされる
  • 全てのLambdaイベントとコンテキストにアクセスが可能

2 インストール

インストールは、npmコマンドで可能です。

$ npm install --save alexa-sdk

node_modulesの下に、必要なモジュールが展開されますので、Lambdaへアップロードする際にzipに含まれるようにします。

3 最初のサンプル

Alexa SDKを使用した最小限のスキルは、次のようになります。

var Alexa = require('alexa-sdk');
exports.handler = function(event, context, callback) {
    var alexa = Alexa.handler(event, context);
    alexa.registerHandlers(handlers);
    alexa.execute();
};

var handlers = {
    'Unhandled': function () {
        this.emit(':tell', 'Hello Workd');
    }
};

Alexa SDKは、Lambdaの起動ハンドラで受け取った、eventcontextをそのまま渡して、インスタンス化します。

また、最小限、Unhandledという名前のハンドラの定義が必要です。このハンドラが無いと、スキルはエラーとなってしまいます。

'Error: No 'Unhandled' function defined for event: Unhandled'

上記のサンプルは、何を話しかけても "Hello World" と返ってくる(だけの)スキルです。

4 アプリケーションID

実は、先のサンプルでは、ログに次のようなWarningが表示されています。

Warning: Application ID is not set

Alexa SDKでは、appIdに、スキルのApplication Idを設定するようになっています。

002

Application Idは、Amazon 開発者ポータルからコピーして、そのまま貼り付けても良いのですが、通常、Lambdaの環境変数経由で指定します。

alexa.appId = process.env.ALEXA_APPLICATION_ID;

001

5 リソース

Alexa SDKでは、複数の言語に応じた文字列を定義するため、resourcesというオブジェクトがあります。

定義されたリソースは、this.t(キー文字列) の形式で利用できます。この形式に従えば、簡単に複数の言語に対応できるので、発声する文字列は、直接書き込まずにresourceで定義する方がいいでしょう。

var Alexa = require('alexa-sdk');
exports.handler = function(event, context, callback) {
    var alexa = Alexa.handler(event, context);
    alexa.appId = alexaAppId;
    alexa.resources = languageStrings;
    alexa.registerHandlers(handlers);
    alexa.execute();
};

const languageStrings = {
  'en-US': {
    'translation': {
      'HELLO' : 'Hello World.',
      'BYE' : "Good by."
    }
  }
};

var handlers = {
    'Unhandled': function () {
        this.emit(':tell', this.t("HELLO"));
    }
};

6 複数のハンドラ

最低限必要なUnhandledハンドラの他にも、ハンドラを追加できます。 下記の例は、AMAZON.StopIntentが来た場合のハンドラを追加しています。

var handlers = {
    'Unhandled': function () {
        this.emit(':ask', this.t("HELLO"));
    },
    "AMAZON.StopIntent": function() {
        this.emit(':tell', this.t("BYE"));
    }
};

試験的に、開発者ポータルで、Intent SchemaAMAZON.StopIntent を追加し、Sample Utterancesで、「stop」という発話に結びつけます。

005 004

テストで、AMAZON.StopIntentを送ると、新しく追加したハンドラに制御が移っていることが確認できます。

003

7 tell と ask

既に気がついた方もおられるかもしれませんが、先の例の、HELLOBYEは、異なるハンドラで処理されていました。

this.emit(':ask', this.t("HELLO"));
this.emit(':tell', this.t("BYE"));

この :tell:ask は、Alexa SDKでレスポンスを返す代表的なハンドラで、会話を継続するかどうかの違いが有ります。

:tellで返した場合、会話は終わりになります。これに対し、:askでは、会話は継続し、次のユーザの発話を待つ体制をとります。

なお、会話の継続若しくは、終了については、レスポンス上では、shouldEndSession に格納されます

"shouldEndSession": false

8 ステート

Alexa SDKでは、ステート(状態) の保存が可能です。ステートは、handler.stateに指定し、次回のリクエストは、そのステートで動作します。

ステートは、ハンドラーを生成する時に指定できます。 下記のコードでは、nextHandersは、ステートが、_NEXTの時だけ動作対象になります。

このスキルは、最初、ステートが空であるため、handlersUnhandledに流れます。そして、そこでhandler.stateに文字列 _NEXT をセットしているため、次回のリクエストは、nextHandlersに向かいます。

exports.handler = function(event, context, callback) {

   // ・・・略

    alexa.registerHandlers(handlers,nextHandlers);

   // ・・・略
};

var handlers = {
    'Unhandled': function () {
        this.handler.state = '_NEXT';
        this.emit(':ask', 'first message');
    }
};

var nextHandlers = Alexa.CreateStateHandler('_NEXT', {
    'Unhandled': function () {
        this.emit(':tell', 'next message');
    }
});

この様に、ステートごとにハンドルが指定できるため、たとえば、よく使用されるAMAZON.YesIntentAMAZON.NoIntentでも、その状況に応じて、使い分けが可能になります。

なお、handler.stateに入れた文字列は、SessionAttributeに格納され、次のリクエストでその値を受け取っています。

"sessionAttributes": {
    "STATE": "_NEXT"
}

9 一連の流れを書いてみる

ハンドラ・ステート・リソースなど、主要な事項について、理解できたので、ここで簡単なスキルを作ってみます。

サンプルは、ユーザの発話内容に関係なく、FirstステージとSecondステージをスイッチし、ストップと発話すると終了します。

動作している様子は、下記のとおりです。

var Alexa = require('alexa-sdk');
const alexaAppId = process.env.ALEXA_APPLICATION_ID

exports.handler = function(event, context, callback) {
    var alexa = Alexa.handler(event, context);
    alexa.appId = alexaAppId;
    alexa.resources = languageStrings;
    alexa.registerHandlers(handlers,firstHandlers,secondHandlers);
    alexa.execute();
};


var STATUS = {
    FIRSTMODE: '_FIRSTMODE',
    SECONDMODE: '_SECONDMODE'
};

const languageStrings = {
  'en-US': {
    'translation': {
      'BYE' : "Good by",
      'FIRST' : "This is the First stage",
      'SECOND' : "This is the 2nd stage"
    }
  }
};

var handlers = {
    'Unhandled': function () {
        this.handler.state = STATUS.FIRSTMODE;
        this.emitWithState("Unhandled", false);
    },
    "stop": function() {
        this.emit(':tell', this.t("BYE"));
    }
};

var firstHandlers = Alexa.CreateStateHandler(STATUS.FIRSTMODE, {
    'Unhandled': function () {
        this.handler.state = STATUS.SECONDMODE;
        this.emit(':ask', this.t("FIRST"));
    },
    "AMAZON.StopIntent": function() {
        this.emit("stop");
    }
});

var secondHandlers = Alexa.CreateStateHandler(STATUS.SECONDMODE, {
    'Unhandled': function () {
        this.handler.state = STATUS.FIRSTMODE;
        this.emit(':ask', this.t("SECOND"));
    },
    "AMAZON.StopIntent": function() {
        this.emit("stop");
    }
});

10 最後に

今回は、「はじめの一歩」という位置づけで、Alexa SDK の最も基本的な動きを確認してみました。 Alexa SDKでは、テーブル名を指定するだけで、DynamoDBでデータの永続化ができたり、tell ask以外にも、多くの便利なハンドラが用意されています。

次回は、そのあたりも纏められればと考えています。

11 参考リンク


Announcing the Alexa Skills Kit for Node.js
GitHub https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs