Alexaにダイアログをデリゲートする



Alexaにダイアログをデリゲートする

コードにスロット値を簡単に収集して確認できるようにするには、ダイアログモデルを作成して、ダイアログをAlexaにデリゲートします。ダイアログモデルは、プロンプト、およびスロット値とインテントを修正、検証、確認する発話を識別します。ダイアログをAlexaにデリゲートする場合、Alexaは、会話の次の手順を判断し、プロンプトを使用してユーザーに情報を求めます。

Alexaにダイアログをデリゲートする方法

Alexaにダイアログをデリゲートするには、2つの方法があります:

  • スキル全体、または特定のインテントのいずれかで、オートデリゲートを有効にします。この場合、Alexaは、ダイアログモデルに従ってすべてのダイアログの手順を完了します。ダイアログが完成すると、Alexaは単一のIntentRequestを送信します。
  • Dialog.Delegateディレクティブで手動でデリゲートします。この場合、Alexaは会話ごとにスキルに IntentRequestを送信します。
    • リクエストは、dialogStateSTARTEDIN_PROGRESS、またはCOMPLETEDに設定して、ダイアログの現在の状態を示します。
    • 完了していないダイアログには、スキルは、Dialog.Delegateディレクティブを返します。これにより、Alexaは次の手順のダイアログモデルを確認し、必要に応じて詳細な情報をユーザーにたずねるプロンプトを使用します。
    • すべての手順が完了したら、スキルはdialogStateCOMPLETEDに設定された最後のIntentRequestを受け取ります。

特定の対話に応じた正しいデリゲートルールは、ダイアログの複雑さによって異なります。コードでダイアログをまったく処理する必要がないため、オートデリゲートはもっと簡単です。Dialog.Delegateで手動でデリゲートする方がより柔軟性があります。値をデフォルトにするなど、スキルがランタイム中に意思決定を行えるためです。Dialogディレクティブと組み合わせることで、必要に応じて、ダイアログを完全に制御するためにDialog.Delegateを使用することもできます。

スキル全体に、包括的なデリゲートルールを設定することも、スキルの各インテントにデリゲートルールを設定することもできます。つまり、インテントの一部にオートデリゲートを使用して、それ以外には手動でデリゲート、またはデリゲートを行わないようにすることもできるということです。

簡単なダイアログを自動的にAlexaにデリゲートする

ダイアログの手順や入力するスロットが、ユーザーとのランタイム中の対話に応じて変化しない場合は、簡単なダイアログをデリゲートするよう、スキルを設定します。たとえば、惑星についての質問に答えるスキルについて考えてみましょう。スキルのインテントは、ユーザーがどの惑星についてたずねているか、名前を知る必要があります。この要件は、ダイアログ内での以前のやり取りに応じて変化するものではないため、これをAlexaにデリゲートします。

たとえば、この対話では、planetスロットの値を収集するためのすべてのやり取りが完了したら、Alexaはスキルにインテントを1つだけ送信します。

ユーザー: 他の惑星の天気はどう?
Alexa: どの惑星について知りたいですか? (AlexaはGetPlanetWeatherインテントのplanetスロットに定義されたプロンプトを使用して、ユーザーにプロンプトを出します。)
ユーザー: 太陽 (ユーザーがplanet スロットの検証ルールに合わない値でプロンプトに応答します)
Alexa: 太陽に天気はありません。代わりに惑星の名前を言ってください。 (ユーザーの値は検証を通りませんでした。Alexaは、使用できる値をたずねるプロンプトで応答します。)
ユーザー: 火星。
ここで、planetスロットには使用できる値が入力されたので、AlexaはスキルにIntentRequestを送信します。インテントはGetPlanetWeatherです。これには「火星」という値を含む単一のスロットplanetが含まれます。

このような対話を作成するには、次の手順を実行します。

  • GetPlanetWeatherインテントのplanetスロットを必須スロットとして設定して、ユーザーに惑星をたずねるプロンプトを出します。
  • planetスロットのスロット検証ルールを有効にします。この場合、2つのルールがあります:
    • 値セットのみを拒否する:「太陽」などの特定の値を拒否して、もっとコンテキストに適したプロンプトを出します。このルールが最初に適用されます。
    • スロットタイプの値と同義語のみを受け付ける:スロットのカスタムタイプに定義された値のみを受け付けます。ユーザーの値が最初のルールを通過した場合に、このルールが適用されます。
  • ダイアログをAlexaに自動的にデリゲートするには、GetPlanetWeatherを設定します。

この例では、このシナリオのGetPlanetWeatherのインテントハンドラーを示しています。ハンドラーはダイアログの状態を確認したり、planetスロット値が有効な惑星を識別しているかどうかを検証する必要はありません。なぜなら、ダイアログモデルのスロット検証とオートデリゲートは、リクエストがスキルに送信される前にこれを処理するためです。

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

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

この例では、惑星についての詳細は、PLANETSマップに保存されています。これは、惑星についてのプロパティを、カスタムスロットタイプであるPlanetのスロット値に定義されたIDにマッピングします。

const GetPlanetWeatherHandler = {
  canHandle(handlerInput){   
    const request = handlerInput.requestEnvelope.request;
    return (request.type === 'IntentRequest'
        && request.intent.name === 'GetPlanetWeather');
  },
  handle(handlerInput){

    const intent = handlerInput.requestEnvelope.request.intent;

    let planetId = intent.slots.Planet.resolutions.resolutionsPerAuthority[0].values[0].value.id;
    let planet = PLANETS[planetId];    
    
    let speechOutput = "" + planet.PrintName + "では、" + planet.Weatherとなるでしょう;

    return handlerInput.responseBuilder
      .speak(speechOutput)
      .withShouldEndSession (true)
      .getResponse();
  }
};

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

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

この例では、惑星についての詳細は、Planet列挙値に保存されています。この列挙値には、天気について説明した文字列などの情報のプロパティと、データを取得するためのgetterメソッドが含まれおり、カスタムスロットタイプであるPlanetのスロット値に定義されたIDと照合します。

package com.ask.factskill.handlers;

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

import com.ask.factskill.enums.Planet;
import java.util.Optional;

import static com.amazon.ask.request.Predicates.intentName;
import static com.amazon.ask.request.Predicates.requestType;
import static com.ask.factskill.util.Constants.*;

public class GetPlanetWeatherHandler implements IntentRequestHandler {

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

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

        Intent intent = intentRequest.getIntent();

        Slot planetSlot = intent.getSlots().get(PLANET_SLOT);
        String planetId = planetSlot
                .getResolutions()
                .getResolutionsPerAuthority().get(0).getValues()
                .get(0).getValue().getId();

        Planet planet = Planet.valueOf(planetId);

        String speechText = "On " + planet.getPrintName() + ", you can expect " +
                planet.getPlanetWeather();

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

最初のダイアログの完了後に別のダイアログを開始する

オートデリゲートが有効な場合でもDialog.Delegateを使うシナリオが1つあります。インテントハンドラーが完了したダイアログからDialog.Delegateを返して、Alexaに別のインテントを呼び出して新しいダイアログを開始するよう指示します。

たとえば、BookFlightインテントのダイアログを完了した後、AlexaにBookRentalCarのダイアログを開始するよう指示する場合などです。BookRentalCarのオートデリゲートが有効な場合、Alexaはダイアログのすべてのステップを処理してから、スキルにBookRentalCarの完了したIntentRequestを送信します。ダイアログが完了したら、スキルはIntentRequestを1つ受け取ります。このとき、dialogStateCOMPLETEDに設定されます。

2つ目のダイアログをトリガーするには、Dialog.Delegateを返す際にupdatedIntentを新しいインテントに設定します。新しいインテントでもオートデリゲートが有効な場合、Alexaはダイアログ全体を処理し、スキルは再びIntentRequestを受け取ります。ダイアログが完了すると、dialogStateCOMPLETEDに設定します。

ダイアログ中にインテントの変更やスロット値の更新を行うを参照してください。

ランタイム時に会話を変更するために手動でデリゲートする

ダイアログ中にもっと柔軟性が必要になった場合、Dialog.Delegateディレクティブを使用します。スキルは、対話のターンごとにIntentRequestを受け取るので、コードはランタイム中の意思決定ができ、デリゲート前に他のアクションが実行できるようになります。複雑なダイアログの場合に、会話の流れをもっとスムーズにしたい場合に便利です。

手動デリゲートが適しているシナリオには、以下のようなものがあります:

  • サービスで既に提供されている情報をユーザーがたずねないようにするために、必要なスロットの詳細を設定する必要がある場合。
  • ユーザーの応答に応じて、さらに複雑なランタイム中の意思決定を行い、ダイアログでデリゲートと手動制御を組み合わせて使用する必要がある場合。

これらのシナリオの例については、次のシナリオを参照してください。

デフォルト値を設定するためにダイアログを手動でデリゲートする

PlanMyTripIntentインテントには、ユーザーの出発地を収集するfromCityスロットがあります。リクエストを完了するには、このスロットが必須ですが、通常、この値は変化しないため、スキルはユーザーの指定するデフォルトの出発地を保存します。ユーザーがこのデフォルトの出発地をシステムに保存した場合、オートデリゲートは毎回出発地をユーザーにたずねることになるので、ユーザーエクスペリエンス的にはよくありません。

代わりに、入力するインテントを更新して、fromCityにデフォルト値を設定し、ダイアログをAlexaにデリゲートするようにできます。Alexaはスロット入力のプロンプトをスキップして、ダイアログの次の手順を実行します:

ユーザー: アレクサ、トリッププランを開いて、京都に行きたいと言って(AlexaはPlanMyTripIntentインテントのtoCityの値に「京都」が入力されているが、fromCityには何も入力されていないIntentRequestを送信します。)
スキルはユーザーを検出して、通常のデフォルトの都市が「神戸」であると判断します。従って、スキルは updatedIntent.slots.fromCity神戸に設定されたDialog.Delegateディレクティブを返します。

Alexa: 出発する日時はいつですか?fromCityを収集するために、Alexaはプロンプトをスキップします。これは既に入力されているためです。その後残りのスロットについての処理を続行します。)
対話が続きます。Alexaは、旅行の日付、旅行のモード、旅行についてのその他の情報を収集するために、引き続きプロンプトを使用します...

Alexa: わかりました。12月10日の神戸から京都への旅行を保存します。 (この対話中に、fromCityがたずねられなかった場合でも、スキルにはこのスロット値を含める必要があることに注意してください。)

このダイアログは別のシナリオで動作します:

  • ユーザーが、都市を指定して対話を開始した場合、fromCityにその値が入力され、デフォルト値は使用されません。
  • ユーザーがデフォルトの都市を以前に設定しなかった場合は、fromCityは空のままになるため、Alexaはダイアログモデルに従って、ユーザーにプロンプトを出します。

このような対話を作成するには、次の手順を実行します。

  • 必要なスロット(toCity, fromCitytravelDate)に応じて、PlanMyTripIntentインテントの該当するスロットを設定し、プロンプトを出します。
  • PlanMyTripIntentインテントのオートデリゲートを無効にします。
  • 出発地が提供されない場合は、fromCityスロットをデフォルト値として、Alexaとの残りのダイアログをデリゲートするようスキルにコードを追加します。

これを行うには、コードに別のハンドラーを使うこともできます。たとえば、次のようになります:

  • StartedPlanMyTripHandlerdialogStateSTARTEDの場合、PlanMyTripIntentリクエストを処理します。
    • fromCityスロットの値を確認します。スロットが空の場合は、ユーザーがよく使うデフォルトの出発地を取得し、インテントのfromCityスロット値を更新します。
    • Dialog.Delegateを返して、ディレクティブのupdatedIntentプロパティに含まれるこの更新したインテントを渡します。
    • Dialog.Delegateを返す場合の、スロット値の変更についての詳細は、ダイアログ中にスロット値と確認ステータスを更新するを参照してください。
  • InProgressPlanMyTripHandlerdialogStateIN_PROGRESSの場合、リクエストを処理します。すべてのリクエストにDialog.Delegateを返すだけです。
  • CompletedPlanMyTripHandlerdialogStateCOMPLETEDの場合、PlanMyTripIntentリクエストを処理します。ダイアログが完了しているため、このハンドラーに送信されるインテントは、常に入力されたすべての必須スロットを含んでいます。ユーザーのリクエストを完了するには、スロット値を使用します。

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

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

const StartedPlanMyTripHandler = {
  canHandle(handlerInput){
    const request = handlerInput.requestEnvelope.request;
    return request.type === 'IntentRequest' &&
      request.intent.name === 'PlanMyTripIntent' &&
      request.dialogState === 'STARTED';
  },
  handle(handlerInput){
    const currentIntent = handlerInput.requestEnvelope.request.intent;       
    let fromCity = currentIntent.slots.fromCity;

    // ユーザーがスロットを入力しなかった場合、fromCityの値は空です。この例では、
    // getUserDefaultCity()は、永続ストレージから、ユーザーのデフォルトの都市を取得します。
    if (!fromCity.value) {
      currentIntent.slots.fromCity.value = getUserDefaultCity();
    }

    // the Dialog.Delegateディレクティブを返します
    return handlerInput.responseBuilder
      .addDelegateDirective(currentIntent)
      .getResponse();
  }
};

const InProgressPlanMyTripHandler = {
  canHandle(handlerInput) {
    const request = handlerInput.requestEnvelope.request;
    return request.type === 'IntentRequest' &&
      request.intent.name === 'PlanMyTripIntent' &&
      request.dialogState === 'IN_PROGRESS';
  },
  handle(handlerInput) {
    const currentIntent = handlerInput.requestEnvelope.request.intent;
    return handlerInput.responseBuilder
      .addDelegateDirective(currentIntent)
      .getResponse();
  },
};

const CompletedPlanMyTripHandler = {
  canHandle(handlerInput) {
    const request = handlerInput.requestEnvelope.request;
    return request.type === 'IntentRequest' && 
      request.intent.name === 'PlanMyTripIntent' &&
      request.dialogState === 'COMPLETED';
  },
  handle(handlerInput) {
    // このインテントがトリガーされた場合、すべての必須スロットが入力されます。
    // このためデータを旅行についての応答にまとめます...
  },
};

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

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

// これらのハンドラーのそれぞれは、Javaクラスです。簡単に説明すると、
// パッケージと各クラスのimport宣言は表示されません。

/**
 * *****************************************
 * StartedPlanMyTripHandler class
 * *****************************************
 */
public class StartedPlanMyTripHandler implements IntentRequestHandler {
    @Override
    public boolean canHandle(HandlerInput handlerInput, IntentRequest intentRequest) {
        return (handlerInput.matches(intentName("PlanMyTripIntent"))
            && intentRequest.getDialogState() == DialogState.STARTED);
    }

    @Override
    public Optional<Response> handle(HandlerInput handlerInput, IntentRequest intentRequest) {
        System.out.println("In StartedPlanMyTripHandler");

        Intent intent = intentRequest.getIntent();

        Slot fromCitySlot = intent.getSlots().get(FROM_CITY_SLOT_NAME);
        if (fromCitySlot.getValue() == null){
            // ユーザーがスロットを入力しなかった場合、getValueはnullを返します 。
            //この例では、getUserDefaultCity()が永続ストレージから
            // ユーザーのデフォルト値を取得します。
            Slot updateSlot = Slot.builder()
                    .withName(FROM_CITY_SLOT_NAME)
                    .withValue(getUserDefaultCity())
                    .build();

            // 更新したスロットをintentオブジェクトにプッシュします
            intent.getSlots().put(FROM_CITY_SLOT_NAME, updateSlot);
        }

        // the Dialog.Delegateディレクティブを返します
        return handlerInput.getResponseBuilder()
                .addDelegateDirective(intent)
                .build();
    }
}

/**
 * *****************************************
 * InProgressPlanMyTripHandler class
 * *****************************************
 */
public class InProgressPlanMyTripHandler implements IntentRequestHandler {

    @Override
    public boolean canHandle(HandlerInput handlerInput, IntentRequest intentRequest) {
        return (handlerInput.matches(intentName("PlanMyTripIntent"))
            && intentRequest.getDialogState() == DialogState.IN_PROGRESS);
    }

    @Override
    public Optional<Response> handle(HandlerInput handlerInput, IntentRequest intentRequest) {
        System.out.println("In InProgressPlanMyTripHandler");

        return handlerInput.getResponseBuilder()
                .addDelegateDirective(intentRequest.getIntent())
                .build();
    }
}

/**
 * *****************************************
 * CompletedPlanMyTripHandler class
 * *****************************************
 */
public class CompletedPlanMyTripHandler implements IntentRequestHandler {
    @Override
    public boolean canHandle(HandlerInput handlerInput, IntentRequest intentRequest) {
        return (handlerInput.matches(intentName("PlanMyTripIntent"))
            && intentRequest.getDialogState() == DialogState.COMPLETED);
    }

    @Override
    public Optional<Response> handle(HandlerInput handlerInput, IntentRequest intentRequest) {
        System.out.println("In CompletedPlanMyTripHandler");

        // このインテントがトリガーされた場合、すべての必須スロットが入力されます。
        // このためデータを旅行についての応答にまとめます...
    }
}

複雑なダイアログを処理するために、デリゲートを組み合わせて手動で制御する

コーヒーショップから飲み物を注文するスキルには、ユーザーが注文したい飲み物の種類についての情報を収集する、いくつかのスロットを持つOrderIntentがあります。drinkスロットは、ユーザーが注文した飲み物がメニューにあるかどうかを確認するためのスロット検証を使用して設定します。インテントには、drinkの値に応じて入力する必要のある、追加スロットがあります:

  • drinkが「コーヒー」の場合は、coffeeRoast
  • drinkが「お茶」の場合は、teaType

この場合、スキルは対話中に何の値を引き出すかの意思決定を行う必要があります。これを管理するには、スキルはdrinkスロットを収集して検証するDialog.Delegateを返して、次にcoffeRoastteaTypeを入力するためにダイアログを手動で処理します。

ユーザー: アレクサ、コーヒーショップを開いて注文を始めて
Alexa: コーヒーとお茶のどちらにしますか?
ユーザー:( AlexaはスキルにOrderIntentを持っているIntentRequestを送信します。drinkスロットには、無効な値が含まれます。)
ダイアログが完了していないので、スキルはDialog.Delegateを返します。

Alexa: 靴はメニューにありません。コーヒーとお茶のどちらにしますか? (Alexaはdrinkスロットのスロット検証ルールで定義されたプロンプトで応答します。)
ユーザー: コーヒー (Alexa はスキルにdrinkコーヒーに設定したOrderIntentを送信します。)
これで飲み物が決まり、スキルはダイアログを引き継いで、 coffeeRoast値をたずねるためにDialog.ElicitSlotを使用します。

Alexa: ローストはライト、ミディアム、ミディアムダーク、ダークのどれにしますか?Dialog.ElicitSlotディレクティブの一部として提供されたプロンプトです。)
対話が続きます...

このような対話を作成するには、次の手順を実行します。

  • OrderIntentインテントのdrink スロットを必須スロットとして設定して、ユーザーにこの値をたずねるプロンプトを出します。飲み物固有のスロット(coffeeRoastteaType)は必須ではありません。
  • drinkスロットのスロット検証ルールを有効にします。これには、スロットのカスタムタイプには、メニューにあるすべての飲み物の値と同義語が含まれていると仮定して、スロットタイプの値を同義語のみを受け付ける ルールを使用できます。
  • OrderIntentのオートデリゲートを無効にします。
  • drinkがまだ提供されていない場合、未完了のダイアログをデリゲートするコードを追加します。
  • drinkの値に応じて、該当するスロットをたずねるコードを追加します。これには、Dialog.ElicitSlotディレクティブを使用します。
    • drinkが「コーヒー」の場合、coffeeRoastの値を引き出します。
    • drinkが「お茶」の場合、teaTypeの値を引き出します。

ダイアログの状態やdrinkスロットの値に応じて、インテントに応答する別のハンドラーを使用することができます:

  • CoffeeGivenOrderIntentHandlerdrinkが「コーヒー」だが、coffeeRoastに値が入っていない場合に、OrderIntentを処理します。コーヒーのローストの種類をたずねるために、Dialog.ElicitSlotを返します。
  • TeaGivenOrderIntentHandlerdrinkが「お茶」だが、teaTypeに値が入っていない場合に、OrderIntentを処理します。お茶の種類をたずねるために、Dialog.ElicitSlotを返します。
  • StartedInProgressOrderIntentHandlerdialogStateが完了していない場合に、OrderIntentリクエストを処理します。Dialog.Delegateを返します。これで、drinkスロットに有効な値が入力されます。

    ASK v2 SDKで、コーヒーとお茶のハンドラーの後にこのハンドラーを登録して、該当するハンドラーがほかにない場合にのみStartedInProgressOrderIntentHandlerが使われるようにする必要があります。

  • CompletedOrderIntentHandlerdialogStateCOMPLETEDの場合に、OrderIntentリクエストを処理します。ここで、インテントにはdrinkスロットの値と、coffeeRoastまたはteaTypeのいずれかが含まれることになります。

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

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

const StartedInProgressOrderIntentHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === "IntentRequest"
      && handlerInput.requestEnvelope.request.intent.name === "OrderIntent"
      && handlerInput.requestEnvelope.request.dialogState !== 'COMPLETED';
  },
  handle(handlerInput) {
    return handlerInput.responseBuilder
      .addDelegateDirective()
      .getResponse();
  }
};

const CoffeeGivenOrderIntentHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === "IntentRequest"
      && handlerInput.requestEnvelope.request.intent.name === "OrderIntent"
      && handlerInput.requestEnvelope.request.intent.slots.drink.value 
      && handlerInput.requestEnvelope.request.intent.slots.drink.value === 'コーヒー'
      && !handlerInput.requestEnvelope.request.intent.slots.coffeeRoast.value
  },
  handle(handlerInput) {
    return handlerInput.responseBuilder
      .speak('ローストはライト、ミディアム、ミディアムダーク、ダークのどれにしますか?')
      .reprompt('ローストはライト、ミディアム、ミディアムダーク、ダークのどれにしますか?')
      .addElicitSlotDirective('coffeeRoast')
      .getResponse();
  }
};

const TeaGivenOrderIntentHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === "IntentRequest"
      && handlerInput.requestEnvelope.request.intent.name === "OrderIntent"
      && handlerInput.requestEnvelope.request.intent.slots.drink.value
      && handlerInput.requestEnvelope.request.intent.slots.drink.value === 'お茶'
      && !handlerInput.requestEnvelope.request.intent.slots.teaType.value
  },
  handle(handlerInput) {
    return handlerInput.responseBuilder
      .speak("紅茶、緑茶、ウーロン茶、白茶のどれにしますか?")
      .reprompt("紅茶、緑茶、ウーロン茶、白茶のどれにしますか?")
      .addElicitSlotDirective('teaType')
      .getResponse();
  }
};

const CompletedOrderIntentHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === "IntentRequest"
        && handlerInput.requestEnvelope.request.intent.name === "OrderIntent"
        && handlerInput.requestEnvelope.request.dialogState === "COMPLETED";
  },
  handle(handlerInput){

    const drink = handlerInput.requestEnvelope.request.intent.slots.drink.value;
    let type; 

    if (drink === 'コーヒー') {
        type = handlerInput.requestEnvelope.request.intent.slots.coffeeRoast.value;
    } else if (drink === 'お茶') {
        type = handlerInput.requestEnvelope.request.intent.slots.teaType.value;
    } else {
        type = ’水';
    }

    const speechText = `${type} ${drink}でよろしいですか?`;
    return handlerInput.responseBuilder
        .speak(speechText)
        .getResponse();
  }
};

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

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

// これらのハンドラーのそれぞれは、Javaクラスです。簡単に説明すると、
// パッケージと各クラスのimport宣言は表示されません。

/**
 * *****************************************
 * StartedInProgressOrderIntentHandler class
 * *****************************************
 */

public class StartedInProgressOrderIntentHandler implements IntentRequestHandler {
    @Override
    public boolean canHandle(HandlerInput handlerInput, IntentRequest intentRequest) {
        return handlerInput.matches(intentName("OrderIntent"))
                && intentRequest.getDialogState() != DialogState.COMPLETED;
    }

    @Override
    public Optional<Response> handle(HandlerInput handlerInput, IntentRequest intentRequest) {
        System.out.println("In StartedInProgressOrderIntentHandler");
        return handlerInput.getResponseBuilder()
                .addDelegateDirective(intentRequest.getIntent())
                .build();
    }
}

/**
 * *****************************************
 * CoffeeGivenOrderIntentHandler class
 * *****************************************
 */

public class CoffeeGivenOrderIntentHandler implements IntentRequestHandler {
    @Override
    public boolean canHandle(HandlerInput handlerInput, IntentRequest intentRequest) {
        Slot coffeeRoastSlot = intentRequest.getIntent().getSlots().get(COFFEE_ROAST_SLOT_NAME);
        return handlerInput.matches(intentName("OrderIntent"))
                && handlerInput.matches(slotValue(DRINK_SLOT_NAME, "コーヒー"))
                && coffeeRoastSlot.getValue() == null;
    }

    @Override
    public Optional<Response> handle(HandlerInput handlerInput, IntentRequest intentRequest) {
        System.out.println("In CoffeeGivenOrderIntentHandler");

        String speeechtext = "ローストはライト、ミディアム、ミディアムダーク、ダークのどれにしますか?";
        String repromptText = "ローストはライト、ミディアム、ミディアムダーク、ダークのどれにしますか?";
        return handlerInput.getResponseBuilder()
                .withSpeech(speeechtext)
                .withReprompt(repromptText)
                .addElicitSlotDirective(COFFEE_ROAST_SLOT_NAME, intentRequest.getIntent())
                .build();
    }
}

/**
 * *****************************************
 * TeaGivenOrderIntentHandler class
 * *****************************************
 */

public class TeaGivenOrderIntentHandler implements IntentRequestHandler {
    @Override
    public boolean canHandle(HandlerInput handlerInput, IntentRequest intentRequest) {
        Slot teaTypeSlot = intentRequest.getIntent().getSlots().get(TEA_TYPE_SLOT_NAME);
        return handlerInput.matches(intentName("OrderIntent"))
                && handlerInput.matches(slotValue(DRINK_SLOT_NAME, "お茶"))
                && teaTypeSlot.getValue() == null;
    }

    @Override
    public Optional<Response> handle(HandlerInput handlerInput, IntentRequest intentRequest) {
        System.out.println("In TeaGivenOrderIntentHandler");
        String speeechtext = "紅茶、緑茶、ウーロン茶、白茶のどれにしますか?";
        String repromptText = "紅茶、緑茶、ウーロン茶、白茶のどれにしますか?";
        return handlerInput.getResponseBuilder()
                .withSpeech(speeechtext)
                .withReprompt(repromptText)
                .addElicitSlotDirective(TEA_TYPE_SLOT_NAME, intentRequest.getIntent())
                .build();
    }
}


/**
 * *****************************************
 * CompletedOrderIntentHandler class
 * *****************************************
 */

public class CompletedOrderIntentHandler implements IntentRequestHandler {
    @Override
    public boolean canHandle(HandlerInput handlerInput, IntentRequest intentRequest) {
        return handlerInput.matches(intentName("OrderIntent"))
                && intentRequest.getDialogState() == DialogState.COMPLETED;
    }

    @Override
    public Optional<Response> handle(HandlerInput handlerInput, IntentRequest intentRequest) {
        System.out.println("In CompletedOrderIntentHandler");

        Map<String, Slot> slots = intentRequest.getIntent().getSlots();

        String drink = slots.get(DRINK_SLOT_NAME).getValue();
        System.out.println("drinkスロットの値: " + drink);
        String drinkType = "";

        if (drink.equals("コーヒー")){
            drinkType = slots.get(COFFEE_ROAST_SLOT_NAME).getValue();
        } else if (drink.equals("お茶")){
            drinkType = slots.get(TEA_TYPE_SLOT_NAME).getValue();
        } else {
            drinkType = "水";
        }

        String speechText = String.format(
                "%1$s %2$sでよろしいですか?",
                drinkType,drink);
        return handlerInput.getResponseBuilder()
                .withSpeech(speechText)
                .withShouldEndSession(true)
                .build();
    }
}

スキルとインテントにデリゲートルールを設定する

すべてのスキルには、スキルレベルのオートデリゲート設定があります。このオプションは有効または無効にできます。オートデリゲートは、新しいスキルではデフォルトで有効になっています。

ダイアログモデルのある各インテントには、ダイアログデリゲートのルール設定があります。これは次の3つのオプションのうちいずれかに設定できます:

  • オートデリゲートを有効化:スキル全体のデリゲートルールに関係なく、このインテントにオートデリゲートを使用します。
  • オートデリゲートを無効化:全体のデリゲートルールに関係なく、このインテントにオートデリゲートを使用しません。
  • スキル設定にフォールバック:スキル全体にオートデリゲートを使用します。最初にダイアログモデルをインテントに追加すると、これはそのインテントのデフォルトになります。

インテントのダイアログデリゲートのルール設定は、常にスキルレベルのオートデリゲート設定より優先されます。たとえば、オートデリゲートがスキルで有効になっているが、ダイアログデリゲートのルールが特定のインテントで無効になっている場合は、そのインテントではデリゲートは使用されません。

スキル全体のオートデリゲートオプションを設定する

スキルレベルのオートデリゲートオプションを、使用を予定しているほとんどのインテントに適用する場合は、必要に応じて、インテントレベルでオプションを上書きします。

  1. 開発者コンソールで、ビルド>カスタム>インターフェースに移動します。
  2. オートデリゲートオプションを有効または無効にし、インターフェースをクリックします。必ずモデルをビルドをクリックして対話モデルを再ビルドしてください。

ユーザーの対話モデルのJSONのオートデリゲートオプションを設定することもできます。

スキルレベルのオートデリゲート設定は、ダイアログモデルのないインテントでは無視されます。

インテントに使用するスキルのオートデリゲートルールを上書きする

ダイアログデリゲートのルールオプションは、ダイアログモデルのないインテントでは無効になります。

  1. 左側のナビゲーションでインテントをクリックして、インテントの詳細ページを開きます。
  2. ダイアログデリゲートのルールで、このインテントに使用するルールを選択します。

インテントのユーザーの対話モデルのJSONのオートデリゲートオプションを設定することもできます。

ダイアログモデル(対話モデルのスキーマ)に含まれるオートデリゲートのJSON

JSONエディターでダイアログモデル内のオートデリゲートオプションのJSON表現を確認し編集することができます。

スキル全体のオートデリゲートオプションは、interactionModel.dialog.delegationStrategyで定義されます。

インテントのオートデリゲート設定は、interactionModel.dialog.intents[].delegationStrategyで設定されます。インテントがスキル設定にフォールバックに設定されている場合は、このプロパティは存在しません

次の例では、スキル全体のオートデリゲート設定は無効になっており、ダイアログモデルを持つ3つのインテントがあります。IntentWithAutoDelegationインテントはスキルの設定を上書きします。IntentWithoutAutoDelegationインテントはオートデリゲートを明示的に無効にします。これは、この場合のスキル全体の設定では冗長になります。IntentFallbackToSkillSettingインテントは、スキル設定にフォールバックするため、delegationStrategyは存在しません。

つまり、languageModelpromptsプロパティは表示されません。対話モデルJSONの詳細については、対話モデルのスキーマを参照してください。

{
  "interactionModel": {
    "dialog": {
      "delegationStrategy": "SKILL_RESPONSE",
      "intents": [
        {
          "name": "IntentWithAutoDelegation",
          "delegationStrategy": "ALWAYS",
          "confirmationRequired": false,
          "prompts": {},
          "slots": [
            {
              "name": "planet",
              "type": "Planet",
              "confirmationRequired": false,
              "elicitationRequired": true,
              "prompts": {
                "elicitation": "Elicit.Slot.227876833021.1360336347514"
              },
              "validations": [
                {
                  "type": "isNotInSet",
                  "prompt": "Slot.Validation.227876833021.1360336347514.650882724620",
                  "values": [
                    "おひさま",
                    "太陽",
                    "われわれの太陽"
                  ]
                },
                {
                  "type": "hasEntityResolutionMatch",
                  "prompt": "Slot.Validation.227876833021.1360336347514.445874299877"
                }
              ]
            }
          ]
        },
        {
          "name": "IntentWithoutAutoDelegation",
          "delegationStrategy": "SKILL_RESPONSE",
          "confirmationRequired": false,
          "prompts": {},
          "slots": [
            {
              "name": "color",
              "type": "AMAZON.Color",
              "confirmationRequired": false,
              "elicitationRequired": true,
              "prompts": {
                "elicitation": "Elicit.Slot.199336742512.1106707015044"
              }
            }
          ]
        },
        {
          "name": "IntentFallbackToSkillSetting",
          "confirmationRequired": false,
          "prompts": {},
          "slots": [
            {
              "name": "name",
              "type": "AMAZON.US_FIRST_NAME",
              "confirmationRequired": false,
              "elicitationRequired": true,
              "prompts": {
                "elicitation": "Elicit.Slot.789417862303.645497121610"
              }
            }
          ]
        }
      ]
    }
  }
}