Dicas de implementação (Dialogflow)

Veja as dicas a seguir para implementar boas práticas de design de conversa na sua ação.

Espere variações

Gerencie isso na entrada "User says" no Dialogflow. Além disso, use mais de uma intent que possa ser mapeada para a mesma ação, em que cada intent pode ser acionada com diferentes conjuntos de frases "O usuário diz".

Forneça solicitações úteis e falhe normalmente

Às vezes, sua ação não consegue avançar porque não recebeu uma entrada (conhecida como "sem entrada") ou não entendeu a entrada do usuário (conhecida como "sem correspondência"). Quando isso acontece, primeiro o Google Assistente tenta determinar se o usuário quer acionar uma ação diferente. Se o Google Assistente não corresponder a entrada do usuário a outra ação, o usuário vai continuar no contexto da ação. Esse cenário pode acontecer a qualquer momento. Portanto, a prática recomendada é lidar exclusivamente com situações sem entrada e sem correspondência em cada turno em uma conversa com um substituto. Usando substitutos, você pode ajudar os usuários a voltar para o caminho certo.

Para fazer isso, inicialize uma variável fallbackCount no seu objeto conv.data e defina-a como 0. Prepare uma matriz de dois prompts substitutos (escalando com clareza) e um prompt substituto final que encerra a conversa.

Em seguida, crie uma intent de fallback (idealmente uma para cada intent acionável no agente). No gerenciador de intent, extraia a contagem de substitutos do objeto conv.data e incremente-a. Se for menor que 3, extraia a solicitação da matriz 3. Se a contagem atingir 4 ou mais, feche a conversa usando o prompt final. Em todas as intents que não sejam substitutos, redefina a contagem de substitutos para 0. O ideal é modelar os substitutos para que intents específicos sejam específicos para eles.

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

Esteja preparado para ajudar a qualquer momento

Crie uma intent que ouça frases de ajuda como "O que posso fazer?", "o que você pode me dizer" ou "ajuda". Nessa intent, ofereça alguma resposta (em rotação) que ofereça uma visão geral do que o agente pode fazer e direcione os usuários para uma possível ação. O ideal é usar intents de ajuda de acompanhamento no Dialogflow para criar diferentes cenários de ajuda para intents acionáveis diferentes.

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

Permitir que os usuários reproduzam informações

Una todos os métodos app.ask(output) com uma função de proxy que adiciona a saída a conv.data.lastPrompt. Crie uma intent repetida que detecte solicitações repetidas do usuário, como "o quê?", "Você pode repetir?" ou "Você pode repetir?" Crie uma matriz de prefixos repetidos que possam ser usados para confirmar que o usuário pediu que algo fosse repetido. No gerenciador de intents repetidas, chame ask() com uma string concatenada do prefixo de repetição e o valor de conv.data.lastPrompt. Lembre-se de que você precisará mudar as tags de abertura SSML se usadas na última solicitação.

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

Personalizar a conversa com as preferências do usuário

Seu Action pode solicitar as preferências dos usuários e lembrá-las para uso posterior, permitindo que você personalize futuras conversas com eles.

Este exemplo de ação fornece aos usuários uma previsão do tempo para um CEP. O exemplo de código a seguir pergunta ao usuário se ele quer que a ação lembre o CEP para conversas posteriores.

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

Depois de perguntar ao usuário em qual CEP ele está na primeira caixa de diálogo, você pode pular essa solicitação durante a próxima invocação e usar o mesmo CEP. Você ainda precisa fornecer uma rota de escape (como um ícone de sugestão que permita escolher um CEP diferente), mas ao reduzir a rotatividade da conversa no caso comum, você cria uma experiência muito mais integrada.

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

Personalizar para usuários retornantes

Manter algum estado entre as conversas garante uma experiência muito mais natural para os usuários retornantes. A primeira etapa para criar essa experiência é saudar os usuários de maneira diferente. Por exemplo, você pode tocar na saudação ou exibir informações úteis com base nas conversas anteriores. Para fazer isso, use a propriedade lastSeen AppRequest.User recebida para determinar se o usuário interagiu com a ação antes. Se a propriedade lastSeen estiver incluída no payload da solicitação, você poderá usar uma saudação diferente do normal.

O código abaixo usa a biblioteca de cliente Node.js para buscar o valor de 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();
}

É possível melhorar ainda mais essa saudação, adaptando a resposta ao valor real de lastSeen. Por exemplo, os usuários cuja última interação ocorreu muitos meses antes da interação atual podem receber uma saudação diferente daquela que usou a ação no dia anterior.

Controle de volume na conversa

Em dispositivos compatíveis, o Assistente permite que os usuários controlem o volume do dispositivo na ação de conversa, dizendo "aumentar o volume" ou "definir o volume para 50%". Se você tiver intents que processam frases de treinamento semelhantes, suas intents terão precedência. Recomendamos permitir que o Assistente gerencie essas solicitações de usuários, a menos que sua ação tenha um motivo específico para isso.