[日本語Alexa] Alexa Skills Kit for Node.js はじめの一歩
0 リプレイスについて
Alexaは、昨年(2017年)11月に日本語対応となりました。ここDevelopers.IOでは、英語でしか利用できない頃から色々Blogに書いてきたのですが、更新された情報も含めて日本語で利用するAlexaについて纏め直してみたいと思います。
この記事は、下記の記事のリプレイス版です。
Alexa Skills Kit for Node.js はじめの一歩
1 はじめに
Alexa Skills Kit for Node.js (以下、Alexa SDK) は、Amazon のAlexaチームによって作成されたスキル作成用のSDKです。
Announcing the Alexa Skills Kit for Node.js
AlexaのSkill開発では、「セッション情報の扱い」「永続性」「レスポンスの組み立て」「動作のモデリング」などに多大な労力が必要ですが、このSDKを利用することで、その殆どを任せることができ、開発者は、ロジックに集中すことができるようになります。
Alexa SDKの機能は、次のように列挙されています。
- NPMパッケージとして提供される
- Eventを利用してレスポンスが構築される
- 新しいセッションや未知のイベントも処理できる
- 定義した「ステート(状態)」に応じてインテントを処理できる
- DynamoDBでデータ処理が可能
- すべての音声出力は、SSMLでラップされる
- 全てのLambdaイベントとコンテキストにアクセスが可能
2 インストール
インストールは、npmコマンドで可能です。
1 | $ npm install --save alexa-sdk |
node_modulesの下に、必要なモジュールが展開されますので、Lambdaへアップロードする際にzipに含まれるようにします。
1 | $ zip -r upload.zip index.js node_modules |
3 最初のサンプル
Alexa SDKを使用した最小限のスキルは、次のようになります。 このサンプルは、何を話しかけても "こんにちは" と返ってくる(だけの)スキルです。
1 2 3 4 5 6 7 8 9 10 11 12 | const Alexa = require( 'alexa-sdk' ); exports.handler = function (event, context, callback) { let alexa = Alexa.handler(event, context); alexa.registerHandlers(handlers); alexa.execute(); }; var handlers = { 'Unhandled' : function () { this .emit( ':tell' , 'こんにちは' ); } }; |
Akexa SDKは、Lambdaの起動ハンドラで受け取った、eventとcontextをそのまま渡して、インスタンス化します。
また、設定されたインテントのハンドラがない場合、Unhandledというハンドラの定義が無いと、スキルはエラーとなってしまいます。
1 | "errorMessage": "In state: . No handler function was defined for event HelloIntent and no 'Unhandled' function was defined.", |
4 アプリケーションID
実は、先のサンプルでは、ログに次のようなWarningが表示されています。
1 | Warning: Application ID is not set |
Alexa SDKでは、appIdに、スキルのApplication Idを設定するようになっています。
Application Idは、Amazon 開発者ポータルからコピーして、そのまま貼り付けても良いのですが、通常、Lambdaの環境変数経由で指定します。
1 | alexa.appId = process.env.ALEXA_APPLICATION_ID; |
5 リソース
Alexa SDKでは、複数の言語に応じた文字列を定義するため、resourcesというオブジェクトがあります。
定義されたリソースは、this.t(キー文字列) の形式で利用できます。この形式に従えば、簡単に複数の言語に対応できるので、発声する文字列は、直接書き込まずにresourceで定義する方がいいでしょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | const Alexa = require( 'alexa-sdk' ); exports.handler = function (event, context, callback) { let alexa = Alexa.handler(event, context); alexa.appId = process.env.ALEXA_APPLICATION_ID; alexa.resources = languageStrings; alexa.registerHandlers(handlers); alexa.execute(); }; const languageStrings = { 'ja-JP' : { 'translation' : { 'HELLO' : 'こんにちは' , 'CANCEL' : 'キャンセルしました' } } }; var handlers = { 'Unhandled' : function () { this .emit( ':tell' , this .t( "HELLO" )); } }; |
6 複数のハンドラ
最低限必要なUnhandledハンドラの他にも、ハンドラを追加できます。 下記の例は、AMAZON.CancelIntentが来た場合のハンドラを追加しています。
1 2 3 4 5 6 7 8 | var handlers = { 'Unhandled' : function () { this .emit( ':ask' , this .t( "HELLO" )); }, "AMAZON.CancelIntent" : function () { this .emit( ':tell' , this .t( "CANCEL" )); } }; |
AMAZON.CancelIntentは、組み込みインテントなので、サンプル発話を設定しなくても利用が可能です
7 tell と ask
既に気がついた方もおられるかもしれませんが、先の例の、HELLOとCANCELは、異なるコマンドで処理されていました。
1 2 | this .emit( ':ask' , this .t( "HELLO" )); this .emit( ':tell' , this .t( "CANCEL" )); |
この :tell と :ask は、Alexa SDKでレスポンスを返す代表的なハンドラで、会話を継続するかどうかの違いが有ります。
:tellで返した場合、会話は終わりになります。これに対し、:askでは、会話は継続し、次のユーザの発話を待つ体制をとります。
なお、会話の継続若しくは、終了につては、レスポンス上では、shouldEndSession に格納されます
1 | "shouldEndSession": false |
8 ステート
Alexa SDKでは、ステート(状態) の保存が可能です。ステートは、handler.stateに指定し、次回のリクエストは、そのステートで動作します。
ステートは、ハンドラーを生成する時に指定できます。 下記のコードでは、nextHandersは、ステートが、_NEXTの時だけ動作対象になります。
このスキルは、最初、ステートが空であるため、handlersのUnhandledに流れます。そして、そこでhandler.stateに文字列 _NEXT をセットしているため、次回のリクエストは、nextHandlersに向かいます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | exports.handler = function (event, context, callback) { // ・・・略 alexa.registerHandlers(handlers,nextHandlers); // ・・・略 }; var handlers = { 'Unhandled' : function () { this .handler.state = '_NEXT' ; this .emit( ':ask' , '最初のメッセージ' ); } }; var nextHandlers = Alexa.CreateStateHandler( '_NEXT' , { 'Unhandled' : function () { this .emit( ':tell' , '次のメッセージ' ); } }); |
この様に、ステートごとにハンドルが指定できるため、たとえば、よく使用されるAMAZON.YesIntentやAMAZON.NoIntentでも、その状況に応じて、使い分けが可能になります。
なお、handler.stateに入れた文字列は、SessionAttributeに格納され、次のリクエストでその値を受け取っています。
1 2 3 | "sessionAttributes": { "STATE": "_NEXT" } |
9 一連の流れを書いてみる
ハンドラ・ステート・リソースなど、主要な事項について、理解できたので、ここで簡単なスキルを作ってみます。
サンプルは、ユーザの発話内容に関係なく、最初のメッセージと2回目以降のメッセージを返します。また、「キャンセル」と言うとスキルは終了します。
動作している様子は、次のとおりです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | const Alexa = require( 'alexa-sdk' ); exports.handler = function (event, context, callback) { let alexa = Alexa.handler(event, context); alexa.appId = process.env.ALEXA_APPLICATION_ID; alexa.resources = languageStrings; alexa.registerHandlers(handlers,firstHandlers,secondHandlers); alexa.execute(); }; var STATUS = { FIRSTMODE: '_FIRSTMODE' , SECONDMODE: '_SECONDMODE' }; const languageStrings = { 'ja-JP' : { 'translation' : { 'CANCEL' : "キャンセルしました。また、遊んで下さいね" , 'FIRST' : "はじめまして。サンプルスキルです。これは、最初のお返事です" , 'SECOND' : "これは、2回目以降のお返事です。ごめんなさい、2回目以降は、これしか言えないのです" } } }; var handlers = { 'Unhandled' : function () { this .handler.state = STATUS.FIRSTMODE; this .emitWithState( "Unhandled" , false ); }, "cancel" : function () { this .emit( ':tell' , this .t( "CANCEL" )); } }; var firstHandlers = Alexa.CreateStateHandler(STATUS.FIRSTMODE, { 'Unhandled' : function () { this .handler.state = STATUS.SECONDMODE; this .emit( ':ask' , this .t( "FIRST" )); }, "AMAZON.CancelIntent" : function () { this .emit( "cancel" ); } }); var secondHandlers = Alexa.CreateStateHandler(STATUS.SECONDMODE, { 'Unhandled' : function () { this .emit( ':ask' , this .t( "SECOND" )); }, "AMAZON.CancelIntent" : function () { this .emit( "cancel" ); } }); |
10 最後に
今回は、「はじめの一歩」という位置づけで、Alexa DSK の最も基本的な動きを確認してみました。
次回は、DynamoDBでデータを使用したデータの永続化について紹介させて頂きます。
11 参考リンク
Announcing the Alexa Skills Kit for Node.js
GitHub https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs