早速演習をしてみましょう。 Alexa開発者コンソールから、前回作成した「コーヒーショップ」スキルを開いてください。
すでにコーヒーショップを開いている方も、一度「スキルの一覧」に戻って、スキルを開き直してみてください。 時間が経っていると、ログインがタイムアウトしている可能性があります。 タイムアウトした状態で変更作業をしていると、せっかくの変更を失う可能性があります。 |
「コーヒー」や「カフェオレ」などの飲み物メニューを作ります。 スキルビルダー左側の「スロットタイプ」横にある「追加」をクリックしてください。 「カスタムスロットタイプを作成」欄で 「MENU」という名前を入力し、「カスタムスロットタイプを作成」ボタンをクリックしてください。
カスタムスロットの中身を編集する画面になりますので、「コーヒー」「カフェオレ」「エスプレッソ」と順次入力してください。各々エンターキーか右側の「+」をクリックすると入力が確定し、下側にリストされます。
確定し忘れにご注意。(特に最後のもの) |
これでカスタムスロットタイプ MENU が作成されました。
次に、左側「OrderIntent」をクリックして、インテントの編集画面にします。
画面下側に「インテントスロット」という欄がありますので、1番の「新しいスロットを作成」をクリックして「menu」(小文字)とテキスト入力し、エンターキーで確定します。 その右側の「スロットタイプ」が選択できるようになりますので、先ほど作成した「MENU」をドロップダウンから選んでください。 これで OrderIntent 内で利用可能なスロット 「menu」を追加しました。
サンプル発話が3つ登録されていると思いますので、その1つの「コーヒー」のテキスト部分をマウスなどで選択してください。下図のようにポップアップ画面が出てきます。 「既存のスロットを選択」の「menu」を選択してください。
サンプル発話すべてについて同様にして、下のような状態にしてください。これで、「コーヒー」以外のメニュー (スロットタイプ MENU のもの) が注文できるようになりました。
今度は新しいサンプル発話を作って数量を指定できるようにします。
「インテントスロット」の2番目の「新しいスロットを作成」に 「amount」(小文字)と入力・確定します。 今度は スロットタイプに AMAZON.NUMBER を選びます。
Alexa Skills Kit(ASK) では、数値・日付・時間・場所をはじめ、よく使うスロットタイプをあらかじめ用意しています。 これを ビルトインスロット と呼びます。 今回使った AMAZON.NUMBER は数字を受け付けるものです。
ビルトインスロットの一覧は こちら。 順次新しいビルトインスロットも追加されていますので、ブログなど確認してください。 |
次に新しいサンプル発話を追加します。 「サンプル発話」のテキストボックスに「コーヒーを一杯お願い」と入力してエンターキーを押してください。 確定したら、コーヒーの部分を選択して、先ほどと同じく menu スロットに置き換えます。 そして今度は「一」(杯は含めない)の部分を選択します。 ポップアップが現れたら、「既存のスロットを選択」から「amount」を選択してください。
これで「〇〇を〇〇杯お願い」という発話が追加されました。
ここまでの作業で、下図のようになっているはずです。インテント名と同様に、スロット名もバックエンドサービスで利用しますので、大文字小文字や綴りなどに気を付けてください。 確認ができたら「モデルをビルド」します。
対話モデルを変更したときには「モデルのビルド」をしないと反映されませんのでご注意ください。 |
左側「JSONエディター」をクリックしてみてください。 変更した対話モデルのソースコードが表示されます。 今回 GUIでスロットを作成しましたが、こちらのほうが分かりやすい方もいるかもしれません。 慣れてきたら、こちらの JSON を直接編集することもできます。
Alexa開発者コンソールの「コードエディタ」タブを選択し、バックエンドサービスの編集画面にします。
下のコードを、HelloWorldIntentHandler と置き換えてください。
// **** HelloWorldIntentHandler から名前を変更 ****
const OrderIntentHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest'
&& handlerInput.requestEnvelope.request.intent.name === 'OrderIntent';
},
handle(handlerInput) {
// menu と amount スロットの値を取得
var menu = handlerInput.requestEnvelope.request.intent.slots.menu.value;
var amount = handlerInput.requestEnvelope.request.intent.slots.amount.value;
// amount スロットが発話されないときは 1 にする
if (amount === undefined) {
amount = 1;
}
// 応答メッセージの作成。変数を展開するため両端はバッククォートにする。
const speechText = `${menu}を${amount}杯承りました`;
return handlerInput.responseBuilder
.speak(speechText)
//.reprompt('add a reprompt if you want to keep the session open for the user to respond')
.getResponse();
}
};
OrderIntentHandler.handle() 関数は、以下のような処理をしています。
スロットの値は handlerInput.requestEnvelope.request.intent.slots の中に入ってきますので、menu と amount の値をそれぞれ取り出します。
「コーヒーをお願い」のように amount は発話されない場合があります。この場合、amount.value は undefined になるので、デフォルトで 1 になるようにしています。
オウム返しの応答メッセージを作成します。
動作には関係ないのですが、前回リクエストハンドラの名前を "HelloWorldIntentHandler" のままにしたので、ここで "OrderIntentHandler" に名前を変更しています。コード末尾にある .addRequestHandlers のハンドラ登録も、"HelloWorldIntentHandler" のかわりに "OrderIntentHandler" にしてください。
exports.handler = Alexa.SkillBuilders.custom()
.addRequestHandlers(
LaunchRequestHandler,
OrderIntentHandler, // **** HelloIntentHandler から変更 ****
HelpIntentHandler,
CancelAndStopIntentHandler,
SessionEndedRequestHandler,
IntentReflectorHandler) // make sure IntentReflectorHandler is last so it doesn't override your custom intent handlers
.addErrorHandlers(
ErrorHandler)
.lambda();
それでは Alexaシミュレータで、動作確認をしてみましょう。
Alexaシミュレータでテキストで発話を入力するとき、数字は「一」「五」「十」などの、漢数字を使ってください。 |
上例では、「カフェオレを2杯お願い」という発話に対して次のような JSON 入力が発生しています (一部を抜粋しています)。 バックエンドコードでスロット値を取り出す部分のコードと見比べてみてください。
"request": {
"type": "IntentRequest",
"intent": {
"slots": {
"menu": {
"value": "カフェオレ",
},
"amount": {
"value": "2",
}
}
}
}
インテントのところで、「人が話すことなので、いろいろなパターンの発話サンプルを用意する」というお話をしましたが、スロットの値にもゆれを持たすことができます。
Alexa開発者コンソール「ビルド」タブに戻って、左側「スロットタイプ」の下の「MENU」をクリックしてください。 スロット値が一覧になっていますが、「コーヒー」の右側「同義語を追加」のところに、「ホット」と「ブレンド」をそれぞれテキスト入力して、エンターキーを押すか右側の「+」をクリックし確定します。同様にカフェラテにも「ラテ」という同義語を追加しましょう。(確定し忘れないように注意)
追加できたら、「モデルのビルド」を行い、Alexaシミュレータでテストしてみてください。
これで同義語も受け付けるようになりました。
この時のJSON入力をみてみましょう(一部抜粋)。認識した同義語が request.intent.slots.menu.value に入っています。 このため、発話された同義語をオウム返しに応答しています。
"slots": {
"menu": {
"value": "ラテ",
"resolutions": {
"resolutionsPerAuthority": [
{
"status": {
"code": "ER_SUCCESS_MATCH"
},
"values": [
{
"value": {
"name": "カフェラテ",
}
}
]
}
]
},
},
}
では MENU に登録されていないメニューを発話するとどうなるのでしょう。 実は「抹茶をお願い」と発話すると、応答メッセージは「抹茶を1杯承りました」となります。 Alexaはどれにも当てはまらない場合、認識した文字をそのまま <slot名>.value に収めます。 ただし、JSON入力をみてみると、intent.slots.menu.resolutions.resolutionsPerAuthority[0].status.code が上の場合 "ER_SUCCESS_MATCH" なのに対して、今回は "ER_SUCCESS_NO_MATCH" となっています。 この値を参照することで、用意されていないメニューが発話されたことを検査することができます。
"slots": {
"menu": {
"value": "抹茶",
"resolutions": {
"resolutionsPerAuthority": [
{
"status": {
"code": "ER_SUCCESS_NO_MATCH"
}
}
]
}
}
}
ここまでで、インテント・サンプル発話・スロット・同義語を紹介しました。 Alexaが用意するビルトインスロットは、開発者が語彙を拡張することもできます。 これらを組み合わせて、ユーザーが自然に発話した内容を、しっかりと受け止められるよう対話モデルをデザインしてください。 公開する時には、実際にユーザーテストを繰り返して、対話モデルを確認・強化することをお勧めします。