各カスタムスキルは、呼び出し名 を1つ持ちます。Alexaは、ユーザーの発話の中から呼び出し名を検出すると、その後の処理を各カスタムスキルで処理するようになります。
カスタムスキルは、通常、ユーザーから質問やその他の情報を受け取って、それに回答したり、何らかのアクション(例:タクシーを呼ぶ、ピザを注文する)を実行したりします。 ユーザーは、呼び出し名を続くフレーズと組み合わせて言うことで、スキルを起動できます。
「コーヒーショップ」が呼び出し名の場合、例えば次のようにスキルを起動するかもしれません。
「アレクサ、コーヒーショップ で カフェオレを注文して 」
「 アレクサ、コーヒーショップ を開いて エスプレッソお願い 」
上記の例では、「カフェオレを注文して」「エスプレッソお願い」はスキルで定義されたサンプル発話、太字は Alexa サービスが処理した部分を示しています。
リクエストの内容が決まっていない場合や、思い出せない場合は、次のように特定の質問やリクエストをせずに、スキルを起動することができます。
「 アレクサ、コーヒーショップ を開いて 」
「 アレクサ、コーヒーショップ を開始して 」
この場合、一般的なスキルの応答は、スキルの使い方を簡単に説明するウェルカムメッセージとなります。その後、例えば次のような対話になるかもしれません。
ユーザー:「 アレクサ、コーヒーショップを開いて 」 コーヒーショップ:「 ようこそ。何を注文しますか 」 ユーザー:「 カフェオレちょうだい 」 コーヒーショップ:「 カフェオレですね。ご注文ありがとうございます 」 (対話終了)
このような一連の対話を セッション と呼びます。セッション中は、アレクサ対応デバイスのリングが青く光って、ユーザーの発話待ちになります。 ひとたびセッションが終了すると、ふたたび呼び出し名を使ったスキルの呼び出しになります。 詳しくは、 テクニカルドキュメント を参照してください。
Alexaの音声ユーザーインターフェースでは、基本的にはユーザーがまず発話し、それにスキルが応答するという手順になります。 GUI では、まずメニューなどを表示して、その中からユーザーが選択しますが、これとは逆の発想になります。
このユーザーの発話による指示を インテント と呼びます。 多くの場合、カスタムスキルはインテントを起点としてアクションを起こします。 また、Alexaの音声ユーザーインターフェースは、人のように自然な会話を目指していますので、同じ目的を指示するにもいろいろな発話パターンを受け入れることが期待されます。 例えばコーヒーを注文するにも、以下のようなバリエーションが含まれるのではないでしょうか。
インテント名: OrderIntent サンプル発話: 「コーヒーをちょうだい」 「コーヒーをお願い」 「コーヒー」 「コーヒーを一杯ください」
このような、いろいろな発話のバリエーションを サンプル発話 とよびます。 ひとつのインテントは、いろいろなバリエーションのサンプル発話で構成されます。 Alexaサービスはユーザーの発話に対し、定義されたサンプル発話を参照してインテントを特定します。 上例のサンプル発話を "OrderIntent" という名前のインテントで定義した場合、ユーザーが「コーヒーをお願い」と発話すると、ユーザーの発話自体ではなく "OrderIntent" というインテント名で処理を求めます。 もしかしたら、次のようなサンプル発話は、別のインテントとして定義されるかもしれません。
インテント名: RecommendIntent サンプル発話: 「お勧めを教えて」 「お勧めはなに」 「なにかお勧めある」
このように開発者が定義するインテントを カスタムインテント と言います。 一方で Alexa は、「キャンセル」「ストップ」「はい・いいえ」のような、共通で使われる 標準ビルトインインテント もあらかじめ用意しています。 公開スキルの場合、いくつかのビルトインインテントに対応している必要があります。
それでは、上例の OrderIntent インテントを定義して、コーヒーを注文する(仮想です)コーヒーショップスキルを作ってみましょう。
この演習は、「初めてのスキル開発」を理解していることを前提にしています。 |
Alexa開発者コンソールから「スキルの作成」ボタンをクリックします。 なにかスキルを開いているときには、「スキルの一覧」をクリックすると、「スキルの作成」ボタンが表示されます。
スキル名に「コーヒーショップ」と入れてください。「スキルのバックエンドをホストする方法」を「Alexaがホスト」に切り替えたら、「スキルの作成」を行います。
デフォルトでスキル名と同じ「コーヒーショップ」が呼び出し名になっていますが、念のため確認します。
まずサンプルとして作られている「HelloWorldIntent」を削除しましょう。 「HelloWorldIntent」右側にあるゴミ箱ボタンをクリックします。
次に、インテントの右側にある「追加」をクリックし、「カスタムインテントを作成」欄のテキストボックスに「OrderIntent」(綴りを間違わないように注意)と入力し、「カスタムインテントを作成」ボタンをクリックします。
サンプル発話のテキストボックスに、「コーヒーをお願い」「コーヒーをください」「コーヒー」などと入力します。それぞれエンターキーを押すと確定し、テキストボックスの下にリストされます。
エンターキーの押し忘れに注意。 |
入力が終わったら、「モデルのビルド」をクリックしてください。
次に、バックエンドサービスのコードを編集します。 上側の「コードエディタ」タブをクリックして、赤枠の部分を変更してください。 * LaunchIntentHandler に記述されている応答文 * HelloWorldIntentHandlerで、'HelloIntent' の代わりに 'OrderIntent' を処理するように変更 * HelloWorldIntentHandlerの応答文を変更します。
OrderIntent の綴りに注意。 |
先に説明したように、フレーズなしでコーヒーショップスキルが起動すると LaunchRequest リクエスト、その後「コーヒーをお願い」のようにサンプル発話で定義したものが発話されると OrderIntent リクエストが発行されます。
上段 LaunchRequestHander 関数で LaunchRequest を処理し、下段 HelloWorldIntentHander で(名前はサンプルのままで変ですが) IntentRequest リクエストでインテント名が 'OrderIntent' のものを処理します。
変更ができたら、「Deploy」ボタンをクリックしてください。
それでは Alexaシミュレータで、動作確認してみましょう。 「テスト」タブに移動して、発話してみてください。思った通りに動きましたでしょうか。
「コーヒーショップでコーヒーをお願い」と発話すると、LaunchRequest は呼ばれずに、OrderIntent が処理されるのも確認してください。
セッション中でない場合、呼び出し名をつけるのを忘れないでください。「コーヒーをください」といきなり発話すると、Amazonショッピングでコーヒーを注文してしまうかもしれません。 逆にセッション中に、呼び出し名をつけると、期待した動きをしないかもしれません。迷ったら「ストップ」と言ってみましょう。 |
演習中、Alexa開発者コンソールの「ビルド」タブ(以下 スキルビルダー とします)でインテントの編集をしました。一方でプログラムコードは「コードエディタ」タブで編集しました。 スキルビルダーで行うインテントをはじめとする音声ユーザーインターフェースの定義を 対話モデル と呼びます。 演習ではインテントの編集を WEB の UI で行いましたが、実際はスキルビルダーの左側 「JSONエディター」をクリックすると表示される JSON 文が対話モデルの実態となります。
対話モデルは Alexaサービスで管理され、「モデルのビルド」によりカスタムスキルの対話モデルを Alexa に学習させています。
一方、「コードエディタ」で編集している内容は、実は Alexa の一部ではなく、外部のバックエンドサービスとなります。 開発者の利便性のために Alexa 開発者コンソールに統合されていますが、実態は AWS Lambada サービスを利用しています。 Alexa は、インテントなどの発生時、REST形式でバックエンドサービスを呼び出しますが、このときリクエストとレスポンスで JSON をインターフェースとしてやり取りをします。 このため、RESTとJSONが扱えれば、AWS Lambda でなくても、任意のサービスやプログラミングサービスを利用することができます。
ユーザーが発話してインテントが特定できたとき、Alexaは IntentRequest というタイプの HTTPリクエストをカスタムスキルのバックエンドサービスに発行します。 演習例の場合、ユーザーが「コーヒーをお願い」と発話すると、ユーザーの発話自体ではなく "OrderIntent" というインテント名で、IntentRequest がバックエンドサービスに発行されます。
また、「コーヒーショップを開いて」のようにフレーズなしで呼び出されたときには、LaunchRequest というタイプの HTTP リクエストが発行されます。フレーズがついた呼び出しや、セッション中は IntentReuqest が基本となります。詳しくは、 リクエストオブジェクトのタイプを参照してください。
「テスト」タブ(以下 Alexaシミュレータ)で、「コーヒーショップでコーヒーをお願い」と発話すると、「ご注文有難うございます」と応答しますが、その時、右側の「スキルI/O」に次のような JSON (一部を抜粋)が表示されます。 これがまさに、そのインターフェースの実際のやりとりになります。
[JSON入力]
{
~ 中略 ~
"request": {
"type": "IntentRequest",
"locale": "ja-JP",
"intent": {
"name": "OrderIntent",
"confirmationStatus": "NONE"
}
}
}
[JSON出力]
{
"body": {
"response": {
"outputSpeech": {
"type": "SSML",
"ssml": "<speak>ご注文ありがとうございます</speak>"
}
},
~ 中略 ~
}
}
Alexaシミュレータは、実デバイスがなくてもテストできるというメリットのほか、このスキルI/Oが確認できるので、デバッグの助けとなります。 |
JSONインターフェースの詳しくは、 カスタムスキルのJSONインターフェースのリファレンス を参照してください。
ここまで Alexa とバックエンドサービスは、JSONでやりとりするというお話をしました。 そのため、バックエンドサービスはどのようなプログラミング言語でもいいのですが、プログラミングを簡易化するために SDK も提供されています。 Alexa Skills Kit (ASK) では、現在のところ以下3つの言語用の SDK が公式にサポートされています。
その他の言語用のサポートライブラリーもいろいろな形で提供されています。
これまでの実習では、実は ASK SDK for Node.js (以下 SDK)を使っています。 SDKを使ったプログラミングは、大まかに以下のような構成になります。
const Alexa = require('ask-sdk-core'); // ① SDKのインポート
// ② リクエストハンドラ
const XXXXHandler = {
canHandle(handlerInput) {
return <boolean>; // ④ リクエストがこのハンドラで処理できれば true を返す
},
handle(handlerInput) {
// リクエストの処理。canHandle() が true を返すとこれが呼ばれる
const request = handlerInput.requestEnvelope.request; // ⑤ 入力 JSON の request部
return handlerInput.responseBuilder
<responseBuilderのメソッド> // ⑥ 出力 JSON の構築
.getResponse();
}
};
// 必要な分だけハンドラを用意
// ③ ハンドラの登録
exports.handler = Alexa.SkillBuilders.custom()
.addRequestHandlers(
XXXXHandler,
<ハンドラを列挙>) // ハンドラを列挙する
.addErrorHandlers(
ErrorHandler) // 特別なハンドラ
.lambda();
① SDKのインポートをします。利用する機能により、SDKはいくつかパッケージが用意されています。'ask-sdk-core' は基本機能のみで、'ask-sdk' が全部込みになります。
② 各リクエストを処理するハンドラです。2つの関数からなり、canHandle()関数で、このハンドラがリクエストを処理できるかどうか判断し、handle() 関数が実際の処理を行います。
③ この部分で、リクエストハンドラを登録します。上から順に、canHandle()関数が呼ばれて行き、true を返したハンドラの handle() 関数が呼ばれます。一度処理が行われると、残りのハンドラの canHandle() 関数は呼ばれずにスキップされます。
④ 典型的な CanHandle()関数は、リクエストタイプやインテント名などを参照して判断をします。
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest'
&& handlerInput.requestEnvelope.request.intent.name === 'OrderIntent';
}
⑥ handlerInput.responseBuilder のメソッドを呼び出すことで、出力 JSON を構築していきます。以下の例は、応答メッセージを話して終わる例と、セッションを継続する例です。詳しくは、 応答のビルド を参照してください。
[応答メッセージを話してセッションを終わる例]
return handlerInput.responseBuilder
.speak("ご利用ありがとうございました")
.getResponse();
[応答メッセージを話してセッションを終わる例]
const speechText = "何を注文しますか?";
return handlerInput.responseBuilder
.speak(speechText)
.reprompt(speechText) // 8秒間応答をまってもう一度同じ質問をします
.getResponse();
Alexa開発者コンソールのコードエディタを利用する場合でも、AWS Lambda 同様に CloudWatch が利用できます。 コードエディタの左下に「Logs: Amazon CloudWatch」というのがあるのでここをクリックしてください。 例えば、次のようなコードを追加すると、変数の値をログに出力でき、CloudWatch で確認できます。
console.log('var1=', var1);
このトレーニングでは、カスタムスキルの基本、言語モデル、バックエンドサービス、SDKについて説明しました。 コーヒーショップスキルを通じて、内容が大まかに理解できたでしょうか。