魅力的なスキルを開発する

オートデリゲートを使ってターンごとにスロット値を収集する

前のセクションでは、Alexaが「こんにちは、 ケークウォークです。」とあいさつするスキルを作成しました。 このセクションでは、ユーザーに誕生日をたずねるという機能をスキルに追加します。ユーザーが答えると、スキルは誕生日を理解して復唱します。

この機能には、発話、インテント、スロットを使います。また、スキルに自動でフォローアップの質問をさせて必要な情報を収集するという、ダイアログ管理の使い方も学びます。たとえば、ユーザーが「7月12日生まれです」と答えた場合、ダイアログ管理は自動で生まれた年をたずねます。

このモジュールを修了すると、次のことができるようになります:

  • ユーザーに質問する
  • 回答を聞き取る
  • ユーザーに答える

 

ブラウザでAlexa開発者コンソールを開きます。コンソールにログインし、Cake Timeスキルを開きます。

Step 1: ユーザーに誕生日をたずねる

現時点でのスキルは、ユーザーにあいさつしたら終了するというシンプルなものです。ようこそメッセージは対話のコンテキストを伝えるのに有効です。これにより、ユーザーは今自分がCake Timeスキルと対話しているのだとわかります。次はユーザーの誕生日情報を入手する必要があります。誕生日がわかったら、次の誕生日までの日数を計算します。そのためには、スキルを更新して、Alexaにユーザーの誕生日をたずねるよう指示するプログラミングロジックを追加します。

a. 開発者コンソールで、 コードエディタタブをクリックします。

code nav link screenshot

LaunchRequestHandlerを探します。ハンドラー内では、.speak()関数にspeechText変数が渡されています。次のステップでは、この文字列を更新してユーザーに誕生日をたずねます。

b. LaunchRequestHandlerhandle()関数で、 const speechTextで始まる行を探します。この行を以下の内容に置き換えます:

const speakOutput = 'こんにちは、 ケーク タイムへようこそ。お誕生日を教えてください。';

このままでは、Alexaが応答したらスキルは終了してしまいます。今回は、Alexaにユーザーから応答があるまで待機させる必要があります。これをするには、前回コメントアウトした .reprompt()関数を使います。

code snippet

c. LaunchRequestHandlerhandle()関数で、.reprompt() 関数の前のスラッシュを2つ(//)削除します。

reprompt code snippet

.reprompt().関数では次のことを行います:

  • スキルに、終了するのではなく、ユーザーからの回答を待機するよう指示します。
  • ユーザーが答えなかった場合に、もう一度たずねる方法を指定します。

初回の内容とは異なるテキストを再プロンプトに指定することをお勧めします。

ユーザーが答えなかった理由は色々と考えられます。スキルは最初の質問をもう一度する必要がありますが、自然な形で行うことが重要です。再プロンプトではより詳しいコンテキストを追加し、ユーザーから答えを引き出しやすくします。repromptTextという新しい変数を作成して、再プロンプトのテキストを指定します。

d. LaunchRequestHandlerhandle()関数で、const speechTextで始まる行を探します。行末をクリックしてEnterを押し、すぐ下に新しい行を追加します。

e. 次のコードをコピーして新しい行に貼り付けます。const repromptText = '私は二千十四年十一月六日に生まれました。あなたの誕生日はいつですか?';

code snippet

この再プロンプトでは、Alexaがまず例として、自分の誕生日を必要な形式で伝えています。これにより、ユーザーは、Alexaがどのような答えを期待しているかを知ることができます。これもお勧めの方法の1つです。

再プロンプトでは数字がアラビア数字ではなく文字で書かれていることに注目してください。音声合成マークアップ言語(SSML)を使って、Alexaに「2014年」と読ませることもできます。

では、.reprompt()関数にrepromptTextを渡すよう、コードを変更しましょう。

f. LaunchRequestHandlerhandle()関数で、.reprompt(speechText).reprompt(repromptText)に置き換えます。

LaunchRequestHandlerhandle()関数は、以下のようになるはずです。:

code snippet

誕生日をたずねる際に複雑化する要因として、ユーザーの答え方が1つではないことが挙げられます。たとえば、月と日しか答えないユーザーもいれば、「来週の火曜日」と言うユーザーもいるかもしれせん。

このコースで、ユーザーが答える可能性のある方法をすべて処理することはできません。しかし、このコースの修了時にはさまざまな方法に対応できるようになっていてください。ここでは、Alexaが年、月、日を確実にユーザーから収集できる方法を考えていきましょう。

次に進む前に、先ほど更新したコードを保存してデプロイします。

g. 保存をクリックします。

save button

h. デプロイをクリックします。

deploy button

編集内容が失われないよう、定期的に変更を保存し、デプロイする習慣をつけましょう。編集したらすぐ保存です。

これで、Cake Timeスキルは誕生日をたずねて答えを待つことができるようになりました。ただし、まだユーザーに応答することはできません。スキルをテストする前に、スキルのフロントエンドを更新する必要があります。具体的には、スキルの質問に対するユーザーの応答を解釈するインテントを作成します。

Step 2: インテントとスロットを使って情報を取得する

では、スキルのフロントエンドに少し変更を加えてみましょう。具体的には、ユーザーがAlexaの質問にどう答えるかを解釈するインテントを作成します。

インテントに名前を付ける際には、何をするインテントなのかを考えます。この場合、インテントはユーザーの誕生日情報を入手することなので、CaptureBirthdayIntentと名付けます。このように、名前を付ける場合は単語間にスペースは入れません。また、各単語の最初の1文字は大文字にします。

a. ビルドタブをクリックします。

 

b. インテントの右にある追加をクリックします。インテントを追加ウィンドウが開きます。

add intent screenshot

c.  カスタムインテントを作成を選択し、インテント名として次のテキストを入力します。 CaptureBirthdayIntent

add intent screenshot

d. Click Create custom intent. The intent is created.

既に説明したとおり、インテントとはユーザーのリクエストを完了するためのアクションのことです。発話とは、インテントを呼び出すためのフレーズです。誕生日をたずねる質問に答えて、ユーザーは「私は千九百八十三年十一月七日に生まれました。」と言うかもしれません。 ユーザーがするであろう言い方で、この発話を正確に入力してCaptureBirthdayIntentに追加します。

e. サンプル発話フィールドに、次の発話を入力してEnterキーを押すか、+アイコンをクリックします。 私は千九百八十三年十一月七日に生まれました

ここでは、句点は付けないことに注意してください。

add intent screenshot

完了すると、Cake Timeスキルは誕生日を取得できるようになります。

この発話では、年、月、日という3つの重要な情報を収集します。これらの情報をスロットと呼びます。Alexaにどの単語がスロットで、どのタイプのスロットなのかを知らせる必要があります。

では、年のスロットから始めましょう。発話の月(十一月)を表す単語を波括弧({ })で囲んだmonthで置き換えます。monthというスロットが作成されました。発話は次のようになります。 私は千九百八十三年{month}月七日に生まれました

スロットの作成には2つの方法があります。1つ目はサンプル発話でスロットが入るべき単語を選択し、スロット名を波括弧({ })で囲んだもの({month}など)で置き換える方法です。

2つ目は、サンプル発話でスロットが入るべき単語を選択すると、表示される既存のスロットを選択ダイアログボックスを使う方法です。ダイアログボックスで新しいスロットを作成の下のフィールドをクリックし、スロット名を波括弧なしで入力(monthなど)してから追加をクリックします。

intents screenshot

f. どちらかの方法でmonthというスロットを作成して、発話内の十一月を置き換えます。

 

g. 変数に置き換えるほかの情報(日、年)についてもこのプロセスを繰り返します。

発話は次のようになります。 私は {year} 年 {month} 月 {day} 日に生まれました

ユーザーが「私は...に生まれました」を省略した場合に備えて、 スロットと年、月、日だけで構成される発話を2つ目に加えておきましょう。

h. サンプル発話フィールドに、{year } 年 {month} 月 {day} 日と入力してEnterキーを押すか、+アイコンをクリックします。スロットを表す記号 { } の外側には半角スペースを挿入するようにしてください。

add intent screenshot

サンプル発話を入力する際、発話を追加するのにEnterキーを2回押さなければならないことがあります。+アイコンをクリックしても追加できます。

次に、ほかにもユーザーが答える可能性のあるスロットの組み合わせを考えておく必要があります。

i. 下の例をすべてサンプル発話として入力します。すべて入力すると、サンプル発話は全部で6つになるはずです。

{month} 月 {day} 日
{year} 年 {month} 月 {day} 日
{year} 年 {month} 月
私は {month} 月 {day} 日に生まれました
私は {year} 年 {month} 月 {day} 日に生まれました
私は {year} 年 {month} 月に生まれました

これで、収集すべきスロットをすべてAlexaに伝えられました(また、ユーザーが言うかもしれない別のパターンもいくつか追加しました)。今度は、各スロットにスロットタイプを割り当てて、それらのスロットを正確に定義する必要があります。

インテントスロットまでページを下にスクロールします。作成したスロットがここに表示されています。

intent slots screenshot

スロットタイプドロップダウンメニューから、各スロットにスロットタイプを割り当てます。

スロットタイプには、カスタムとビルトインの2つのタイプがあります。できるだけ、ビルトインスロットを使ってください。ビルトインスロットの定義はAlexaが管理しています。これらのスロットの名前は、AMAZONで始まり、定義する内容が続きます(AMAZON.NUMBERなど)。

該当するビルトインスロットがない場合は、カスタムスロットを作って値を定義します。このコースでは、ビルトインスロットのみを使います。

intent slots screenshot

j. monthスロットの右側にあるスロットタイプドロップダウンメニューから、AMAZON.NUMBERを選択します。

k. dayスロットにも、AMAZON.NUMBERスロットタイプを選択します。

l. yearスロットには、AMAZON.FOURDIGITNUMBERスロットタイプを選択します。

month day year slots

これで、ユーザーの誕生日を収集するインテントを作成できました。

では、ユーザーがこれら3つのスロット値を一部しか答えなかった場合はどうでしょうか? たとえば、「七月」と答えた場合です。 続けて、こうした問題を解決する方法を見ていきましょう。

m. ページ上部にあるモデルを保存をクリックします。

save model button

Step 3: ダイアログ管理を使う

必須のスロットもあれば、任意のスロットもあります。つまり、ユーザーから必ず入手しなければならない値は、ダイアログ管理を使って必須スロットとして指定できます。必須スロットにすると、Alexaはその値を入手しようとします。では、各スロットを必須にしてみましょう。

a. インテントスロットセクションで、monthスロットの右側にあるダイアログを編集をクリックします。

b. スロット入力の下で、スロットを必須に切り替えます。

slot filling screenshot

Alexaの音声プロンプトフィールドが表示されます。ここには、ユーザーがmonthスロットの値を提供しなかった場合にAlexaが言うテキストを入力します。

c. このフィールドに、何月生まれですか?と入力し、Enterキーを押すか、+アイコンをクリックします。

slot filling screenshot

d. yearスロットとdayスロットも同様に設定します。

注: 左側のパネルでCaptureBirthdayIntentをクリックし、サンプル発話を入力した画面に戻ります。

スロットを必須にしたので、ユーザーがもし「千九百八十二年七月」と答えた場合、Alexaはmonthとyearのスロットの値は収集できたけれども、dayスロットは収集できていないと認識できるようになりました。

収集できていない各スロットについて、Alexaはプロンプトを出します。この例では、「何日生まれですか?」とたずねます。

ダイアログ管理のメリットは、ユーザーが提供しなかった情報があったり、想定した順序で提供しなかったりした場合でも、スキルの停止や混乱を避けられるという点です。スキルがうまく機能するように、Alexaが責任を持って必須の情報を収集してくれます。

これで、誕生日の質問に対するユーザーの答えを待つインテントを作成できました。ユーザーが答えると、Alexaはユーザーの生まれた年、月、日を収集します。この情報は、JSONリクエストにあるスキルのバックエンドに送られます。

次に進む前に、左側のパネルにあるHelloWorldIntentインテントを見てください。これは、スターターテンプレートの内容が残っているだけなので、ここでは必要ありません。

e. 右側にあるゴミ箱アイコンをクリックして、HelloWorldIntentインテントを削除します。プロンプトが出たら、インテントを削除をクリックします。

削除するのはHelloWorldIntentのみです。CaptureBirthdayIntentは削除しないでください。

helloworldintent screenshot

ほかのインテント(AMAZON.HelpIntentなど)が自動でスキルに追加されているのに気付きましたか?これらはすべてのスキルに必須のインテントです。キャンセルや終了、ヘルプなどに使います。これらのインテントは削除しないでください。

f. ページ上部にあるモデルを保存をクリックします。

save model button

g. モデルをビルドをクリックします。

build model button

モデルをビルドをクリックすると、スキルはトレーニングデータのビルドを開始します。このデータは、ユーザーの発話をスキルのインテントにマッピングする方法をAlexaに知らせるためのものです。モデルのビルドには1分ほどかかる場合があります。

ここまでで、スキルは誕生日をたずね、答えを収集することができるようになりました。次は、応答できるようにしましょう。

Step 4: 新しいハンドラーを定義する

Cake Timeスキルに応答させるには、バックエンドのコードを更新する必要があります。

a. コードエディタタブをクリックします。

code nav link screenshot

LaunchRequestHandlerを変更したのを覚えていますか? 今回は、新しいハンドラーを作成します。このハンドラーは、ユーザーが誕生日情報を提供したことを認識し、誕生日を復唱するためのものです。

コードに、HelloWorldIntentHandlerがあるのに気付きましたか?HelloWorldIntentインテントを削除したのに、 なぜ残っているのでしょうか?インテントをフロントエンドから削除しても、バックエンドのハンドラーは削除されないためです。新しいハンドラーをより簡単に作成できるよう、これを新しいCaptureBirthdayIntentHandlerハンドラーとして再利用しましょう。

b. const HelloWorldIntentHandlerで始まる行を探します。その行にあるHelloWorldIntentHandlerという名前をCaptureBirthdayIntentHandlerに変更します。

c. CaptureBirthdayIntentHandlerの&& handlerInputで始まる行で、'HelloWorldIntent''CaptureBirthdayIntent'に変更します。

これにより、CaptureBirthdayIntentリクエストを受け取るとcanHandle()関数が呼び出されるようになります。ハンドラーのコードは次のようになります:

intent handler code snippet

次に、ハンドラーのロジックを更新して、Alexaが誕生日を聞き取れたことをユーザーに確認するようにします。この場合、Alexaに次のように誕生日を復唱させます。 「ありがとうございます。誕生日は{year}年{month}月{day}日ですね。」

まず、スキルが収集したスロットを保存する3つの変数を、ハンドラーに作成します。

d. CaptureBirthdayIntentHandlerにあるhandle(handlerInput) {で始まる行を探します。その行の下に新しい行を追加します。

e. 今作成した行に次のコードをコピーして貼り付けます:

const year = handlerInput.requestEnvelope.request.intent.slots.year.value;
const month = handlerInput.requestEnvelope.request.intent.slots.month.value;
const day = handlerInput.requestEnvelope.request.intent.slots.day.value;

ハンドラーのコードは次のようになります:

handler code snippet

次に、speechTextを更新します。文字列の補間を使います。これにより、新しい変数をテキスト文字列に入れることができます。以下は、文字列補間の例です:

${month}月生まれですね。

スロットの宣言に似ていますが、違いが2つあります。1つ目は、波括弧({ })の前にあるドル記号($)です。2つ目は、文を、一重引用符や二重引用符ではなく、バッククォート(`)でカプセル化する点です。

f. CaptureBirthdayIntentHandlerのhandle()関数で、const speechTextで始まる行を探します。この行を以下のコードに置き換えます:

const speakText = `ありがとうございます。誕生日は${year}年${month}月${day}日ですね。`;

完成までもう少しです。先ほど、HelloWorldIntentHandlerという名前をCaptureBirthdayIntentHandlerに変更しました。 SDKを使うすべてのスキルには、使用できるハンドラーをSDKに知らせる場所があります。これを登録と呼びます。コードを更新して、新しいハンドラーを登録します。

g. exports.handlerで始まる行まで、コードを下にスクロールします。

この行の下に、.addRequestHandlers()関数があります。この関数の中に、スキルで使うハンドラーのリストがあります。 ここにあるHelloWorldIntentHandlerを、CaptureBirthdayIntentHandlerに変更してください。そうしないと、スキルがエラーになります。

h. .addRequestHandlers()関数にあるHelloWorldIntentHandlerCaptureBirthdayIntentHandlerに置き換えます。

置き換える際、CaptureBirthdayIntentHandlerの後にカンマ(,)が残っていることを確認してください。

ハンドラーのコードは次のようになります:

handler code snippet

i. 保存をクリックします。

save button

j. デプロイをクリックします。新しいハンドラーを作成したため、スキルのデプロイには多少時間がかかります。

deploy button

Step 5: スキルのテスト

では、テストしてみましょう。 Cake Timeスキルでは、次のことができます:

  • ユーザーに誕生日をたずねる
  • ユーザーの回答を聞き取り、必須スロット(年、月、日)が1つでも不足していれば自動でフォローアップの質問をする
  • 誕生日を復唱してユーザーに応答する

では、テストを開始しましょう。

a.  テストタブをクリックします。

テストするには、左上部のボックスにユーザーの発話を入力するか、マイクアイコンを押しながら話しかけます。

b. Cake Timeを開き、Alexaが誕生日をたずねてきたらそれに答えてスキルをテストします。

test screenshot

ユーザーの発話を入力してテストする場合、数字はアラビア数字ではなく文字で書いてください(千九百八十三年十一月七日など)。そうしないと、数字が認識されません。このコースを通して、数字が意図的に漢数字で書かれていることに気付かれたかと思います。これが、入力する際の唯一の要件です。スキルに音声で話しかける場合は、数字は自動で漢数字に変換されます。

Alexaは、「ありがとうございます。誕生日は${year}年${month}月${day}日ですね。」と答えるはずです。

テストを続けて、年だけ、年と日だけなど、いろいろな組み合わせを試してみてください。どのスロット値を省略しても、Alexaはプロンプトを出すはずです。

まとめ

ここまでで、スキルは前よりきめ細かいやり取りができるようになりました。ユーザーに誕生日をたずね、復唱できます。お疲れさまでした。

ですが、誕生日を聞くことはできても、次に起動したときまで覚えておくことはできません。Cake Timeがユーザーの誕生日を覚えられれば、ユーザーエクスペリエンスはさらに向上します。次のセクションでは、スキルに記憶させる方法を学びます。

コード

スキルが動作しない、または構文エラーが出るといった問題がある場合は、以下の完成したコードサンプルをダウンロードしてください。Alexa開発者コンソールのコードエディタタブを開き、このコードをindex.jsファイルにコピーして貼り付けます。テストする前に、必ずコードを保存してデプロイしてください。