[日本語Alexa] AMAZON.Numberで作成した必須スロットで適当な数(たくさん!とか、いっぱい!)を受け付ける〜より自然に会話できるスキル作成のために〜

2018.02.03

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

1 はじめに

前回、「〜より自然に会話できるスキル作成のために〜」とい題して、自然な発話でインテントに導入する方法を検討してみました。

今回は、数の表現を、自然な会話で受け取る方法を考えてみました。

2 バラの注文

サンプルは、バラの注文を行うスキルです。注文には、「本数」の指定が必要です。

U:アレクサ、お花屋さんでバラを下さい
A:何本になさいますか?
U:5本、下さい
A:ありがとうございます。バラを5本、お届けします。

OrderIntentには、本数を表現するnumberというスロットがあり、これが必須となっています。

numberのタイプは、AMAZON.Numberとなっており、数値を受け取れるようになっています。

ダイアログモデルのコードは、非常にシンプルです。

const handlers = {
  'LaunchRequest': function () {
    this.emit(':ask','お花屋さんスキルでは、バラの注文ができます。バラを5本下さい。と言ってみて下さい。');
  },
  'OrderIntent': function () {
    if (this.event.request.dialogState !== 'COMPLETED'){
        this.emit(':delegate');
    } else {
      let intent = this.event.request.intent;
      let number = intent.slots.number.value;
      this.emit(':tell', 'ありがとうございます。バラを' + number + '本、お届けします。');
    }
  },
  // ・・・省略・・・
};

しかし、ユーザーは、正確に本数を言わない場合があります。 「たくさん下さい!」とか、「いっぱい下さい!」みたいな・・・・

numberスロットのタイプは、AMAZON.Numberなので、当然これを受け取ることができず、スキルは、次のようにポンコツな答えを返してしまう事になります。

U:アレクサ、お花屋さんでバラを下さい
A:何本になさいますか?
U:沢山下さい
A:ありがとうございます。バラを 本、お届けします。

3 スロット値のバリデート

elicitSlotを使用すると、スロット値のバリデートが可能です。require指定で入ってきたnumberスロットが想定外の値の場合、これを聞き直すようにコーディングすると、以下のようになります。

let intent = this.event.request.intent;
let number = intent.slots.number.value;

let num = Number(number);
if(1 <= num && num <= 100) {
    this.emit(':tell', 'ありがとうございます。バラを' + number + '本、お届けします。');
} else {
    let slotToElicit = 'number';
    let speechOutput = 'すいません、ちょっと聞き取れませんでした。もう一度、本数を教えてください';
    this.emit(':elicitSlot', slotToElicit, speechOutput, speechOutput);
}

これにより、お花屋さんスキルは、適当な数値(ここでは、1本以上100本以下としました)を聞き取れなかった場合、「すいません、ちょっと聞き取れませんでした・・・」のように、もう一度、本数を言ってもらうようになります。

U:アレクサ、お花屋さんでバラを下さい
A:何本になさいますか?
U:沢山下さい
A:すいません、ちょっと聞き取れませんでした。もう一度、本数を教えてください
U:100本下さい
A:ありがとうございます。バラを100本、お届けします。

しかし、やっぱり本数を答える必要があります。「たくさん」とか「いっぱい」のようなユーザーの自然な対話には対応出来ていません。

4 自然な表現をカスタムスロットで受け取る

まずは、「たくさん」とか「いっぱい」を表現する言葉を列挙し、カスタムスロットを作成します。

次に、このカスタムスロットをタイプとしたスロット「ここではmany」をnumberUtterancesにひっそり入れておきます。

こうすることで、本数を問い合わせた際に、多くの数を表現する言葉が含まれていると、manyに値が入ることになります。

numberに値が入っているがその内容が無効で、かつ、manyに値が入っていれば、ユーザーが数字ではなく「たくさん」を表現した言葉を発話したことになります。

そこで、この条件をトリガーに、this.emit(':delegate') で、numberスロットを100に書き換えることにしました。

ただし、this.emit(':delegate') は、dialogStateCOMPLETEDになってからは使用できませんので、コードは、説明上、ちょっと雑ですが以下のようになります。

'OrderIntent': function () {

    let intent = this.event.request.intent;

    if (intent.slots.number.value) { // 値が入っている場合
      let number = intent.slots.number.value;
      let num = Number(number);
      if(!(1 <= num && num <= 100)) {
        if (intent.slots.many != undefined && intent.slots.many.value != undefined) {
          // 「たくさん」を表現するカスタムスロットにに値が入っている場合
          let updatedIntent = this.event.request.intent;
          updatedIntent.slots.number.value = '100';
          this.emit(':delegate', updatedIntent);
          return;
        }
      }
    }

    if (this.event.request.dialogState !== 'COMPLETED'){
        this.emit(':delegate');
    } else {
      let intent = this.event.request.intent;
      let number = intent.slots.number.value;

      let num = Number(number);
      if(1 <= num && num <= 100) {
        this.emit(':tell', 'ありがとうございます。バラを' + number + '本、お届けします。');
      } else {
        let slotToElicit = 'number';
        let speechOutput = 'すいません、ちょっと聞き取れませんでした。もう一度、本数を教えてください';
        this.emit(':elicitSlot', slotToElicit, speechOutput, speechOutput);
      }
    }
},

動作しているようすです。「たくさん」みたいな表現も本数指定も受け付けることが出来ました。

5 最後に

今回は、数値指定を自然に入力できる方法について検討してみました。ドキュメントに明確な記述があるわけでは無いので、あくまで、やって見た上での結果でしかありません。 でも、自然な発話に対応できるインテントを定義する方法としては、一考の余地はあるのでは?と考えています。