Alexaから送信されたリクエストを処理する



Alexaから送信されたリクエストを処理する

カスタムスキルの場合、AWS Lambda関数かウェブサービスのいずれかを作成してAlexaからのリクエストを処理できます。このサービスのコードでは以下を行う必要があります。

  • Alexaから受信するすべてのリクエストを認識します。
  • 適切な応答を返します。

このドキュメントでは、リクエストを処理して応答を返す方法の詳細とコードの例を紹介します。

スキルから受け取ったリクエストを確認する

ウェブサービスまたはLambda関数は、リクエストを受け取る前に、そのリクエストがスキルから送信されたものであることを確認します。これにより、悪意あるユーザーがエンドポイントからスキルの設定を変更し、そのスキルを使ってサービスにリクエストを送るのを防ぐことができます。

この検証を行うために、Alexaから送信されるすべてのリクエストにはスキルIDが含まれています。このスキルIDを実際のスキルIDと照合することで、リクエストがサービスに対するものであることを確認できます。

スキルのIDを取得する

スキルIDは、開発者コンソールに表示されます。開発者コンソールを開いてスキルのリストを参照します。すべてのスキルには、名前の下にスキルIDの表示リンクが表示されます。このリンクをクリックしてIDを確認します。

スキルリストでスキルIDを確認する
スキルリストでスキルIDを確認する

スキルIDはエンドポイントページでも確認できます。ここには、クリップボードにコピーボタンも表示されます。

コードでスキルIDの検証を行う

以下の例のようにスキルIDを渡すと、Alexa Skills Kit SDKは自動でスキルIDを検証します。SDKを使用していない場合、JSONリクエストからIDを取得して独自のコードで比較を行います。

クリップボードにコピーされました。

このサンプルコードはAlexa Skills Kit SDK for Node.js(v2)を使用しています。

コードでスキルIDを検証するには、スキルインスタンスの設定時にスキルIDをSkillBuilder.withSkillIdメソッドに渡します。このコード例では、"amzn1.ask.skill.1"に一致しないスキルIDのリクエストをすべて拒否します。

const skillBuilder = Alexa.SkillBuilders.custom();

exports.handler = skillBuilder
  .withSkillId("amzn1.ask.skill.1")
  .addRequestHandlers(
    HelloWorldIntentHandler,
    LaunchRequestHandler,
    HelpIntentHandler,
    CancelAndStopIntentHandler,
    SessionEndedRequestHandler,
  )
  .addErrorHandlers(ErrorHandler)
  .addRequestInterceptors(LoggingRequestInterceptor)
  .addResponseInterceptors(LoggingResponseInterceptor)
  .lambda();

クリップボードにコピーされました。

このサンプルコードはAlexa Skills Kit SDK for Pythonを使用しています。

コードでスキルIDを検証するには、スキルインスタンスの設定時にskill_idアトリビュートをSkillBuilderオブジェクトにセットします。このコード例では、"amzn1.ask.skill.1"に一致しないスキルIDのリクエストをすべて拒否します。

from ask_sdk_core.skill_builder import SkillBuilder

sb = SkillBuilder()

# スキルIDを設定します。
sb.skill_id = "amzn.ask.skill.1"

# すべてのリクエストハンドラーをスキルに追加します。
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_request_handler(FallbackIntentHandler())

# 例外ハンドラーをスキルに追加します。
sb.add_exception_handler(CatchAllExceptionHandler())

# ログリクエストインターセプターをスキルに追加します。
sb.add_global_request_interceptor(LogRequestInterceptor())

# ログ応答インターセプターをスキルに追加します。
sb.add_global_request_interceptor(LogResponseInterceptor())

# AWS Lambdaに登録するLambdaハンドラーを提示します。
lambda_handler = sb.lambda_handler()

クリップボードにコピーされました。

このサンプルコードはAlexa Skills Kit SDK for Javaを使用しています。

コードでスキルIDを検証するには、スキルインスタンスの設定時にスキルIDをSkillBuilder.withSkillIdメソッドに渡します。このコード例では、"amzn1.ask.skill.1"に一致しないスキルIDのリクエストをすべて拒否します。


import com.amazon.ask.Skill;
import com.amazon.ask.SkillStreamHandler;
import com.amazon.ask.Skills;

import handlers.*;
import interceptors.*;

public class HandleReqCodeSamplesStreamHandler extends SkillStreamHandler {

    private static Skill getSkill() {
        return Skills.standard()
                .withSkillId("amzn1.ask.skill.1")
                .addRequestHandlers(
                    new HelloWorldIntentHandler(),
                    new LaunchRequestHandler(),
                    new HelpIntentHandler(),
                    new CancelandStopIntentHandler(),
                    new SessionEndedRequestHandler(),
                    new FallbackIntentHandler())
                .addRequestInterceptors(
                    new LogRequestInterceptor()
                )
                .addResponseInterceptors(
                    new LogResponseInterceptor()
                )
                .build();
    }

    public HandleReqCodeSamplesStreamHandler() {
        super(getSkill());
    }

}


クリップボードにコピーされました。

スキルIDには、context.System.application.applicationIdプロパティで受け取るリクエストでアクセスできます。スキルIDはsessionオブジェクトにもありますが、sessionオブジェクトはcontextオブジェクトと違い、受信するすべてのリクエストに含まれるわけではありません。

この例でのスキルIDは、amzn1.ask.skill.1です。簡潔に表現するため、この例ではrequestオブジェクトを省略しています。

{
  "version": "1.0",
  "context": {
    "AudioPlayer": {
      "playerActivity": "IDLE"
    },
    "System": {
      "application": {
        "applicationId": "amzn1.ask.skill.1"
      },
      "user": {
        "userId": "amzn1.ask.account.1"
      },
      "device": {
        "supportedInterfaces": {
          "AudioPlayer": {}
        }
      }
    }
  },
  "session": {
    "new": true,
    "sessionId": "amzn1.echo-api.session.1",
    "application": {
      "applicationId": "amzn1.ask.skill.1"
    },
    "user": {
      "userId": "amzn1.ask.account.1"
    },
    "attributes": {}
  },
  "request": {}
}

スキルで処理する必要のあるリクエストのタイプ

Alexaは、スキルにさまざまなタイプのリクエストを送信します。リクエストは以下のいずれかを表します。

  • ユーザーがしたいことを表します。たとえば、ゲームスキルの場合には新しいゲームを開始したいといった、ユーザーの意図です。この場合、StartNewGameIntentと呼ばれるIntentRequestとしてスキルに送られます。
  • ユーザーに代わってAlexaが送信するイベントを表します。たとえば、AudioPlayer.PlaybackStartedリクエストでは、スキルが開始したオーディオストリームの再生をAlexaが開始したことをスキルに通知します。

スキルは、受け取ったすべてのリクエストに対して有効な応答を返す必要があります。

リクエストとリクエストタイプについて

カスタムスキルに送信されるリクエストには、typeを指定するrequestオブジェクトとリクエストの詳細が含まれます。たとえば、ユーザーが具体的なリクエストなしでスキルを呼び出すと、スキルはLaunchRequestを受け取ります。このリクエストのJSONコードは次のようになります(簡潔に表現するため、この例にはsessionオブジェクトとcontextオブジェクトの一部のみを記載しています)。typeには、LaunchRequestのリクエストタイプがセットされています。

{
  "version": "1.0",
  "request": {
    "type": "LaunchRequest",
    "requestId": "amzn1.echo-api.request.1",
    "timestamp": "2016-10-27T18:21:44Z",
    "locale": "ja-JP"
  },
  "session": {},
  "context": {}
}

考えられるリクエストタイプ

スキルで使用する機能やインターフェースに応じて、処理するリクエストタイプは異なります。以下の表は、リクエストタイプの概要と処理が必要なケースを表しています。

リクエストタイプ 説明 処理が必要かどうか

AudioPlayer*

オーディオストリーミングにAudioPlayerインターフェースを使う場合に適用されます。再生ステータスについてスキルに通知するために送信されるリクエストです。

〇(長いオーディオのストリーミングにAudioPlayerを使用する場合)

CanFulfillIntentRequest

ユーザーがスキルで処理が必要な可能性のあるリクエストを行った場合に送信されます。Alexaは、スキルに検出されたスロットを使用してインテントリクエストを理解して実行できるかどうかをたずねるためにこのリクエストを送信します。これは、通常のIntentRequestからアクションを実行するようスキルにリクエストする前に行われます。カスタムスキルの無指名対話を理解するを参照してください。

〇(スキルのCanFulfillIntentRequestオプションを有効にしている場合)

Connections.ResponsepurchaseResult

Connections.SendRequestを使ってスキル内課金(ISP)の購入フローを開始する場合に適用されます。

〇(スキル内課金(ISP)を実装する場合)

GadgetController*

GadgetControllerインターフェースを使ってEcho Buttonsを制御する場合に適用されます。

〇(GadgetControllerインターフェースを使う場合)

InputHandlerEvent*リクエスト

GameEngineインターフェースを使ってEcho Buttonsからの入力を受け取る場合に適用されます。

〇(GameEngineを使う場合)

IntentRequest

ユーザーがインテントのいずれかにマッピングされるコマンドを発話すると送信されます。リクエストにはインテント名が含まれます。

〇(スキルでは対話モデルに定義済みで、想定されるインテントをすべて処理する必要があります)

LaunchRequest

ユーザーが具体的なコマンドを指定せずにスキルを呼び出す場合に送信されます。例:

ユーザー: アレクサ、十二星座占いを開いて

PlaybackController*

オーディオストリーミングにAudioPlayerインターフェースを使う場合に適用されます。リクエストは、ユーザーが、リモコンなどハードウェアのボタンを使ってデバイスを操作した場合に送信されます。

〇(オーディオのストリーミングにAudioPlayerを使用する場合)

SessionEndedRequest

ユーザーがスキルを終了した、ユーザーがスキルに理解できない応答を返した、エラーが発生した、のいずれかにより現在開いているスキルセッションが閉じられた場合に送信されます。

リクエストハンドラーについて

リクエストハンドラーは、さまざまな受信リクエストに対してアクションを実行するためのコードです。

Alexa Skills Kit SDKを使用する場合、RequestHandlerインターフェースを実装することでこれらのハンドラーを定義できます。定義すると、SDKは受信したリクエストを自動的に適切なハンドラーにルーティングします。SDKを使用しない場合、受信したリクエストをチェックして正しいハンドラーにルーティングするロジックを独自にコーディングする必要があります。

ハンドラーが処理できるリクエストを判断する

リクエストハンドラーは、それぞれに処理できるリクエストを判断する必要があります。各ハンドラーの要件は、スキルのデザインや機能によって異なります。応答のコードを他で再利用できる場合は、1つのハンドラーで複数のリクエストタイプを処理できるようコーディングすると効率的です。一般に、ハンドラーは次のような条件で決まります。

  • リクエストタイプが条件の場合、そのハンドラーはリクエストタイプを見て、特定のタイプ(LaunchRequestなど)のリクエストだけを処理します。
  • リクエストに含まれるその他のデータが条件の場合、そのハンドラーは、特定のデータを含むIntentRequest(インテント名がHelloWorldなど)を処理します。
  • リクエストに含まれるダイアログ状態データが条件の場合、そのハンドラーは、特定のダイアログ状態(インテント名がPlanMyTripで、dialogStateIN_PROGRESSなど)のIntentRequestを処理します。
  • 特定のスロット値が条件の場合、そのハンドラーは特定のスロット値(インテント名がOrderIntentdrinkスロットの値が「コーヒー」など)を持つIntentRequestを処理します。
  • その他のスロット状態データが条件の場合、特定のセッションアトリビュートなどで判断します。
  • リクエストのデータから取得できる任意の条件で判断します。

Alexa Skills Kit SDKを使ってcanHandleメソッドを実装し、特定のハンドラーが処理できるリクエストを判断します。以下は、リクエストタイプに基づいて判断を行うシンプルなハンドラーを定義する方法の例です。

クリップボードにコピーされました。

このサンプルコードはAlexa Skills Kit SDK for Node.js(v2)を使用しています。

この例では、LaunchRequestHandlerというリクエストハンドラーを定義しています。リクエストタイプがLaunchRequestの場合、canHandleメソッドはtrueを返します。

const LaunchRequestHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'LaunchRequest';
  },
  // このハンドラーのその他のメソッド...
};

クリップボードにコピーされました。

このサンプルコードはAlexa Skills Kit SDK for Pythonを使用しています。

この例では、LaunchRequestHandlerというリクエストハンドラーを定義しています。リクエストタイプがLaunchRequestの場合、canHandleメソッドはtrueを返します。Python SDKが提供するユーティリティ関数のutilsクラスにより、受信リクエストの評価が簡単になります。

from ask_sdk_core.handler_input import HandlerInput
from ask_sdk_core.dispatch_components import AbstractRequestHandler
from ask_sdk_core.utils import is_request_type


class LaunchRequestHandler(AbstractRequestHandler):
    """スキルを起動するハンドラー"""
    def can_handle(self, handler_input):
        # type: (HandlerInput) -> bool
        return is_request_type("LaunchRequest")(handler_input)

    # このハンドラーのその他のメソッド...

クリップボードにコピーされました。

このサンプルコードはAlexa Skills Kit SDK for Javaを使用しています。

この例では、LaunchRequestHandlerというリクエストハンドラーを定義しています。リクエストタイプがLaunchRequestの場合、canHandleメソッドはtrueを返します。Java SDKが提供するstaticメソッドのPredicatesクラスにより、受信リクエストの評価が簡単になります。

package handlers;

import com.amazon.ask.dispatcher.request.handler.HandlerInput;
import com.amazon.ask.dispatcher.request.handler.RequestHandler;
import com.amazon.ask.model.LaunchRequest;
import com.amazon.ask.model.Response;

import java.util.Optional;

import static com.amazon.ask.request.Predicates.requestType;

public class LaunchRequestHandler implements RequestHandler {

    @Override
    public boolean canHandle(HandlerInput input) {
        return input.matches(requestType(LaunchRequest.class));
    }

    // このハンドラーのその他のメソッド...
}

クリップボードにコピーされました。

スキルに送信されるJSONのrequest.typeプロパティに"LaunchRequest"が指定されます。この値を取得し、リクエストタイプを評価して、リクエストを適切なハンドラーにルーティングするコードを書く必要があります。

{
  "version": "1.0",
  "request": {
    "type": "LaunchRequest",
    "requestId": "amzn1.echo-api.request.1",
    "timestamp": "2016-10-27T18:21:44Z",
    "locale": "ja-JP"
  },
  "session": {},
  "context": {}
}

一般的に、インテントハンドラーはスキルのハンドラーの中で最も複雑です。詳細については、後述するインテントとスロットを処理するを参照してください。

リクエストを処理して応答を返す

スキルが受け取ったリクエストを特定のリクエストハンドラーにルーティングして以降は、ハンドラーがリクエストを処理して応答を返します。リクエストハンドラーのコードには、以下のようにスキルが実行する必要のある機能を記述します。

  • ユーザーリクエストに含まれるデータを分析して、適切な応答を決定します。
  • ダイアログのやり取りの一環としてユーザーに追加の情報をリクエストします。
  • 別のAlexa APIを呼び出して、ユーザーに関する情報を取得したり、他の関数を実行したりします。以下はその例です。
    • Alexa設定APIを呼び出して、ユーザーのタイムゾーンやその他の設定を取得します。
    • デバイスアドレスAPIを呼び出して、デバイスに設定された住所を取得します。
    • リマインダーAPIを呼び出して、ユーザーのリマインダーの作成や更新を行います。
  • その他のAPIを呼び出して、リクエストを実行するための情報を取得します。たとえば、ハンドラーで天気予報サービスAPIを呼び出して予報を取得したり、フライト追跡APIを呼び出してフライトを検索したりできます。

ほとんどのハンドラーは、Alexaがユーザーに読み上げるテキストを返します。一部のハンドラーでは、より特殊な応答を返す必要があります。たとえば、CanFulfillIntentRequestのハンドラーでは、スキルがユーザーのリクエストを受け入れられるかどうかを表すのに、独自のリクエスト形式を返す必要があります。ハンドラーで完了していないダイアログを処理する場合は、Dialog.Delegateディレクティブを返してAlexaにダイアログをデリゲートできます。

Alexa Skills Kit SDKを使っている場合、handleメソッドを実装してリクエストを処理し、結果を返します。handleメソッドはHandlerInputオブジェクトを受け取ります。このオブジェクトには、スキルに送信された完全なJSONリクエストが含まれています。このオブジェクトを使って、リクエストのデータにアクセスします。

この例は、Alexaにシンプルなようこそメッセージを読み上げさせ、Alexaアプリにシンプルなカードを表示するハンドラーです。

クリップボードにコピーされました。

このサンプルコードはAlexa Skills Kit SDK for Node.js(v2)を使用しています。

HandlerInput.responseBuilderメソッドはResponseBuilderオブジェクトを返します。このオブジェクトのメソッドを使って、テキスト読み上げとカード表示用の応答を作成します。

const LaunchRequestHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'LaunchRequest';
  },
  handle(handlerInput) {
    const speechText = 'ようこそ、アレクサスキルキットへ。こんにちは、と言ってみてください。';

    return handlerInput.responseBuilder
      .speak(speechText)
      .reprompt(speechText)
      .withSimpleCard('ハローワールド', speechText)
      .getResponse();
  },
};

クリップボードにコピーされました。

このサンプルコードはAlexa Skills Kit SDK for Pythonを使用しています。

handler_input.response_builderメソッドは、ResponseFactoryオブジェクトを返します。このオブジェクトのメソッドを使って、テキスト読み上げとカード表示用の応答を作成します。

from ask_sdk_core.skill_builder import SkillBuilder
from ask_sdk_core.dispatch_components import AbstractRequestHandler
from ask_sdk_core.utils import is_request_type
from ask_sdk_core.handler_input import HandlerInput
from ask_sdk_model.ui import SimpleCard
from ask_sdk_model import Response


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).ask(
            "続けて、こんにちは、と言ってみてください。").set_card(
            SimpleCard("ハローワールド", speech_text))
        return handler_input.response_builder.response

クリップボードにコピーされました。

このサンプルコードはAlexa Skills Kit SDK for Javaを使用しています。

HandlerInput.getResponseBuilder()メソッドは、ResponseBuilderオブジェクトを返します。このオブジェクトのメソッドを使って、テキスト読み上げとカード表示用の応答を作成します。

package handlers;

import com.amazon.ask.dispatcher.request.handler.HandlerInput;
import com.amazon.ask.dispatcher.request.handler.RequestHandler;
import com.amazon.ask.model.LaunchRequest;
import com.amazon.ask.model.Response;

import java.util.Optional;

import static com.amazon.ask.request.Predicates.requestType;

public class LaunchRequestHandler implements RequestHandler {

    @Override
    public boolean canHandle(HandlerInput input) {
        return input.matches(requestType(LaunchRequest.class));
    }

    @Override
    public Optional<Response> handle(HandlerInput input) {
        String speechText = "ようこそ、アレクサスキルキットへ。こんにちは、と言ってみてください。";
        return input.getResponseBuilder()
                .withSpeech(speechText)
                .withReprompt("続けて、こんにちは、と言ってみてください。")
                .build();
    }
}

クリップボードにコピーされました。

スキルが返すJSONのresponse.outputSpeechプロパティには、Alexaが読み上げるテキストが指定されています。

{
  "response": {
    "outputSpeech": {
      "type": "SSML",
      "ssml": "<speak>ようこそ、アレクサスキルキットへ。こんにちは、と言ってみてください。</speak>"
    },
    "reprompt": {
      "outputSpeech": {
        "type": "SSML",
        "ssml": "<speak>続けて、こんにちは、と言ってみてください。</speak>"
      }
    },
    "shouldEndSession": false
  },
  "version": "1.0",
  "sessionAttributes": {}
}

応答の返し方の詳細については、後述する応答を返すを参照してください。

インテントとスロットを処理する

インテントとは、ユーザーがしたいアクションのことです。インテントは、対話モデルを作成する際に定義します。ユーザーがインテントを呼び出すと、スキルに次の情報が指定されたIntentRequestを含むリクエストが送信されます。

{
  "version": "1.0",
  "request": {
    "type": "IntentRequest",
    "requestId": "amzn1.echo-api.request.1",
    "timestamp": "2019-03-21T22:32:09Z",
    "locale": "ja-JP",
    "intent": {
      "name": "HelloWorldWithNameIntent",
      "confirmationStatus": "NONE",
      "slots": {
        "firstName": {
          "name": "firstName",
          "value": "恵美",
          "confirmationStatus": "NONE"
        },
        "favoriteColor": {
          "name": "favoriteColor",
          "confirmationStatus": "NONE"
        }
      }
    },
    "dialogState": "STARTED"
  },
  "session": {},
  "context": {}
}

スキルでは対話モデルに定義済みの想定されるインテントをすべて処理する必要があります。複数のインテントで使用されるハンドラーを作成することもできますが、通常は各インテントのハンドラーでこの処理を行います。前述のハンドラーが処理できるリクエストを判断するを参照してください。

詳細については、以下のセクションを参照してください。

リクエストからスロット値を取得する

インテントには1つ以上のスロットを含めることができます。スロットを使うと、都市名や特定の日付といった、可変情報をユーザーから取得できます。スロット値は通常、ユーザーのリクエストを実行するのに必要な重要な情報です。たとえば、PlanMyTripインテントは、ユーザーの出発地、目的地、出発日がわからなければフライトを検索できません。

対話モデルでインテントに定義したすべてのスロットは、IntentRequestrequest.intent.slotsプロパティに含まれます。slotsプロパティは、キーと値のペアのマップです。キーがスロット名、値がSlotオブジェクトとなります。リクエストのSlotオブジェクトには、少なくともnameプロパティが含まれます。ユーザーからスロット値が提供されない場合、valueプロパティは含まれません。

Alexa Skills Kit SDKには、より直接的にスロット値を取得できるユーティリティ関数やヘルパー関数が提供されています。以下の例を参照してください。

クリップボードにコピーされました。

このサンプルコードはAlexa Skills Kit SDK for Node.js(v2)を使用しています。

このハンドラーでは、RequestEnvelopeUtilsで提供される関数を使ってスロット値を取得しています。スロットには、リクエストのintentオブジェクトからアクセスすることもできます。値以外のデータも取得する必要がある場合は、この方法が便利です。


const {
  getRequestType,
  getIntentName,
  getSlotValue,
  getDialogState,
} = require('ask-sdk-core');

const HelloWorldWithNameIntentHandler = {
  canHandle(handlerInput) {
    return getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
    && getIntentName(handlerInput.requestEnvelope) === 'HelloWorldWithNameIntent';
  },
  handle(handlerInput) {
    const firstNameValue = getSlotValue(handlerInput.requestEnvelope, 'firstName');
    const speechText = firstNameValue
    ? `こんにちは、${firstNameValue}さん。`
    : `こんにちは、 すみません、まだお名前を聞いていませんでした。`;

  return handlerInput.responseBuilder
    .speak(speechText)
    .getResponse();
  }
};

クリップボードにコピーされました。

このサンプルコードはAlexa Skills Kit SDK for Pythonを使用しています。

このハンドラーは、get_slot_valueメソッドを使ってスロット値を取得しています。値以外のデータも取得する必要がある場合は、get_slotメソッドを使用することもできます。

from ask_sdk_core.dispatch_components import AbstractRequestHandler
from ask_sdk_core.utils import is_intent_name, get_slot_value
from ask_sdk_core.handler_input import HandlerInput
from ask_sdk_model import Response


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

        # このハンドラーはユーザー名が提供された場合に名前を呼びかけてあいさつします
        # 名前が提供されない場合は一般的なハローワールド応答のみを返します

        first_name_value = get_slot_value(
            handler_input=handler_input, slot_name="firstName")

        if first_name_value:
            speech_text = "こんにちは、{}さん。".format(first_name_value)
        else:
            speech_text = "こんにちは、 すみません、まだお名前を聞いていませんでした。"

        return handler_input.response_builder.speak(speech_text).response

クリップボードにコピーされました。

このサンプルコードはAlexa Skills Kit SDK for Javaを使用しています。

このハンドラーは、RequestHelperメソッドを使ってスロット値を取得しています。スロットには、リクエストのintentオブジェクトからアクセスすることもできます。値以外のデータもスロットから取得する必要がある場合は、この方法が便利です。

package handlers;

import static com.amazon.ask.request.Predicates.intentName;

import java.util.Optional;

import com.amazon.ask.dispatcher.request.handler.HandlerInput;
import com.amazon.ask.dispatcher.request.handler.impl.IntentRequestHandler;
import com.amazon.ask.model.IntentRequest;
import com.amazon.ask.model.Response;
import com.amazon.ask.request.RequestHelper;

public class HelloWorldWithNameIntentHandler implements IntentRequestHandler {

    //  このハンドラーではIntentRequestリクエストの処理だけを行うため、
    //  汎用インターフェース(RequestHandler)の代わりにタイプを指定した
    //  IntentRequestHandlerインターフェースを実装します。これにより、
    //  リクエストを取得して、タイプを確認し、正しいタイプに割り付ける必要がなくなります。

    @Override
    public boolean canHandle(HandlerInput handlerInput, IntentRequest intentRequest) {
        return handlerInput.matches(intentName("HelloWorldWithNameIntent"));
    }

    @Override
    public Optional<Response> handle(HandlerInput handlerInput, IntentRequest intentRequest) {
        // このハンドラーはユーザー名が提供された場合に名前を呼びかけてあいさつしますが、
        // 名前が提供されない場合は一般的なハローワールド応答のみを返します

        RequestHelper requestHelper = RequestHelper.forHandlerInput(handlerInput);

        // ヘルパーメソッドを使って、Optionalにラップされたスロット値を取得します。
        Optional<String> firstNameValue = requestHelper.getSlotValue("firstName");

        // Optional.map()メソッドを使い、スロットに値が含まれているかどうかに基づいて
        // 別の応答を作成します。
        String speechText = firstNameValue.map(firstName -> "こんにちは、" + firstName + "さん。")
            .orElse("こんにちは、 すみません、まだお名前を聞いていませんでした。");

        return handlerInput.getResponseBuilder()
                .withSpeech(speechText)
                .build();
    }
}

クリップボードにコピーされました。

以下の例は、2つのスロットを持つリクエストのJSONコードです。firstNameスロットにはvalueプロパティがありますが、favoriteColorスロットにはありません。この例では、ユーザーがfirstNameスロットの値のみを提供し、favoriteColorスロットの値は提供していません。

{
  "version": "1.0",
  "session": {},
  "context": {},
  "request": {
    "type": "IntentRequest",
    "requestId": "amzn1.echo-api.request.e8d12d6f-bb5b-48f2-8899-7cbbd764bf26",
    "timestamp": "2019-02-07T21:21:06Z",
    "locale": "ja-JP",
    "intent": {
      "name": "HelloWithNameIntent",
      "slots": {
        "firstName": {
          "name": "firstName",
          "value": "早紀",
          "confirmationStatus": "NONE"
        },
        "favoriteColor": {
          "name": "favoriteColor",
          "confirmationStatus": "NONE"
        }
      },
      "confirmationStatus": "NONE"
    }
  }
}

スロット値の解決を使用する

カスタムスロットタイプを使うスロットの場合、SlotオブジェクトにはResolutionsオブジェクトが含まれます。このオブジェクトには、スロットのエンティティ解決の結果が含まれます。カスタムスロットタイプ値に同義語と一意のIDが含まれる場合、エンティティ解決の結果は非常に有効です。複数の同義語を1つのIDにマッピングして、コード内で列挙値として活用できるためです。

エンティティ解決の詳細については、スロットタイプ値の同義語とIDを定義する(エンティティ解決)を参照してください。

ダイアログを使ってスロット値を収集する

ユーザーが1回の発話で必須のスロット値をすべて提供してくれることはほとんどありません。そのため、ダイアログのやり取りを何度か行って、ユーザーから追加の値を引き出す必要があります。その1つの方法がダイアログモデルを設定してから、ダイアログをAlexaにデリゲートすることです。これによりコードがシンプルになります。インテントハンドラーが、必須スロット値に有効な値が入っていることを確認する必要がなくなるためです。

ダイアログのデリゲートの例と詳細は、Alexaにダイアログをデリゲートするを参照してください。

プログレッシブ応答を送信する

ユーザーのリクエストに完全な応答を返す前に、スキルでAlexa APIを呼び出し、割り込みのSSMLコンテンツ(読み上げファイルや短い音声ファイルなど)を送ることができます。スキルが完全な応答を作成する前に集中処理を行う必要があるときに便利です。

たとえば、次のような対話が考えられます。

ユーザー: アレクサ、タクシー予約に空港までの配車を予約して。 (通常のIntentRequestがタクシー予約スキルに送られます)
このインテントを満たすのに必要なすべての情報を収集するための以後のやりとり

Alexa: わかりました。配車の詳細を調べるまでお待ちください...(スキルが完全な応答を準備する間のプログレッシブ応答です。)
Alexa: お待たせしました。配車を予約しました。30分以内にご自宅に到着します。IntentRequestへの通常の応答)

プログレッシブ応答によって、完全な応答を待つまでの間、ユーザーを飽きさせずにいることができます。

プログレッシブ応答を送るには、プログレッシブ応答APIを呼び出して、SSMLでdirectiveを送るか、Alexaがユーザーに話すプレーンテキストを送ります。完全な応答を返す前に、プログレッシブ応答APIに最大5つまでディレクティブを送信できます。

プログレッシブ応答APIを使うと、ハンドラーがプログレッシブ応答のAPI呼び出しを実行します。この場合でも、ほかのリクエストと同様、Alexaに最終的な応答を返す必要はあります。

詳細については、ユーザーにプログレッシブ応答を送信するを参照してください。

応答を返す

特定のリクエストのハンドラーが処理を完了したら、ハンドラーはAlexaに応答を送信します。応答は、Alexaに実行内容を指示するJSON構造です。LaunchRequestIntentRequestタイプのリクエストへの応答には通常、少なくともAlexaが音声に変換してユーザーに読み上げるテキスト文字列が含まれます。これ以外の情報を応答として送信するスキルもあります。たとえば、Alexaアプリに表示するカード、オーディオを再生したり、画面付きデバイスに情報を表示したりするディレクティブなどです。リクエストのタイプによっては、特定の種類の応答しか送信できないものもあります。

Alexa Skills Kit SDKを使用している場合は、handleメソッドがスキルの応答を返します。SDKには、ResponseBuilderオブジェクトやhelperメソッドが提供されているため、JSON形式の応答を簡単に作成できます。JSON形式の応答の詳細については、応答の形式を参照してください。

音声とカードで応答する

シンプルな応答には音声に変換されるテキストだけが含まれる場合と、Alexaアプリに表示するカードも含まれる場合があります。これらの情報は、応答のoutputSpeechプロパティとcardプロパティに指定します。

クリップボードにコピーされました。

このサンプルコードはAlexa Skills Kit SDK for Node.js(v2)を使用しています。

ResponseBuilderオブジェクトのspeakメソッドとwithSimpleCardメソッドを使って、読み上げとカードのテキストを定義します。getResponse()メソッドは、指定されたプロパティを含む応答を返します。

return handlerInput.responseBuilder
  .speak("これはAlexaが読み上げるテキストです。Alexaアプリを開いてカードを見てください。")
  .withSimpleCard(
    "これはカードのタイトルです",
    "これはAlexaアプリのカードに表示されるテキストです。")
  .getResponse();

クリップボードにコピーされました。

このサンプルコードはAlexa Skills Kit SDK for Pythonを使用しています。

ResponseFactoryオブジェクトのspeakメソッドとset_cardメソッドを使って、応答に含める読み上げとカードのテキストを定義します。

from ask_sdk_core.dispatch_components import AbstractRequestHandler
from ask_sdk_core.utils import 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 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 = "これはAlexaが読み上げるテキストです。Alexaアプリを開いてカードを見てください。"
        card_title = "これはカードのタイトルです"
        card_text = "これはAlexaアプリのカードに表示されるテキストです。"

        return handler_input.response_builder.speak(speech_text).set_card(
            SimpleCard(card_title, card_text)).response

クリップボードにコピーされました。

このサンプルコードはAlexa Skills Kit SDK for Javaを使用しています。

ResponseBuilderオブジェクトのwithSpeech()メソッドとwithSimpleCardメソッドを使って、読み上げとカードのテキストを定義します。build()メソッドは、指定されたプロパティを含む応答を返します。

@Override
public Optional<Response> handle(HandlerInput handlerInput, IntentRequest intentRequest) {

    String speechText ="これはAlexaが読み上げるテキストです。Alexaアプリを開いてカードを見てください。";
    String cardTitle = "これはカードのタイトルです";
    String cardText = "これはAlexaアプリのカードに表示されるテキストです。";

    return handlerInput.getResponseBuilder()
        .withSpeech(speechText)
        .withSimpleCard(cardTitle, cardText)
        .build();
}

クリップボードにコピーされました。

このJSON応答は、シンプルなプレーンテキストのoutputSpeech文字列を返す方法の例です。この応答には、タイトルとシンプルなコンテンツを表示するカードも含まれています。

{
  "version": "1.0",
  "response": {
    "outputSpeech": {
      "type": "PlainText",
      "text": "これはAlexaが読み上げるテキストです。Alexaアプリを開いてカードを見てください。"
    },
    "card": {
      "type": "Simple",
      "title": "これはカードのタイトルです",
      "content": "これはAlexaアプリのカードに表示されるテキストです。"
    }
  }
}

outputSpeechは、プレーンテキストか音声合成マークアップ言語(SSML)のいずれかの形式で指定できます。SSMLは、合成音声の生成用にテキストをマークアップする標準的な手段を提供するマークアップ言語です。SSMLを使うと、短い音響効果を再生させたり、話し言葉を強調したり、発音を変えたりといった効果をAlexa音声に加えることができます。Alexa Skills Kitでサポートされているのは、SSML仕様で定義されているタグのサブセットです。

outputSpeechでSSMLを使う場合、SSMLプロパティにテキストを指定し、typeSSMLに設定し、SSMLマークアップを使ったテキストを<speak>タグで囲みます。ヘルパーメソッドを使ってoutputSpeechを設定すると、上記の処理はAlexa Skills Kit SDKで自動的に行われます。

SSMLアトリビュートを囲む際、二重引用符(")を必ずエスケープするようにしてください。

クリップボードにコピーされました。

このサンプルコードはAlexa Skills Kit SDK for Node.js(v2)を使用しています。

この例では、speechText文字列にSSMLタグが含まれています。これにより、「こんにちは」という単語が強調され、「うふふ」というSpeechconが使われます。 speakメソッドは自動的に文字列を<speak>タグで囲み、ssmlプロパティを設定して、typeSSMLにセットします。

const HelloSSMLResponseIntent = {
  canHandle(handlerInput){
    return handlerInput.requestEnvelope.request.type === 'IntentRequest'
      && handlerInput.requestEnvelope.request.intent.name === 'HelloSSMLResponseIntent';
  },
  handle(handlerInput){
    const speechText =
        "<emphasis level=\"strong\">こんにちは</emphasis>、皆さん" +
        "<say-as interpret-as=\"interjection\">うふふ</say-as>";
    return handlerInput.responseBuilder
        .speak(speechText)
        .withSimpleCard("ハローワールド", "ハローワールド(気持ちを込めて)")
        .getResponse();
  }
}

クリップボードにコピーされました。

このサンプルコードはAlexa Skills Kit SDK for Pythonを使用しています。

この例では、speech_text文字列にSSMLタグが含まれています。これにより、「こんにちは」という単語が強調され、「うふふ」というSpeechconが使われます。 speakメソッドは自動的に文字列を<speak>タグで囲み、ssmlプロパティを設定して、typeSSMLにセットします。

from ask_sdk_core.dispatch_components import AbstractRequestHandler
from ask_sdk_core.utils import is_intent_name
from ask_sdk_core.handler_input import HandlerInput
from ask_sdk_model import Response


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 = ('<emphasis level="strong">こんにちは</emphasis>、皆さん'
                       '<say-as interpret-as="interjection">うふふ</say-as>')

        return handler_input.response_builder.speak(speech_text).response

クリップボードにコピーされました。

このサンプルコードはAlexa Skills Kit SDK for Javaを使用しています。

この例では、speechText文字列にSSMLタグが含まれています。これにより、「こんにちは」という単語が強調され、「うふふ」というSpeechconが使われます。 withSpeechメソッドは自動的に文字列を<speak>タグで囲み、ssmlプロパティを設定して、typeSSMLにセットします。

@Override
public Optional<Response> handle(HandlerInput handlerInput) {
    String speechText =
            "<emphasis level=\"strong\">こんにちは</emphasis>、皆さん" +
            "<say-as interpret-as=\"interjection\">うふふ</say-as>";
    return handlerInput.getResponseBuilder()
            .withSpeech(speechText)
            .build();
}

クリップボードにコピーされました。

このJSON応答では、outputSpeech文字列で提供されるテキストにSSMLタグが含まれています。これにより、「こんにちは」という単語が強調され、「うふふ」というSpeechconが使われます。 SSMLが正しくレンダリングできるようにするには、outputSpeechtypeプロパティをSSMLに設定し、読み上げテキストをssmlプロパティで定義して<speak>タグで囲む必要があります。

{
  "version": "1.0",
  "response": {
    "outputSpeech": {
      "type": "SSML",
      "ssml": "<speak><emphasis level=\"strong\">こんにちは</emphasis>、皆さん。<say-as interpret-as=\"interjection\">うふふ</say-as>!</speak>"
    </span>},
    "card": {
      "type": "Simple",
      "title": "ハローワールド",
      "content": "ハローワールド(気持ちを込めて)"
    }
  }
}

ユーザーの応答を聞き、必要に応じて再プロンプトを出す

スキルが応答を返せば対話が終わるというスキルはほとんどありません。たいていは、スキルの応答でユーザーに何かを言うよう促し、スキルに送る新しいリクエストを引き出す必要があります。また、LaunchRequestの応答で簡単なようこそメッセージを読み上げ、ユーザーにしたいことをたずねるという方法も一般的です。例:

ユーザー: 宇宙博士を開いて。
Alexa: 宇宙博士へようこそ。私は、ある惑星からほかの惑星まで旅するのにかかる時間から、宇宙についてのジョークまで、宇宙に関するいろいろなことを知っています。何を知りたいですか?
Alexaはユーザーの応答を待機中です。

ユーザー: 火星の天気を教えて。 (この応答はGetPlanetWeatherインテントにマッピングされます。)
Alexa: 火星の最高気温は摂氏21度、最低気温は摂氏マイナス126度です。晴れですが、午後ところにより砂嵐となるでしょう。GetPlanetWeatherインテントからの応答です。)

Alexaにユーザーの応答を待機させるには、以下の手順を実行します。

  • shouldEndSessionプロパティをfalseに設定します。
  • 応答のrepromptプロパティに読み上げる内容を指定します。Alexaは、ユーザーの応答が理解できないときにこのテキストを読み上げます。

これは、Alexaにユーザーの応答を聞くように指示する際によくある手順です。

  1. Alexaは、スキルにIntentRequestLaunchRequestのいずれかを送信します。
  2. スキルは次のように応答します。
    • outputSpeechオブジェクトのテキストを読み上げます。
    • shouldEndSessionfalseにセットします。
    • repromptオブジェクトのテキストで再度ユーザーに問いかけます。
  3. Alexaは、outputSpeechに指定したテキストを読み上げてから、オーディオストリームを開いて、ユーザーの応答を8秒間待機します。デバイスによっては、このとき青いライトリングが点灯します。
  4. この後の状況としては以下のいずれかが考えられます。
    • ユーザーが対話モデルに一致する何かを答えます。たとえば、インテントのいずれかにマッピングされている発話などです。この場合、Alexaはスキルにユーザーの応答を表す新しいリクエスト(新しいIntentRequestなど)を送信します。再びステップ1からプロセスが開始します。
    • ユーザーが何も言わないことも考えられます。この場合、Alexaはrepromptに定義したテキストでユーザーに問いかけて、ストリームを再度開き、8秒間ユーザーの応答を待機します。
    • ユーザーが対話モデルに一致しないことを言う場合も考えられます。この場合も、Alexaはrepromptに定義したテキストでユーザーに問いかけて、ストリームを再度開き、8秒間ユーザーの応答を待機します。

Alexaが再プロンプトを読み上げるのは1回だけです。それでもユーザーの応答が理解できない場合、対話全体が終了します。

ユーザー: 宇宙博士を開いて。
Alexa: 宇宙博士へようこそ。私は、ある惑星からほかの惑星まで旅するのにかかる時間から、宇宙についてのジョークまで、宇宙に関するいろいろなことを知っています。何を知りたいですか?
ユーザー:(ユーザーが何かを言います。)
Alexa: 宇宙について私に何か聞いてください。
ユーザー: えーっと…
対話が終了します。

クリップボードにコピーされました。

このサンプルコードはAlexa Skills Kit SDK for Node.js(v2)を使用しています。

ResponseBuilderオブジェクトのrepromptメソッドは再プロンプトのテキストを設定し、shouldEndSessionfalseにセットします。これにより、Alexaにユーザーの応答を待機するよう指示します。

const HelloWorldIntentHandler = {
  canHandle(handlerInput) {
    return getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
    && getIntentName(handlerInput.requestEnvelope) === 'HelloWorldIntent';
  },
  handle(handlerInput) {
    const speechText = `宇宙博士へようこそ。私は、
    ある惑星からほかの惑星まで旅するのにかかる時間から、宇宙についてのジョークまで、
    宇宙に関するいろいろなことを知っています。何を知りたいですか?`;
    const repromptText = '宇宙について私に何か聞いてください。';

    return handlerInput.responseBuilder
      .speak(speechText)
      .reprompt(repromptText)
      .getResponse();
  }
};

クリップボードにコピーされました。

このサンプルコードはAlexa Skills Kit SDK for Pythonを使用しています。

ResponseFactoryオブジェクトのaskメソッドは再プロンプトのテキストを設定し、shouldEndSessionfalseにセットします。これにより、Alexaにユーザーの応答を待機するよう指示します。

from ask_sdk_core.dispatch_components import AbstractRequestHandler
from ask_sdk_core.utils import is_intent_name
from ask_sdk_core.handler_input import HandlerInput
from ask_sdk_model import Response


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 = ("宇宙博士へようこそ。私は、"
                       "ある惑星からほかの惑星まで旅するのにかかる時間から、宇宙についてのジョークまで、"
                       "宇宙に関するいろいろなことを知っています。何を知りたいですか?")

        reprompt_text = "宇宙について私に何か聞いてください。"

        return handler_input.response_builder.speak(speech_text).ask(
            reprompt_text).response

クリップボードにコピーされました。

このサンプルコードはAlexa Skills Kit SDK for Javaを使用しています。

ResponseBuilderオブジェクトのwithRepromptメソッドは再プロンプトのテキストを設定し、shouldEndSessionfalseにセットします。これにより、Alexaにユーザーの応答を待機するよう指示します。

@Override
public Optional<Response> handle(HandlerInput handlerInput) {
    String speechText = "宇宙博士へようこそ。私は、" +
        "ある惑星からほかの惑星まで旅するのにかかる時間から、宇宙についてのジョークまで、" +
        "宇宙に関するいろいろなことを知っています。何を知りたいですか?";

    String repromptText = "宇宙について私に何か聞いてください。";

    return handlerInput.getResponseBuilder()
        .withSpeech(speechText)
        .withReprompt(repromptText)
        .build();
}

クリップボードにコピーされました。

JSONでは、repromptプロパティに再プロンプトのテキストを設定し、shouldEndSessionfalseにセットします。これにより、Alexaにユーザーの応答を待機するよう指示します。

{
  "version": "1.0",
  "response": {
    "outputSpeech": {
      "type": "SSML",
      "ssml": "<speak>宇宙博士へようこそ。私は、ある惑星からほかの惑星まで旅するのにかかる時間から、宇宙についてのジョークまで、宇宙に関するいろいろなことを知っています。何を知りたいですか?</speak>"
    },
    "card": {
      "type": "Simple",
      "title": "宇宙博士のサンプル",
      "content": "宇宙博士へようこそ。私は、ある惑星からほかの惑星まで旅するのにかかる時間から、宇宙についてのジョークまで、宇宙に関するいろいろなことを知っています。何を知りたいですか?"
    },
    "reprompt": {
      "outputSpeech": {
        "type": "SSML",
        "ssml": "<speak>宇宙について私に何か聞いてください。</speak>"
      }
    },
    "shouldEndSession": false
  }
}

この会話のやり取りの間、スキルセッションは開いたままです。セッションが閉じると、対話は終了します。セッションの詳細については、スキルセッションとセッションアトリビュートの管理を参照してください。

ディレクティブを使って応答する

応答にディレクティブを含めることで、Alexaにほかのアクションを実行させることができます。ディレクティブはインターフェースに含まれています。たとえば、AudioPlayerインターフェースにはオーディオファイルの再生開始と停止のディレクティブが含まれています。ディレクティブの種類によって必要なプロパティが異なります。

ディレクティブを送信すると、スキルはインターフェース固有のリクエストを受け取ることができます。たとえば、AudioPlayer.Playを使ってオーディオの再生を開始すると、スキルはスキルに再生が開始したことを知らせるAudioPlayer.PlaybackStartedリクエストを受け取ります。

使用できるディレクティブと対応する応答の詳細については、関連するインターフェースのリファレンスを参照してください。

応答にディレクティブを含めるには、応答のdirectives配列にディレクティブを追加します。Alexa Skills Kit SDKには、応答にディレクティブを追加するヘルパーメソッドが用意されています。

入力エラーを処理する

音声インターフェースでは、ユーザーの無効なデータの入力を回避することができません。また、ユーザーの言葉を間違って解釈して、スロット値に誤りが生じる可能性もあります。こうしたエラーがないかをチェックし、適切に処理するコードを作成する必要があります。

カスタムスロットタイプの値

カスタムスロットタイプとして定義されたスロットが、そのタイプで定義された値のリストに含まれない値を受け取る場合があります。

たとえば、カスタムLIST_OF_SIGNSスロットタイプで12個の星座を定義したとします。ユーザーが「青色のホロスコープを教えて」と言った場合、AlexaはSignスロットに「青色」という単語が与えられたリクエストをサービスに送信します。この場合、Signの値を検証し、有効な星座ではないことをユーザーに伝えるコードが必要となります。

カスタムスロットタイプが列挙値として特定のリストに含まれる値だけを返すようにしたい場合は、スロットに検証ルールを設定してからダイアログをデリゲートします。これにより、Alexaはユーザーが無効な値を提供するとプロンプトを出すようになります。

空のスロット値

インテントに必須のスロット値がいくつかある場合、1回の発話ですべてをたずねるのではなく、何回かに分けてユーザーから情報を引き出すことができます。たとえば、PlanMyTripインテントには、fromCitytoCitytravelDateというスロットがあります。3つすべてを1回の発話で求めるのは無理があるため、次のように発話を定義します。

{
  "samples": [
    "{travelDate}に{fromCity}から{toCity}に行きたい",
    "{toCity}に行きたい",
    "{travelDate}に旅行に行きます",
    "{fromCity}からの旅行を計画します"
  ]
}

ユーザーが「札幌に行きたい」と言った場合、3つのスロットはすべてスキルに送られるIntentRequestに含まれますが、値はtoCityにしか入っていません。fromCitytravelDateはいずれも空です。

{
  "request": {
    "type": "IntentRequest",
    "requestId": "amzn1.echo-api.request.1",
    "timestamp": "2019-03-23T00:34:14Z",
    "locale": "ja-JP",
    "intent": {
      "name": "トリッププラン",
      "confirmationStatus": "NONE",
      "slots": {
        "toCity": {
          "name": "toCity",
          "value": "札幌",
          "confirmationStatus": "NONE"
        },
        "travelDate": {
          "name": "travelDate",
          "confirmationStatus": "NONE"
        },
        "fromCity": {
          "name": "fromCity",
          "confirmationStatus": "NONE"
        }
      }
    },
    "dialogState": "STARTED"
  },
  "version": "1.0",
  "session": {},
  "context": {}
}

このため、それらの値を使う前にスロットに値が入っているかどうかを確認します。リクエストからスロット値を取得するで説明した例では、値がnullではないことを確認しています。

または、Dialogディレクティブを使用して、次のようにスロット値を簡単に収集することもできます。Alexaにダイアログをデリゲートするを参照してください。