IBM Cloud Docs
Implementación de respuestas de diálogo en una aplicación cliente

Implementación de respuestas de diálogo en una aplicación cliente

Un nodo de diálogo puede responder a los usuarios con una respuesta que incluya texto, imágenes o elementos interactivos, como opciones que se pueden pulsar. Si está construyendo su propia aplicación cliente, debe implementar la correcta visualización de todos los tipos de respuesta que son devueltos por su diálogo. Para obtener más información sobre las respuestas de diálogo, consulte Respuestas.

Formato de salida de las respuestas

De forma predeterminada, las respuestas de un nodo de diálogo se especifican en el objeto output.generic en la respuesta JSON que de devuelve desde la API /message. El objeto generic contiene una matriz de un máximo de 5 elementos de respuesta que están pensados para cualquier canal. En el siguiente ejemplo de JSON se muestra una respuesta que incluye texto y una imagen.

{
  "output": {
    "generic": [
      {
        "response_type": "text",
        "text": "OK, here's a picture of a dog."
      },
      {
        "response_type": "image",
        "source": "http://example.com/dog.jpg"
      }
    ],
    "text" : ["OK, here's a picture of a dog."]
  },
  "user_id": "faf4a112-f09f-4a95-a0be-43c496e6ac9a"
}

Como se ve en este ejemplo, la respuesta de texto (OK, here's a picture of a dog.) también se devuelve en la matriz output.text. La matriz se incluye para la compatibilidad con aplicaciones anteriores que no dan soporte al formato output.generic.

Es responsabilidad de su aplicación cliente manejar todos los tipos de respuesta. En este caso, la aplicación tendrá que mostrar el texto y la imagen especificados al usuario.

Tipos de respuesta

Cada elemento de una respuesta es de uno de los tipos de respuesta admitidos. Cada tipo de respuesta se especifica utilizando un conjunto diferente de propiedades JSON, por lo que las propiedades que se incluyen para cada respuesta varían en función del tipo de respuesta. Para obtener información completa sobre el modelo de respuesta de la API /message, consulte la Referencia de la API.

En esta sección se describen los tipos de respuesta disponibles y cómo se representan en el archivo JSON de respuesta de la API /message. Si está utilizando el SDK Watson, puede utilizar las interfaces que se proporcionan para su idioma para acceder a los mismos objetos.

Los ejemplos de esta sección muestran el formato de los datos JSON devueltos por /message API en tiempo de ejecución, y es diferente del formato JSON que se utiliza para definir las respuestas dentro de un nodo de diálogo. Puede utilizar el formato de los ejemplos para actualizar output.generic con webhooks. Para actualizar output.generic con el editor JSON, consulte Definición de respuestas utilizando el editor JSON).

Texto

El tipo de respuesta text se utiliza para las respuestas de texto normales desde el diálogo:

{
  "output": {
    "generic":[
      {
        "response_type": "text",
        "text": "OK, you want to fly to Boston next Monday."
      }
    ]
  },
  "user_id": "faf4a112-f09f-4a95-a0be-43c496e6ac9a"
}

Por compatibilidad, el mismo texto también se incluye en la matriz output.text en la respuesta del diálogo.

Imagen

El tipo de respuesta image indica a la aplicación cliente que muestre una imagen, acompañada opcionalmente por un título y una descripción:

{
  "output": {
    "generic":[
      {
        "response_type": "image",
        "source": "http://example.com/image.jpg",
        "title": "Image example",
        "description": "This is an example image"
      }
    ]
  },
  "user_id": "faf4a112-f09f-4a95-a0be-43c496e6ac9a"
}

Su aplicación es responsable de recuperar la imagen especificada por la propiedad source y mostrarla al usuario. Si se proporcionan los valores opcionales title y description, la aplicación puede mostrarlos de la manera adecuada (por ejemplo, mostrando el título después de la imagen y la descripción como texto contextual).

Vídeo

El tipo de respuesta video indica a la aplicación cliente que muestre un vídeo, opcionalmente acompañado de title, description, y alt_text para la accesibilidad:

{
  "output": {
    "generic":[
      {
        "response_type": "video",
        "source": "http://example.com/video.mp4",
        "title": "Video example",
        "description": "This is an example video",
        "alt_text": "A video showing a great example",
        "channel_options": {
          "chat": {
            "dimensions": {
              "base_height": 180
            }
          }
        }
      }
    ]
  },
  "user_id": "faf4a112-f09f-4a95-a0be-43c496e6ac9a"
}

Su aplicación es responsable de recuperar el vídeo especificado por la propiedad source y mostrarlo al usuario. Si se proporcionan los valores opcionales title y description, la aplicación puede mostrarlos de la manera adecuada.

La propiedad opcional channel_options.chat.dimensions.base_height toma un número que indica que el vídeo se representa a una anchura de 360 píxeles. Tu aplicación utiliza este valor para mantener la relación de aspecto adecuada del vídeo si se renderiza en un tamaño no estándar.

Audio

El tipo de respuesta audio indica a la aplicación cliente que debe reproducir un archivo de audio:

{
  "output": {
    "generic":[
      {
        "response_type": "audio",
        "source": "http://example.com/audio.mp3",
        "channel_options": {
          "voice_telephony": {
            "loop": true
          }
        }
      }
    ]
  },
  "user_id": "faf4a112-f09f-4a95-a0be-43c496e6ac9a"
}

La aplicación es la responsable de reproducir el archivo de audio.

La propiedad opcional channel_options.voice_telephony.loop toma un valor booleano que indica si el archivo de audio se reproduce como un bucle continuo. Esta opción se utiliza normalmente para la música de espera que puede necesitar continuar durante un tiempo indefinido.

iframe

El tipo de respuesta iframe indica a la aplicación cliente que muestre contenido en un elemento iframe incluido, que puede ir acompañado de un título:

{
  "output": {
    "generic":[
      {
        "response_type": "iframe",
        "source": "http://example.com/iframe.html",
        "title": "My IFrame"
      }
    ]
  },
  "user_id": "faf4a112-f09f-4a95-a0be-43c496e6ac9a"
}

La aplicación es la responsable de visualizar el contenido de iframe. El contenido de un iframe incluido es útil para visualizar contenido de terceros o para el contenido de su propio sitio que no quiera volver a crear utilizando el tipo de respuesta user_defined.

Pausa

El tipo de respuesta pause indica a la aplicación que espere un intervalo especificado antes de la siguiente respuesta:

{
  "output": {
    "generic":[
      {
        "response_type": "pause",
        "time": 500,
        "typing": false
      }
    ]
  },
  "user_id": "faf4a112-f09f-4a95-a0be-43c496e6ac9a"
}

Esta pausa puede ser solicitada por el diálogo para dar tiempo a que se complete una solicitud, o para imitar la apariencia de un agente humano que podría hacer una pausa entre respuestas. La pausa puede durar un máximo de 10 segundos.

La respuesta pause se suele enviar en combinación con otras respuestas. Su aplicación hace una pausa durante el intervalo especificado por la propiedad time (en milisegundos) antes de la siguiente respuesta en el array. La propiedad opcional typing solicita que la aplicación cliente muestre un indicador user is typing, si es compatible, para simular un agente en directo.

Opción

El tipo de respuesta option indica a la aplicación cliente que muestre un control de interfaz de usuario que permita al usuario seleccionar entre una lista de opciones y, a continuación, enviar la entrada de nuevo al asistente en función de la opción seleccionada:

{
  "output": {
    "generic":[
      {
        "response_type": "option",
        "title": "Available options",
        "description": "Please select one of the following options:",
        "preference": "button",
        "options": [
          {
            "label": "Option 1",
            "value": {
              "input": {
                "text": "option 1"
              }
            }
          },
          {
            "label": "Option 2",
            "value": {
              "input": {
                "text": "option 2"
              }
            }
          }
        ]
      }
    ]
  },
  "user_id": "faf4a112-f09f-4a95-a0be-43c496e6ac9a"
}

La app puede mostrar las opciones especificadas mediante cualquier control de interfaz de usuario adecuado (por ejemplo, un conjunto de botones o una lista desplegable). La propiedad opcional preference indica el tipo de control preferido que utiliza su aplicación ( button o dropdown ), si es compatible. Para una mejor experiencia del usuario, se recomienda ofrecer como botones si hay hasta tres opciones y, si hay más de tres opciones, como lista desplegable.

Para cada opción, la propiedad label especifica el texto de la etiqueta que aparece para la opción en el control UI. La propiedad value especifica la entrada que se devuelve al asistente (utilizando la API /message ) cuando el usuario selecciona la opción correspondiente.

Para ver un ejemplo de implementación de respuestas de option en una aplicación de cliente sencilla, consulte Ejemplo: implementación de respuestas de opciones.

Sugerencia

Plus

El tipo de respuesta suggestion es utilizado por la función de desambiguación para sugerir posibles coincidencias que aclaren lo que el usuario quiere hacer. Una respuesta de suggestion incluye una matriz suggestions, cada una de ellas correspondiente a un posible nodo de diálogo coincidente:

{
  "output": {
    "generic": [
      {
        "response_type": "suggestion",
        "title": "Did you mean:",
        "suggestions": [
          {
            "label": "I'd like to order a drink.",
            "value": {
              "intents": [
                {
                  "intent": "order_drink",
                  "confidence": 0.7330395221710206
                }
              ],
              "entities": [],
              "input": {
                "suggestion_id": "576aba3c-85b9-411a-8032-28af2ba95b13",
                "text": "I want to place an order"
              }
            },
            "output": {
              "text": [
                "I'll get you a drink."
              ],
              "generic": [
                {
                  "response_type": "text",
                  "text": "I'll get you a drink."
                }
              ],
              "nodes_visited_details": [
                {
                  "dialog_node": "node_1_1547675028546",
                  "title": "order drink",
                  "user_label": "I'd like to order a drink.",
                  "conditions": "#order_drink"
                }
              ]
            },
            "source_dialog_node": "root"
          },
          {
            "label": "I need a drink refill.",
            "value": {
              "intents": [
                {
                  "intent": "refill_drink",
                  "confidence": 0.2529746770858765
                }
              ],
              "entities": [],
              "input": {
                "suggestion_id": "6583b547-53ff-4e7b-97c6-4d062270abcd",
                "text": "I need a drink refill"
              }
            },
            "output": {
              "text": [
                "I'll get you a refill."
              ],
              "generic": [
                {
                  "response_type": "text",
                  "text": "I'll get you a refill."
                }
              ],
              "nodes_visited_details": [
                {
                  "dialog_node": "node_2_1547675097178",
                  "title": "refill drink",
                  "user_label": "I need a drink refill.",
                  "conditions": "#refill_drink"
                }
              ]
            },
            "source_dialog_node": "root"
          }
        ]
      }
    ],
  },
  "user_id": "faf4a112-f09f-4a95-a0be-43c496e6ac9a"
}

La estructura de una respuesta suggestion es similar a la estructura de una respuesta option. Al igual que con las opciones, cada sugerencia incluye un label que puede mostrarse al usuario y un value que especifica la entrada que se devuelve al asistente si el usuario elige la sugerencia correspondiente. Para implementar las respuestas de suggestion en la aplicación, puede utilizar el mismo enfoque que utilizaría para las respuestas de option.

Para obtener más información sobre la función de desambiguación, consulte Desambiguación.

Definida por el usuario

Un tipo de respuesta definido por el usuario puede contener hasta 5000 KB de datos para dar soporte a un tipo de respuesta que haya implementado en su cliente. Por ejemplo, puede definir un tipo de respuesta definida por el usuario para mostrar una tarjeta con un código de color especial o para dar formato a los datos de una tabla o un gráfico.

La propiedad user_defined de la respuesta es un objeto que puede contener cualquier dato JSON válido:

{
  "output": {
    "generic":[
      {
        "response_type": "user_defined",
        "user_defined": {
          "field_1": "String value",
          "array_1": [
            1,
            2
          ],
          "object_1": {
            "property_1": "Another string value"
          }
        }
      }
    ]
  },
  "user_id": "faf4a112-f09f-4a95-a0be-43c496e6ac9a"
}

La aplicación puede analizar y visualizar los datos de cualquier forma que elija el usuario.

Ejemplo: Implementación de respuestas de opciones

Para mostrar cómo una aplicación cliente puede gestionar las respuestas de opción, que piden al usuario que seleccione entre una lista de opciones, podemos ampliar el ejemplo de cliente que se utiliza en Creación de una aplicación cliente. El ejemplo es una aplicación cliente simplificada que utiliza la entrada y salida estándar para gestionar tres intentos (enviar un saludo, mostrar la hora actual y salir de la aplicación):

Welcome to the example!
>> hello
Good day to you.
>> what time is it?
The current time is 12:40:42 PM.
>> goodbye
OK! See you later.

Si desea probar el código de ejemplo, configure el espacio de trabajo necesario y obtenga los detalles de la API que necesita. Para obtener más información, consulte Creación de una aplicación cliente.

Recepción de una respuesta de opción

La respuesta option se puede utilizar cuando quiera ofrecer al usuario una lista finita de opciones, en lugar de interpretar la entrada de lenguaje natural. La respuesta puede utilizarse en cualquier situación en la que desee permitir al usuario seleccionar rápidamente entre un conjunto de opciones inequívocas.

En nuestra aplicación cliente simplificada, utilizamos esta capacidad para seleccionar de una lista las acciones que admite el asistente (saludar, mostrar la hora y salir). Además de las tres intenciones anteriores (#hello, #time y #goodbye), el espacio de trabajo de ejemplo admite una cuarta intención: #menu, que se activa cuando el usuario solicita ver la lista de acciones disponibles.

Cuando el espacio de trabajo reconoce la intención #menu, el diálogo responde con una respuesta option:

{
  "output": {
    "generic": [
      {
        "title": "What do you want to do?",
        "options": [
          {
            "label": "Send greeting",
            "value": {
              "input": {
                "text": "hello"
              }
            }
          },
          {
            "label": "Display the local time",
            "value": {
              "input": {
                "text": "time"
              }
            }
          },
          {
            "label": "Exit",
            "value": {
              "input": {
                "text": "goodbye"
              }
            }
          }
        ],
        "response_type": "option"
      }
    ],
    "intents": [
      {
        "intent": "menu",
        "confidence": 0.6178638458251953
      }
    ],
    "entities": []
  },
  "user_id": "faf4a112-f09f-4a95-a0be-43c496e6ac9a"
}

La respuesta de option contiene varias opciones que se presentarán al usuario. Cada opción incluye dos objetos, label y value. label es una cadena que identifica la opción de cara al usuario; value especifica el mensaje de entrada correspondiente que se devuelve al asistente si el usuario elige la opción.

Nuestra aplicación cliente necesita utilizar los datos de esta respuesta para construir la salida que mostramos al usuario, y para enviar el mensaje apropiado al asistente.

Listado de las opciones disponibles

El primer paso en el manejo de una respuesta de opción es mostrar las opciones al usuario, utilizando el texto especificado por la propiedad label de cada opción. Puede visualizar las opciones utilizando cualquier técnica admitida por su aplicación, normalmente una lista desplegable o un conjunto de botones que se puedan pulsar. La propiedad opcional preference de una respuesta de opción, si se especifica, indica qué tipo de visualización utiliza la aplicación si es posible.

Nuestro ejemplo simplificado utiliza la entrada y salida estándar, por lo que no tenemos acceso a una interfaz de usuario real. En su lugar, presentamos las opciones en forma de lista numerada.

// Option example 1: lists options.

const prompt = require('prompt-sync')();
const AssistantV2 = require('ibm-watson/assistant/v2');
const { IamAuthenticator } = require('ibm-watson/auth');

// Set up Assistant service wrapper.
const service = new AssistantV2({
  version: '2019-02-28',
  authenticator: new IamAuthenticator({
    apikey: '{apikey}', // replace with API key
  })
});

const assistantId = '{assistant_id}'; // replace with assistant ID
let sessionId;

// Create session.
service
  .createSession({
    assistantId,
  })
  .then(res => {
    sessionId = res.result.session_id;
    sendMessage({
      messageType: 'text',
      text: '',  // start conversation with empty message
    });
  })
  .catch(err => {
    console.log(err); // something went wrong
  });

// Send message to assistant.
function sendMessage(messageInput) {
  service
    .message({
      assistantId,
      sessionId,
      input: messageInput,
    })
    .then(res => {
      processResponse(res.result);
    })
    .catch(err => {
      console.log(err); // something went wrong
    });
}

// Process the response.
function processResponse(response) {

  let endConversation = false;

  // Check for client actions requested by the assistant.
  if (response.output.actions) {
    if (response.output.actions[0].type === 'client'){
      if (response.output.actions[0].name === 'display_time') {
        // User asked what time it is, so we output the local system time.
        console.log('The current time is ' + new Date().toLocaleTimeString() + '.');
      } else if (response.output.actions[0].name === 'end_conversation') {
        // User said goodbye, so we're done.
        console.log(response.output.generic[0].text);
        endConversation = true;
      }
    }
  } else {
    // Display the output from assistant, if any. Supports only a single
    // response.
    if (response.output.generic) {
      if (response.output.generic.length > 0) {
        switch (response.output.generic[0].response_type) {
          case 'text':
            // It's a text response, so we just display it.
            console.log(response.output.generic[0].text);
            break;
          case 'option':
            // It's an option response, so we'll need to show the user
            // a list of choices.
            console.log(response.output.generic[0].title);
            const options = response.output.generic[0].options;
            // List the options by label.
            for (let i = 0; i < options.length; i++) {
              console.log((i+1).toString() + '. ' + options[i].label);
            }
            break;
        }
      }
    }
  }

  // If we're not done, prompt for the next round of input.
  if (!endConversation) {
    const newMessageFromUser = prompt('>> ');
    newMessageInput = {
      messageType: 'text',
      text: newMessageFromUser,
    }
    sendMessage(newMessageInput);
  } else {
    // We're done, so we delete the session.
    service
      .deleteSession({
        assistantId,
        sessionId,
      })
      .then(res => {
        return;
      })
      .catch(err => {
        console.log(err); // something went wrong
      });
  }
}

Echemos un vistazo más detallado al código de salida de la respuesta desde el asistente. Ahora, en lugar de presuponer una respuesta de text, la aplicación admite los tipos de respuesta text y option:

    // Display the output from assistant, if any. Supports only a single
    // response.
    if (response.output.generic) {
      if (response.output.generic.length > 0) {
        switch (response.output.generic[0].response_type) {
          case 'text':
            // It's a text response, so we just display it.
            console.log(response.output.generic[0].text);
            break;
          case 'option':
            // It's an option response, so we'll need to show the user
            // a list of choices.
            console.log(response.output.generic[0].title);
            const options = response.output.generic[0].options;
            // List the options by label.
            for (let i = 0; i < options.length; i++) {
              console.log((i+1).toString() + '. ' + options[i].label);
            }
            break;
        }
      }
    }

Si response_type = text, mostramos la salida, como antes. Pero si response_type = option, debemos trabajar un poco más. En primer lugar, mostramos el valor de la propiedad title, que sirve como texto introductorio para presentar la lista de opciones; a continuación, enumeramos las opciones, utilizando el valor de la propiedad label para identificar cada una de ellas. (Una aplicación en el mundo real mostraría estas etiquetas en una lista desplegable o como las etiquetas en botones que se pueden pulsar).

Puede ver el resultado activando la intención #menu:

Welcome to the example!
>> what are the available actions?
What do you want to do?
1. Send greeting
2. Display the local time
3. Exit
>> 2
Sorry, I have no idea what you're talking about.
>>

Como puede ver, la aplicación ahora está manejando correctamente la respuesta option listando las opciones disponibles. Sin embargo, todavía no estamos traduciendo la elección del usuario en una entrada significativa.

Selección de una opción

Además de label, cada opción de la respuesta incluye también un objeto value, que contiene los datos de entrada que se devuelven al asistente si el usuario elige la opción correspondiente. El objeto value.input es equivalente a la propiedad input de la API /message, lo que significa que podemos devolver este objeto al asistente tal cual.

Establecemos un nuevo distintivo promptOption cuando el cliente recibe una respuesta option. Cuando este distintivo es verdadero, sabemos que queremos aceptar la siguiente ronda de entrada de value.input en lugar de aceptar la entrada de texto de lenguaje natural del usuario. De nuevo, no tenemos una interfaz de usuario real, así que pedimos al usuario que seleccione una opción válida de la lista por número.

// Option example 2: sends back selected option value.

const prompt = require('prompt-sync')();
const AssistantV2 = require('ibm-watson/assistant/v2');
const { IamAuthenticator } = require('ibm-watson/auth');

// Set up Assistant service wrapper.
const service = new AssistantV2({
  version: '2019-02-28',
  authenticator: new IamAuthenticator({
    apikey: '{apikey}', // replace with API key
  })
});

const assistantId = '{assistant_id}'; // replace with assistant ID
let sessionId;

// Create session.
service
  .createSession({
    assistantId,
  })
  .then(res => {
    sessionId = res.result.session_id;
    sendMessage({
      messageType: 'text',
      text: '',  // start conversation with empty message
    });
  })
  .catch(err => {
    console.log(err); // something went wrong
  });

// Send message to assistant.
function sendMessage(messageInput) {
  service
    .message({
      assistantId,
      sessionId,
      input: messageInput,
    })
    .then(res => {
      processResponse(res.result);
    })
    .catch(err => {
      console.log(err); // something went wrong
    });
}

// Process the response.
function processResponse(response) {

  let endConversation = false;
  let promptOption = false;

  // Check for client actions requested by the assistant.
  if (response.output.actions) {
    if (response.output.actions[0].type === 'client'){
      if (response.output.actions[0].name === 'display_time') {
        // User asked what time it is, so we output the local system time.
        console.log('The current time is ' + new Date().toLocaleTimeString() + '.');
      } else if (response.output.actions[0].name === 'end_conversation') {
        // User said goodbye, so we're done.
        console.log(response.output.generic[0].text);
        endConversation = true;
      }
    }
  } else {
    // Display the output from assistant, if any. Supports only a single
    // response.
    if (response.output.generic) {
      if (response.output.generic.length > 0) {
        switch (response.output.generic[0].response_type) {
          case 'text':
            // It's a text response, so we just display it.
            console.log(response.output.generic[0].text);
            break;
          case 'option':
              // It's an option response, so we'll need to show the user
              // a list of choices.
              console.log(response.output.generic[0].title);
              const options = response.output.generic[0].options;
              // List the options by label.
              for (let i = 0; i < options.length; i++) {
                console.log((i+1).toString() + '. ' + options[i].label);
              }
            promptOption = true;
            break;
        }
      }
    }
  }

  // If we're not done, prompt for the next round of input.
  if (!endConversation) {
    let messageInput;
    if (promptOption == true) {
      // Prompt for a valid selection from the list of options.
      let choice;
      do {
        choice = prompt('? ');
          if (isNaN(choice)) {
            choice = 0;
          }
      } while (choice < 1 || choice > response.output.generic[0].options.length);
      const value = response.output.generic[0].options[choice-1].value;
      // Use message input from the selected option.
      messageInput = value.input;
    } else {
      // We're not showing options, so we just prompt for the next
      // round of input.
      const newText = prompt('>> ');
      messageInput = {
        text: newText,
      }
    }
    sendMessage(messageInput);
  } else {
    // We're done, so we delete the session.
    service
      .deleteSession({
        assistantId,
        sessionId,
      })
      .then(res => {
        return;
      })
      .catch(err => {
        console.log(err); // something went wrong
      });
  }
}

Todo lo que debemos hacer es utilizar el objeto value.input de la respuesta seleccionada como la siguiente ronda de entrada de mensajes, en lugar de construir un nuevo objeto input utilizando la entrada de texto. El asistente responde entonces exactamente igual que si el usuario hubiera tecleado directamente el texto introducido.

Welcome to the example!
>> hi
Good day to you.
>> what are the choices?
What do you want to do?
1. Send greeting
2. Display the local time
3. Exit
? 2
The current time is 1:29:14 PM.
>> bye
OK! See you later.

Ahora podemos acceder a todas las funciones del asistente ya sea haciendo solicitudes en lenguaje natural o seleccionando en un menú de opciones.

El mismo enfoque se utiliza también para las respuestas de suggestion. Si el plan admite la función de desambiguación, puede utilizar lógica similar para solicitar a los usuarios que seleccionen en una lista cuando no está claro cuál de las distintas opciones posibles es correcta. Para obtener más información sobre la función de desambiguación, consulte Desambiguación.