Alexa Conversations アノテーション付きダイアログの作成方法


はじめにお断り

11月末頃に、Alexa Conversationsのアノテーションの付け方が変わってしまい、設定方法もガラッと変わってしまいました。ユーザー発話にはアノテーション不要とも書いてあります。ダイアログアクトとも言わないようです😢
その前から書いていた、この記事はまったく意味のないものとなってしまいました・・・が、せっかく書いたのでとりあえず公開しちゃいます。以下を読んでも最新版のAlexa Conversationsのダイアログは作成できないのでご注意ください。新バージョン版の作成方法をこれからまとめようと思います😢

|No|状況|ロケール|
|:--|:--|:--|
|1|一般公開|en-US|
|2|ベータ|en-AU, en-CA, en-IN, en-GB, de-DE, <font color="OrangeRed">**ja-JP**（2021/11にベータに昇格）</font>, es-ES, es-US|
|3|プライベートプレビュー|なし|

なんで同じ英語圏なのに、`ベータ`なのかと思いつつ。

# Alexa Conversationスキル開発の流れ

Alexa Conversationを用いたスキル開発の流れは以下のようなイメージとなる。今回は、アノテーションを付ける部分を中心にして記載していく。

- スキルの内容を考える（机上）
    - どのようなスキルにするか
    - 全てをAlexa Conversationで作るか、ハイブリッドスキルとして作るか
- バックエンド処理を考える（机上）
- ハッピーパスを考える（机上）
- アノテーションの部品を作成する
    - 発話セット（Utterance Sets）
    - 応答（Responses）
    - API定義（API Definitions）
    - スロットタイプ（Slot Types）
- サンプルダイアログを作成する
- サンプルダイアログにスロットを設定する
- アノテーションを設定する
    - ダイアログアクトを決める
    - アノテーション部品を配置する
- サンプルダイアログのバリエーションを増やす
- ビルドする
- テストする
- 申請する
- 公開


# アノテーションとは

Alexa Conversations では、まずサンプルダイアログ（Dialogs）を作成する。そして、各ダイアログ内でユーザーの発話セット（Utterance Sets）、Alexaの応答（Responses）やAPI（API Definitions）の情報を付加していく。このことを `アノテーション` と呼んでいるみたい。そして、このマークアップされたダイアログのことを、`アノテーション付きダイアログ` と呼んでいる。 

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/6be30239-cfcb-dc0a-c8a0-15a2c4e45d58.png)

表示されないハズだったのですが・・・ 2021/11/27 hostedスキルのテンプレートに `Alexa Conversations入門` というものを確認。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/5765dd5e-4573-cbd7-fa9b-f8d73154d1c6.png)



# アノテーションの部品を作成する

#### 発話セット（Utterance Sets）

ユーザー発話を定義するもの。これは、`ユーザー入力のダイアログアクト` （後述）に対して、サンプル発話を作成する作業となる。カスタムスキルのインテントモデルと同じイメージ。ただし、`Inform Args` と `Invoke APIs` ダイアログアクトで、発話内容が同じであったとしてもそれぞれの`発話セット（Utterance Sets）`を作成しなければならない。つまり共有できないということ。

スロットが不要な場合は、スロットタイプを設定せずに、発話だけを定義する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/d95bf3c1-3db4-4601-7af1-4392eebc17f0.png)



#### 応答（Responses）

`Responses` はAlexaの応答を作成する作業となる。`Responses` は `Alexa Prompts` にAlexaの発話を入力していけば良いが、発話自体はAPL for Audioで定義されるので慣れている人は `APL for Audio editor` で設定すると良い。。また、必要あれば `Visual Response` も設定することができる。これはAPLのことであり、こちらも 'APL editor` で設定することができる。

注意）APL for Audio に When 条件を入れている場合は、`Add prompt` は使用せずに `APL for Audio editor` を使用すること。保存するとWhen条件が全部消えてしまう。


Arguments で設定された戻り値は、APL for Audio にバインドされているようなので、応答の発話の中で使うことができる。Arguments に返却された値を判断して、発話内容を変えることができる。

以下のサンプルは、`API Success` ダイアログアクトに設定された Alexaレスポンス。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/908cb38b-5098-ad72-75c7-13981954b5c5.png)


#### API定義（API Definitions）

Alexa Conversations とバックエンド処理を紐づける作業となる。関数定義みたいなもので、API名、引数と戻り値を指定する。

|No.|名称|説明|
|:-:|:--|:--|
|1|API Definitions|関数名 Lambdaで識別する名称|
|2|Arguments|引数 スロットタイプ（値）|
|3|Return|戻り値 スロットタイプ（プロパティ）|

API Definitions は、呼び出すAPIの種類が複数ある場合、その分だけ定義する。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/f81a3cf5-6411-1be9-0a8a-59a060a389bf.png)


バックエンド側の処理は以下の様になる。これまでのカスタムスキルと同様にハンドラーで受け取る。Alexa Conversationsの `API Definitions` で設定したAPI名を識別するためには、リクエストの `apiRequest.name` で API名 `getRecommendation` が取得できるので、この値を使って判別する。

```
const buildSuccessApiResponse = (returnEntity) => {
    return { apiResponse: returnEntity };
};

const GetRecommendationAPIHandler = {
    canHandle(handlerInput) {
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'Dialog.API.Invoked'
            && handlerInput.requestEnvelope.request.apiRequest.name === 'getRecommendation';　// <-- ココ
    },
    handle(handlerInput) {
        const apiRequest = handlerInput.requestEnvelope.request.apiRequest;
        let energy      = // ※スロット値 "energy" を取得する。
        let size        = // ※スロット値 "size" を取得する。
        let temperament = // ※スロット値 "temperament" を取得する。

        const recommendationEntity = {};
        recommendationEntity.name = // ※名前を設定
        recommendationEntity.size = apiRequest.arguments.size
        recommendationEntity.energy = apiRequest.arguments.energy
        recommendationEntity.temperament = apiRequest.arguments.temperament;

        const response = buildSuccessApiResponse(recommendationEntity);
        return response;
    }
};
```

#### スロットタイプ（Slot Types）

スロットを定義する作業となる。スロットタイプだけは今までのカスタムスキルで使っていたスロットタイプのセクションで作成する。Alexa Conversations用に `Create a custom slot type with properties（プロパティをもつカスタムスロットを作成）` が追加されている。

> プロパティを持つカスタムスロットタイプは階層型で、Alexa Conversations内でコンポーネント（API定義、応答など）間のデータをやり取りするためにのみ使用されます


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/343749f3-31be-7cc8-9bbe-aca83be94711.png)



#### プロパティを持つカスタムスロットタイプを作成

API（バックエンド処理）からの戻り値として使用する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/6c5c99ee-2ae4-c7fe-d401-b82b9036c92c.png)


#### 値を持つカスタムスロットタイプを作成

これまでのカスタムスキルと同じで、ユーザー発話のスロットとして使用する。それと、API呼び出しの引数として使用する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/8cea9796-d85e-8bb4-a8e0-6bdeee769d4f.png)



# サンプルダイアログを作成する

サンプルダイアログは、Alexaとユーザーの会話を上から下へ一直線で記載する方式になっている。
新規でダイアログをつくったらとにかく会話の流れ、ハッピーパスを作成する。`ALEXA:` と `USER:` は `User says` と `Alexaa says` ボタアンをクリックすることで簡単に追加、削除でき、そして順番の入れ替えもドラッグで簡単にできる。ただし、この時点ではまだアノテーションはつけない。赤丸の:exclamation:マークは気にせず、とにかくハッピーパスだけを考える。

>  `ハッピーパス`とは、ユーザーがたどる（ユーザーにたどってほしい）一番シンプルで簡単な道筋のことです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/55cd8b89-3f7c-28dc-89bf-ae862d37a63d.png)



# サンプルダイアログにスロットをつける

サンプルダイアログのユーザー発話で、スロット値として取得したい部分をマーキングする。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/98d8987f-6a15-b043-36ab-ac6a48f02e97.png)



# ダイアログにアノテーションを設定する

`ALEXA:` と `USER:` の１行１行に対して、アノテーションを設定していく。１行毎にダイアログアクト（Dialog Act）を決めて、それに伴うアノテーションの部品を設定していく作業となる。ダイアログアクトの種類は、以下のユーザー入力と、Alexa応答を合わせた11種類が用意されている。

#### ユーザー入力のダイアログアクト
発話セットを作成するときは、次のいずれかのダイアログアクトに関連付ける。

|No | ダイアログアクト | 説明 |
|:--|:--|:--|
|1|Affirm|ユーザーは、前のターンのリクエストを肯定しています。|
|2|Deny|ユーザーは、前のターンのリクエストを否定しています。|
|3​|Inform Args|ユーザーは、前のターンでAlexaがリクエストした引数を提供しています。|
|4​|Invoke APIs|ユーザーは、APIを呼び出すダイアログパス上にいます。|


#### Alexa応答のダイアログアクト
Alexa応答を設定するときには、その応答を次のいずれかのダイアログアクトに関連付ける。

|No | ダイアログアクト | 説明 |API呼出|
|:--|:--|:--|:-:|
|1|API Failure|API呼び出しが失敗したことをユーザーに通知しています。||
|2|API Success|API呼び出しが成功したことをユーザーに通知しています。||
|3|Confirm API|必須のAPI引数（すべて）と任意のAPI引数（任意の数）をユーザーに確認しています。|✖|
|4|Confirm Args|1つ以上の引数をユーザーに確認しています。|✖|
|5|Offer Next API|会話を続けるために、別のAPIを呼び出すダイアログパスをユーザーに提供しています。|✖|
|6|Request Alt|同じAPIを呼び出すダイアログパスを提供し、指定された引数をリクエストしています。|✖|
|7|Request Args|1つ以上の引数をユーザーにリクエストしています。|✖|

ダイアログアクトが理解できたら、次はアノテーションを設定していく作業となる。以下のサンプルは、ターンが２回あるダイアログで、ダイアログ内には３つのスロットタイプが設定されている。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/61a0bce3-ceb5-0772-39fd-2332d014f7bb.png)

まず、ユーザー入力（User Input）に対してアノテーションを設定をする。一番最初なので使用するダイアログアクトは `Invoke APIs` 。そして、どの`Utterance Sets`を使うかを選択する。`Utterance Sets` 内にスロットが設定されている場合は、スロット変数をスロットに追加するために、ドロップダウンから対応するスロット変数を選択する。こんな感じで、各ダイアログに対して、ポチポチ設定していく。条件式などは、自身で入力する必要があるが、基本的にはマウスで選択するだけで簡単に設定できる。

それぞれのターンでどの様な振る舞いをさせるかを、11種類のダイアログアクトとアノテーションの部品を組み合わせて定義していく。ダイアログアクトの種類によって、設定できるパラメータも異なる。

上記のサンプルはたぶんこんなイメージだと思う。最初の `ALEXA:` のAPI定義の引数がいまいちピンとこないけど、こんな感じだろう。APIの戻り値は、Alexa応答にバインドされるのでAlexa応答の発話の中で使用することができる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/99d85000-aad5-6370-f249-57e5eeece1d4.png)


# サンプルダイアログのバリエーションを増やす

ひととおり動くハッピーパスが完成したら、１つの会話の流れに対して、あらゆる会話のやりとりをカバーするために、複数のサンプルダイアログを作成する。複数用意した方が動作が安定する。つまり、１つのサンプルダイアログだけでは意図した通りに動作しにくいということ。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/7c7e667f-71c7-8f2a-fb58-b1bab0fc061e.png)

これで、ビルドをすればAlexa Conversationsスキルの完成となる。ビルドに20分かかるのがツライところ。


# 付録：サンプルダイアログのイメージ

英語版スキルに日本語を入力してみたら、けっこう雰囲気がでた。
以下のサンプルはAPIを２回呼び出すことを想定したもので、`API Success` と `Offer Next API` をデイジーチェーン接続させ、別のAPIが呼ばれるようにしている。
２回目のAPI呼び出しは、`Affirm` を使用せず、`API Success` の条件（Condition (Optional)）で、`はい` `いいえ`を判定させる想定で、このようなアノテーションにした。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/8f9af510-cf06-10ff-717f-1f54f97df022.png)



# 付録：Alexa ConversationsのExport / Inport

Alexa Conversations で設定したダイアログなどの一式は Skill Inoprt/Export（スキルの読み込み/書き出し） で簡単にダウンロードできる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/33c4eb9e-2385-03d8-b26e-5f086e80b623.png)



#### エクスポート（Export）

ボタンひとつでエクスポートできる。
エクスポート時に、スロットタイプも含まれてはいる。

#### インポート（Inport）

ボタンひとつでインポートできますが、インポート時にスロットタイプだけがなぜかインポートはされない。
エクスポート時に、スロットタイプもエクスポートされているが、インポート時に、これだけはインポートされない。
スロットタイプはどうするかというと、`対話モデル` セクションにある `JSONエディター` を使って設定を保存してあげる必要がある。

1. Alexa Conversations のインポート（zipファイルをインポート）
2. スロットタイプ のインポート（JSONのコピペ）


# 参考
- [Alexa Conversationsとは （Amazon公式ドキュメント 日本語）](https://developer.amazon.com/ja-JP/docs/alexa/conversations/about-alexa-conversations.html)
- [音声デザインプロセスとは （Amazon公式ドキュメント 日本語）](https://developer.amazon.com/ja-JP/docs/alexa/alexa-design/which-skill.html)