AmazonAlexaのサンプルを動かしながら、じっくりと基本を学んでみよう


# はじめに

**Amazon Alexa**ってご存知でしょうか。
残念ながら現在は日本語には対応していませんが、EchoやTapなどのデバイスに喋りかけると、あらかじめ紐付けておいた**スキル**に応じて色々な処理を動かすことができます。

* 「今日の東京の天気はどんな感じ？」
* 「明日の東京は晴れですよ！」

というステートレスでオープンな簡単な応答もできますし、Alexaとの会話を通じて難しい応答を返すことも作りこみ次第では可能です。

* 私「旅行に行きたいなぁ」
* Alexa「どこに行きたいですか？」
* 私「アジアがいいなぁ」
* Alexa「アジアのどの辺ですか？」

みたいなやり取りから旅行プランをおすすめしてくれて、チケットを予約してくれる日も近そうです。

この**「スキル」**は自分で作って公開することもできますし、公開しないでクローズドに動かすこともできます。
今回は入門編ということでAlexaのサンプルを動かしながら、Alexaで出てくる用語や、少しソースの解説をしてみます。

**私は少し遠回りしましたが１時間もあれば試すことができます。**
**サンプルをひとつ動かすだけで、「あれ。ちょっとしたことなら簡単にできるかも。」と思えてしまいますので是非お試しください。**

---

# Alexaを構成する登場人物たちを抑えよう

ドキュメントを読むにも登場人物を抑えないとはじまりません。

* **デバイス**
    * Echo / Tap / dot / KindleFireTV / Amazonアプリなど色々ありますが、Echoなどは日本では技適が通ってませんのでAmazon.comで購入しても動かしてはいけません。
    * 今のところはiPhoneアプリや公式のシミュレータであるEchosimを使いましょう。
* **Alexa App**
    * Alexaのスキルの検索、登録 / カードの表示 / 追加したスキルの設定ができます。
    * PCからでもスキルの追加・変更が可能。http://alexa.amazon.com/spa/index.html
    * アプリを入手するにはアメリカのAppleIDが必要で、ログインにはAmazon.comのアカウントが必要です。
* **Alexa Service**
    * デバイスからのデータを受け取り、Lambdaイベントを発火するサービス
* **AWS Lambda**
    * Alexa Serviceからデータを受け取り、解析して処理を行いレスポンスを返却します。
* **[Amazon Developer Console](https://developer.amazon.com/edw/home.html)**
    * カスタムスキルを作る場合はDeveloper Consoleで追加しテストなどを行います。ログインにはAmazon.comのアカウントが必要です。
    * スキルの登録には審査が必要で、7日くらいかかるらしいです。
* **[AVS(Alexa Voice Service)](https://www.slideshare.net/AmazonWebServices/mbl310-alexa-voice-service-under-the-hood)**
    * 自分で作ったデバイスに組み込むことができるSDK。今回は直接関係ありません。

---

# サンプルを動かしてみよう

* HelloWorldのようなステートレスなものではつまらないので、[SessionというAmazonが提供しているサンプル](https://github.com/amzn/alexa-skills-kit-java/tree/master/samples/src/main/java/session)を動かしてみます。
* このサンプルは自分の好きな色をAlexaに教えるとAlexaが記憶してくれて、「私の好きな色は何？」と聞くと先ほど記憶してくれた色を答えてくれるサンプルです。

## 環境を用意する

* Java8とMavenをインストールしましょう（mvnwくらいサンプルに置いといてくれたらいいのに。。）

## LambdaFunctionを作る

* Githubからサンプルをダウンロードする

```
git clone https://github.com/amzn/alexa-skills-kit-java.git
```

* パッケージングする

```
cd alexa-skills-kit-java/samples
mvn assembly:assembly -DdescriptorId=jar-with-dependencies package
```

* target/alexa-skills-kit-1.3.0-jar-with-dependencies.jarができていることを確認する

* ManagementConsole - Lambdaを開く。この時リージョンは必ず**「バージニア北部」**であることを確認する
    * https://console.aws.amazon.com/lambda/home?region=us-east-1#/functions?display=list

* LambdaFunctionをデプロイする（難しくないので割愛）
    * トリガーには「Alexa Skills Kit」を選択
    * Java8
    * Handler:session.SessionSpeechletRequestStreamHandler
    * Roleは初回の場合は「テンプレートから新しいRolwを作成」を選択し、適当な名前付けておきます。

* デプロイが成功したら後で必要となるARNをコピーしておきましょう。

![image](https://qiita-image-store.s3.amazonaws.com/0/57686/8b385026-cee9-7e37-a523-b8e43dfc6857.png)

* LambdaFunctionのテストをしたいところですが、DeveloperConsoleから行うのがおススメですのでここからは行いません。

## Developer Console

* [Alexa Skills Kit] - [Get Started]を選択
* [Add New Skill]を選択し、SkillInformationにこんな感じで登録する

![image](https://qiita-image-store.s3.amazonaws.com/0/57686/9e3558f5-8c6d-9fe4-c74a-a4cfcabfbfad.png)

* Interaction Modelに「IntentSchema」「CustomSlotType」「Sample Utterances」を登録する
    * Githubのサンプル内に入っているのでコピペしよう
* Configuration
    * Endpointsは「AWS Lambda ARN (Amazon Resource Name)」を選択
    * North Americaを選択し、先ほどコピーしたARNを入力します。
* Test
    * ここではLambdaFunctionのテストができます。
    * ためしに「alexa, open session」と入力してみましょう。
    * うまくレスポンスが返ってくればLambdaResponseにjsonが返却されるはずです。
    * 右下のListenというボタンを押すと実際にechoなどのデバイスから聞こえてくる音声が聞こえてくるはずです。
 
![image](https://qiita-image-store.s3.amazonaws.com/0/57686/985123d8-3155-03cb-ee69-aa516cd0af8b.png)

さぁここまでできればデバイスから呼べる準備が完了です。
なお、更改するには審査にパスする必要があり、DeveloperConsoleの内容を全部埋める必要があります。

## スキルを追加する

* スキルを追加するにはAlexaのポータルか、AlexaAppを利用して登録します。
* 画面左の「Skills」をクリックし、画面右上の「YourSkill」を選択すると先ほどDeveloperConsoleで作成したSkillが表示されるはずです。

![image](https://qiita-image-store.s3.amazonaws.com/0/57686/a4e20f96-c450-46b7-bbd8-cf701f3a8be8.png)

## デバイスを用意する

* 開発中は[echosim.io](http://echosim.io)を利用すると良いでしょう。
* Amazonアカウントでログインすることで使用できます。
* 本物のデバイスでは「Alexa」と呼びかけるのですが、Echosimはボタンを押すことで「Alexa」と呼んだことと同じ意味になります。

![image](https://qiita-image-store.s3.amazonaws.com/0/57686/9cc48edd-b1d4-5408-8374-de45d3a30771.png)

* ちなみに私だけかもしれませんがEchosimはMacだと快調に動くのですが、Windows10だとなぜか認識しないことがありました。また、スマホアプリでAVSで作られた無料のものがありますが、これもうまくいったりいかなかったりしました。
* 色々試した結果、良い感じだったのは**Amazonモバイルアプリ**でした。これのロケールをアメリカにすると音声検索でAlexaが利用できますよ。

## しゃべってみましょう

こんな感じのやりとりができますのでやってみましょう。

* 私「Alexa,open session」（Echo使っていない場合はAlexaは不要です）
* Alexa「Hello. Welcome to the Alexa Skills Kit sample. Please tell me your favorite color by saying, my favorite color is blue」
* 私「my color is red」
* Alexa「I now know that your favorite color is red. You can ask me your favorite color by saying, what's my favorite color?」
* 私「whats my color」
* Alexa「Your favorite color is red」

---

# Alexa用語を覚えておこう

* そんなに多くないので覚えるのはそんなに大変じゃありません。

## Invocation Name

* Developer Consoleで設定するSkillを特定する一意な名前。
* Invocation Nameをsessionという名前にしておくと、「Alexa, tell session」でSkillを呼び出すことができます。
* tell / ask / openなどのあとにInvocationNameを話すとLambdaFunction#onLaunchが呼ばれます
* 今回のサンプルでは#getWelcomeResponseが呼ばれ、「Hello. Welcome to the Alexa Skills Kit sample. Please tell me your favorite color by saying, my favorite color is blue」を喋らせています。

## Intent Schema

* 説明が難しいですが、Alexaとのやりとりの枠組みを定義します。（そのままじゃん・・・）
* やり取りの中でSlotを使う場合は合わせてそれを定義します。
* 今回の場合はMyColorIsIntentとWhatsMyColorIntentというIntentが使われ、MyColorIsIntentではLIST_OF_COLORSというカスタムSlotが利用されることを示しています。このIntentで会話される内容はUtteranceに定義します。

```
{
  "intents": [
    {
      "slots": [
        {
          "name": "Color",
          "type": "LIST_OF_COLORS"
        }
      ],
      "intent": "MyColorIsIntent"
    },
    {
      "intent": "WhatsMyColorIntent"
    }
  ]
}
```

## Utterance

* 日本語では「発声」です。IntentとAlexaが認識できる文章を紐付ける役目を持ちます。

```
MyColorIsIntent  my color is {Color}
MyColorIsIntent  my favorite color is {Color}
WhatsMyColorIntent whats my color
WhatsMyColorIntent what is my color
WhatsMyColorIntent say my color
WhatsMyColorIntent tell me my color
WhatsMyColorIntent whats my favorite color
WhatsMyColorIntent what is my favorite color
WhatsMyColorIntent say my favorite color
WhatsMyColorIntent tell me my favorite color
WhatsMyColorIntent tell me what my favorite color is
```

* 「whats my color」と発音するとIntentRequestのintentNameにWhatsMyColorIntentが入りますので、以下のコードのように分岐することでintentに対応する応答を実現することができます。
* ここでは対応するintentが無い場合は例外を発声させていますが、「再度言ってください」みたいな流れにしてもよさそうですね

```
        if ("MyColorIsIntent".equals(intentName)) {
            return setColorInSession(intent, session);
        } else if ("WhatsMyColorIntent".equals(intentName)) {
            return getColorFromSession(intent, session);
        } else {
            throw new SpeechletException("Invalid Intent");
        }
```

* {Color}というのはSlotを表します。Intent SchemaのNameに対応しており、slotの型はDeveloperConsoleで設定が必要です。

## Slot

* LIST_OF_COLORSに以下のように定義するとUtteranceで{Color}となっている部分が取り得る値を定義することができます。

```
green
blue
purple
red
orange
yellow
```

## Buildin Intent/Slots

* Amazon側でデフォルトで持っているIntentやSlotがあります。（Help,Cancel,Yes,No,Stopなど）
* 先ほどの例ではColorというのをカスタムで定義しましたが、AMAZON.ColorというBuiltInSlotを使った方がもれなく定義できそうですね。またAMAZON.CountryやAMAZON.Comicなんていう面白いSlotもありますのでここはうまく活用していきたいところです。
* 参考
    * 公式：[Implementing the Built-in Intents](https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/implementing-the-built-in-intents)
    * 日本語ブログ：[Alexa Skill KitでAmazonが用意したBuilt-In IntentとBuilt-In Slot Typeをひたすらまとめてみる](http://dev.classmethod.jp/cloud/summary-about-built-in-intent-and-built-in-slot-type/)

## SSML(Speech Synthesis Markup Language)

* 読み方とかしゃべる間隔などをマークアップで記載することができます。（TwilioのTwiMLとは違うマークアップのようです）
* 強く読んだり、アルファベットを読み上げたりS3に挙げたmp3を再生することも可能です。
* マークアップの書き方は[Speech Synthesis Markup Language (SSML) Reference](https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/speech-synthesis-markup-language-ssml-reference)をどうぞ。
* 例えば、リストを読み上げるときに一定の間隔をあける場合は以下のように書くことができます。

```
        String repromptText = "Please choose a category by saying, " +
                "books <break time=\"0.2s\" /> " +
                "fashion <break time=\"0.2s\" /> " +
                "movie <break time=\"0.2s\" /> " +
                "kitchen";
```

* どんな感じでしゃべるのかな～というのはDeveloperConsoleのVoiceSimulatorで試すことができます。結構遊べます。

![image](https://qiita-image-store.s3.amazonaws.com/0/57686/03b8225b-6f58-f35a-3e1d-8f71e225e9f1.png)

## Cards

* 「Alexa, open session」なんて話した後に、スマホのAlexaAppを開いてみます。
* そうするとやりとりした内容が表示されていると思います。これをCardと言います。

<img width="375" alt="alexaapp.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/57686/848f4e80-5df6-7d78-cfc8-55b8109c26c9.jpeg">

* Cardに表示させるにはこんな感じで書くことで表示されます。

```
        SimpleCard card = new SimpleCard();
        card.setTitle("Session");
        card.setContent("表示したいコンテンツ");
```

* Cardは3種類あって、テキストのみ表示する場合はSimple、画像などのイメージを使って表示したい場合はStandard、既存サイトのアカウントとリンクするときのみ使えるのがLinkAccountとなっています。
* さらに詳しく理解したい場合は[Including a Card in Your Skill's Response](https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/providing-home-cards-for-the-amazon-alexa-app)を読みましょう。

---

# その他

他は一旦紹介だけしておきます。

## Link Account

* OAuth2.0([RFC6749](http://openid-foundation-japan.github.io/rfc6749.ja.html)、[RFC6750](http://openid-foundation-japan.github.io/rfc6750.ja.html))を使ってアカウントをリンクすることができます。
* 簡単に流れを説明するとこんな感じです。
   * AlexaAppなどでスキルをEnableにする
   * LinkAccountをクリックすることで認証させたいサイトのログイン画面が開く
   * ユーザがアプリの画面などでID/PWを入力する。
   * 認証をクリアしscopeを承諾すると、AccessTokenが保管される。
   * ここまでがリンクの仕組みで以降のやりとりは音声と一緒にAccessTokenが流れてくるのでこれをLambdaで取得後、認可＆リソースにアクセスすることで実現します。
* 「Authorization code grant」と「Implicit grant」に対応しています。どちらの方式にするか検討が必要ですが、通常はよりセキュアな「Authorization code grant」を選択します。
* 「Authorization code grant」はcodeを取得し、client_id,secretを使ってAccessTokenを取得する仕組みとなります。client_id,secretの値はあらかじめDeveloperConsoleで登録しておく必要があります。
* また、tokenの有効期限切れの場合の為にrefresh_tokenの仕組みがOAuthには定義されていますが、こちらはAlexaのドキュメントによればOptionalとなっているので利用するか、tokenの有効期限が切れたら再認証させるかについては決めておく必要があります。
* [Linking an Alexa User with a User in Your System](https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/linking-an-alexa-user-with-a-user-in-your-system)

## Alexa BestPractice

* 公開前には要確認ですね。読んでおきましょう。
    * https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/alexa-skills-kit-voice-design-best-practices

---

# おわりに

どうでしたか？結構簡単に作れることがわかると思います。
英語でもかなり色々遊べますので触ってみてはどうでしょうか。

次回はいつになるかわかりませんが、LinkAccountについて書こうかと思います。