Wskazówki dotyczące implementacji (Dialogflow)

Zapoznaj się z poniższymi wskazówkami, aby wdrożyć w akcji sprawdzone metody dotyczące projektowania rozmowy.

Spodziewaj się wahań

Wykonaj te czynności w polu „Informację użytkownika” w Dialogflow. Używaj też więcej niż 1 zamiaru, które można zmapować na to samo działanie, gdzie każda intencja może być aktywowana z różnymi zestawami wyrażeń „Wypowiedź użytkownika”.

Przekazuj pomocne powtórzenia i powołaj się z powodu gratulacji.

Czasami akcja nie może przejść dalej, ponieważ nie otrzymała danych wejściowych (brak danych wejściowych) lub nie zrozumiała danych użytkownika (jest to tzw. brak dopasowania). W takim przypadku Asystent najpierw próbuje określić, czy użytkownik chce wywołać inną akcję. Jeśli Asystent nie dopasuje danych wejściowych użytkownika do innej akcji, użytkownik będzie kontynuował w kontekście Twojej akcji. Ten scenariusz może wystąpić w dowolnym momencie, więc najlepiej jest postępować zgodnie z sytuacjami typu „brak danych wejściowych” i „brak dopasowań” na każdym etapie rozmowy z wartością zastępczą. Stosując wartości zastępcze, możesz pomóc użytkownikom wrócić na właściwe tory.

Aby to zrobić, zainicjuj zmienną fallbackCount w obiekcie conv.data i ustaw ją na 0. Przygotuj tablicę 2 komunikatów zastępczych (przekazywanie ich w jasny sposób) i końcowy komunikat zastępczy, który kończy rozmowę.

Następnie utwórz intencję zastępczą (najlepiej po jednej dla każdej intencji działania w agencji). W module obsługi intencji pobierz liczbę kreacji zastępczych z obiektu conv.data, zwiększ ją, a jeśli jest mniejsza niż 3, pobierz prompt z tablicy 3. Jeśli liczba wynosi co najmniej 4, zamknij rozmowę, korzystając z ostatniego komunikatu. We wszystkich intencji, które nie są reklamami zastępczymi, zresetuj liczbę kreacji zastępczych do 0. Najlepiej utworzyć szablony kreacji zastępczych dla konkretnych intencji, które odpowiadają tym intencjom.

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

Przygotuj się do pomocy w każdej chwili

Stwórz intencję, która będzie wykrywać podpowiedzi takie jak „co mogę zrobić?”, „Co mi powiesz” lub „pomoc”. W takiej sytuacji zaoferuj odpowiedź (na bieżąco) informującą użytkowników o możliwościach agenta i zachęcającą użytkowników do wykonania możliwego działania. Najlepiej jest też korzystać z intencji pomocy w Dialogflow, aby tworzyć różne scenariusze pomocy dla różnych intencji.

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

Pozwól użytkownikom na ponowne odtwarzanie informacji

Spakuj wszystkie metody app.ask(output) za pomocą funkcji serwera proxy, która dodaje dane wyjściowe do parametru conv.data.lastPrompt. Intencja powtarzająca się w poszukiwaniu próśb od użytkownika, takich jak „Co?” „Powtórz to jeszcze raz” albo „Czy możesz to powtórzyć?”. Utwórz tablicę powtarzających się prefiksów, które mogą służyć do potwierdzania, że użytkownik poprosił o powtórzenie czegoś. W module obsługi powtórzonych intencji wywołaj funkcję ask() ze połączonym ciągiem znaków powtórzonego prefiksu i wartością conv.data.lastPrompt. Pamiętaj, że musisz przesunąć wszystkie tagi otwierające SSML, jeśli zostały użyte w ostatnim promptie.

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

Personalizowanie rozmów z wykorzystaniem preferencji użytkownika

Akcja może prosić użytkowników o ich ustawienia i zapamiętywać je na później. Pozwala to personalizować przyszłe rozmowy z tym użytkownikiem.

Ta przykładowa akcja wyświetla użytkownikom prognozę pogody dla danego kodu pocztowego. Poniższy przykładowy kod służy do pytania użytkownika, czy akcja ma zapamiętać kod pocztowy na potrzeby późniejszych rozmów.

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

Gdy zapytasz użytkownika, jaki kod pocztowy znajduje się w pierwszym oknie, możesz pominąć ten prompt przy kolejnym wywołaniu i użyć tego samego kodu pocztowego. Nadal musisz zapewnić trasę ucieczki (np. element z sugestią pozwalającą wybrać inny kod pocztowy), ale w częstych przypadkach skrócenie ścieżki ucieczki z drugiej ręki znacznie zwiększy wygodę korzystania z usługi.

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

Dostosowywanie pod kątem powracających użytkowników

Zachowywanie pewnego stanu pomiędzy rozmowami zapewnia o wiele bardziej naturalne wrażenia użytkownikom powracającym. Pierwszym krokiem na drodze do stworzenia takiego wrażenia jest odmienne powitanie powracających użytkowników. Możesz na przykład zwęzić powitanie lub podać przydatne informacje z wcześniejszych rozmów. W tym celu użyj właściwości AppRequest.User lastSeen, aby określić, czy użytkownik wcześniej wszedł w interakcję z akcją. Jeśli ładunek żądania zawiera właściwość lastSeen, możesz użyć innego powitania niż zwykle.

Ten kod korzysta z biblioteki klienta w środowisku Node.js do pobierania wartości 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();
}

Możesz ulepszyć to powitanie, dostosowując odpowiedź do rzeczywistej wartości lastSeen. Na przykład użytkownicy, których ostatnia interakcja miała miejsce wiele miesięcy przed bieżącą interakcją, mogą otrzymać inne powitanie niż użytkownicy, którzy skorzystali z akcji dzień wcześniej.

Regulacja głośności w rozmowie

Na obsługiwanych urządzeniach Asystent pozwala użytkownikom sterować głośnością urządzenia w ramach akcji konwersacyjnej. Wystarczy, że powiesz „Zwiększ głośność” lub „Ustaw głośność na 50 procent”. Jeśli masz intencje, które obsługują podobne wyrażenia do trenowania, intencje mają pierwszeństwo. Zalecamy, aby Asystent odpowiadał na te prośby, chyba że akcja ma konkretny powód.