Dicas de implementação (Dialogflow)

Confira as dicas abaixo para implementar boas práticas de design de conversas na sua Ação.

Espere variações

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

Fornecer novas solicitações úteis e falhar normalmente

Às vezes, a Ação não pode avançar porque não recebeu uma entrada (conhecida como "sem entrada") ou não entendeu a entrada de um usuário (o que é conhecido como não correspondência). Quando isso acontece, o Google Assistente primeiro tenta determinar se o usuário quer acionar uma ação diferente. Se o Google Assistente não corresponder a entrada do usuário com 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 de forma exclusiva com situações sem entrada e sem correspondência em cada turno em uma conversa com um substituto. Com substitutos, você pode ajudar os usuários a voltar aos trilhos.

Para isso, inicialize uma variável fallbackCount no objeto conv.data e defina-a como 0. Prepare uma matriz de dois prompts substitutos (com encaminhamento para um supervisor em clareza) e um comando substituto final que encerre a conversa.

Em seguida, crie uma intent substituta (de preferência uma para cada intent acionável no agente). No gerenciador de intents, extraia a contagem de substitutos do objeto conv.data e a incremente e, se for menor que três, extraia a solicitação da matriz de 3. Se a contagem for quatro ou mais, feche a conversa usando a solicitação final. Em todas as intents que não sejam substitutas, redefina a contagem de substitutos para 0. O ideal é criar modelos para intents específicas de maneira específica.

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

Estar 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 uma resposta (em rotação) que ofereça uma visão geral do que o agente pode fazer e direcione os usuários a uma ação possível. O ideal é usar também intents de ajuda de continuidade 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 as informações

Envolva todos os métodos app.ask(output) com uma função de proxy que adicione a saída a conv.data.lastPrompt. Crie uma intent de repetição que detecte solicitações repetidas do usuário, como "o quê?", "Diga isso de novo" ou "você pode repetir isso?". Crie uma matriz de prefixos de repetição que possam ser usados para confirmar que o usuário solicitou algo a ser repetido. No gerenciador de intent de repetição, chame ask() com uma string concatenada do prefixo de repetição e o valor de conv.data.lastPrompt. Lembre-se de que será necessário alterar as tags de abertura SSML se usadas no último prompt.

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

Personalize a conversa com preferências do usuário

Sua Ação pode solicitar preferências e lembrar dos usuários para uso posterior, permitindo que você personalize conversas futuras com esse usuário.

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

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 qual CEP ele está na primeira caixa de diálogo, é possível 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 chip de sugestão que permite escolher um CEP diferente), mas ao reduzir a rodada de 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 um estado entre conversas garante uma experiência muito mais natural para os usuários que retornam. A primeira etapa para criar essa experiência é saudar os usuários retornantes de forma diferente. Por exemplo, você pode diminuir a saudação ou mostrar informações úteis com base em conversas anteriores. Para fazer isso, use a propriedade lastSeen AppRequest.User recebida para determinar se o usuário já interagiu com a ação. 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();
}

Para melhorar ainda mais essa saudação, adapte a resposta para o valor real de lastSeen. Por exemplo, os usuários que tiveram a última interação realizada muitos meses antes da interação atual podem receber uma saudação diferente daqueles que usaram a ação no dia anterior.

Controle do volume na conversa

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