こんにちはアドバンストテクノロジー部の@y-matsushitaです。
最近はAmazon Echo、Google Home、Clova WAVEと次々に新しいスマートスピーカーが出ていますね。
私も流行りに乗ってAmazon Echo用のAlexaスキルの作成を試してみたのでご紹介します。
作成するAlexaスキル
今回はクラッシュフィーバーのクイズを出題するスキルを試作します。
予め設定した問題を出題しユーザの回答(番号)によって正解か不正解かを返します。
流れとしては以下のような形です。
- Alexaから問題を出題
- ユーザが1番〜n番までの番号で回答
- Alexaから「正解」or「不正解」を返す
- 1に戻る(最後まで出題し終えたらAlexaから正解数を発表)
それでは早速始めます。
必要な事前登録
以下への登録が必要です。
developer.amazon.com
aws.amazon.com
スキルの作成
開発者ポータルからAlexaのタブを開いて「Alexa Skills Kit」を始めます。
開いたら画面右上の「新しいスキルを追加する」から新規作成します。
スキル情報を入力して保存します。
今回は以下のように設定しました。
・スキルの種類:カスタム対話モデル
・言語:Japanese
・スキル名:クラフィクイズ
・呼び出し名:クラッシュフィーバー
・グローバルフィールド:全ていいえ
対話モデルの生成
次に対話モデルを生成します。
ユーザから受けた命令がどういった意図なのかAlexaが判断できるようにするための設定です。
今回はスキルビルダーから生成します。*1
以下のボタンからスキルビルダーを起動してください。
Intentの追加
Intentsタブの「ADD+」をクリックします。
追加できるIntentはユーザが独自で定義するCustom intentと予め用意されたBuilt-in libraryの2種類があります。
今回は以下のIntentを追加します。
-
Custom intent
- QuizIntent
(ユーザがクイズに回答する際に呼び出し) -
Built-in library
- AMAZON.RepeatIntent
(ユーザがクイズの問題を聞き直したい場合に呼び出し)
- AMAZON.StartOverIntent
(クイズをスタートする場合に呼び出し)
まずはCustom intentのQuizIntentから追加します。
テキストボックスに「QuizIntent」と入力しCreate intentを押下します。
次にユーザが話す想定の文章を入力します。
ここでは以下のような文章を入力しました。
{Answer} 番です
{Answer} 番
正解は {Answer} 番
ユーザには回答を番号で答えてもらう想定で、情報を取得したい箇所を{Answer}
としています。
注意点として{Answer}
の前後には半角スペースが入っていないと後のビルド時にエラーとなってしまうようです。
編集箇所が多くなった後でエラーが発生すると、どこが該当箇所か分からなくなるので注意しましょう。
また、Intentに{Answer}
が入った文章を追加するとIntentSlotsに自動でAnswerが追加されます。
Slotには{Answer}
で取得できる情報の例を追加します。今回は定義済みのAMAZON.NUMBER(数値情報)がありますので、それを設定してCustom intent の設定は完了です。
あとはBuilt-in libraryのAMAZON.RepeatIntentとAMAZON.StartOverIntentも同様に追加します。
Built-in library のIntentは何も例文を入れなくてもある程度認識してくれますが、ひとまず絶対に認識してほしい文章を入れておきます。今回はAMAZON.RepeatIntentに「もう一回教えて」などのようにクイズの問題を聞き直す文章を入れ、AMAZON.StartOverIntentに「ゲームスタート」とクイズを開始する言葉を入れました。
AMAZON.RepeatIntentとAMAZON.StartOverIntentからは取得したい情報が無いためSlotは設定しません。
まとめると今回は以下のような形で登録しました。
Intent | Slot | |
---|---|---|
QuizIntent | ・{Answer} 番です ・{Answer} 番 ・正解は {Answer} 番 |
{Answer} ・AMAZON.NUMBER |
AMAZON.RepeatIntent | もう一回教えて もう一度教えて |
無し |
AMAZON.StartOverIntent | ゲームスタート | 無し |
最後まで完了すると以下のようにIntentsとSlot Typesが並びます。
ビルド
Intentの登録が完了したらモデルをセーブして「Build Model」を押下します。
問題なく完了すれば以下のような表示がされます。
ビルドで失敗してしまった場合は以下が原因でエラーが出ていることが多かったので見直してみてください。
エラー内容 | 確認する箇所 |
---|---|
Error building interaction model A sample utterance for a slot in the interaction model is invalid |
設定した{Answer}の前後に半角スペースがあるかどうか |
Error building interaction model Bad request | {Answer}にAMAZON.NUMBERのSlotTypeを追加しているかどうか |
ビルドが完了したら画面右上の「Configuration」から設定画面へ戻ります。
AWS Lambda
ここから先はAWS Lambdaで編集します。
AWS LambdaでAlexaから受け取ったIntentをもとにユーザに返すべき発言をAlexaを通して返します。
Alexa用のLambda関数を作成
Lambdaの関数の作成から「alexa-skill-kit-sdk-factskill」のテンプレートを選びます。
適当な関数名とロールを入力して作成します。
トリガーにAlexa Skils Kitを追加します。
追加すると以下のようになります。
コードを書き換え
デフォルトのコードを以下に書き換え保存します。
"use strict"; const Alexa = require('alexa-sdk'); // ステートの定義 const states = { QUIZ: '_QUIZMODE', START: "_STARTMODE" }; // クイズ内容の定義 const questions = [ { 'q' : 'クラッシュフィーバーの略称は? 1.クラフィ、2.クラッシュ', 'a' : '1' }, { 'q' : 'ワンダープラネットの略称は? 1.ダープラ、2.ワンプラ、3.ワンダー', 'a' : '2'}, { 'q' : 'クラッシュフィーバーの通貨単位は次のうちどれ? 1.ゼニー、2.ゴールド、3.ビット', 'a' : '3'}, { 'q' : '存在しないタイプはどれ? 1.魔法、2.体力、3.バランス', 'a' : '1'}, { 'q' : '果実を使うと上がるのはどれ? 1.レベル、2.バグ、3.ステータス', 'a' : '3'}, { 'q' : 'クエストに参加するのに必要なものは次のうちどれ? 1.スタミナ、2.ビット、3.エナジー', 'a' : '3'}, ]; var languageString = { "ja-JP": { "translation": { "WELCOME_MESSAGE": "クラフィクイズへようこそ。 ", "HELP_MESSAGE": "正解だと思う番号を回答してください。", "START_MESSAGE": "ゲームを始める場合は「ゲームスタート」と言ってください。 ", "ANSWER_CORRECT_MESSAGE": "正解。 ", "ANSWER_WRONG_MESSAGE": "残念、不正解。 ", "TELL_QUESTION_MESSAGE": "第%s問。 ", "GAME_OVER_MESSAGE": "全ての問題が終わりました。あなたの点数は%s点でした。遊んでくれてありがとう。 ", "UNHANDLED_MESSAGE": "すみません、よく聞きとれませんでした。" } } }; exports.handler = function(event, context, callback) { var alexa = Alexa.handler(event, context); alexa.resources = languageString; alexa.registerHandlers(handlers, startStateHandlers, quizHandlers); alexa.execute(); }; var handlers = { 'LaunchRequest': function () { this.handler.state = states.START; this.emitWithState("StartGame"); }, "AMAZON.StartOverIntent": function() { this.handler.state = states.START; this.emitWithState("StartGame"); }, 'AMAZON.HelpIntent': function () { this.emit(':ask', this.t("HELP_MESSAGE") + this.t("START_MESSAGE")); }, 'Unhandled': function () { var speechOutput = this.t("UNHANDLED_MESSAGE") + this.t("START_MESSAGE"); this.emit(":ask", speechOutput, speechOutput); } }; // ゲーム開始ステート var startStateHandlers = Alexa.CreateStateHandler(states.START, { "StartGame": function () { this.handler.state = states.QUIZ; // クイズ回答ステートをセット this.attributes['advance'] = 1; // 進行状況をセッションアトリビュートにセット this.attributes['correct'] = 0; // 正解数を初期化 var message = this.t("WELCOME_MESSAGE") + this.t("HELP_MESSAGE") + this.t("TELL_QUESTION_MESSAGE", "1") + questions[0].q; var reprompt = this.t("TELL_QUESTION_MESSAGE") + questions[0].q; this.emit(':ask', message, reprompt); // 相手の回答を待つ console.log(message); } }); // クイズ回答ステート var quizHandlers = Alexa.CreateStateHandler(states.QUIZ, { 'QuizIntent': function() { // スロットから回答を参照 var usersAnswer = this.event.request.intent.slots.Answer.value; if(!usersAnswer){ this.emitWithState("Unhandled"); } var resultMessage; if(questions[this.attributes['advance']-1].a == usersAnswer){ resultMessage = this.t("ANSWER_CORRECT_MESSAGE") //正解 this.attributes['correct'] ++; }else{ resultMessage = this.t("ANSWER_WRONG_MESSAGE") //不正解 } if(this.attributes['advance'] < questions.length){ // まだ問題が残っている場合 var nextMessage = this.t("TELL_QUESTION_MESSAGE", this.attributes['advance']+1) + questions[this.attributes['advance']].q; this.attributes['advance'] ++; this.emit(':ask', resultMessage+nextMessage, nextMessage); }else{ // 全ての問題が終了した場合 var endMessage = this.t("GAME_OVER_MESSAGE", this.attributes['correct']) // スキルを初期状態に戻すためステートをリセット this.handler.state = ''; this.attributes['STATE'] = undefined; this.attributes['advance'] = 0; this.emit(':tell', resultMessage + endMessage, endMessage); } }, "AMAZON.RepeatIntent": function() { var nextMessage = this.t("TELL_QUESTION_MESSAGE", this.attributes['advance']) + questions[this.attributes['advance']-1].q; this.emit(':ask', nextMessage, nextMessage); }, 'Unhandled': function() { var reprompt = this.t("UNHANDLED_MESSAGE") + this.t("HELP_MESSAGE"); this.emit(':ask', reprompt, reprompt); } });
このスキルでは主にstartStateHandlersとquizHandlersの2つのステートで状態を管理しています。
まずスキル起動直後はAlexaからAMAZON.StartOverIntentが来るのを待ちます。ユーザが「ゲームスタート」と言うとAMAZON.StartOverIntentが発生するのでstartStateHandlersで初期化を行い、ステートをquizHandlersへ移します。AMAZON.StartOverIntent以外(ユーザが「ゲームスタート」と言っていない)の場合は、誘導用のメッセージを返します。
quizHandlersではユーザの発言した回答の番号がthis.event.request.intent.slots.Answer.value
で取得できるので、questionsのaと比較し正否の判定を行います。またAMAZON.RepeatIntentが呼ばれている場合は再度問題文を読み上げます。もし何にも引っかからない場合はUnhandledが呼ばれ、ヘルプ用のメッセージを表示してユーザを誘導します。
正否判定の後には問題数と進行状況を比較し、クイズを続けるか、終えるかを判断しています。クイズが続く場合はthis.emitを:ask
にして再度Alexaから問いかけを行い、終了する場合はthis.emitを:tell
にしてクイズの正解数を読み上げて対話を終了します。
ここまででコードの保存が完了したらLambdaの画面右上のARNをコピーします。
以上でLambda側の操作は完了です。
ARNの設定
Alexaの開発者コンソールに戻り、先ほどコピーしたARNを入力します。
他にも設定項目がいくつかありますが、今回はデフォルトのままでOKです。
入力後「次へ」を押して問題なければ作成したスキルでテストが可能になります。
シミュレータでのテスト
画面をスクロールするとサービスシミュレータという項目があります。
色々入力してどういう結果が返ってくるかWEB上でテストしてみましょう。
問題なければ「ゲームスタート」と送るとクイズが始まるはずです。
何かおかしな挙動がみつかれば対話モデルやLambdaのプログラムを修正していく流れになります。
サービスリクエストに使われたJSONはそのままLambdaに渡すとLambda側のみでテストすることも可能です。
実機でのテスト
シミュレータでのテストで問題が無ければ、いよいよ実機でテストを行います。
Alexaの開発者アカウントと実機テストをする端末のアカウントが同じであれば、すでに実機テストが可能です。
また「Skills Beta Testing」を使ってテストユーザとして招待する形で実機テストも可能です。
「Skills Beta Testing」を有効化するには、「公開情報」と「プライバシーとコンプライアンス」を記入していきます。
この作業をしても申請をしなければ一般公開されないのでご安心ください。
公開情報を入力
公開情報のタブを開き、必要事項を記入していきます。
全て入力する必要がありますが、申請しないのであれば適当に入力してもテストは可能です。
今回は以下のように設定しました。
・カテゴリー:Games, Trivia & Accessories
・サブカテゴリー:Games
・テストの手順:任意の文書
・国と地域:国と地域を選択する→Japan
・スキルの簡単な説明:任意の文書
・スキルの詳細な説明:任意の文書
・サンプルフレーズ:任意の文書(ウェイクワードなどを記載)
・キーワード:任意のキーワード (省略可)
・アイコン:108x108と512x512の画像を設定
プライバシーとコンプライアンスを入力
プライバシーとコンプライアンスのタブを開き、該当する項目にチェックを入れます。
「公開情報」と「プライバシーとコンプライアンス」を入力して問題なければ、
以下のボタンが押せるようになり、実機でのベータテストが可能になります。
テストユーザを登録
スキルを動かしたいユーザのAlexaアカウントのメールアドレスを入力します。
登録したメールアドレスの受信ボックスを確認すると以下のような文面のメールが確認できます。
リンクが2つあるのが確認できますが、下の「JP customers」のリンクをクリックしてください。
Alexaのスキル管理画面は英語版の「.com」と日本語版の「.co.jp」があります。
上のリンクをクリックすると英語版として認識されるため日本語のAlexaスキルが実行できない恐れがあります。
リンク先ではスキルの設定ができるので「有効にする」をクリックしましょう。
有効化すると遂にスキル一覧に作成したスキルが出てきます!
Alexaに呼びかけてみよう!
以上の手順を終えて呼びかけると、他の公開されているスキルと同様に反応してくれるはずです。
*2
手順が長くなりましたが、なんとかAlexaに独自のスキルを追加することができました。
実機のAlexaに話しかけて反応を楽しみましょう!