再プロンプト(Dialogflow)

Dialogflow で調べる

[続行] をクリックして、Dialogflow に再プロンプトのサンプルをインポートします。次に、以下の手順に沿ってサンプルをデプロイしてテストします。

  1. エージェント名を入力し、サンプル用に新しい Dialogflow エージェントを作成します。
  2. エージェントのインポートが完了したら、[Go to agent] をクリックします。
  3. メインのナビゲーション メニューから [Fulfillment] に移動します。
  4. [インライン エディタ] を有効にして、[デプロイ] をクリックします。エディタにはサンプルコードが含まれています。
  5. メインのナビゲーション メニューから [Integrations] に移動し、[Google Assistant] をクリックします。
  6. 表示されたモーダル ウィンドウで [変更の自動プレビュー] を有効にし、[Test] をクリックして Actions シミュレータを開きます。
  7. シミュレータで「Talk to my test app」と入力してサンプルをテストします。
続行

次の機能を使用して、ユーザーがアクションに入力しない場合(入力なしエラー)に対処できます。

  • システムデフォルトの再プロンプト - 事前定義された汎用的な再プロンプトを自動的に提示します。
  • 動的再プロンプト - 再プロンプトをユーザー自身で処理し、未入力が発生するたびにインテント(Actions SDK)またはイベント(Dialogflow)を受信すると宣言し、ケースバイケースで処理できます。

システムデフォルトの再プロンプト

デフォルトでは、アシスタントにレスポンスを返すと、システムはデフォルトの再プロンプトを使用して、入力を繰り返すか再入力するようユーザーに求めます。

Dialogflow

Dialogflow では、一致しない入力と未入力の入力を合わせて最大 3 つまで適用します。会話での収集が 3 回試行されると、Dialogflow エージェントはデフォルトのレスポンスで会話を終了します。Dialogflow での不一致入力は、フォールバック インテントの 1 つがトリガーされたときに行われます。

動的再プロンプト

アクションが入力の受信に失敗するたびに、インテントまたは Dialogflow イベントを受け取ることができます。これにより、必要に応じて、ロジックに基づいて異なるレスポンスを返し、ユーザーに適切に再プロンプトを表示できます。

Dialogflow

次の 2 種類の未入力インテントを作成できます。

  • 通常インテント - この方法はどのようなコンテキストも適用しません。そのため、トリガーすべき別のよりコンテキストに即したインテントがない場合にトリガーされます。これは、ほとんどのケースに適用できる汎用的な再プロンプトに役立ちます。

  • フォローアップ インテント - フォローアップ インテントは、Dialogflow コンテキストを介して適用されます。これにより、会話の特定のターン後にのみ再プロンプトがトリガーされます。これは、特定の状況に適用するカスタマイズした再プロンプトを行う場合に便利です。

未入力イベントを処理するには:

  1. 左側のナビゲーションで [Intents] をクリックします。
  2. 標準インテントまたはフォローアップ インテントを作成します。
    • 通常のインテントの場合: [Intent](インテント)メニュー項目の横にある [+] アイコンをクリックし、インテントに「Reprompt」などの名前を付けます。

    • フォローアップ インテントの場合: 未入力再プロンプトをカスタマイズする対象のインテントにカーソルを合わせ、[Add follow-up intent] > [custom] の順にクリックします。元のインテントの下に新しいインテントが作成されます。

  3. 新しく作成されたインテントをクリックしてインテント エディタを開きます。
  4. [Events] セクションをクリックし、[Add event] フィールドに「actions_intent_NO_INPUT」と入力します。
  5. [Actions] セクションで、アクション名を入力するか、デフォルトで提供されている名前を使用します。この例では「no.input」を使用します。

  6. [保存] をクリックします。
  7. 左側のナビゲーションで [Integrations] をクリックします。
  8. [Google Assistant] を選択し、[Test] をクリックして、変更内容が Actions プロジェクトに反映されていることを確認します。

このインテントで未入力が発生した場合は、フルフィルメントを使用して適切なレスポンスを返すか、Dialogflow でレスポンスを作成できます。たとえば、クライアント ライブラリを使用して「Reprompt」という通常の未入力インテントを処理するフルフィルメント コードを次に示します。

Node.js

const {dialogflow} = require('actions-on-google');
const functions = require('firebase-functions');

const app = dialogflow({debug: true});

app.intent('Reprompt', (conv) => {
  const repromptCount = parseInt(conv.arguments.get('REPROMPT_COUNT'));
  if (repromptCount === 0) {
  conv.ask(`What was that?`);
  } else if (repromptCount === 1) {
  conv.ask(`Sorry I didn't catch that. Could you repeat yourself?`);
  } else if (conv.arguments.get('IS_FINAL_REPROMPT')) {
  conv.close(`Okay let's try this again later.`);
  }
});

exports.dialogflowFirebaseFulfillment = functions.https.onRequest(app);

Java

package com.example;

import com.google.actions.api.ActionRequest;
import com.google.actions.api.ActionResponse;
import com.google.actions.api.DialogflowApp;
import com.google.actions.api.ForIntent;
import com.google.actions.api.response.ResponseBuilder;

public class MyActionsApp extends DialogflowApp {

  @ForIntent("Reprompt")
  public ActionResponse reprompt(ActionRequest request) {
    ResponseBuilder responseBuilder = getResponseBuilder(request);
    int repromptCount = request.getRepromptCount();
    String response;
    if (repromptCount == 0) {
      response = "What was that?";
    } else if (repromptCount == 1) {
      response = "Sorry, I didn't catch that. Could you repeat yourself?";
    } else {
      responseBuilder.endConversation();
      response = "Okay let's try this again later.";
    }
    return responseBuilder.add(response).build();
  }
}

JSON をリクエストする

下記の JSON は Webhook リクエストを示します。

{
  "responseId": "f26a9188-4998-42eb-ac16-d0e6e273b137-712767ed",
  "queryResult": {
    "queryText": "actions_intent_NO_INPUT",
    "parameters": {},
    "allRequiredParamsPresent": true,
    "fulfillmentText": "Webhook failed for intent: Reprompt",
    "fulfillmentMessages": [
      {
        "text": {
          "text": [
            "Webhook failed for intent: Reprompt"
          ]
        }
      }
    ],
    "outputContexts": [
      {
        "name": "projects/df-reprompts-kohler/agent/sessions/ABwppHFi9Dpwy6KiEtS0UIPDNVfa7mlkrPIEZRlikFkjuN_4SGPixgX8OCatpXu38ln7VG43-nk-7veZWhds3nLljA/contexts/actions_capability_media_response_audio"
      },
      {
        "name": "projects/df-reprompts-kohler/agent/sessions/ABwppHFi9Dpwy6KiEtS0UIPDNVfa7mlkrPIEZRlikFkjuN_4SGPixgX8OCatpXu38ln7VG43-nk-7veZWhds3nLljA/contexts/actions_capability_account_linking"
      },
      {
        "name": "projects/df-reprompts-kohler/agent/sessions/ABwppHFi9Dpwy6KiEtS0UIPDNVfa7mlkrPIEZRlikFkjuN_4SGPixgX8OCatpXu38ln7VG43-nk-7veZWhds3nLljA/contexts/actions_capability_audio_output"
      },
      {
        "name": "projects/df-reprompts-kohler/agent/sessions/ABwppHFi9Dpwy6KiEtS0UIPDNVfa7mlkrPIEZRlikFkjuN_4SGPixgX8OCatpXu38ln7VG43-nk-7veZWhds3nLljA/contexts/google_assistant_input_type_voice"
      },
      {
        "name": "projects/df-reprompts-kohler/agent/sessions/ABwppHFi9Dpwy6KiEtS0UIPDNVfa7mlkrPIEZRlikFkjuN_4SGPixgX8OCatpXu38ln7VG43-nk-7veZWhds3nLljA/contexts/actions_intent_no_input",
        "parameters": {
          "REPROMPT_COUNT": 2,
          "IS_FINAL_REPROMPT": true
        }
      }
    ],
    "intent": {
      "name": "projects/df-reprompts-kohler/agent/intents/75dfd97d-6368-4436-9533-70f05ae76c96",
      "displayName": "Reprompt"
    },
    "intentDetectionConfidence": 1,
    "languageCode": "en"
  },
  "originalDetectIntentRequest": {
    "source": "google",
    "version": "2",
    "payload": {
      "user": {
        "locale": "en-US",
        "userVerificationStatus": "VERIFIED"
      },
      "conversation": {
        "conversationId": "ABwppHFi9Dpwy6KiEtS0UIPDNVfa7mlkrPIEZRlikFkjuN_4SGPixgX8OCatpXu38ln7VG43-nk-7veZWhds3nLljA",
        "type": "ACTIVE",
        "conversationToken": "[]"
      },
      "inputs": [
        {
          "intent": "actions.intent.NO_INPUT",
          "rawInputs": [
            {
              "inputType": "VOICE"
            }
          ],
          "arguments": [
            {
              "name": "REPROMPT_COUNT",
              "intValue": "2"
            },
            {
              "name": "IS_FINAL_REPROMPT",
              "boolValue": true
            }
          ]
        }
      ],
      "surface": {
        "capabilities": [
          {
            "name": "actions.capability.MEDIA_RESPONSE_AUDIO"
          },
          {
            "name": "actions.capability.ACCOUNT_LINKING"
          },
          {
            "name": "actions.capability.AUDIO_OUTPUT"
          }
        ]
      },
      "availableSurfaces": [
        {
          "capabilities": [
            {
              "name": "actions.capability.AUDIO_OUTPUT"
            },
            {
              "name": "actions.capability.SCREEN_OUTPUT"
            },
            {
              "name": "actions.capability.WEB_BROWSER"
            }
          ]
        }
      ]
    }
  },
  "session": "projects/df-reprompts-kohler/agent/sessions/ABwppHFi9Dpwy6KiEtS0UIPDNVfa7mlkrPIEZRlikFkjuN_4SGPixgX8OCatpXu38ln7VG43-nk-7veZWhds3nLljA"
}

レスポンス JSON

下記の JSON は Webhook レスポンスを示します。

{
  "payload": {
    "google": {
      "expectUserResponse": false,
      "richResponse": {
        "items": [
          {
            "simpleResponse": {
              "textToSpeech": "Okay let's try this again later."
            }
          }
        ]
      }
    }
  }
}

Actions SDK

未入力インテントを処理するには:

  1. アクション パッケージ内の conversations オブジェクトで、ユーザーが入力を行わないときは常に actions.intent.NO_INPUT インテントを受け取ることを宣言します。
    
    {
      "actions": [
        {
          "description": "Default Welcome Intent",
          "name": "MAIN",
          "fulfillment": {
            "conversationName": "conversation_1"
          },
          "intent": {
            "name": "actions.intent.MAIN"
          }
        }
      ],
      "conversations": {
        "conversation_1": {
          "name": "conversation_1",
          "url": "YOUR_FULFILLMENT_URL",
          "inDialogIntents": [
            {
              "name": "actions.intent.NO_INPUT"
            }
          ]
        }
      }
    }
    
  2. アシスタントがユーザーから入力を受けなかったとき、次のフルフィルメントへのリクエストで未入力インテントを受け取ります。そのインテントを処理して適切な再プロンプトのレスポンスを返すことができます。次の例をご覧ください。

    Node.js

    const {actionssdk} = require('actions-on-google');
    const functions = require('firebase-functions');
    
    const app = actionssdk({debug: true});
    
    app.intent('actions.intent.MAIN', (conv) => {
      conv.ask(`Hi! Try this sample on a speaker device, ` +
        `and stay silent when the mic is open. If trying ` +
        `on the Actions console simulator, click the no-input ` +
        `button next to the text input field.`);
    });
    
    app.intent('actions.intent.TEXT', (conv, input) => {
      conv.ask(`You said ${input}`);
      conv.ask(`Try this sample on a speaker device, ` +
        `and stay silent when the mic is open. If trying ` +
        `on the Actions console simulator, click the no-input ` +
        `button next to the text input field.`);
    });
    
    app.intent('actions.intent.NO_INPUT', (conv) => {
      const repromptCount = parseInt(conv.arguments.get('REPROMPT_COUNT'));
      if (repromptCount === 0) {
        conv.ask(`What was that?`);
      } else if (repromptCount === 1) {
        conv.ask(`Sorry I didn't catch that. Could you repeat yourself?`);
      } else if (conv.arguments.get('IS_FINAL_REPROMPT')) {
        conv.close(`Okay let's try this again later.`);
      }
    });
    
    exports.dialogflowFirebaseFulfillment = functions.https.onRequest(app);

    Java

    package com.example;
    
    import com.google.actions.api.ActionRequest;
    import com.google.actions.api.ActionResponse;
    import com.google.actions.api.ActionsSdkApp;
    import com.google.actions.api.ConstantsKt;
    import com.google.actions.api.ForIntent;
    import com.google.actions.api.response.ResponseBuilder;
    import com.google.actions.api.response.helperintent.Confirmation;
    import com.google.actions.api.response.helperintent.DateTimePrompt;
    import com.google.actions.api.response.helperintent.Permission;
    import com.google.actions.api.response.helperintent.Place;
    import com.google.api.services.actions_fulfillment.v2.model.DateTime;
    import com.google.api.services.actions_fulfillment.v2.model.Location;
    
    public class MyActionsApp extends ActionsSdkApp {
    
      @ForIntent("actions.intent.MAIN")
      public ActionResponse welcome(ActionRequest request) {
        ResponseBuilder responseBuilder = getResponseBuilder(request);
        responseBuilder.add("Hi! Try this sample on a speaker device, and stay silent when the mic is open. If trying on the Actions console simulator, click the no-input button next to the text input field.");
        return responseBuilder.build();
      }
    
      @ForIntent("actions.intent.TEXT")
      public ActionResponse fallback(ActionRequest request) {
        ResponseBuilder responseBuilder = getResponseBuilder(request);
        responseBuilder.add("You said " + request.getRawInput().getQuery());
        responseBuilder.add("Try this sample on a speaker device, and stay silent when the mic is open. If trying on the Actions console simulator, click the no-input button next to the text input field.");
        return responseBuilder.build();
      }
    
      @ForIntent("actions.intent.NO_INPUT")
      public ActionResponse reprompt(ActionRequest request) {
        ResponseBuilder responseBuilder = getResponseBuilder(request);
        int repromptCount = request.getRepromptCount();
        String response;
        if (repromptCount == 0) {
          response = "What was that?";
        } else if (repromptCount == 1) {
          response = "Sorry, I didn't catch that. Could you repeat yourself?";
        } else {
          responseBuilder.endConversation();
          response = "Okay let's try this again later.";
        }
        return responseBuilder.add(response).build();
      }
    
    }

    JSON をリクエストする

    下記の JSON は Webhook リクエストを示します。

    {
      "user": {
        "locale": "en-US",
        "userVerificationStatus": "VERIFIED"
      },
      "conversation": {
        "conversationId": "ABwppHEVDuKUPjdZ4Ud-F2yBXN5ssRg2funUp59hSHQheAi-B5Y3EzehAKFtVwMkduqMRWscUp77ScrDjYnYxISqAM-qOXuXEuCw",
        "type": "ACTIVE",
        "conversationToken": "{\"data\":{}}"
      },
      "inputs": [
        {
          "intent": "actions.intent.NO_INPUT",
          "rawInputs": [
            {
              "inputType": "VOICE"
            }
          ],
          "arguments": [
            {
              "name": "REPROMPT_COUNT",
              "intValue": "2"
            },
            {
              "name": "IS_FINAL_REPROMPT",
              "boolValue": true
            }
          ]
        }
      ],
      "surface": {
        "capabilities": [
          {
            "name": "actions.capability.AUDIO_OUTPUT"
          },
          {
            "name": "actions.capability.ACCOUNT_LINKING"
          },
          {
            "name": "actions.capability.MEDIA_RESPONSE_AUDIO"
          }
        ]
      },
      "availableSurfaces": [
        {
          "capabilities": [
            {
              "name": "actions.capability.SCREEN_OUTPUT"
            },
            {
              "name": "actions.capability.WEB_BROWSER"
            },
            {
              "name": "actions.capability.AUDIO_OUTPUT"
            }
          ]
        }
      ]
    }

    レスポンス JSON

    下記の JSON は Webhook レスポンスを示します。

    {
      "expectUserResponse": false,
      "finalResponse": {
        "richResponse": {
          "items": [
            {
              "simpleResponse": {
                "textToSpeech": "Okay let's try this again later."
              }
            }
          ]
        }
      }
    }