スロットを使って任意の入力を受け付けよう

2018年7月24日(火)
畠中 幸司(はたなか・こうじ)
連載2回目は「スロット」と呼ばれる機能を用いて、Alexaをより多くの入力に対応できるようにする。

こんにちは。

スキルの開発の手順にはもう慣れましたか?

前回は開発の流れと、基本的なカスタムスキルの作り方について学びました。ユーザーがスキルとどのように対話をするかを決める「対話モデル」の設計方法についても学びましたね。

前回行った対話モデルの設計では、Alexaスキルが備える機能を表す「インテント」と、インテントをどのような言葉で呼び出すかを決める「サンプル発話」を扱いました。今回はさらに「スロット」と呼ばれる機能を使って、既定の発話サンプルにない情報を受け取る方法について学んでいきましょう。

連載2回目は、以下のテーマについて学びます。

  • スロットを加えた設計
  • スロットタイプ(組み込みスロットタイプ)を割り当てる
  • カスタムスロットタイプを定義する
  • Alexaとの複数回に渡るやりとり(ダイアログ)

それではさっそく始めましょう。

スロットを加えた設計

スロットとは簡単に言うと、「プログラミング言語における関数の引数のようなもの」です。次の対話例を見てください。

ユーザー:アレクサ、明日東京都の天気を教えて
Alexa:○月○日の東京都の天気は概ね晴れ……

Alexaに天気を尋ねる際に、日付や場所の情報を指定することができます。上記の例では「明日」や「東京都」の部分ですが、これを受け取る引数に相当するのがスロットの役割です。

皆さんが開発するカスタムスキルではどうでしょうか。前回作成したスキルの例を見てみましょう。

ユーザー:アレクサ、来場者カウントで来場者数を教えて
Alexa:今日の来場者数は156人です

この対話では何のイベントであるか、いつの情報が欲しいかということを伝えていません。これらを追加すると、例えば次のような対話になるでしょう。

ユーザー:アレクサ、来場者カウントでAIフェスタ昨日の来場者数を教えて
Alexa: AIフェスタの○月○日の来場者数は208人でした

AIフェスタという(架空の)イベントが複数の日程で行われていて、そのうち昨日の来場者数が何人だったかを尋ねています。この問い合わせのサンプル発話を表現すると、次のようになります。

{eventName}の{eventDate}の来場者数を教えて

プログラミング言語のように、引数に当たる部分を{ }で囲んだ文字列に置き換えました。{eventName}の部分にユーザーが発した任意のイベント名、{eventDate}の部分に日付の情報が入ることを表しています。インテントのサンプル発話を表現する際にはこの記法を使います。さらにサンプルを追加しましょう。

{eventName}の来場者数を教えて

{eventName}の{eventDate}の来場者数を教えて

{eventCity}で開催の{eventName}の来場者数を教えて

{eventCity}で開催の{eventName}の{eventDate}来場者数を教えて

2行目は先ほどの例と同じですが、様々な問い合わせパターンに対応できるようにしています。省略した情報については既定の値({eventDate}であれば今日の日付など)を用いることにし、イベント名{eventName}は必須としましょう。ここまでの例で、次の3つのスロットが必要なことがわかりました。

  • eventCity
  • eventName
  • eventDate

次は、これらのスロットにスロットタイプを割り当てます。

スロットタイプ(組み込みスロットタイプ)を割り当てる

多くのプログラミング言語の変数に「」があるように、Alexaのスロットにも型が存在します。それを「スロットタイプ」と呼びます。Alexa Skills Kitには予めいくつかのスロットタイプが用意されており(組み込みのスロットタイプ)、日付や数値など、ほとんどのスキルに共通して使われそうなものはここから選択できます。以下にスロットタイプの一例を示します。

スロットタイプの例

スロットタイプ説明
AMAZON.DATE日付を表します。「今日」「明日」のように言葉で表現された場合には、内部で自動的に「2018-04-03」のような日付形式に変換されます
AMAZON.TIME時刻を表します。「午後三時」などの言葉で表現された場合には、内部で自動的に「15:00」のような時刻形式に変換されます
AMAZON.NUMBER数値を表します。
AMAZON.City都市の名前を表します。「東京都中央区」「京都市」などの都市の名前を表します。開発者が拡張することもできます。

より詳しい説明は、スロットタイプリファレンスのページをご覧ください。

スロットタイプリファレンス

組み込みのスロットタイプは、上記のように「AMAZON.」で始まる文字列で識別できるようになっています。

では、Alexa Skills Kit開発者コンソール(以降、開発者コンソール)でスロットの設定をしてみましょう。

前回作成した「来場者カウント」スキルの「VisitorCountIntent」編集画面を開きます。「インテントスロット」セクションにある「新しいスロットを作成」と書かれた入力欄に「eventCity」と入力して、その横の「+」をクリックします。

インテントスロット

インテントスロット

すると「スロットタイプを選択」と書かれた選択欄が有効になるので、「AMAZON.City」を選択します。色は自動的に割り当てられるようで、下記の画面とは異なるかもしれませんが、問題ありません。

スロットタイプの選択

スロットタイプの選択

同じようにして「eventDate」スロットを作成し、「AMAZON.DATE」を割り当てます。

スロットタイプの割り当て

スロットタイプの割り当て

さて、都市の名前でも日付でも数値でもない、{eventName}の場合はどのようにすればよいのでしょうか。イベント名としては任意の名称を受け付ける必要があります。このような場合には、組み込みのスロットタイプを使うのではなく、専用のスロットタイプタイプを新たに定義することができます。それが「カスタムスロットタイプ」です。

カスタムスロットタイプを定義する

それでは、イベント名を扱うためのカスタムスロットタイプを開発者コンソールで定義してみましょう。

まず左メニューから「スロット値(2)」と書かれた項目をクリックしてみてください。( )内の数値には現在使われているスロットタイプの数が入ります。「スロットのタイプ」ページが表示されたら「+スロットタイプ」をクリックして、新しいスロットタイプを作成します。

新しいスロットタイプの作成

新しいスロットタイプの作成

スロットタイプ」欄に、これから作成するスロットタイプの名称を入力します。使用できるのは、アルファベットとアンダースコア(_)のみです。組み込みのスロットタイプで使われているピリオド(.)はここでは使用できません。今回は「EVENT_NAME」と入力して「カスタムスロットタイプを作成」をクリックします。

カスタムスロットタイプの作成

カスタムスロットタイプの作成

スロットのタイプ」ページが表示されたら、このスロットが取り得る値、つまりここでは想定されるイベント名を入力していきます。「このスロットタイプの新しい値を入力」欄に文字列を入力し、[Enter]を押すことで値を登録できます。ここでは次の3つを登録しています。英数文字列と日本語文字列の間には、スペースを入れておきましょう。

  • AI フェスタ
  • ASK ユーザー会
  • ジャパンアレクサデイズ
スロットタイプの追加

スロットタイプの追加

スロットタイプができたので、左メニューからVisitorCountIntentの編集ページに戻り、「eventName」スロットを追加します。スロットタイプには今作成した「EVENT_NAME」を選びます。

スロットタイプの選択

スロットタイプの選択

スロットの準備が整ったたら、サンプル発話を追加しておきましょう。

元々あったサンプル発話に加え、設計の項で挙げた4つのサンプルを新たに登録しています。スロットの部分は自動的に色分け表示されます。下記の画面の例と色が異なっていても問題ありません。完了したら、「モデルを保存」「モデルをビルド」の両方を実行しておきます。

インテントの保存とビルド

インテントの保存とビルド

ここまでの設定ができたら、スロットのデータを扱えるようにスキル・サービスにも変更を加えます。前回作成したAWS Lambdaの関数の中身を、以下のコード例1のように書き換えてください。APP_IDはスキルのエンドポイントのページで取得したスキルIDです。

リスト1:コード例1:code2-1/index.js

'use strict';
const Alexa = require('alexa-sdk');

//=============================================================================
//TODO: The items below this comment need your attention.
//=============================================================================

//Replace with your app ID (OPTIONAL).  You can find this value at the top of
 your skill's page on http://developer.amazon.com.
//Make sure to enclose your value in quotes, like this: const APP_ID = 'amzn1
.ask.skill.bb4045e6-b3e8-4133-b650-72923c5980f1';
const APP_ID = 'amzn1.ask.skill.b6030ab0-d878-4bd3-bff0-790750d638d2';

const SKILL_NAME = '来場者カウント';
const HELP_MESSAGE = '来場者数を教えて、で今日の来場者数をお知らせします';
const HELP_REPROMPT = '何をお調べしますか';
const STOP_MESSAGE = '終了します';

const visitorCount = 156;

//=============================================================================
//Editing anything below this line might break your skill.
//=============================================================================

const handlers = {
    'LaunchRequest': function () {
        // このスキルを「開いて」とリクエストした場合に呼び出される
        // 今回は既定では何もしない

        this.emit(':responseReady');
    },
    'VisitorCountIntent': function () {
        // VisitorCountIntent に対応して呼び出される

        // インテントObjectを取得する
        let intent = this.event.request.intent;

        // スロットの値を取得する
        var eventCity = intent.slots.eventCity.value;
        var eventName = intent.slots.eventName.value;
        var eventDate = intent.slots.eventDate.value;

        // 結果を生成する
        const speechOutput =
            eventCity + 'の' +
            eventName + 'の' +
            eventDate + 'の来場者数は' +
            visitorCount + '人です';
        // Alexa アプリで参照できる情報 (カード) を返す
        this.response.cardRenderer(SKILL_NAME, speechOutput);
        // 音声で返答する
        this.response.speak(speechOutput);

        this.emit(':responseReady');
    },
    'AMAZON.HelpIntent': function () {
        const speechOutput = HELP_MESSAGE;
        const reprompt = HELP_REPROMPT;

        this.response.speak(speechOutput).listen(reprompt);
        this.emit(':responseReady');
    },
    'AMAZON.CancelIntent': function () {
        this.response.speak(STOP_MESSAGE);
        this.emit(':responseReady');
    },
    'AMAZON.StopIntent': function () {
        this.response.speak(STOP_MESSAGE);
        this.emit(':responseReady');
    },
};

exports.handler = function (event, context, callback) {
    const alexa = Alexa.handler(event, context, callback);
    alexa.APP_ID = APP_ID;
    alexa.registerHandlers(handlers);
    alexa.execute();
};

VisitorContentIntentのハンドラーで、インテントのObjectからスロットの値を取得しています。

リスト2:インテントObjectからスロット値を取得

        //インテントObjectを取得する
        let intent = this.event.request.intent;

        //スロットの値を取得する
        var eventCity = intent.slots.eventCity.value;
        var eventName = intent.slots.eventName.value;
        var eventDate = intent.slots.eventDate.value;

eventCity、eventName、eventDateの3つのスロット値を取得し、それをそのまま返答内容に含めています。まずはこの状態でテストしてみましょう。

スキルのテスト

スキルのテスト

期待した結果になりましたでしょうか。サンプル発話が長く、一息で間違えずに言うのはなかなか難しいかもしれません。

次に、日付を省略すると今日の情報を返すというように、より簡単な言葉でもリクエストできるようにしたいと思います。Alexa Skills Kitに用意されている「ダイアログ」という機能を使うと、これらを簡単に実現できます。

著者
畠中 幸司(はたなか・こうじ)

音楽と自然と猫を愛するソフトウェア&インフラエンジニア。日本ヒューレット・パッカード株式会社でクラウドネイティブなアプリケーションのためのインフラ提案、および構築業務に従事。2000年にウェブスタートアップでエンジニアとしてのキャリアをスタートして以来、メガソフト株式会社の3Dマイホームデザイナーシリーズの開発や、マイクロソフト日本法人にて Windows Phone、Microsoft Officeシリーズの開発など、数多くの国内およびグローバルな開発プロジェクトに携わる。建設業向けモバイルアプリSTUCCO(スタッコ)のスタートアップ起業経験、500 KOBE Pre-Acceleratorへの参加等を経て2017年より現職。

連載バックナンバー

開発ツール技術解説
第6回

Alexa:ステートフルなサービスと聞き取りやすい音声応答

2018/9/20
連載6回目は、ステートフルなサービスの構築と、Alexaの音声応答を聞き取りやすくする手法を紹介します。
開発ツール技術解説
第5回

Alexaのためのローカル開発環境を整備する

2018/9/4
連載5回目は、より高度なプログラム作成に先駆けて、Alexaのためのローカル開発環境を整備していきます
開発ツール技術解説
第4回

Alexa Skills Kit SDK for Node.jsについて知る

2018/8/21
連載4回目は、スキル・サービス側のコードに着目して、フレームワークの理解を深めていきます。

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています