初めてのスキル開発



初めてのスキル開発

ASK SDKのセットアップガイドでは、ASK SDK for Pythonを特定のディレクトリまたはvirtualenvを使用して仮想環境にインストールする方法を説明しました。このガイドでは、ASK SDK for Pythonを使ったスキル開発の手順を説明します。

前提条件

ASK SDK for Pythonのインストールに加えて、以下のものが必要です。

Hello Worldの作成

hello_world.pyというPythonファイルにHello Worldを作成します。先ほど作成したskillフォルダに、お好みのテキストエディターやIDEを使用してhello_world.pyという名前のファイルを作成してください。

Hello Worldの実装

リクエストハンドラー

Alexaサービスによって送信されたイベントに応答するには、カスタムスキルが必要です。たとえば、Alexaデバイス(Echo、Echo Dot、Echo Showなど)に「ハローワールドを開いて」と頼む場合、スキルがHello Worldスキルに送信されたLaunchRequestに応答する必要があります。ASK SDK for Pythonを使用すれば、リクエストハンドラーを作成するだけで済みます。リクエストハンドラーは受け取ったリクエストを処理して応答を返すコードです。このコードは受け取ったリクエストを正しいリクエストハンドラーを使用して処理し、応答を返します。ASK SDK for Pythonでは、次のいずれかの方法でリクエストハンドラーを作成することができます。

  1. ask_sdk_core.dispatch_componentsパッケージの下にAbstractRequestHandlerクラスを実装します。このクラスにはcan_handleおよびhandleメソッドの実装が含まれている必要があります。詳細についてはハンドラークラスを使用した実装セクションで説明しています。
  2. インスタンス化されたスキルビルダーオブジェクトのrequest_handlerデコレーターを使用して、異なる受信リクエストのハンドラーとして動作するように関数をタグ付けします。詳細についてはデコレーターを使用した実装セクションで説明しています。

Hello Worldスキルの実装を通じて、まずハンドラークラスの使用方法、次に同じスキルをデコレーターを使用して作成する方法を説明します。機能は同じです。どちらを使用してもかまいません。

例外ハンドラー

うまくいかないことが起こったときに、スキルコードで問題を正常に処理する方法が必要です。ASK SDK for Pythonは、リクエストの処理と似た方法で例外処理をサポートします。クラスまたはデコレーターを選んで使用できます。以下の実装セクションで、例外処理の実装方法を説明します。

オプション1: ハンドラークラスを使用した実装

最初にスキルビルダーオブジェクトを作成します。スキルビルダーオブジェクトは、スキルで入力されたリクエストの処理とカスタム応答の生成を担当するコンポーネントを追加するのに便利です。

以下のコードをhello_world.pyファイルに入力するか貼り付けます。

from ask_sdk_core.skill_builder import SkillBuilder

sb = SkillBuilder()

ハンドラークラスを使用するには、AbstractRequestHandlerクラスの2つのメソッドcan_handleおよびhandleを実装するクラスとして各リクエストハンドラーを作成する必要があります。

can_handleメソッドは、リクエストハンドラーがリクエストに対して適切な応答を作成できるかを示すブール値を返します。can_handleメソッドは、スキルが前回のリクエストに設定したり、前回のやり取りで保存した、リクエストタイプやそのほかのアトリビュートにアクセスできます。Hello Worldスキルで参照する必要があるのは、各ハンドラーが受け取ったリクエストに応答できるかどうかを判断するリクエスト情報のみです。

LaunchRequestハンドラー

以下は、スキルがLaunchRequestを受け取ったときに呼び出されるハンドラーを設定するコードのサンプルです。LaunchRequestイベントは、特定のインテントなしでスキルが呼び出された場合に発生します。

以下のコードをhello_world.pyファイルの、前述のコードの後に入力するか貼り付けます。

from ask_sdk_core.dispatch_components import AbstractRequestHandler
from ask_sdk_core.utils import is_request_type, is_intent_name
from ask_sdk_core.handler_input import HandlerInput
from ask_sdk_model import Response
from ask_sdk_model.ui import SimpleCard

class LaunchRequestHandler(AbstractRequestHandler):
    def can_handle(self, handler_input):
        # type: (HandlerInput) -> bool
        return is_request_type("LaunchRequest")(handler_input)

    def handle(self, handler_input):
        # type: (HandlerInput) -> Response
        speech_text = "ようこそ、アレクサスキルキットへ。こんにちは、と言ってみてください。"

        handler_input.response_builder.speak(speech_text).set_card(
            SimpleCard("ハローワールド", speech_text)).set_should_end_session(
            False)
        return handler_input.response_builder.response

受け取ったリクエストがLaunchRequestの場合、can_handle関数はTrueを返します。handle関数は、基本的なあいさつの応答を生成して返します。

HelloWorldIntentハンドラー

以下は、スキルがHelloWorldIntentという名前のインテントリクエストを受け取った時に呼び出されるハンドラーを設定するコードのサンプルです。以下のコードをhello_world.pyファイルの、前述のハンドラーの後に入力するか貼り付けます。

class HelloWorldIntentHandler(AbstractRequestHandler):
    def can_handle(self, handler_input):
        # type: (HandlerInput) -> bool
        return is_intent_name("HelloWorldIntent")(handler_input)

    def handle(self, handler_input):
        # type: (HandlerInput) -> Response
        speech_text = "こんにちは"

        handler_input.response_builder.speak(speech_text).set_card(
            SimpleCard("ハローワールド", speech_text)).set_should_end_session(
            True)
        return handler_input.response_builder.response

can_handle関数は受け取るリクエストがIntentReqiestかどうかを検出し、インテント名がHelloWorldIntentの場合にTrueを返します。handle関数は、基本的な「こんにちは」という応答を生成して返します。

HelpIntentハンドラー

以下は、スキルがビルトインインテントAmazon.HelpIntentを受け取ったときに呼び出されるハンドラーを設定するコードのサンプルです。以下のコードをhello_world.pyファイルの、前述のハンドラーの後に入力するか貼り付けます。

class HelpIntentHandler(AbstractRequestHandler):
    def can_handle(self, handler_input):
        # type: (HandlerInput) -> bool
        return is_intent_name("AMAZON.HelpIntent")(handler_input)

    def handle(self, handler_input):
        # type: (HandlerInput) -> Response
        speech_text = "こんにちは。と言ってみてください。"

        handler_input.response_builder.speak(speech_text).ask(speech_text).set_card(
            SimpleCard("ハローワールド", speech_text))
        return handler_input.response_builder.response

先ほどのハンドラー同様、このハンドラーはIntentRequestを想定されるインテント名と照合します。基本的なヘルプ手順が返され、.ask(speech_text)によってユーザーのマイクがオンになりユーザーの応答を待ちます。

CancelAndStopIntentハンドラー

CancelAndStopIntentHandlerもビルトインインテントAMAZON.CancelIntentまたはAMAZON.StopIntentによって呼び出されるため、HelpIntentハンドラーに似ています。以下は、1つのハンドラーを使用して両方のインテントに応答する例です。以下のコードをhello_world.pyファイルの、前述のハンドラーの後に入力するか貼り付けます。

class CancelAndStopIntentHandler(AbstractRequestHandler):
    def can_handle(self, handler_input):
        # type: (HandlerInput) -> bool
        return is_intent_name("AMAZON.CancelIntent")(handler_input)
                or is_intent_name("AMAZON.StopIntent")(handler_input)

    def handle(self, handler_input):
        # type: (HandlerInput) -> Response
        speech_text = "さようなら"

        handler_input.response_builder.speak(speech_text).set_card(
            SimpleCard("ハローワールド", speech_text)).set_should_end_session(True)
        return handler_input.response_builder.response

両方のインテントに対する応答は同じであるため、1つのハンドラーにすることで重複するコードを減らせます。

SessionEndedRequestハンドラー

SessionEndedRequestを受け取った後は音声、カード、ディレクティブを使った応答を返すことはできませんが、クリーンアップロジックを追加するにはSessionEndedRequestHandlerが最適な場所です。以下のコードをhello_world.pyファイルの、前述のハンドラーの後に入力するか貼り付けます。

class SessionEndedRequestHandler(AbstractRequestHandler):
    def can_handle(self, handler_input):
        # type: (HandlerInput) -> bool
        return is_request_type("SessionEndedRequest")(handler_input)

    def handle(self, handler_input):
        # type: (HandlerInput) -> Response
        # クリーンアップロジックをここに追加します

        return handler_input.response_builder.response

例外ハンドラーの実装

以下は、catch all例外ハンドラーをスキルに追加して、すべての例外に対してスキルが意味のあるメッセージを返すようにする例です。以下のコードをhello_world.pyファイルの、前述のハンドラーの後に入力するか貼り付けます。

from ask_sdk_core.dispatch_components import AbstractExceptionHandler

class AllExceptionHandler(AbstractExceptionHandler):

    def can_handle(self, handler_input, exception):
        # type: (HandlerInput, Exception) -> bool
        return True

    def handle(self, handler_input, exception):
        # type: (HandlerInput, Exception) -> Response
        # Cloudwatchログに例外を記録します
        print(exception)

        speech = "すみません、わかりませんでした。もう一度言ってください。"
        handler_input.response_builder.speak(speech).ask(speech)
        return handler_input.response_builder.response

Lambdaハンドラーの作成

Lambdaハンドラーは、AWS Lambda関数のエントリポイントとなります。以下は、スキルが受信するすべてのリクエストのルーティングを行うLambdaハンドラー関数のコードサンプルです。Lambdaハンドラー関数は、作成したリクエストハンドラーを使用して設定されたSDKのスキルインスタンスを作成します。以下のコードをhello_world.pyファイルの、前述のハンドラーの後に入力するか貼り付けます。

sb.add_request_handler(LaunchRequestHandler())
sb.add_request_handler(HelloWorldIntentHandler())
sb.add_request_handler(HelpIntentHandler())
sb.add_request_handler(CancelAndStopIntentHandler())
sb.add_request_handler(SessionEndedRequestHandler())

sb.add_exception_handler(AllExceptionHandler())

handler = sb.lambda_handler()

オプション2: デコレーターを使用した実装

以下は、上記と同じ機能を実装するコードですが、関数デコレーターを使用しています。デコレーターは、上記の各ハンドラークラスに実装されたcan_handleメソッドに代わるものと考えてください。

最初にスキルビルダーオブジェクトを作成します。スキルビルダーオブジェクトは、スキルで入力されたリクエストの処理とカスタム応答の生成を担当するコンポーネントを追加するのに便利です。

以下のコードをhello_world.pyファイルに入力するか貼り付けます。

from ask_sdk_core.skill_builder import SkillBuilder

sb = SkillBuilder()

LaunchRequestハンドラー

以下は、スキルがLaunchRequestを受け取ったときに呼び出されるハンドラーを設定するコードのサンプルです。LaunchRequestイベントは、特定のインテントなしでスキルが呼び出された場合に発生します。

以下のコードをhello_world.pyファイルの、前述のコードの後に入力するか貼り付けます。

from ask_sdk_core.utils import is_request_type, is_intent_name
from ask_sdk_core.handler_input import HandlerInput
from ask_sdk_model import Response
from ask_sdk_model.ui import SimpleCard

@sb.request_handler(can_handle_func=is_request_type("LaunchRequest"))
def launch_request_handler(handler_input):
    # type: (HandlerInput) -> Response
    speech_text = "ようこそ、アレクサスキルキットへ。こんにちは、と言ってみてください。"

    handler_input.response_builder.speak(speech_text).set_card(
        SimpleCard("ハローワールド", speech_text)).set_should_end_session(
        False)
    return handler_input.response_builder.response

クラスパターンのLaunchRequestHandlerのcan_handle関数と同様に、デコレーターは受け取るリクエストがLaunchRequestの場合にTrueを返します。handle関数は、クラスパターンのhandle関数と同じ方法で基本的なあいさつの応答を生成して返します。

HelloWorldIntentハンドラー

以下は、スキルがHelloWorldIntentという名前のインテントリクエストを受け取った時に呼び出されるハンドラーを設定するコードのサンプルです。以下のコードをhello_world.pyファイルの、前述のハンドラーの後に入力するか貼り付けます。

@sb.request_handler(can_handle_func=is_intent_name("HelloWorldIntent"))
def hello_world_intent_handler(handler_input):
    # type: (HandlerInput) -> Response
    speech_text = "こんにちは"

    handler_input.response_builder.speak(speech_text).set_card(
        SimpleCard("ハローワールド", speech_text)).set_should_end_session(
        True)
    return handler_input.response_builder.response

HelpIntentハンドラー

以下は、スキルがビルトインインテントAmazon.HelpIntentを受け取ったときに呼び出されるハンドラーを設定するコードのサンプルです。以下のコードをhello_world.pyファイルの、前述のハンドラーの後に入力するか貼り付けます。

@sb.request_handler(can_handle_func=is_intent_name("AMAZON.HelpIntent"))
def help_intent_handler(handler_input):
    # type: (HandlerInput) -> Response
    speech_text = "こんにちは。と言ってみてください。"

    handler_input.response_builder.speak(speech_text).ask(speech_text).set_card(
        SimpleCard("ハローワールド", speech_text))
    return handler_input.response_builder.response

先ほどのハンドラー同様、このハンドラーはIntentRequestを想定されるインテント名と照合します。基本的なヘルプ手順が返され、.ask(speech_text)によってユーザーのマイクがオンになりユーザーの応答を待ちます。

CancelAndStopIntentハンドラー

CancelAndStopIntentHandlerもビルトインインテントAMAZON.CancelIntentまたはAMAZON.StopIntentによって呼び出されるため、HelpIntentハンドラーに似ています。以下は、1つのハンドラーを使用して両方のインテントに応答する例です。以下のコードをhello_world.pyファイルの、前述のハンドラーの後に入力するか貼り付けます。

@sb.request_handler(
    can_handle_func=lambda handler_input :
        is_intent_name("AMAZON.CancelIntent")(handler_input) or
        is_intent_name("AMAZON.StopIntent")(handler_input))
def cancel_and_stop_intent_handler(handler_input):
    # type: (HandlerInput) -> Response
    speech_text = "さようなら"

    handler_input.response_builder.speak(speech_text).set_card(
        SimpleCard("ハローワールド", speech_text)).set_should_end_session(
            True)
    return handler_input.response_builder.response

上記の例では、can_handleには渡す関数が必要です。is_intent_nameは関数を返しますが、リクエストがAMAZON.CancelIntentなのかAMAZON.StopIntentなのかを確認する必要があります。これを行うには、Pythonの組み込みlambda関数を使用して、途中に無名関数を作成します。

両方のインテントに対する応答は同じであるため、1つのハンドラーにすることで重複するコードを減らせます。

SessionEndedRequestハンドラー

SessionEndedRequestを受け取った後は音声、カード、ディレクティブを使った応答を返すことはできませんが、クリーンアップロジックを追加するにはSessionEndedRequestHandlerが最適な場所です。以下のコードをhello_world.pyファイルの、前述のハンドラーの後に入力するか貼り付けます。

@sb.request_handler(can_handle_func=is_request_type("SessionEndedRequest"))
def session_ended_request_handler(handler_input):
    # type: (HandlerInput) -> Response
    # クリーンアップロジックをここに追加します

    return handler_input.response_builder.response

例外ハンドラーの実装

以下は、catch all例外ハンドラーをスキルに追加して、すべての例外に対してスキルが意味のあるメッセージを返すようにする例です。以下のコードをhello_world.pyファイルの、前述のハンドラーの後に入力するか貼り付けます。

@sb.exception_handler(can_handle_func=lambda i, e: True)
def all_exception_handler(handler_input, exception):
    # type: (HandlerInput, Exception) -> Response
    # Cloudwatchログに例外を記録します
    print(exception)

    speech = "すみません、わかりませんでした。もう一度言ってください。"
    handler_input.response_builder.speak(speech).ask(speech)
    return handler_input.response_builder.response

Lambdaハンドラーの作成

Lambdaハンドラーは、AWS Lambda関数のエントリポイントとなります。以下は、スキルが受信するすべてのリクエストのルーティングを行うLambdaハンドラー関数のコードサンプルです。Lambdaハンドラー関数は、作成したリクエストハンドラーを使用して設定されたSDKのスキルインスタンスを作成します。

以下のコードをhello_world.pyファイルの、前述のハンドラーの後に入力するか貼り付けます。

handler = sb.lambda_handler()

デコレーターを使用する場合、リクエストハンドラーと例外ハンドラーはコードの最初にインスタンス化されたスキルビルダーオブジェクトによって自動的に識別されます。

ソースコード全文

hello_world.pyのソースコード全文はこちらにあります。

AWS Lambda用にコードを準備する

コードが完成したので、Lambdaにアップロードするファイルを含む.zipファイルを作成する必要があります。

コードをAWS Lambdaにアップロードする際に、スキルコードとその依存関係をフラットファイル構造でzipファイル内に含める必要があります。こうすると、zipでの圧縮前にコードがASK SDK for Pythonと同じフォルダに配置されます。

仮想環境を使ってSDKをセットアップする場合、依存関係は仮想環境のsite-packagesフォルダにインストールされます。そのため、skill_envsite-packagesフォルダに移動します。

hello_world.pyファイルをsite-packagesフォルダにコピーして、(そのフォルダ自体ではなく)そのフォルダのコンテンツの.zipファイルを作成します。ファイルにskill.zipという名前を付けます。デプロイパッケージの作成について詳しくは、AWS Lambdaドキュメントを確認してください。

SDKを特定のフォルダにセットアップする場合、依存関係はその特定のフォルダにインストールされます。セットアップガイドの手順に従った場合、そのフォルダはskill_envです。

hello_world.pyファイルをskill_envフォルダにコピーして、(そのフォルダ自体ではなく)そのフォルダのコンテンツの.zipファイルを作成します。ファイルにskill.zipという名前を付けます。デプロイパッケージの作成について詳しくは、AWS Lambdaドキュメントを確認してください。

コードをAWS Lambdaにアップロードする前に、AWS Lambda関数を作成する必要があります。また、Alexa開発者ポータルでスキルを作成する必要があります。

AWS Lambda関数の作成

スキルに適切なロールでAWS Lambda関数を作成する手順については、カスタムスキルをAWS Lambda関数としてホスティングするを参照してください。関数作成時には、一から作成オプションを選択し、ランタイムとしてPython 2.7またはPython 3.6を選択します。

AWS Lambda関数が作成されたら、Alexaサービスでそれを呼び出すことができるようにします。これを行うには、Lambdaのコンフィギュレーションでトリガータブに移動して、Alexa Skills Kitをトリガータイプとして追加します。これが完了したら、前の手順で作成したskill.zipファイルをアップロードし、ハンドラー情報とmodule_name.handlerを入力します。この例ではhello_world.handlerです。

スキルの設定とテストを行う

スキルコードをAWS Lambdaにアップロードしたら、Alexaのスキルを設定できます。

  • 以下の手順に従って新しいスキルを作成します。
    1. Alexa Skills Kit開発者コンソールにログインします。
    2. 右上のスキルの作成ボタンをクリックします。
    3. スキル名として「HelloWorld」と入力します。
    4. カスタムスキルを選択してからスキルを作成をクリックします。
  • 次に、スキルの対話モデルを定義します。サイドバーの呼び出し名を選択し、スキルの呼び出し名に「ごあいさつ」を入力します。
  • 次に、HelloWorldIntentというインテントを対話モデルに追加します。対話モデルのインテントセクションにある追加ボタンをクリックします。カスタムインテントを作成を選択した状態で、インテント名として「HelloWorldIntent」を入力し、インテントを作成します。インテントの詳細ページで、ユーザーがこのインテントを呼び出すのに使用できるサンプル発話をいくつか追加します。この例では、以下のようなサンプル発話が適当ですが、これ以外に追加してもかまいません。
    こんにちはと言って
      ハローワールドと言って
      こんにちは
      ハイと言って
      ハイワールドと言って
      ハイ
      ごきげんいかが
    
  • AMAZON.CancelIntentAMAZON.HelpIntentAMAZON.StopIntentはAlexaのビルトインインテントのため、サンプル発話を追加する必要はありません。
  • 開発者コンソールでは、スキルモデル全体をJSON形式で編集できます。サイドバーでJSONエディターを選択します。この例では、以下のJSONスキーマを使用できます。
      {
        "interactionModel": {
          "languageModel": {
            "invocationName": "ごあいさつ",
            "intents": [
              {
                "name": "AMAZON.CancelIntent",
                "samples": []
              },
              {
                "name": "AMAZON.HelpIntent",
                "samples": []
              },
              {
                "name": "AMAZON.StopIntent",
                "samples": []
              },
              {
                "name": "HelloWorldIntent",
                "slots": [],
                "samples": [
                  "ごきげんいかが",
                  "ハイ",
                  "ハイワールドと言って",
                  "ハイと言って",
                  "こんにちは",
                  "ハローワールドと言って",
                  "こんにちはと言って"
                ]
              }
            ],
            "types": []
          }
        }
      }
    
  • 対話モデルの編集が完了したら、モデルを保存してビルドします。
  • 次に、スキルのエンドポイントを設定します。これを行うには次の手順に従います。
    1. スキルの中でエンドポイントタブをクリックし、AWS LambdaのARNを選択して、作成したスキルのスキルIDをコピーします。
    2. 新しいタブでAWS開発者コンソールを開きます。
    3. 前の手順で作成したAWS Lambda関数に移動します。
    4. Designerメニューから、Alexa Skills Kitトリガーメニューを追加し、スクロールダウンしてスキルID検証コンフィギュレーションにスキルIDを貼り付けます。完了したら追加、保存の順にクリックしてAWS Lambda関数を更新します。
    5. ページ右上隅のAWS Lambda関数ARNをコピーします。ARNは一意のリソース番号です。Alexaサービスはこれを使用して、スキルの呼び出し中に必要になるAWS Lambda関数を識別します。
    6. Alexa Skills Kit開発者コンソールに移動して、HelloWorldスキルをクリックします。
    7. スキルの中でエンドポイントタブをクリックし、AWS LambdaのARNを選択して、デフォルトの地域にARNを貼り付けます。
    8. 残りの設定は、デフォルト値のままで構いません。エンドポイントを保存をクリックします。
    9. 呼び出し名タブをクリックして、モデルを保存およびビルドします。
  • この時点で、スキルをテストできるようになります。上部メニューでテストをクリックします。このスキルでは、テストは有効になっていますオプションがONになっていることを確認します。テストページを使って、テキストや音声でリクエストをシミュレーションできます。
  • 呼び出し名と、サンプル発話のうちの1つを使います。たとえば、「アレクサ、あいさつして」と言うと、スキルは「こんにちは」と音声で応え、ディスプレイ付きのデバイスでは「Hello World」カードが表示されるはずです。また、スマートフォンのAlexaアプリやhttps://alexa.amazon.comスキルにスキルが表示されていることを確認できます。
  • さまざまなインテントや、スキルコードに対応するリクエストハンドラーを試してみてください。ひととおりのテストが完了したら、スキルの認定を申請して世界中のユーザーに公開するプロセスに進むことができます。