とりあえずこれだけで対話を動かせる！Amazon Echo ALEXA(Alexa Skill Set)カスタムスキル開発入門

#AmazonEcho ASK(Alexa Skill Set)開発の概要

こんにちは、会社が吸収され運営サービスの移管のゴタゴタも過ぎ、ちょっと職場で暇なのでAlexaの勉強をしています。
知識の整理を兼ねてカスタムスキル開発入門を書いてみました。

lamdbaのインラインエディッタのみでできますので、node.jsの実行環境やテキストエディッタも不要です。ブラウザのみでAlexaと会話するサンプルを実装します。

Amazon Echo に ALEXAのスキルを開発する方法とスキル開発の基礎をまとまって説明するものが乏しいので書き起こしてみました。
実機をお持ちでなくても実装の雰囲気を試し、開発コンソールに用意されているテストツールでAlexaとサーバーサイドスクリプト間のインタフェースの動作確認ができます。
ご興味がある方は是非ご一読ください。

Amazon Echo スキル開発を行うのに必要な要点は大きく、対話モデルの設定と実装に分かれます。
対話モデルとは、AmazonEcho(ALEXA)に開発者が実装するスキルの対話スクリプトがどういったものであるかを既定したものです。
Alexaに対してインテントと言う単位で実装するスキルがどう言う対話インタフェースがあるのかを教える物と考えれば良いと思います。
営業のお仕事をされたことがある方は、営業トークのスクリプトを使った練習をやったことがあると思います。それと同じようなを物を設計しあらかじめAlexaに教えておく雰囲気です。

対話インタフェースがインテントと言う単位で成立するのであれば、実装とはインテントのハンドラーを実装することに他なりません。
AmazonEchoのカスタムスキルを実装するというのは、Alexaがユーザーの発話した対話から。
あらかじめ指定されたトークスクリプトに沿って、変数となる語句を特定し、指定されたイベントハンドラをコールするので、開発者は自分たちのインタフェースでイベントを受け取り応答パラメタを生成してレスポンスを返すことです。レスポンスを受け取ったAlexaはその内容に従って　指定された音声を発話してユーザーにフィードバックを返します。

イメージとしては宅急便の再配達をダイヤルボタンだけでなく、声で応答できる物を設計して実装すると考えれば概ね間違いないと思います。

##対象者とゴール

この記事では、AmazonEchoの実機または開発コンソールのテストツールを使い、セッションを維持してパラメタを引き回しユーザーと対話するインタフェースを構築するところをゴールとします。
開発コンソールの基本的な設定方法と対話モデル構築方法の説明、インテントハンドラの実装方法、　セッション管理とパラメタの受け取りについての実践を解説して基本的な開発手順を確認できるように努めます。

具体的には以下のような対話をユーザーと行うカスタムスキルを作成いたします。

```text:実装対話の完成図
スキル呼び出し：Alexa ハローワールドを開いて
Alexa「こんにちは、あなたの名前を教えてください」
ユーザー「こんにちは私の名前は太郎です」
Alexa「太郎さんですね。よろしくお願いします。血液型も教えてください」
ユーザー「A型です」
Alexa「真面目かー」
```
対象者としては基本的なプログラミングスキルを有しており、javascriptとjsonのソースコードを理解できる方となります。

## AmazonEcho(Alexa)開発の要点
- 対話モデルの構築(Amazon 開発者コンソールでの設定)
 - インテントスキーマー
 - カスタムスロットタイプ
 - サンプル発話
- 実装(ここではLambda関数としてjavascriptで実装する(
 - インテントハンドラの実装
 - セッションの管理

#0.前準備 Amazon開発者コンソールとAWSに登録

事前準備としてAmazon開発者コンソールとAWSに登録しておいてください。
この二つの登録に関しては案内に沿って登録すればOKです。
AWSはLambdaしか使いませんので無料の範囲で済むと思います。
また、Echoをお持ちの方はセットアップを済ませ、いくつかストアのスキルを試してAmazon Echoの使い方に慣れておきましょう。

#1.Amazon開発者コンソールにサンプルアプリを設定する
Amazon開発者コンソールのタブにある「ALEXA」を選択してください。

![スクリーンショット_2018-01-15_10_25_40.png](https://qiita-image-store.s3.amazonaws.com/0/43688/cecadf11-9bb8-cc4f-0cc2-69701e1ffea5.png)

「Alexa Skill Kit」を選択します。
![スクリーンショット_2018-01-15_10_39_34.png](https://qiita-image-store.s3.amazonaws.com/0/43688/2bccf8f3-4fe4-7d78-e5df-2d1dabfc4816.png)

##1-1.スキル情報を登録

「新しいスキルを作成する」を押し、スキル情報の登録を行います。

![スクリーンショット 2018-01-15 10.45.28.png](https://qiita-image-store.s3.amazonaws.com/0/43688/f5d3c8a0-f3b4-2e22-ce67-7ca623d630db.png)

スキルの種類を「カスタム対話モデル」にします。
アプリ名や呼び出し名は好きに登録してください。
AmazonEchoでスキルを実行する場合の呼び出しは呼び出し名になります。
「ハローワールド」であれば「アレクサ ハローワールドを開いて」と呼びかける形になります。

設定したら「保存する」を押して保存してください。

##1-2.対話モデルの設定

作成たカスタムスキルを実機のEchoやEcho Dotでテストするには左側メニューのチェックボックスを全て緑色にする必要があります。
続けて対話モデルの設定に入ります。左側のメニューから「対話モデル」を選択してください。

![スクリーンショット 2018-01-15 10.48.52.png](https://qiita-image-store.s3.amazonaws.com/0/43688/6413df26-bb57-65b8-006d-941d07a0f69c.png)
※これが全てグリーンになると、左側メニューの下にある「Skills Veta Testing」が「アクティブ」と言う表示になり実機テスト可能になる。

###インテントスキーマの設定

今回の完成予想図ではスキルが起動してから、Alexaは２回質問を受け取ります。それぞれの対話を受け取るためのインテントを２つ定義します。
インテントは、インテントの名前と受け取るパラメタに当たるスロットについてどのスロットを受け取るのか定義します。スロットは複数定義可能です、今回のサンプルでは複数のスロットを利用しません　。複数のスロットを受け取るスキルを作る場合はslots配列に複数のslotオブジェクトを詰め込みます。

- FirstNameIntet
 - FirstNameIntentはユーザの名前を受け取るハンドルです。 イベントハンドラが受け取るパラメタに当たるスロット　としてAMAZON.FirstNameを利用します。

```text:インテントスキーマ
{
  "intents": [
    {
      "intent": "FirstNameIntent",
      "slots": [
        {
          "name": "firstName",
          "type": "AMAZON.FirstName"
        }
      ]
    },
    {
      "intent": "BloodTypeIntent",
      "slots": [
        {
          "name": "bloodType",
          "type": "BLOOD_TYPE"
        }
      ]      
    }
    ]
}
```
インテントスキーマはjsonで定義を書くことができます。intents配列にintentオブジェクトを複数設定することができます。intentsオブジェクトではintent要素でインテント名を、slotsでURLのクエリパラメタに当たる対話からキャッチアップする変数となる要素を指定できます。
FirstNameIntentでは「AMAZON.FirstName」と言うAmazonが用意している既存のスロットタイプを使います。
BloodTypeIntentでは　この後の手順で作成する、開発者がカスタムしたカスタムスロットタイプ「BLOOD_TYPE」をユーザーの対話から切り出して、lamdba側のインテントハンドラに渡せるようにします。
とりあえず今はそのままコンソールに貼り付けてください。

![スクリーンショット 2018-01-15 11.11.00.png](https://qiita-image-store.s3.amazonaws.com/0/43688/cd9cbebb-100f-9e8b-3769-35a424409327.png)


### カスタムスロットタイプの設定

インテントスキーマのすぐ下にあるカスタムスロットにオリジナル血液型スロット 「BLOOD_TYPE」カスタムスロットを設定します。

```texxt:BLOOD_TYPE
a型
b型
c型
ab型
```
![スクリーンショット 2018-01-15 11.25.41.png](https://qiita-image-store.s3.amazonaws.com/0/43688/d6716439-bb4e-f5ef-31b9-8a89b1ba9c6d.png)

入力したら追加を押してください。

### サンプル発話の設定

サンプル発話はユーザーがカスタムスキルにどのように話しかけるのかを定義するもので、これが音声ユーザーインタフェースそのものになります。
Intet名とサンプルの発話テキストの組み合わせで定義していきます。
一つのIntentに対して複数のサンプル発話を定義できますので、言い回しを増やすことができます。
またちょっとした言い回しの違いについてはある程度は自動で埋め合わせてくれるようです。

```text:対話モデル
FirstNameIntent こんにちは私の名前は {firstName} です
FirstNameIntent 私は {firstName} です
FirstNameIntent {firstName} です
BloodTypeIntent {bloodType} 型です
```

FirstNameIntentについては複数の対話モデルを定義してみました。
一番はじめに想定した会話ではこんにちはと挨拶を返すことを想定していましたが。挨拶を返さずお名前だけを発話する方もいるのではないかと言う想定です。

対話モデルの書式は以下の通りです。

```text:対話モデルの書式
[インテント名][半角スペース][対話テキスト]
```

```text:対話テキストの書式
[文字列][半角スペース][スロット名][半角スペース][文字列]
```

インテント名を指定して、半角スペースを開けて発話を記述します。
発話のどの部分がインテントハンドラが受け取るパラメタとなるのかを指定することで、　　適切な語句をALEXAが判断してslotをインテントハンドラに渡します。
文字列中に半角スペースで区切り、スロット名を記述することで、どの部分がどのスロットのパラメタになるのかを指定することができます。

今回の例ではFirstNameIntentでは、「こんにちは私の名前は太郎です。」と言う発話を想定した時に、ユーザーの名前に当たる部分は「太郎」ですから。
田中の箇所に「 firstName 」を埋め込みます。
なおこの既存のAMAZON.FirstNameスロットは苗字を発話してもきちんと受け取るようです。

対話モデルを記述したら保存するを押してください。
コンソール側で対話モデルを構築するため数分ほど処理待ちをします。

![スクリーンショット 2018-01-15 11.35.46.png](https://qiita-image-store.s3.amazonaws.com/0/43688/764410a7-3ed5-4b5b-cc4c-3684e4b9b750.png)

##1-3. グローバルフィールドの設定

対話モデルが構築されたら「次へ」を押すと、グローバルフィールドの設定画面が出ます。
開発者が作成する、カスタムスキルの実装へのエンドポイントを指定します。

###AWS Lambdaへ関数を追加する。

AWS Lambdaへ関数を追加してエンドポイントを確定させましょう。
Lambdaがどこにあるか分からない場合は、AWSコンソールの上部にサービスというメニューがあるのでクリックして一覧から探すか検索してください。

![スクリーンショット 2018-01-15 14.50.29.png](https://qiita-image-store.s3.amazonaws.com/0/43688/762c02cc-e94e-1a5b-690e-6324bbd404cb.png)
関数の作成から設計図選び「alexa」で検索します。
「alexa-skill-kit-sdk-factskill」を選びます。

![スクリーンショット 2018-01-15 14.51.56.png](https://qiita-image-store.s3.amazonaws.com/0/43688/e136864b-f751-7a03-2088-62f2ea996d03.png)
基本情報を入力します。
- 名前：helloworld_alexa(なんでも良い)
- ランタイム:Node.js 6.10
- ロール名:hellowalexa(なんでも良い。適当なのものがすでにあればそれを使ってください。特にロールの中身は必要ありません)
- ポリシーテンプレート:空で良い

<img src="https://camo.qiitausercontent.com/750468cae7be9a446d3d9313b3a640adef358f24/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f34333638382f64623466646436342d393464312d343866322d353637612d6263303339376139353462332e706e67" width=800>
左側から「Alexa skills kit」を選択してトリガーを追加します。
追加したら保存してください。

![スクリーンショット_2018-01-15_12_16_02.png](https://qiita-image-store.s3.amazonaws.com/0/43688/30ac5927-f984-ce0d-a8d3-98440cae43a6.png)

右上の「ARN-」以降がエンドポイントになりますのでコピーします。

![スクリーンショット_2018-01-15_12_16_54.png](https://qiita-image-store.s3.amazonaws.com/0/43688/8c218e4f-e255-f27c-15d5-8eb7d85737da.png)

- サービスエンドポイントのタイプ:「AWS Lambda の ARN」を選択します。
- デフォルト：ここに先ほどコピーしたarnのテキストを入れます。
- 地域的なリージョン：いいえ
- アカウントリンク：いいえ
- アクセス権限：未チェック

設定したら保存を押します。

##1-4. 公開情報の設定

続いてテストの項目は今飛ばして次へ押して公開情報の設定を行います。
公開情報の設定をしておかないと実機でテストができませんのでとりあえず適当に設定します。

![スクリーンショット 2018-01-15 12.39.41.png](https://qiita-image-store.s3.amazonaws.com/0/43688/ec84fdd3-1f5b-4792-498c-e091324f2ddb.png)
こんな感じで適当で大丈夫です。
サンプルフレーズは直接インテントを呼び出すことができる発話のサンプルを記述してスキルストアに表示することができます。
例えば音楽をかけるスキルを作ったとした場合。
 「アレクサ サンプルプレイヤーでサンプルという曲をかけて」と行った発話を受けられるとしたら、このフレーズをサンプルとして記載しておきます。

![スクリーンショット 2018-01-15 12.42.30.png](https://qiita-image-store.s3.amazonaws.com/0/43688/f0befbdc-58c1-6936-5cc6-4ecc23ef35c3.png)
下にスクロールするとアイコンに設定があります。こちらも設定が必要です。

それぞれダミーファイルを用意しましたのでお使いください。
![512x512.png](https://qiita-image-store.s3.amazonaws.com/0/43688/04345e58-fa06-a97b-754b-6b627b2862c0.png)

![108x108.png](https://qiita-image-store.s3.amazonaws.com/0/43688/6ba68841-9ef2-e191-74fe-7c008390d550.png)

設定は「UPLOAD IMAGE」と記載されている丸いアイコンをタップすると画像をアップロードできます。
設定したら保存して次へ進んでください。

##1-5.プライバシーとコンプライアンスを設定

ここは機械的に以下の画像の通り設定してください。
![スクリーンショット 2018-01-15 12.46.16.png](https://qiita-image-store.s3.amazonaws.com/0/43688/338186a6-6a3f-f6f7-2570-b5a06bffbdf7.png)

設定したら保存します。

これでSkills Beta Testingが有効になります。
![スクリーンショット 2018-01-15 12.47.58.png](https://qiita-image-store.s3.amazonaws.com/0/43688/cdc73eaf-9513-2985-7ac7-c6e97372caae.png)

##1-6.テスター登録
 テスターを登録してテストを開始します。amazonアカウントを登録しているメールアドレスにしておくのが良いとは思います。
![スクリーンショット_2018-01-15_12_50_55.png](https://qiita-image-store.s3.amazonaws.com/0/43688/7cbe290b-ddb8-9719-ed4e-bbad728e42f4.png)

テストを開始するとあなたのAlexaアプリのスキルにサンプルアプリが追加されていると思います。
<img src="https://qiita-image-store.s3.amazonaws.com/0/43688/4da15c57-8544-1939-edb7-549b24099557.png" width=300>

#2.インテントハンドラの実装

AWS Lambdaの設定画面に戻りハンドラを実装していきます。
コードをインラインではなく直接編集される場合は。ASKのサンプルを適当にgithubから落としてlamdba/custom/index.jsを書き換えてください。
lamdbaにはindex.jsとnode_modulesをzipに固めてアップロードします。

![スクリーンショット 2018-01-15 14.26.38.png](https://qiita-image-store.s3.amazonaws.com/0/43688/7c70210d-e1ef-00e9-d052-f0224de63278.png)

##2-1. インテントハンドラコーディング
ソースコードを実装していきます。
![スクリーンショット 2018-01-15 14.52.54.png](https://qiita-image-store.s3.amazonaws.com/0/43688/bad86083-0918-3e3d-7458-addb591fe715.png)
関数の　設定画面でhelloworld_alexaを選択し、インラインエディッタを表示します。

![スクリーンショット 2018-01-15 14.52.47.png](https://qiita-image-store.s3.amazonaws.com/0/43688/3f15943b-6342-8341-75c6-d7aa8d2a7a37.png)

node_modulesがバインドされていることを確認します。
index.jsを開いて、下記のソースコードで全て置き換えます。

```javascript:index.js
'use strict';
const Alexa = require("alexa-sdk");

//セッションネームです。
const states = {
  NAMEMODE: '_NAMEMODE'
};

//インテントハンドラーを登録します。今回は名前を覚えている状態のセッションが必要なので２つハンドラを登録します。
exports.handler = function(event, context, callback) {
    const alexa = Alexa.handler(event, context);
    alexa.registerHandlers(handlers,nameStateHandlers);
    alexa.execute();
};

//ハンドラの実装です。こちらが通常状態のハンドラです。
const handlers = {
    //起動時に呼ばれます。
    'LaunchRequest': function () {
        this.emit('SayHello');
    },
    //FirstNameIntentがコールされるハンドラです。
    'FirstNameIntent': function () {
        this.emit('SayName');
    },
    'SayHello': function () {
        const speechOutput = 'こんにちは、あなたの名前を教えてください。';
        //askのレスポンスをするとalexaはセッションを閉じずにユーザーの発話を待ちます。
        this.emit(':ask', speechOutput);
     },
    'SayName': function () {
        var speakOut = "";
        if(!this.event.request.intent.slots.firstName.value){
                speakOut = 'お名前が聞き取れませんでした。もう一度お願いします。';
        }
        else {
                //名前を聞けたので名前知っているよモードにstateを切り替えます。次の発話がNAMEMODEセッション用のハンドラに切り替えます
                this.handler.state = states.NAMEMODE; 
                //slotに入るパラメタは、event.request.intent.slotsにコンソールで設定したオブジェクト名で格納されていて取り出すことができます。
                //attributesに値を格納することでセッション中にパラメタを引回すことができます。応答で交わされるjsonに含まれ、継続的に送受信されます。
                this.attributes['firstName'] =  this.event.request.intent.slots.firstName.value; 
                speakOut = this.attributes['firstName'] + "さんですね。よろしくお願いします。 血液型も教えてください。";
        }
       this.emit(':ask',speakOut);
    },
    //以下は既定のハンドラです。今回は特に触れません。
    'AMAZON.HelpIntent': function () {
        const speechOutput = 'このスキルはあなたの名前を覚えて血液型を聞いてくるスキルです。 ';
        const reprompt = 'まずはお名前を教えてください。';

        this.response.speak(speechOutput).listen(reprompt);
        this.emit(':responseReady');
    },
    'AMAZON.CancelIntent': function () {
        this.response.speak('さようなら!');
        this.emit(':responseReady');
    },
    'AMAZON.StopIntent': function () {
        this.response.speak('また今度ね!');
        this.emit(':responseReady');
    }
};

//名前を覚えている状態用の　インテントハンドラです。
var nameStateHandlers = Alexa.CreateStateHandler(states.NAMEMODE, {
    "BloodTypeIntent": function () {
        var outputSpeak = "すみません上手く聞き取れませんでした。もう一度、血液型をお願いします。";
        if(!this.event.request.intent.slots.bloodType.value){
            this.emit(':ask',outputSpeak);
            return;
        }
        else {
            outputSpeak = this.attributes['firstName']+"さんの血液型は"+ this.event.request.intent.slots.bloodType.value + "ですかー。";;
        }
        
        if(this.event.request.intent.slots.bloodType.value  == "a 型"){
            outputSpeak = outputSpeak + "真面目かーーーー";
        }
        else if (this.event.request.intent.slots.bloodType.value  == "b 型"){
            outputSpeak = outputSpeak + "自己中心的って言われませんか？";
        }
        else if (this.event.request.intent.slots.bloodType.value  == "O 型"){
            outputSpeak = outputSpeak + "陽キャかよ。";
        }
        else if (this.event.request.intent.slots.bloodType.value  == "ab 型"){
            //発音が「あぶがた」言うのが嫌なら、ここでカタカナで指定するか、key/valuの文字列リソースを上で当てるようにしてください。
            outputSpeak = outputSpeak + "不思議ちゃんですか……";
        }
        //状態を戻します
        this.handler.state = ''; 
        this.attributes['STATE'] = '';
        this.response.speak(outputSpeak);
        this.emit(':responseReady');
    },
    //どのインテントにも属さない発話だとUnhandledがコールされます。
    'Unhandled': function() {
        var message = '血液型を教えてください。';
        this.emit(':ask', message, message);
    }    
});
```
説明はコメントに入れておきましたので確認してください。
intent.slotsからのパラメタの受け取り、attributesに格納してセッション中の引き回し。状態によりハンドラを切り替えて適切に対話を処理する方法が含まれています。

##2-2 テスト
Amazon開発コンソールに戻り、テストタブの画面を開いてください。
下の方にサービスシミュレータがあると思います。サービスシミュレータを使って
FistNameIntentを呼び出してテストしてみましょう。

![スクリーンショット 2018-01-15 15.21.44.png](https://qiita-image-store.s3.amazonaws.com/0/43688/c58495f8-d545-3d9f-6116-37d6fb677b4c.png)
私は○◯ですと発話サンプルで作成した発話のパターンを入力して実行するとlamdbaが生成したサービスレスポンスが返されるずです。
続けて
「B型です」と発話すると名前を覚えている状態ハンドラのBloodTypeIntentがコールされ、血液型に応じたテキストが返されると思います。

##2-3 実機で動かしてみよう

Amazon Echoなどの実機がセットアップされている状態で、Alexaアプリでテストアプリが有効となって入れば実機で試すことができます。
実機で動かしてみましょう。
呼び出し方は「アレクサ ハローワールドを開いて」　または「アレクサ ハローワールドで私の名前は◯◯です」で
あなたが実装したレスポンスを読み上げ始めるはずです

#まとめ
うまく動きましたでしょうか？
Amazon Alexaのカスタムをスキルを実装する基本を説明してみました。

- 対話モデルの構築(Amazon 開発者コンソールでの設定)
 - インテントスキーマー
 - カスタムスロットタイプ
 - サンプル発話
- インテントハンドラの実装
  - slotsのパラメタ受け取り
  - 応答の設定
- セッションの管理
  - attributesへのパラメタ渡し

だいたいこの辺りを抑えれば簡単なトークスクリプトを組んで遊んでみることができると思います。

僕自身はAelxaスキルの実装担当者ではありませんが、全体の仕様を把握するためにサンプルを組んでみました。
Alexaでできることできないことを考えるいい機会になったと思います。
ここまで読んでくださいましてありがとうございました。