naritoブログ

【お知らせ】
・コメントで質問等をしたが返事が返ってこない場合、私はそれを見落としています。
その場合は再度コメントをするかメールをしてください(toritoritorina@gmail.com)。

・近いうちに新しいブログが作成されます。わーお!

Alexa Skills Kit入門④(Python、AWS Lambda使用)

約208日前 2018年2月27日15:56
プログラミング関連
Python Alexa Skills Kit
「Alexa Skills Kit入門シリーズ(Python、AWS Lambda使用)」の1つです。
https://torina.top/detail/441/

まずですが、スキルの各種設定を行ったAlexaのページに行きます。


左側にある「テスト」のページに移動します。その後、下にスクロールすると「サービスシミュレーター」があります。



「お辞儀をするのだ」と入力し、呼び出すボタンを押すと、サービスリクエスト・サービスレスポンスそれぞれにJSONが出力されました。


ざっくり言うと、「お辞儀をするのだ」と喋るとその内容をAlexaがまとめ、サービスリクエストのJSONが作成されます。そのJSONがLambdaの関数に渡されて、Lambdaの関数でAlexaに喋って欲しいJSONを作成します。この作成するのがサービスレスポンスのJSONになります。

なので、Lambda側で行うのはサービスリクエストのJSON(これは辞書として渡されます)を解析し、どんなインテントか(ユーザーがどんな風にAlexaに喋ったか)、現在の状態...などを踏まえて、レスポンスとなる辞書オブジェクト(これがJSONに変換されます)を作成するのが基本の流れです。


②で作成したコードを解説していきます。まず、lambda_handler関数が呼び出されます。
def lambda_handler(event, context):
    """最初に呼び出される関数"""
    # リクエストの種類を取得
    request = event['request']
    request_type = request['type']

    # LaunchRequestは、特定のインテントを提供することなく、ユーザーがスキルを呼び出すときに送信される...
    # つまり、「アレクサ、ハローワールドを開いて」のようなメッセージ
    # 「アレクサ、ハローワールドで挨拶しろ」と言うとこれはインテントを含むので、IntentRequestになる
    if request_type == 'LaunchRequest':
        return welcome()

    # 何らかのインテントだった場合
    elif request_type == 'IntentRequest':
        intent_name = request['intent']['name']

        # 「挨拶しろ」「挨拶して」等で呼ばれる。サンプル発話に書いた部分
        if intent_name == 'Greet':
            return hello()

        # 「ヘルプ」「どうすればいいの」「使い方を教えて」で呼ばれる、組み込みインテント
        elif intent_name == 'AMAZON.HelpIntent':
            return welcome()

        # 「キャンセル」「取り消し」「やっぱりやめる」等で呼び出される。組み込みのインテント
        elif intent_name == 'AMAZON.CancelIntent' or intent_name == 'AMAZON.StopIntent':
            return bye()




even引数に渡されるのは、以下のような辞書です。今回重要なのは、request typeの"IntentRequest"とrequest intent nameのGreetです。特にintent nameは、ユーザーが何を喋ったか、要求したか、が分かる部分になります。
{
  "session": {
    "new": true,
    "sessionId": "SessionId.f2d82374-effd-4610-a9b1-83921ecc5a55",
    "application": {
      "applicationId": "amzn1.ask.skill.3621a4cd-02a2-411f-a667-cc02211d65e8"
    },
    "attributes": {},
    "user": {
      "userId": "amzn1.ask.account.AFFE2MJMEZIRJMIABNVOEGCRC3IJCHEMCI6AA5EQSFEMCLBUO2XYNCR3HG6ZFZPRLZ2RF5SVVEM5NVYUDIU3LFRWK77IMT7XTAULB6LZ6NNZCLGAPDXLSIFPMV3KIR23VBWG35OCMOWHGXYXNP5EBCHA53M2ZZIFFPHAJSDCUYDM3ZOZ6T7O4WIR7AIN5JTAI7OKPORNSPHHKVQ"
    }
  },
  "request": {
    "type": "IntentRequest",
    "requestId": "EdwRequestId.53cc922c-fde6-43dd-a75d-65d8bc789da1",
    "intent": {
      "name": "Greet",
      "slots": {}
    },
    "locale": "ja-JP",
    "timestamp": "2018-02-28T02:43:10Z"
  },
  "context": {
    "AudioPlayer": {
      "playerActivity": "IDLE"
    },
    "System": {
      "application": {
        "applicationId": "amzn1.ask.skill.3621a4cd-02a2-411f-a667-cc02211d65e8"
      },
      "user": {
        "userId": "amzn1.ask.account.AFFE2MJMEZIRJMIABNVOEGCRC3IJCHEMCI6AA5EQSFEMCLBUO2XYNCR3HG6ZFZPRLZ2RF5SVVEM5NVYUDIU3LFRWK77IMT7XTAULB6LZ6NNZCLGAPDXLSIFPMV3KIR23VBWG35OCMOWHGXYXNP5EBCHA53M2ZZIFFPHAJSDCUYDM3ZOZ6T7O4WIR7AIN5JTAI7OKPORNSPHHKVQ"
      },
      "device": {
        "supportedInterfaces": {}
      }
    }
  },
  "version": "1.0"
}



request typeが'LaunchRequest'は、スキルの起動時がそれです。もう少し具体的に言うとコメントにあるように、「アレクサ、ハローワールドを開いて」のように開いた場合で、「アレクサ、ハローワールドを開いてお辞儀をするのだ」のようにインテントを含んだ内容になるとIntentRequestとなります。
    request = event['request']
    request_type = request['type']

    # LaunchRequestは、特定のインテントを提供することなく、ユーザーがスキルを呼び出すときに送信される...
    # つまり、「アレクサ、ハローワールドを開いて」のようなメッセージ
    # 「アレクサ、ハローワールドで挨拶しろ」と言うとこれはインテントを含むので、IntentRequestになる
    if request_type == 'LaunchRequest':
        return welcome()



IntentRequestだった場合は、どのインテントかを判断します。今回はGreetというインテントしか作っていませんが、実は組み込みのインテントがいくつかあります。'AMAZON.HelpIntent'や、'AMAZON.CancelIntent'、 'AMAZON.StopIntent'がそれにあたります。どう喋ったら呼び出されるかは、コメントのとおりです。
    # 何らかのインテントだった場合
    elif request_type == 'IntentRequest':
        intent_name = request['intent']['name']

        # 「挨拶しろ」「挨拶して」等で呼ばれる。サンプル発話に書いた部分
        if intent_name == 'Greet':
            return hello()

        # 「ヘルプ」「どうすればいいの」「使い方を教えて」で呼ばれる、組み込みインテント
        elif intent_name == 'AMAZON.HelpIntent':
            return welcome()

        # 「キャンセル」「取り消し」「やっぱりやめる」等で呼び出される。組み込みのインテント
        elif intent_name == 'AMAZON.CancelIntent' or intent_name == 'AMAZON.StopIntent':
            return bye()




welcome関数は、結果的にスキル起動時に呼び出されます。QuestionSpeechクラスは、何かを喋って応答を待ちたい際に使うクラスとして作りました。repromptメソッドは、Alexaがユーザーの声を聞き取れなかった場合に発するメッセージを指定できます。
def welcome():
    """ようこそと言って、ユーザーの返事を待つ"""
    return QuestionSpeech('ようこそ!').reprompt('よく聞こえませんでした').build()


welcome関数の結果、以下のようなJSONになります。
{
  "version": "1.0",
  "sessionAttributes": {},
  "response": {
    "outputSpeech": {
      "type": "PlainText",
      "text": "ようこそ!"
    },
    "reprompt": {
      "outputSpeech": {
        "type": "PlainText",
        "text": "よく聞こえませんでした"
      }
    },
    "shouldEndSession": false
  }
}



キャンセル、取り消し、等と言うと結果的にbye関数が呼ばれます。OneSpeechは応答を待つのではなく、喋った後にスキルを終了する点がQuestionSpeechと異なります。JSON的には、shouldEndSessionにTrueを指定しているだけです。応答を待つことはないので、このクラスにrepromptメソッドはありません。
def bye():
    """グッバーイといって終わる"""
    return OneSpeech('グッバーイ').simple_card('遊んでくれてありがとう!').build()


JSONでは、以下のようになります。
{
  "version": "1.0",
  "sessionAttributes": {},
  "response": {
    "outputSpeech": {
      "type": "PlainText",
      "text": "グッバーイ"
    },
    "card": {
      "type": "Simple",
      "title": "遊んでくれてありがとう!'",
      "content": "グッバーイ"
    },
    "shouldEndSession": true
  }
}


「カード」という機能があります。これはスキルの応答の際に捕捉や説明を入れることができるもので、Alexaを使っている人ならば見たことがあるでしょう。以下の画像の部分です。simple_cardメソッドは、このカード部分を追加するメソッドです。今回はグッバーイで終わるときだけ、カードを追加しています。



hello関数は、ハローハローハローと言って終了ですね。シンプルです。
def hello():
    """ハローと言っておわり"""
    return OneSpeech('ハロー、ハロー、ハロー').build()



JSONでは以下のようになります。
{
  "version": "1.0",
  "sessionAttributes": {},
  "response": {
    "outputSpeech": {
      "type": "PlainText",
      "text": "ハロー、ハロー、ハロー"
    },
    "shouldEndSession": true
  }
}