Alexa スキル公開申請時の審査ポイント

# 最初に
初めて Alexa スキルを公開した際に、審査でいくつか指摘されたので、Alexa スキル公開時に気を付けるポイントとしてまとめておきます。本記事では、カスタムスキルが対象になります。 Amazon の公式ページの、[カスタムスキルの認証要件](https://developer.amazon.com/ja/docs/custom-skills/certification-requirements-for-custom-skills.html) や [Alexaスキルの機能テスト](https://developer.amazon.com/ja/docs/custom-skills/functional-testing-for-a-custom-skill.html) などに詳細に記述されているので、そこに記述されている内容を守っていれば審査も問題ないはずですが、内容が膨大で全て読むのはなかなか辛いと思いますので、私が何も読まずにトライ＆エラーで審査を通った際の指摘事項を紹介させて頂きます。Alexa Skill チームの方には出来が悪くお手を煩わせてしまったかもしれません。

# 審査ポイント

今回指摘された事項は、大きく分けて下記3つでした。

・セッション管理
・ビルトインインテントの扱い
・スキルのホームカード


## セッション管理

セッション管理の詳細に関しては、公式ドキュメントの [こちら](
https://developer.amazon.com/ja/docs/custom-skills/voice-interface-and-user-experience-testing-for-a-custom-skill.html#session-management) を参照して下さい。以下2つが、期待される挙動として記述されています。

* *スキルの応答がユーザーへの質問である場合、それらの応答の後に、セッションがオープンのままで、端末がユーザーの応答を待つこと。*
* *スキルの応答によってユーザーからのリクエストが完結する場合、それらの応答の後に、セッションをオープンせず、対話が終了すること。*

**セッション管理に関して、 これらの期待値通りになっているか、すべてのインテントにおいてしっかりチェックされている** 印象を受けました。

1番目に関しては、ユーザーの発話を求める応答を返したにも関わらず、ユーザーの発話を待たずにセッションを閉じてしまうと、「質問しといて、勝手に切っちゃうの？」的な感じになって、だいぶ感じ悪くユーザーエクスペリエンス的に悪いのでちゃんとチェックされているのだと思います。2番目に関しても、会話が終わっているにもかかわらず、セッションをオープンにしたままにしていると、他のスキル含め、次の発話を受け付けられないので、こちらもユーザーエクスペリエンスを損なうのでちゃんとチェックされているのだと思います。

セッション管理に関して、上記の期待値通りに実装しておくことをおすすめします。

## ビルトインインテントの扱い
ビルトインインテントの詳細に関しては、公式ドキュメントの [利用できる標準ビルトインインテント](https://developer.amazon.com/ja/docs/custom-skills/standard-built-in-intents.html#available-standard-built-in-intents) や [標準インテントを実装する](https://developer.amazon.com/ja/docs/custom-skills/implement-the-built-in-intents.html) を参照してください。簡単に説明すると、Amazon 側で用意しているインテントで、例を挙げると「AMAZON.HelpIntent」「AMAZON.StopIntent」「AMAZON.CancelIntent」などがあり、スキルを使用中に、それぞれ、「ヘルプ」「ストップ」「キャンセル」などと話しかけて、スキルの使い方を聞いたり、あるいは、スキルの使用、会話を途中で止めたりするときに使用することができます。この「ヘルプ」といったサンプル発話自体は、各スキルの開発者がコンソールで定義します。ビルトインインテントの実装は推奨はされているものの、必須ではありません。ただ、コンソール上でサンプル発話を定義した場合は、インテントが送信されてきますので、各インテントに応じた実装をしておく必要があります。私の場合、コンソールでサンプル発話を定義していたにもかかわらず、実装するのを忘れていたため指摘されてしまいました。当たり前ですが、**ビルトインインテント含む全てのインテントで、インテントを定義したにも関わらず、実装していないと当然怒られます**。実装していないと、

「スキルからの応答に問題があります」

という応答になってしまい、ユーザーエクスペリエンスを損なう形になってしまいますので。

### 「AMAZON.HelpIntent」

「AMAZON.HelpIntent」に関しては、[こちら](https://developer.amazon.com/ja/docs/custom-skills/voice-interface-and-user-experience-testing-for-a-custom-skill.html#providing-help) にどのような応答を返せばよいかが記述されています。ここに関しては、 **最初に指摘され1回修正したのち再度指摘されたので、しっかりチェックされている** と思います。下記を確実に順守して実装しておくことをおすすめします。

* *ユーザーがスキルのコア機能を操作するための指示が提供されていること。*
* *インテントなしでスキルを起動したときにユーザーが聞いたプロンプトよりも、詳細な情報が提供されていること。たとえば、ヘルププロンプトで、スキルが実行できることをより詳しく説明したり、ユーザーにスキルの終了方法を知らせたりしていること。*
* *ユーザーがスキルを機能させるために何を言うべきかではなく、このスキルで何ができるかについて、ユーザーに知らせるものであること。*
* *プロンプトが、ユーザーがリクエストを完了できるための質問で終わっていること。*
* *ユーザーからの応答があるまでセッションをオープンのままにしていること。*

#### （おまけ）AWS Lambda の Alexa 用 Blueprint をもとに作成する場合の注意点

スキルのサービスエンドポイントを AWS Lambda にする場合、Alexa 用の Blueprint が用意されていて、あらかじめコードの大枠が記述されています。私は、python 版の「alexa-skills-kit-color-expert-python」を使用したのですが、

![1.png](https://qiita-image-store.s3.amazonaws.com/0/65831/286e276b-864d-817a-0642-c06aee06827e.png)

各インテントの処理を分岐させる部分で、"AMAZON.HelpIntent" が "LaunchRequest" と同じ処理 get_welcome_response() に飛ばされています。

```
def on_launch(launch_request, session):
    """ Called when the user launches the skill without specifying what they
    want
    """

    print("on_launch requestId=" + launch_request['requestId'] +
          ", sessionId=" + session['sessionId'])
    # Dispatch to your skill's launch
    return get_welcome_response()


------------------------------------ 省略 ------------------------------------


def on_intent(intent_request, session):
    """ Called when the user specifies an intent for this skill """

    print("on_intent requestId=" + intent_request['requestId'] +
          ", sessionId=" + session['sessionId'])

    intent = intent_request['intent']
    intent_name = intent_request['intent']['name']

    # Dispatch to your skill's intent handlers
    if intent_name == "MyColorIsIntent":
        return set_color_in_session(intent, session)
    elif intent_name == "WhatsMyColorIntent":
        return get_color_from_session(intent, session)
    elif intent_name == "AMAZON.HelpIntent":
        return get_welcome_response()
    elif intent_name == "AMAZON.CancelIntent" or intent_name == "AMAZON.StopIntent":
        return handle_session_end_request()
    else:
        raise ValueError("Invalid intent")


------------------------------------ 省略 ------------------------------------


# --------------- Main handler ------------------

def lambda_handler(event, context):
    """ Route the incoming request based on type (LaunchRequest, IntentRequest,
    etc.) The JSON body of the request is provided in the event parameter.
    """
    print("event.session.application.applicationId=" +
          event['session']['application']['applicationId'])

    """
    Uncomment this if statement and populate with your skill's application ID to
    prevent someone else from configuring a skill that sends requests to this
    function.
    """
    # if (event['session']['application']['applicationId'] !=
    #         "amzn1.echo-sdk-ams.app.[unique-value-here]"):
    #     raise ValueError("Invalid Application ID")

    if event['session']['new']:
        on_session_started({'requestId': event['request']['requestId']},
                           event['session'])

    if event['request']['type'] == "LaunchRequest":
        return on_launch(event['request'], event['session'])
    elif event['request']['type'] == "IntentRequest":
        return on_intent(event['request'], event['session'])
    elif event['request']['type'] == "SessionEndedRequest":
        return on_session_ended(event['request'], event['session'])
```

"LaunchRequest" というのは、「アレクサ、◯◯◯ を開いて」「アレクサ、◯◯◯ につないで」と言って、スキルをインテントなしで起動したときにくるリクエストです。"LaunchRequest" の際に、毎回前述のような「AMAZON.HelpIntent」に求められるような詳細な応答を返さないと思いますので、ここは処理を分けて、「AMAZON.HelpIntent」用の詳細な応答を返すように修正したほうが良いと思います。実際、上記２番目にも、

* *インテントなしでスキルを起動したときにユーザーが聞いたプロンプトよりも、詳細な情報が提供されていること。たとえば、ヘルププロンプトで、スキルが実行できることをより詳しく説明したり、ユーザーにスキルの終了方法を知らせたりしていること。*

と記述されています。

## スキルのホームカード

ホームカードの詳細に関しては [こちら](https://developer.amazon.com/ja/docs/custom-skills/include-a-card-in-your-skills-response.html) を参照してください。ホームカードに関して簡単に説明すると、ユーザーと Alexa との対話の内容を、Amazon Alexa のコンパニオンアプリなどに表示するものです。Alexa の天気アプリだとこんな感じのカードになります。

![3.png](https://qiita-image-store.s3.amazonaws.com/0/65831/df2f4131-c288-bd27-5366-8ff2da7d412e.png)

ホームカードに表示するタイトルやコンテンツも、会話の内容をちゃんと反映したものになっていなければなりません。私の場合、Lambda の Alexa 用の Blueprint の最初から記述されている下記のコードそのままにしていたら、

```
def build_speechlet_response(title, output, reprompt_text, should_end_session):
    return {
        'outputSpeech': {
            'type': 'PlainText',
            'text': output
        },
        'card': {
            'type': 'Simple',
            'title': "SessionSpeechlet - " + title,
            'content': "SessionSpeechlet - " + output
        },
        'reprompt': {
            'outputSpeech': {
                'type': 'PlainText',
                'text': reprompt_text
            }
        },
        'shouldEndSession': should_end_session
    }
```

> ホームカードに「SessionSpeechlet」のようなコードが表示されないよう修正してください。
> ユーザーフレンドリーなコンテンツにするために、コード参照はすべて削除してください。

のように指摘されてしまいました… そりゃそうですね。

# まとめ

初めて Alexa スキルの審査を受けてみて思ったのは、ユーザーエクスペリエンスにかかわる部分はしっかりチェックされている印象を受けました。いくら 3rd Party のスキルとはいえ、ユーザーエクスペリエンスを損なうようなものだと、Amazon Alexa や Amazon Echo 自体の印象も悪くなりかねないので、Amazon としてもそこは気をつけているのではないのかなと思いました。あと、今回、Lambda の Alexa 用の Blueprint をもとに作りましたが、何も疑わずほぼ流用してそのまま作ったのは反省でした。もっと仕様を理解して疑うところは疑うべきでした。まだ簡単なスキルを一個作っただけですが、もっと複雑なスキルを作って公開する際には新たな指摘事項が増えるかも知れないので、その際には追記していこうと思います。