Alexaのカスタムスキルを設計するときのTipsまとめ

## はじめに
本記事は:christmas_tree:[オープンストリーム Advent Calendar 2017](https://qiita.com/advent-calendar/2017/opst-advent2017):christmas_tree:の12日目の記事です。
<font color="#cccccc">もう今年が終わってしまうなんて信じたくない。。。</font>

自分の備忘録かねて、Alexaのスキル審査関連でフィードバック食らったものをいくつかピックアップしてまとめてみました。
実はもう[日本語のドキュメントが公開されている](https://developer.amazon.com/ja/docs/custom-skills/voice-interface-and-user-experience-testing-for-a-custom-skill.html)のでそっちを読めという話なのですが、なにせ備忘録ですのでご了承ください。 :bow:
※やってみた系の記事とは少し性格が違うので、単語の説明などは特に行っていません。[公式のドキュメントにある用語集](https://developer.amazon.com/ja/docs/ask-overviews/alexa-skills-kit-glossary.html)なども併せてご確認ください。

## サンプルスキルの仕様
本記事では、例として、呼び出し名（Invocation Name）を「お天気スキル」としたカスタムスキルを作ることを前提にしています。
仕様としては、「東京」「大阪」「九州」から1つの地域を選んでもらって、対象の地域の天気をアナウンスするだけのシンプル設計です。

## Tips

### `AMAZON.StopIntent` , `AMAZON.CancelIntent` は必ず設定する

- 終了
- やめた
- もういいよ
- 大丈夫
- ストップ

など、思いつく限りの停止コマンドに相当する発話を `AMAZON.StopIntent`,または `AMAZON.CancelIntent` に設定しましょう。

悪い例：

```
ユーザ：「アレクサ、お天気スキルで東京の今日の天気を教えて」
Alexa：「はい、今日の東京の天気は晴れ、ところにより曇りです。最高気温は…（略）。他にききたい地域はありますか？」
ユーザ：「もういいよ」
Alexa：「それについてどうお手伝いできるかわかりません。（※エラー応答）」
```

良い例：

```
ユーザ：「アレクサ、お天気スキルで東京の今日の天気を教えて」
Alexa：「はい、今日の東京の天気は晴れ、ところにより曇りです。最高気温は…（略）。他にききたい地域はありますか？」
ユーザ：「もういいよ」
Alexa：「終了します。」（スキル終了）
```

この設定は対応必須のようですのでご注意ください。
https://developer.amazon.com/ja/docs/custom-skills/voice-interface-and-user-experience-testing-for-a-custom-skill.html#stopping-and-canceling

ちなみに、スキル応答の最中にユーザが「アレクサ」と問いかけた場合、発話がキャンセルされて次のプロンプト状態に移行します。
なので、

```
ユーザ：「アレクサ、お天気スキルで東京の今日の天気を教えて」
Alexa：「はい、今日の東京の天気は晴れ、ところにより曇りです。最高気温は…」
ユーザ：「（上記の応答発話中に）アレクサ、もういいよ」
Alexa：「終了します。」（スキル終了）
```

こんな感じの会話も、ちゃんと動作するようテストする必要があります。


### ウェルカムプロンプトではアプリの紹介をする

ウェルカムプロンプト＝ユーザがスキルを起動しただけ（インテントを指定していない）のときの応答メッセージでは、スキルの使い方を**適度に**説明してあげましょう。
主に初めてスキルを使うユーザが通るフローかなと思います（慣れた人であればワンショットで起動しますしね）ので、

- このスキルは何をするものなのか（コアファンクションの説明）
- どのように問いかけるとうまく返事がもらえるのか（どのような問いかけをしてくれるとスキル的にうれしいのか）

などを説明してあげるとよいです。
このときサーバには `LaunchRequest` としてリクエストが飛んでくるはずなので、 `LaunchRequest` の応答内容にそのような応答を設定しておけばOKです。

悪い例：

```
ユーザ：「アレクサ、お天気スキルを起動して」
Alexa：「こんにちは。お天気スキルへようこそ。」（応答待ち）
```

良い例：

```
ユーザ：「アレクサ、お天気スキルを起動して」
Alexa：「こんにちは。お天気スキルでは、ご指定の地域の天気予報をお知らせします。東京、大阪、九州のうち、どこの天気が知りたいですか？」（応答待ち）
```

ユーザはAlexaの発話を遮って次の指示を出すこともできる（上述）ので、「毎回こんな長い口上聞いてられないんじゃないかしら」とまでの心配はしなくてもよさそう。[^1]

[^1]: これを心配して応答内容を短くしていたら、「もっと詳しく」とツッコミをもらいました。。。

### 再プロンプト文を設定する
再プロンプト（Re-prompt）とは、応答待ちの状態でユーザの応答が聞き取れなかった場合、Alexaが「どうした？聞こえんぞ？何ぞ申してみ？」と一度だけユーザに発言を促してくれる機能です。
サーバからの応答時に再プロンプトメッセージを設定するとこの機能が有効になりますが、設定しないと再プロンプトなしでスキルは終了してしまいます。Alexaに何かしら発言させるときは、基本的に設定しておいた方がよいみたいです。

例：

```
ユーザ：「アレクサ、お天気スキルを開いて」
Alexa：「こんにちは。お天気スキルでは…（以下Welcomeメッセージ）。どこの天気が知りたいですか？」（応答待ち）
ユーザ：「（応答なし）」
Alexa：「もし使い方がわからない場合は、ヘルプと言ってみてください。」（応答待ち）　// ←これが再プロンプト
ユーザ：「ヘルプ」
（以下略）
```

### スキルの終了はユーザに判断させる

会話（スキル）を終了させるかどうかは、ユーザが明示的にそう指示したときのみとした方がよいようです。

悪い例：

```
ユーザ：「アレクサ、お天気スキルで東京の天気を教えて」
Alexa：「今日の東京の天気は晴れ、ところにより曇りです。最高気温は10度です。」（スキル終了）
```

こんな感じで、スキル側が喋りたいことだけ喋って会話を終了させてしまうような会話設計はよろしくないとのこと。
ユーザは他にも天気をききたい地域があるかもしれません。なので、お天気を案内した後、もう一度質問してあげるようにしましょう。

良い例：

```
ユーザ：「アレクサ、お天気スキルで今日の天気を教えて」
Alexa：「今日の東京の天気は晴れ、ところにより曇りです。最高気温は10度です。他に天気を知りたい場所はありますか？」（応答待ち）
ユーザ：「もういいよ。」
Alexa：「わかりました。よい一日を！」（スキル終了）
```

### 会話の応答は期待値通りにいかない（はい・いいえの応答）

会話の流れで、想定していた応答以外に「はい」や「いいえ」などの応答がありえるパターンがあったりします。
例えば、下記のようなやりとりを想定してインテントを定義したとします。

```
ユーザ：「アレクサ、お天気スキルで東京の天気を教えて」
Alexa：「今日の東京の天気は…（略）。他にお天気を聞きたい地域はありますか？」（応答待ち）
ユーザ：「大阪」
Alexa：「今日の大阪の天気は…（略）」
```

この時、ユーザはこんな応答もできますよね、という話です。

```
ユーザ：「アレクサ、お天気スキルで東京の天気を教えて」
Alexa：「今日の東京の天気は…（略）。他にお天気を聞きたい地域はありますか？」（応答待ち）
ユーザ：「うん」   // ← 期待した地域ではなく、YESの意味の応答のみが返るパターン
```

この時の「うん」に対応するインテントとして使えるのが `AMAZON.YesIntent` です。
上記の通り `AMAZON.YesIntent` の応答に「地域を教えてください。（応答待ち）」等の応答を設定しておけば、スムーズに会話が進みそうです。

```
ユーザ：「アレクサ、お天気スキルで東京の天気を教えて」
Alexa：「今日の東京の天気は…（略）。他にお天気を聞きたい地域はありますか？」
ユーザ：「うん」
Alexa：「地域を東京、大阪、九州から選んでください。」（応答待ち）
ユーザ：「大阪かな」
Alexa：「今日の大阪の天気は…（略）」
```

同様に、「（他の地域は）いらない」等の応答に相当する `AMAZON.NoIntent` の設定もしておくとよさそうですね。


### サンプル会話にスロットのみの定義がある場合、インテント定義を別にわけておく
ちょっと例が難しいのですが、天気予報を返すインテントで、ユーザに「東京」「大阪」「九州」のいずれかを応答してほしいような場合、まずは `place` のようにスロットを1つ定義して、値に「東京」「大阪」「九州」の3つを設定しておき、さらに発話例として以下の会話を登録すると思います。

悪い例：

```
WeatherIntent ${place} の天気を教えて
WeatherIntent ${place} の天気
WeatherIntent ${place}
```

このとき、3つ目のスロットのみの応答例は、スロットを別の定義にしておいたほうがよいそうです。こんな感じに。

良い例：

```
WeatherIntent ${place} の天気を教えて
WeatherIntent ${place} の天気
WeatherOnlyIntent ${place}
```

サーバ側で `WeatherOnlyIntent` の応答をするときは、 `WeatherIntent` の応答そのままで問題ありません。
VUI設計というより、どちらかというとスロット認識率アップのためのTipsですね:smile:

## おわりに
以上、他にも色々ありそう（あった）ですが、ひとまずはこの辺で。
基本的なことが多いですが、これからカスタムスキルをつくるよー！という方のご参考になれば幸いです。

## 補足
**会話（VUI）設計はきちんとしましょう！**
たとえ簡単なワンショットのみのスキルであっても、検討すべきパターンが何パターンもありました。
とりあえず作ってみよう！と実装に手を付ける前に、スキルの起動から終了まで会話のフローや状態管理などをきちんと検討することが大事です。