実装のヒント(Dialogflow)

以下のヒントを参考にして、会話デザインのベスト プラクティスを実践してください。 追加することもできます

バリエーションを想定する

これは Dialogflow で、「ユーザーの発言内容」入力で処理します。また、1 つ以上の 同じアクションにマッピングできる複数のインテントがあります。各インテントは、 「ユーザーの発言」とありません。

有用な再プロンプトを提供して失敗時に適切な処理を行う

アクションが入力を受け取っていないため、先に進めないことがあります (いわゆる「入力なし」)や、ユーザーの入力を理解しなかった(一致しない)などがこれに該当します。 この場合、アシスタントはまずユーザーが 別のアクションをトリガーできますアシスタントがユーザーの入力と一致しない場合 ユーザーがアクションのコンテキストで続行します。 このシナリオは常に発生する可能性があるため、ベスト プラクティスは、 フォールバックあり、会話の各ターンで no-input および no-match の状況が発生する。 フォールバックを使用すると、ユーザーが元の状態に戻れるようにサポートできます。

これを行うには、conv.data オブジェクトで fallbackCount 変数を初期化します。 0 に設定します2 つのフォールバック プロンプトの配列を用意します(明確に伝える)。 最後のフォールバック プロンプトで会話を終了します。

次に、フォールバック インテントを作成します(理想的には、 できます。インテント ハンドラで、conv.data からフォールバック カウントを取得します。 増分して 3 より小さい場合は配列からプロンプトを pull する 3。数が 4 以上の場合は、最後の 表示されます。フォールバック以外のすべてのインテントで、フォールバック カウントを 0 にリセットします。 理想的には、特定のインテントのフォールバックをインテント固有にするためテンプレート化します。

Node.js

const GENERAL_FALLBACK = [
   'Sorry, what was that?',
   'I didn\'t quite get that. I can help you find good local restaurants, what do you want to know about?',
];

const LIST_FALLBACK = [
   'Sorry, what was that?',
   'I didn\'t catch that. Could you tell me which one you prefer?',
];

const FINAL_FALLBACK = 'I\'m sorry I\'m having trouble here. Let\'s talk again later.';

const handleFallback = (conv, promptFetch, callback) => {
 conv.data.fallbackCount = parseInt(conv.data.fallbackCount, 10);
 conv.data.fallbackCount++;
 if (conv.data.fallbackCount > 2) {
   conv.close(promptFetch.getFinalFallbackPrompt());
 } else {
   callback();
 }
}
// Intent handlers below
const generalFallback = (conv) => {
  handleFallback = (conv, promptFetch, () => {
    conv.ask(GENERAL_FALLBACK[conv.data.fallbackCount],
      getGeneralNoInputPrompts());
 });
}

const listFallback = (conv) => {
  handleFallback = (conv, promptFetch, () => {
   conv.ask(LIST_FALLBACK[conv.data.fallbackCount],
       getGeneralNoInputPrompts());
 });
}

const nonFallback = (conv) => {
  conv.data.fallbackCount = 0;
  conv.ask('A non-fallback message here');
}

Java

private static final List<String> GENERAL_FALLBACK =
    Arrays.asList(
        "Sorry, what was that?",
        "I didn\'t quite get that. I can tell you all about IO, like date or location, or about the sessions. What do you want to know about?");
private static final List<String> LIST_FALLBACK =
    Arrays.asList(
        "Sorry, what was that?",
        "I didn\'t catch that. Could you tell me which one you liked?");
private static final List<String> FINAL_FALLBACK =
    Arrays.asList("I\'m sorry I\'m having trouble here. Maybe we should try this again later.");

@ForIntent("General Fallback")
public ActionResponse generalFallback(ActionRequest request) {
  ResponseBuilder responseBuilder = getResponseBuilder(request);
  int fallbackCount = (Integer) request.getConversationData().get("fallbackCount");
  fallbackCount++;
  request.getConversationData().put("fallbackCount", fallbackCount);
  if (fallbackCount > 2) {
    responseBuilder.add(getRandomPromptFromList(FINAL_FALLBACK)).endConversation();
  } else {
    responseBuilder.add(getRandomPromptFromList(GENERAL_FALLBACK));
  }
  return responseBuilder.build();
}

private String getRandomPromptFromList(List<String> prompts) {
  Random rand = new Random();
  int i = rand.nextInt(prompts.size());
  return prompts.get(i);
}

@ForIntent("List Fallback")
public ActionResponse listFallback(ActionRequest request) {
  ResponseBuilder responseBuilder = getResponseBuilder(request);
  int fallbackCount = (Integer) request.getConversationData().get("fallbackCount");
  fallbackCount++;
  request.getConversationData().put("fallbackCount", fallbackCount);
  if (fallbackCount > 2) {
    responseBuilder.add(getRandomPromptFromList(FINAL_FALLBACK)).endConversation();
  } else {
    responseBuilder.add(getRandomPromptFromList(LIST_FALLBACK));
  }
  return responseBuilder.build();
}

@ForIntent("Non Fallback")
public ActionResponse nonFallback(ActionRequest request) {
  ResponseBuilder responseBuilder = getResponseBuilder(request);
  request.getConversationData().put("fallbackCount", 0);
  responseBuilder.add("Non Fallback message");
  return responseBuilder.build();
}

いつでも手伝うことができるようにする

「What can I do?」のようなヘルプフレーズをリッスンするインテントを作成します。 「何を教えてくれる?」、「手伝って」などと話しかけます。このインテントでは、いくつか(ローテーション)を提示する エージェントができることの概要を示し、ユーザーを できます。Dialogflow でフォローアップ ヘルプ インテントも使用して、 アクションにつながるインテントごとに異なるヘルプシナリオを作成する。

Node.js

const HELP_PROMPTS = [
   'There\'s a lot you might want to know about the local restaurants, and I can tell you all about it, like where it is and what kind of food they have. What do you want to know?',
   'I\'m here to help, so let me know if you need any help figuring out where or what to eat. What do you want to know?',
];

// Intent handler
const help = (conv) => {
 reply(conv, promptFetch.getHelpPrompt(), // fetches random entry from HELP_PROMPTS
     promptFetch.getGeneralNoInputPrompts());
}

Java

private static final List<String> HELP_PROMPTS =
    Arrays.asList(
        "There's a lot you might want to know about IO, and I can tell you all about it, like where it is and what the sessions are. What do you want to know?",
        "IO can be a little overwhelming, so I\'m here to help. Let me know if you need any help figuring out the event, like when it is, or what the sessions are. What do you want to know?");

@ForIntent("Help")
public ActionResponse help(ActionRequest request) {
  return getResponseBuilder(request).add(getRandomPromptFromList(HELP_PROMPTS)).build();
}

ユーザーが情報を再度聞けるようにする

すべての app.ask(output) メソッドを、 conv.data.lastPrompt に出力されます。リッスンする繰り返しインテントを作成する 「何?」のようなプロンプトを「もう一度言ってください」、「お願いします」 それを繰り返す」と質問します。繰り返しの接頭辞の配列を作成する ユーザーが何かを繰り返すよう求めたことを認めます。繰り返し インテント ハンドラで、繰り返し接頭辞を連結した文字列で ask() を呼び出します。 conv.data.lastPrompt の値。すべての変数をずらす必要があります。 SSML の開始タグ(最後のプロンプトで使用されている場合)。

Node.js

const REPEAT_PREFIX = [
    'Sorry, I said ',
    'Let me repeat that. ',
];

const reply = (conv, inputPrompt, noInputPrompts) => {
  conv.data.lastPrompt = inputPrompt;
  conv.data.lastNoInputPrompts = noInputPrompts;
  conv.ask(inputPrompt, noInputPrompts);
}
// Intent handlers
const normalIntent = (conv) => {
  reply(conv, 'Hey this is a question', SOME_NO_INPUT_PROMPTS);
}

const repeat = (conv) => {
  let repeatPrefix = promptFetch.getRepeatPrefix(); // randomly chooses from REPEAT_PREFIX
  // Move SSML start tags over
  if (conv.data.lastPrompt.startsWith(promptFetch.getSSMLPrefix())) {
    conv.data.lastPrompt =
        conv.data.lastPrompt.slice(promptFetch.getSSMLPrefix().length);
    repeatPrefix = promptFetch.getSSMLPrefix() + repeatPrefix;
  }
  conv.ask(repeatPrefix + conv.data.lastPrompt,
      conv.data.lastNoInputPrompts);
}

Java

private final List<String> REPEAT_PREFIX = Arrays.asList("Sorry, I said ", "Let me repeat that.");

private final String SsmlPrefix = "<speak>";

@ForIntent("Normal Intent")
public ActionResponse normalIntent(ActionRequest request) {
  ResponseBuilder responseBuilder = getResponseBuilder(request);
  responseBuilder.getConversationData().put("lastPrompt", "Hey this is a question");
  return responseBuilder.build();
}

@ForIntent("repeat")
public ActionResponse repeat(ActionRequest request) {
  ResponseBuilder responseBuilder = getResponseBuilder(request);
  String repeatPrefix = getRandomPromptFromList(REPEAT_PREFIX);
  // Move SSML start tags over
  String lastPrompt = (String) responseBuilder.getConversationData().get("lastPrompt");
  if (lastPrompt.startsWith(SsmlPrefix)) {
    String newLastPrompt = lastPrompt.substring(SsmlPrefix.length());
    responseBuilder.getConversationData().put("lastPrompt", newLastPrompt);
    repeatPrefix = SsmlPrefix + repeatPrefix;
  }
  responseBuilder.add(repeatPrefix + lastPrompt);
  return responseBuilder.build();
}

ユーザーの好みに合わせて会話をカスタマイズする

アクションでユーザーに対し好みを尋ね、後で使用できるようにその内容を記憶できます。これにより、ユーザーに合わせてそれ以降の会話をカスタマイズできます。

この例のアクションは、郵便番号に対応する天気予報をユーザーに提供します。次のサンプルコードは、後の会話で使用できるようにアクションでユーザーの郵便番号を記憶しておくかどうかをユーザーに尋ねます。

Node.js

app.intent('weather_report', (conv) => {
  let zip = conv.arguments.get('zipcode');
  conv.data.zip = zip;
  conv.ask(getWeatherReport(zip));
  conv.ask(new Confirmation(`Should I remember ${zip} for next time?`));
});

app.intent('remember_zip', (conv, params, confirmation) => {
  if (confirmation) {
    conv.user.storage.zip = conv.data.zip;
    conv.close('Great! See you next time.');
  } else conv.close('Ok, no problem.');
});

Java

@ForIntent("weather_report")
public ActionResponse weatherReport(ActionRequest request) {
  ResponseBuilder responseBuilder = getResponseBuilder(request);
  String zip = (String) request.getArgument("location").getStructuredValue().get("zipCode");
  responseBuilder.getConversationData().put("zip", zip);
  responseBuilder.add(getWeatherReport(zip));
  responseBuilder.add(
      new Confirmation().setConfirmationText("Should I remember " + zip + " for next time?"));
  return responseBuilder.build();
}

@ForIntent("remember_zip")
public ActionResponse rememberZip(ActionRequest request) {
  ResponseBuilder responseBuilder = getResponseBuilder(request);
  if (request.getUserConfirmation()) {
    responseBuilder.getUserStorage().put("zip", responseBuilder.getConversationData().get("zip"));
    responseBuilder.add("Great! See you next time.").endConversation();
  } else {
    responseBuilder.add("Ok, no problem.").endConversation();
  }
  return responseBuilder.build();
}

最初のダイアログでユーザーに郵便番号を尋ねた後、 次回の呼び出し時にそのプロンプトをスキップして、同じ郵便番号を使用できます。 その場合も、エスケープ ルート(候補ワードを表示または拒否する 選択することもできますが、会話のターンを減らすことで、 よりシームレスなエクスペリエンスを実現できます。

Node.js

app.intent('weather_report', (conv) => {
  let zip = conv.arguments.get('zipcode');
  if (zip) {
    conv.close(getWeatherReport(zip));
  } else if (conv.user.storage.zip) {
    conv.ask(new SimpleResponse(getWeatherReport(conv.user.storage.zip)));
    conv.ask(new Suggestions('Try another zipcode'));
  } else {
    conv.ask('What\'s your zip code?');
  }
});

app.intent('provide_zip_df', (conv) => {
  conv.user.storage.zip = conv.arguments.get('zipcode');
  conv.close(getWeatherReport(conv.user.storage.zip));
});

Java

public ActionResponse weatherReport2(ActionRequest request) {
  ResponseBuilder responseBuilder = getResponseBuilder(request);
  String zip = (String) request.getArgument("location").getStructuredValue().get("zipCode");
  if (zip != null) {
    responseBuilder.add(getWeatherReport(zip)).endConversation();
  } else if ((zip = (String) responseBuilder.getUserStorage().get("zip")) != null) {
    responseBuilder.add(new SimpleResponse().setTextToSpeech(getWeatherReport(zip)));
    responseBuilder.add(new Suggestion().setTitle("Try another zipcode"));
  } else {
    responseBuilder.add("What's your zip code?");
  }
  return responseBuilder.build();
}

リピーター向けにカスタマイズする

会話と会話の間になんらかの状態を維持することで、 より優れたエクスペリエンスを構築できますこの体験を作り出すための最初のステップは リピーターに異なる方法で挨拶します。たとえば、挨拶をテーパリングしたり、 過去の会話に基づいて有用な情報を表示する。これを行うには、 受信する AppRequest.User lastSeen プロパティを使用して、ユーザーが アクションとやり取りしたことがありませんlastSeen プロパティが含まれている場合 では、通常とは異なる挨拶を使用できます。

以下のコードは、Node.js クライアント ライブラリを使用して last.seen

Node.js

// This function is used to handle the welcome intent
// In Dialogflow, the Default Welcome Intent ('input.welcome' action)
// In Actions SDK, the 'actions.intent.MAIN' intent
const welcome = (conv) => {
  if (conv.user.last.seen) {
    conv.ask(`Hey you're back...`);
  } else {
    conv.ask('Welcome to World Cities Trivia!...');
  }
}

Java

// This function is used to handle the welcome intent
// In Dialogflow, the Default Welcome Intent ('input.welcome' action)
// In Actions SDK, the 'actions.intent.MAIN' intent
public ActionResponse welcome(ActionRequest request) {
  ResponseBuilder responseBuilder = getResponseBuilder(request);
  if (request.getUser().getLastSeen() != null) {
    responseBuilder.add("Hey you're back...");
  } else {
    responseBuilder.add("Welcome to Number Genie!...");
  }
  return responseBuilder.build();
}

実際のメッセージに合わせてレスポンスをカスタマイズすることで、この挨拶をさらに拡張できます。 lastSeen の値。たとえば、前回のやり取りが何度も発生し、 現在のやり取りがそれとは異なる挨拶メッセージを受け取る可能性があります。 前日にアクションを使用したユーザーです

会話中の音量の調整

対応デバイスでは、Google アシスタントを使用して 会話型アクション: 「音量を上げて」や「音量を上げて」 音量を 50 パーセントにして」のように表示されます。類似したトレーニング フレーズを処理するインテントがある場合、 優先されます。こうした処理はアシスタントにお任せください リクエストする特別な理由がある場合を除いて、ユーザーのリクエストには応じられません。