Советы по реализации (Dialogflow)

Ознакомьтесь со следующими советами, чтобы внедрить хорошие методы построения диалога в свое действие.

Ожидайте изменений

Обработайте это во входных данных «Пользователь говорит» в Dialogflow. Кроме того, используйте несколько намерений, которые могут сопоставляться с одним и тем же действием, причем каждое намерение может быть вызвано разными наборами фраз «Пользователь говорит».

Предоставляйте полезные подсказки и изящно завершайте работу

Иногда ваше действие не может двигаться дальше, потому что оно не получило входные данные (так называемые «нет ввода») или не распознало ввод пользователя (так называемое «несоответствие»). Когда это происходит, Ассистент сначала пытается определить, хочет ли пользователь инициировать другое действие. Если Ассистент не сопоставляет ввод пользователя с другим действием, пользователь продолжает работу в контексте вашего действия. Этот сценарий может произойти в любое время, поэтому рекомендуется однозначно обрабатывать ситуации отсутствия ввода и совпадения на каждом этапе диалога с запасным вариантом. Используя резервные варианты, вы можете помочь пользователям вернуться в нужное русло.

Для этого инициализируйте переменную fallbackCount в объекте conv.data и установите для нее значение 0. Подготовьте массив из двух резервных подсказок (с возрастающей четкостью) и последней резервной подсказки, завершающей диалог.

Затем создайте резервное намерение (в идеале — по одному для каждого действующего намерения в агенте). В обработчике намерений извлеките резервный счетчик из объекта conv.data , увеличьте его, а если он меньше 3, извлеките приглашение из массива 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');
}

Джава

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();
}

Будьте готовы помочь в любое время

Создайте намерение, которое слушает справочные фразы, такие как «что я могу сделать?», «что вы можете мне сказать» или «помочь». С этой целью предложите некоторый (чередующийся) ответ, который дает обзор того, что может делать агент, и направляет пользователей к возможному действию. В идеале также используйте последующие намерения помощи в 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());
}

Джава

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);
}

Джава

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.');
});

Джава

@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));
});

Джава

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();
}

Настройте для вернувшихся пользователей

Сохранение некоторого состояния между разговорами обеспечивает гораздо более естественный опыт для вернувшихся пользователей. Первый шаг в создании этого опыта — по-другому приветствовать вернувшихся пользователей. Например, вы можете сократить приветствие или раскрыть полезную информацию, основанную на прошлых разговорах. Для этого используйте входящее свойство lastSeen AppRequest.User , чтобы определить, взаимодействовал ли пользователь с вашим действием раньше. Если свойство 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!...');
  }
}

Джава

// 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 . Например, пользователи, чье последнее взаимодействие произошло за много месяцев до текущего взаимодействия, могут получить другое приветствие, чем те, кто использовал действие накануне.

Регулятор громкости во время разговора

На поддерживаемых устройствах Ассистент позволяет пользователям управлять громкостью устройства во время разговорного действия, говоря, например , «увеличь громкость» или «установи громкость на 50 процентов» . Если у вас есть намерения, которые обрабатывают похожие обучающие фразы, ваши намерения имеют приоритет. Мы рекомендуем разрешить Помощнику обрабатывать эти запросы пользователей, если у вашего действия нет особой причины.