Ознакомьтесь с советами по проектированию диалогов.
Ожидайте вариации
Обработайте это в поле ввода "User says" (Пользователь говорит) в 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'); }
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(); }
Будьте готовы помочь в любой момент
Создайте намерение, которое будет реагировать на фразы с просьбой о помощи, например "Что я могу сделать?", "Что ты умеешь?" или "Помощь". В этом намерении предложите несколько (чередующихся) ответов, которые дают представление о том, что может делать агент, и направляют пользователей к возможному действию. В идеале также следует использовать дополнительные интенты помощи в 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(); }
Настройте рекламу для вернувшихся пользователей
Сохранение некоторых данных между разговорами обеспечивает гораздо более естественный опыт для вернувшихся пользователей. Первый шаг в создании такого опыта –
по-другому приветствовать вернувшихся пользователей. Например, вы можете настроить приветствие или
показать полезную информацию на основе прошлых разговоров. Для этого используйте свойство 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!...'); } }
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. Например, пользователи, чье последнее взаимодействие произошло много месяцев назад, могут получить другое приветствие, чем те, кто использовал Действие накануне.
Регулировка громкости во время разговора
На поддерживаемых устройствах Ассистент позволяет пользователям управлять громкостью устройства в рамках вашего разговорного действия, говоря такие вещи, как "увеличить громкость" или "установить громкость на 50 процентов". Если у вас есть намерения, которые обрабатывают похожие фразы для обучения, то ваши намерения будут иметь приоритет. Мы рекомендуем разрешить Ассистенту обрабатывать эти запросы пользователей, если у вашего Действия нет конкретной причины для этого.