Sugerencias de implementación (Dialogflow)

Revisa las siguientes sugerencias para implementar buenas prácticas de diseño de conversación en tu Action.

Se esperan variaciones

Controla esto en la entrada “El usuario dice” en Dialogflow. Además, usa más de un intent que pueda asignarse a la misma acción, en la que cada intent se pueda activar con diferentes conjuntos de frases "El usuario dice".

Proporcionar nuevas solicitudes útiles y fallar de forma controlada

A veces, tu acción no puede avanzar porque no recibió una entrada (conocida como "sin entrada") o no comprendió la entrada de un usuario (lo que se conoce como "sin coincidencia"). Cuando esto sucede, Asistente primero intenta determinar si el usuario desea activar una Acción diferente. Si Asistente no asocia la entrada del usuario con otra Acción, este continúa en el contexto de tu Acción. Esta situación puede ocurrir en cualquier momento, por lo que la práctica recomendada es controlar de forma única situaciones sin entrada y sin coincidencia en cada turno de una conversación con un resguardo. Con los resguardos, puedes ayudar a los usuarios a volver a la normalidad.

Para ello, inicializa una variable fallbackCount en tu objeto conv.data y establécela en 0. Prepara un array de dos mensajes de resguardo (escalamiento claro) y un mensaje de resguardo final que finalice la conversación.

Luego, crea un intent de resguardo (idealmente uno para cada intent accionable en el agente). En el controlador de intents, extrae el recuento de resguardo del objeto conv.data, auméntalo y, si es inferior a 3, extrae el mensaje del array de 3. Si el recuento es de 4 o más, cierra la conversación con la instrucción final. En todos los intents que no sean resguardos, restablece el recuento de resguardo a 0. Lo ideal es crear plantillas de resguardo para intents específicos que sean específicos de ellos.

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

Prepárate para ayudar en cualquier momento

Crear un intent que escuche frases de ayuda como "¿qué puedo hacer?", "¿qué me puedes decir?" o "ayuda". En este intent, ofrece una respuesta (rotativa) que ofrezca una descripción general de lo que puede hacer el agente y dirija a los usuarios a una acción posible. Lo ideal es que también uses intents de ayuda de seguimiento en Dialogflow a fin de crear diferentes situaciones de ayuda para distintos intents prácticos.

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

Permite que los usuarios vuelvan a reproducir la información

Une todos tus métodos app.ask(output) con una función de proxy que agregue el resultado a conv.data.lastPrompt. Crea un intent repetido que escuche los mensajes que se repitan del usuario, como "¿qué?", "Di eso otra vez" o "¿Puedes repetir eso?". Crea un array de prefijos de repetición que se puedan usar para confirmar que el usuario solicitó que se repita algo. En el controlador de intents de repetición, llama a ask() con una cadena concatenada del prefijo de repetición y el valor de conv.data.lastPrompt. Ten en cuenta que deberás cambiar las etiquetas de apertura de SSML si las usas en la última instrucción.

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

Personaliza la conversación con preferencias del usuario

Tu Acción puede solicitar a los usuarios sus preferencias y recordarlas para usarlas más adelante, lo que te permite personalizar conversaciones futuras con ese usuario.

Esta acción de ejemplo proporciona a los usuarios un informe meteorológico para un código postal. En el siguiente código de ejemplo, se le pregunta al usuario si desea que la acción recuerde su código postal para conversaciones 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();
}

Después de preguntarle al usuario qué código postal tiene en el primer diálogo, puedes omitir ese mensaje en la próxima invocación y usar el mismo código postal. Debes proporcionar una ruta de escape (como un chip de sugerencias que les permita elegir un código postal diferente), pero, si reduces el turno de la conversación en el caso común, crearás una experiencia mucho más fluida.

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

Personalización para usuarios recurrentes

Mantener algún estado entre las conversaciones garantiza una experiencia mucho más natural para los usuarios recurrentes. El primer paso para crear esta experiencia es saludar a los usuarios recurrentes de manera diferente. Por ejemplo, puedes ajustar el saludo o mostrar información útil sobre la base de conversaciones pasadas. Para ello, usa la propiedad AppRequest.User lastSeen entrante a fin de determinar si el usuario interactuó antes con tu acción. Si la propiedad lastSeen se incluye en la carga útil de la solicitud, puedes usar un saludo diferente al normal.

El siguiente código usa la biblioteca cliente de Node.js para recuperar el 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 mejorar aún más este saludo, adapta la respuesta al valor real de lastSeen. Por ejemplo, los usuarios cuya última interacción ocurrió muchos meses antes de la interacción actual podrían recibir un saludo diferente al de los que usaron la acción el día anterior.

Control de volumen en las conversaciones

En los dispositivos compatibles, Asistente permite que los usuarios controlen el volumen del dispositivo dentro de la acción conversacional. Para ello, pueden decir "subir el volumen" o "establecer el volumen al 50%". Si tienes intents que controlan frases de entrenamiento similares, estos tienen prioridad. Te recomendamos que permitas que Asistente controle estas solicitudes de los usuarios, a menos que la Acción tenga un motivo específico para hacerlo.