Implementación de componentes personalizados

Para implantar componentes personalizados, se utiliza el SDK Node.js de Oracle Digital Assistant para interactuar con el servicio de componentes personalizados de Digital Assistant.

Para implantar los componentes personalizados que se pueden desplegar en el contenedor incrustado de Digital Assistant, Oracle Cloud Infrastructure Functions, un backend Mobile Hub o un servidor Node.js:

  1. Instale el software para crear componentes personalizados.

  2. Cree el paquete de componentes personalizados.

  3. Cree y compile un componente personalizado.

Nota

Si planea desplegar el paquete de componentes personalizados en un servicio de componentes personalizados incrustados, cada aptitud que agregue al paquete se contabiliza como un servicio independiente. Hay un límite para el número de servicios de componentes personalizados incrustados que puede tener una instancia. Si no conoce el límite, pida al administrador del servicio que obtenga su embedded-custom-component-service-count como se describe en Visualización de los límites de servicio en la consola de Infrastructure. Considere la posibilidad de empaquetar varios componentes por paquete para minimizar el número de servicios de componentes incrustados que vaya a utilizar. Si intenta agregar un servicio de componentes después de haber alcanzado ese límite, fallará la creación del servicio.

Paso 1: instalación del software para crear componentes personalizados

Para crear un paquete de componentes personalizados, necesita Node.js, Node Package Manager y el SDK Node.js de los bots de Oracle Digital Assistant.

Nota

En Windows, el SDK del nodo de bots no funciona en Windows si la instalación del nodo es de la versión 20.12.2 o superior debido a un cambio incompatible con versiones anteriores en Node.js. Si ya tiene instalada la versión 20.12.2 o posterior del nodo, deberá desinstalarlo y, a continuación, instalar la versión 20.12.1 o anterior para que funcione el SDK del nodo de bots.
  1. Si aún no lo ha hecho, descargue Node.js de https://nodejs.org e instálelo para el acceso global. Node Package Manager (npm) se distribuye con Node.js.

    Para comprobar si se han instalado Node.js y npm, abra una ventana de terminal y escriba los siguientes comandos:

    node –v 
    npm –v
  2. Para instalar el SDK de bots Node.js de Oracle Digital Assistant para el acceso global, introduzca este comando en una ventana de terminal:
    npm install -g @oracle/bots-node-sdk

    En Mac, puede utilizar el comando sudo:

    sudo npm install -g @oracle/bots-node-sdk

    Si utiliza la opción -g (global), tendrá acceso directo a la interfaz de línea de comandos bots-node-sdk. De lo contrario, utilice npx @oracle/bots-node-sdk.

  3. Para verificar la instalación del SDK de bots Node.js de Oracle Digital Assistant, escriba el siguiente comando:
    bots-node-sdk -v
    El comando debe imprimir la versión del SDK de bots Node.js de Oracle Digital Assistant.

Paso 2: creación del paquete de componentes personalizados

Para iniciar un proyecto, utilice el comando bots-node-sdk init desde la interfaz de línea de comandos (CLI) del SDK para crear los archivos y la estructura de directorios necesarios para la estructura de componentes.

El comando init tiene algunas opciones, como el uso de JavaScript (valor por defecto) o TypeScript, y el nombre del archivo JavaScript del componente inicial. Estas opciones se describen en CLI Developer Tools. Este es el comando básico para iniciar un proyecto de JavaScript:

bots-node-sdk init <top-level folder path> --name <component service name>

Este comando realiza las acciones siguientes para un paquete de JavaScript:

  • Crea la carpeta de nivel superior.

  • Crea una carpeta components y agrega un archivo JavaScript de componente de ejemplo denominado hello.world.js. Aquí puede colocar los archivos JavaScript del componente.

  • Agrega un archivo package.json, que especifica main.js como punto de entrada principal y muestra @oracle/bots-node-sdk como devDependency. El archivo de paquete también apunta a algunas secuencias de comandos bots-node-sdk.

    {
      "name": "myCustomComponentService",
      "version": "1.0.0",
      "description": "Oracle Bots Custom Component Package",
      "main": "main.js",
      "scripts": {
        "bots-node-sdk": "bots-node-sdk",
        "help": "npm run bots-node-sdk -- --help",
        "prepack": "npm run bots-node-sdk -- pack --dry-run",
        "start": "npm run bots-node-sdk -- service ."
      },
      "repository": {},
      "dependencies": {},
      "devDependencies": {
        "@oracle/bots-node-sdk": "^2.2.2",
        "express": "^4.16.3"
      }
    }
  • Agrega un archivo main.js, que exporta la configuración del paquete y apunta a la carpeta de componentes para la ubicación de los componentes, en la carpeta de nivel superior.

  • Agrega un archivo .npmignore a la carpeta de nivel superior. Este archivo se utiliza al exportar el paquete de componentes. Debe excluir los archivos .tgz del paquete. Por ejemplo: *.tgz.

  • Para algunas versiones de npm, crea un archivo package-lock.json.

  • Instala todas las dependencias del paquete en la subcarpeta node_modules.
Nota

Si no utiliza el comando bots-node-sdk init para crear la carpeta del paquete, asegúrese de que la carpeta de nivel superior contiene un archivo .npmignore que contiene una entrada *.tgz. Por ejemplo:
*.tgz
spec
service-*

De lo contrario, cada vez que se empaquetan los archivos en un archivo TGZ, se incluirá el archivo TGZ que ya existe en la carpeta de nivel superior, y el archivo TGZ seguirá doblando su tamaño.

Si desea desplegar en el contenedor integrado, el paquete debe ser compatible con el nodo 14.17.0.

Paso 3: creación y compilación de un componente personalizado

Creación del archivo del componente

Utilice el comandoinit component de la CLI del SDK para crear un archivo JavaScript o TypeScript con el marco para trabajar con el SDK de Node.js de Oracle Digital Assistant para escribir un componente personalizado. El idioma especificado al ejecutar el comando init para crear el paquete de componentes determina si se ha creado un archivo JavaScript o TypeScript.

Por ejemplo, para crear un archivo para el componente personalizado, desde una ventana de terminal, haga CD hasta la carpeta de nivel superior del paquete y escriba el siguiente comando, sustituyendo <component name> por el nombre del componente:

bots-node-sdk init component <component name> c components

Para JavaScript, con este comando se agrega <component name>.js a components folder. Para TypeScript, el archivo se agrega a la carpeta src/components. El argumento c indica que el archivo es para un componente personalizado.

Tenga en cuenta que el nombre del componente no puede superar los 100 caracteres. Solo puede utilizar caracteres alfanuméricos y guiones bajos en el nombre. No se pueden utilizar guiones. Tampoco puede tener un prefijo System.. Oracle Digital Assistant no le permitirá agregar un servicio de componente personalizado que tenga nombres de componentes no válidos.

Para obtener más información, consulte https://github.com/oracle/bots-node-sdk/blob/master/bin/CLI.md.

Agregar código a los metadatos y llamar a las funciones

Su componente personalizado debe exportar dos objetos:

  • metadata: proporciona la siguiente información del componente a la aptitud.
    • Nombre del Componente
    • Propiedades soportadas
    • Acciones de transición soportadas

    Para los flujos de diálogo basados en YAML, el componente personalizado soporta las siguientes propiedades por defecto. Estas propiedades no están disponibles para las aptitudes diseñadas en el modo de diálogo Visual.

    • autoNumberPostbackActions: booleano. No necesario. Cuando el valor es true, los botones y las opciones de lista se enumeran automáticamente. El valor por defecto es false. Consulte Numeración automática para canales de solo texto en flujos de diálogo de YAML.
    • insightsEndConversation: booleano. No necesario. Cuando el valor es true, la sesión deja de grabar la conversación para la generación de informes de estadísticas. El valor por defecto es false. Consulte Modelo del flujo de diálogo.
    • insightsInclude: booleano. No necesario. Cuando el valor es true, el estado se incluye en los informes de estadísticas. El valor por defecto es true. Consulte Modelo del flujo de diálogo.
    • translate: booleano. No necesario. Cuando el valor es true, se activa la traducción automática para este componente. El valor por defecto es el valor de la variable de contexto autotranslation. Consulte Servicios de traducción en aptitudes.
  • invoke: contiene la lógica que se va a ejecutar. En este método, puede leer y escribir variables de contexto de aptitud, crear mensajes de conversación, definir transiciones de estado, realizar llamadas de REST, entre otras acciones. Normalmente, se utiliza la palabra clave async con esta función para gestionar promesas. La función invoke toma el siguiente argumento:
    • context, que asigna un nombre a la referencia al objeto CustomComponentContext del SDK de Node.js de Digital Assistant. Esta clase se describe en la documentación del SDK, en https://oracle.github.io/bots-node-sdk/. En las versiones anteriores del SDK, el nombre era conversation. Puede utilizar cualquiera de los nombres.
    Nota

    Si utiliza una biblioteca JavaScript que no soporta promesas (y, por lo tanto, no utiliza la palabra clave async), también es posible agregar un argumento done como devolución de llamada que llama el componente cuando ha terminado el procesamiento.

Ejemplo:

'use strict';

module.exports = {

  metadata: {
    name: 'helloWorld',
    properties: {
      human: { required: true, type: 'string' }
    },
    supportedActions: ['weekday', 'weekend']
  },

  invoke: async(context) => {
    // Retrieve the value of the 'human' component property.
    const { human } = context.properties();
    // determine date
    const now = new Date();
    const dayOfWeek = now.toLocaleDateString('en-US', { weekday: 'long' });
    const isWeekend = [0, 6].indexOf(now.getDay()) > -1;
    // Send two messages, and transition based on the day of the week
    context.reply(`Greetings ${human}`)
      .reply(`Today is ${now.toLocaleDateString()}, a ${dayOfWeek}`)
      .transition(isWeekend ? 'weekend' : 'weekday');   
  }
}

Para obtener más información y conocer algunos ejemplos de código, consulte Escritura de componentes personalizados en la documentación del SDK de Bots Node.

Control del flujo con keepTurn y transition

Puede utilizar diferentes combinaciones de las funciones keepTurn y transition del SDK del nodo de bot para definir cómo interactúa el componente personalizado con un usuario y cómo continúa la conversación después de que el componente haya devuelto el control de flujo a la aptitud.

  • keepTurn(boolean) especifica si la conversación debe pasar a otro estado sin solicitar primero la entrada del usuario.

    Tenga en cuenta que si desea definir keepTurn en true, debe llamar a keepTurn después de llamar a reply porque reply define implícitamente keepTurn en false.

  • transition(action) hace que el cuadro de diálogo pase al siguiente estado después de enviar todas las respuestas, si las hay. El argumento action opcional nombra esa acción (resultado) que devuelve el componente.

    Si no llama a transition(), la respuesta se envía, pero el cuadro de diálogo permanece en el estado y la siguiente entrada de usuario vuelve a este componente. Es decir, se vuelve a llamar a invoke().

invoke: async (context) ==> {
   ...
   context.reply(payload);
   context.keepTurn(true);
   context.transition ("success"); 
}

A continuación se muestran algunos casos de uso comunes en los que utilizaría keepTurn y transition para controlar el flujo de diálogo:

caso de uso Valores definidos para keepTurn y transition

Componente personalizado que pasa a otro estado sin solicitar primero la entrada al usuario.

  1. Si procede, utilice context.reply(<reply>) para enviar una respuesta.

  2. Defina context.keepTurn(true).

  3. Defina context.transition con una cadena supportedActions (por ejemplo, context.transition("success")) o sin argumento (por ejemplo, context.transition()).

Por ejemplo, este componente personalizado actualiza una variable con una lista de valores que se mostrará inmediatamente en el estado siguiente del flujo de diálogo.
invoke: async (context) => {
    const listVariableName = context.properties().variableName;
    ...
    // Write list of options to a context variable
    context.variable(listVariableName, list);
   // Navigate to next state without 
   // first prompting for user interaction.
   context.keepTurn(true);
   context.transition();
 }

Componente personalizado que permite a la aptitud esperar la entrada después de que el control vuelva a la aptitud y antes de que la aptitud pase a otro estado.

  1. Si procede, utilice context.reply(<reply>) para enviar una respuesta.

  2. Defina context.keepTurn(false).

  3. Defina context.transition con una cadena supportedActions (context.transition("success")) o sin argumentos (context.transition()).

Por ejemplo:
context.keepTurn(false);
context.transition("success");
Componente personalizado que obtiene la entrada del usuario sin devolver el control de flujo a la aptitud. Por ejemplo:
  • Un componente transfiere la entrada del usuario para consultar un motor de búsqueda de backend. Si la aptitud solo puede contener un resultado, pero la consulta devuelve varios aciertos, el componente puede solicitar al usuario más información para filtrar los resultados. En este caso, el componente personalizado sigue manejando la entrada del usuario; mantiene la conversación hasta que el motor de búsqueda devuelve un solo acierto. Cuando obtiene un único resultado, el componente llama a context.transition() para pasar a otro estado como se define en la definición de flujo de diálogo.

  • Un componente procesa un cuestionario y solo pasa al siguiente estado cuando todas las preguntas están respondidas.

  1. No llame a transition.

  2. Defina keepTurn(false).

Por ejemplo, este componente personalizado genera una oferta y, a continuación, muestra los botones Yes y No para solicitar otra oferta. Pasa de nuevo a la aptitud cuando el usuario hace clic en No.
  invoke: async (context) => {
    // Perform conversation tasks.
    const tracking_token = "a2VlcHR1cm4gZXhhbXBsZQ==";    
    const quotes = require("./json/Quotes.json");
    const quote = quotes[Math.floor(Math.random() * quotes.length)];
    
    // Check if postback action is issued. If postback action is issued, 
    // check if postback is from this component rendering. This ensures
    // that the component only responds to its own postback actions.     
    if (context.postback() && context.postback().token == tracking_token && context.postback().isNo) {
      context.keepTurn(true);
      context.transition();
    } else {
      // Show the quote of the day.
      context.reply("'" + quote.quote + "'");
      context.reply(" Quote by: " + quote.origin);
      // Create a single message with two buttons to 
      // request another quote or not.
      const mf = context.getMessageFactory();
      const message = mf.createTextMessage('Do you want another quote?')
        .addAction(mf.createPostbackAction('Yes', { isNo: false, token: tracking_token }))
        .addAction(mf.createPostbackAction('No', { isNo: true, token: tracking_token })); 
      context.reply(message);
      // Although reply() automatically sets keepTurn to false, 
      // it's good practice to explicitly set it so that it's
      // easier to see how you intend the component to behave.
      context.keepTurn(false);
    };
  }

Si un componente no pasa a otro estado, debe realizar un seguimiento de este, como se muestra en el ejemplo anterior.

En el caso de una gestión de estado más compleja, como ofrecer al usuario la opción de cancelar si una recuperación de datos tarda demasiado tiempo, puede crear y utilizar una variable de contexto. Por ejemplo: context.variable("InternalComponentWaitTime", time). Si utiliza una variable de contexto, no olvide restablecerla o definirla en nula antes de llamar a context.transition.

Tenga en cuenta que mientras no realice la transición, todos los valores transferidos como propiedades del componente estarán disponibles.

La llamada de componente se repite sin entrada de usuario. Por ejemplo:

  • Un componente hace ping en un servicio remoto en relación con el estado de una orden hasta que el estado se devuelve como accepted o el componente sufre un timeout. Si no se devuelve el estado accepted después del quinto ping, el componente realiza la transición con el estado failedOrder.

  • El componente personalizado transfiere el usuario a un agente activo. En este caso, las respuestas y la entrada del usuario se envían al agente. El componente pasa a otro estado cuando el usuario o el agente finalizan su sesión.

  • No llame a transition.

  • Defina context.keepTurn(true).

A continuación se muestra un ejemplo algo rebuscado que muestra cómo se repite la invocación sin esperar la entrada del usuario y, a continuación, cómo se realiza la transición una vez que ha finalizado:
invoke: async (context) => {

  const quotes = require("./json/Quotes.json");
  const quote = quotes[Math.floor(Math.random() * quotes.length)];
  
  // Check if postback action is issued and postback is from this component rendering. 
  // This ensures that the component only responds to its own postback actions.     
  const um = context.getUserMessage()
  if (um instanceof PostbackMessage && um.getPostback() && um.getPostback()['system.state'] === context.getRequest().state && um.getPostback().isNo) {
    context.keepTurn(true);
    context.transition();
  } else {
    // Show the quote of the day.
    context.reply(`'${quote.quote}'`);
    context.reply(`Quote by: ${quote.origin}`);
    // Create a single message with two buttons to request another quote or not.
    let actions = [];

    const mf = context.getMessageFactory();
    const message = mf.createTextMessage('Do you want another quote?')
      .addAction(mf.createPostbackAction('Yes', { isNo: false }))
      .addAction(mf.createPostbackAction('No', { isNo: true }));
    context.reply(message);
    // Although reply() automatically sets keepTurn to false, it's good practice to explicitly set it so that it's
    // easier to see how you intend the component to behave.
    context.keepTurn(false);
  }
}

Acceso al backend

Observará que se han creado varias bibliotecas Node.js para facilitar las solicitudes HTTP y que la lista cambia con frecuencia. Debe revisar las ventajas y los inconvenientes de las bibliotecas disponibles actualmente y decidir cuál funciona mejor para usted. Recomendamos utilizar una biblioteca que soporte compromisos para poder aprovechar la versión async del método invoke, que se presentó en la versión 2.5.1, y utilizar la palabra clave await para escribir sus llamadas de REST de forma síncrona.

Una opción es la API de node-fetch que está preinstalada con el SDK de Bots Node. La sección sobre acceso al backend mediante llamadas de REST HTTP de la documentación de Bots Node SDK contiene algunos ejemplos de código.

Uso del SDK para acceder a las cargas útiles de respuesta y solicitud

Utilice los métodos de la instancia CustomComponentContext para obtener el contexto para la llamada, acceder a las variables y cambiarlas y devolver los resultados al motor de diálogo.

Puede encontrar varios ejemplos de código para utilizar estos métodos en Escritura de componentes personalizados y Mensajes de conversación en la documentación del SDK de Bots Node

La documentación de referencia del SDK se encuentra en https://github.com/oracle/bots-node-sdk.

Componentes personalizados para aptitudes en varios idiomas

Al diseñar un componente personalizado, debe considerar si el componente lo utilizará una aptitud que soporte más de un idioma.

Si el componente personalizado debe soportar aptitudes en varios idiomas, debe saber si las aptitudes están configuradas para el soporte de idioma nativo o el servicio de traducción.

Al utilizar un servicio de traducción, puede traducir el texto de la aptitud. Tiene estas opciones:

Para las aptitudes de idioma nativo, tiene estas opciones:

  • Transfiera los datos a la aptitud en variables y, a continuación, genere el texto de un componente del sistema transfiriendo los valores de las variables a una clave de grupo de recursos, como se describe en Uso de un componente del sistema para hacer referencia a un grupo de recursos. Con esta opción, el componente personalizado debe tener propiedades de metadatos para que la aptitud transfiera los nombres de las variables en las que almacenar los datos.

  • Utilice el grupo de recursos del componente personalizado para redactar la respuesta del componente personalizado, como se describe en Paquetes de recursos de referencia del componente personalizado. Utilice el método conversation.translate() para obtener la cadena de grupo de recursos que se utilizará para la llamada a context.reply(). Esta opción solo es válida para las definiciones de grupos de recursos que usan parámetros posicionales (numerados). No funciona para parámetros con nombre. Con esta opción, el componente personalizado debe tener una propiedad de metadatos para el nombre de la clave del grupo de recursos, y los parámetros de la clave del grupo de recursos con nombre deben coincidir con los utilizados en la llamada a context.reply().

A continuación, se muestra un ejemplo del uso del grupo de recursos del componente personalizado. En este ejemplo, fmTemplate se definiría como ${rb('date.dayOfWeekMessage', 'lundi', '19 juillet 2021')}.

'use strict';

var IntlPolyfill    = require('intl');
Intl.DateTimeFormat = IntlPolyfill.DateTimeFormat;

module.exports = {
  metadata: () => ({
    name: 'Date.DayOfWeek',
    properties: {
      rbKey:   { required: true,  type: 'string'    }
    },
    supportedActions: []
  }),
  invoke: (context, done) => {
    const { rbKey } = context.properties();
    if (!rbKey || rbKey.startsWith('${')){
      context.transition();
            done(new Error('The state is missing the rbKey property or it uses an invalid expression to pass the value.'));
    }
    //detect user locale. If not set, define a default
    const locale  = context.getVariable('profile.locale') ? 
      context.getVariable('profile.locale') : 'en-AU';  
    const jsLocale     = locale.replace('_','-');
    //when profile languageTag is set, use it. If not, use profile.locale
    const languageTag = context.getVariable('profile.languageTag')?
                      context.getVariable('profile.languageTag') : jslocale;
   /* =============================================================
      Determine the current date in local format and 
      the day name for the locale
      ============================================================= */
    var now          = new Date();
    var dayTemplate  = new Intl.DateTimeFormat(languageTag,
      { weekday: 'long' });
    var dayOfWeek    = dayTemplate.format(now);
    var dateTemplate = new Intl.DateTimeFormat(languageTag, 
      { year: 'numeric', month: 'long', day: 'numeric'});
    var dateToday    = dateTemplate.format(now);

   /* =============================================================
      Use the context.translate() method to create the ${Freemarker} 
      template that's evaluated when the reply() is flushed to the 
      client.
      ============================================================= */
    const fmTemplate = context.translate(rbKey, dateToday, dayOfWeek );

    context.reply(fmTemplate)
                .transition()
                .logger().info('INFO : Generated FreeMarker => ' 
                + fmTemplate);
    done();  
  }
};

Asegurarse de que el componente funciona en los asistentes digitales

En una conversación de asistente digital, un usuario puede interrumpir un flujo de conversación cambiando el asunto. Por ejemplo, si un usuario inicia un flujo para realizar una compra, es posible que interrumpa ese flujo para preguntar cuánto crédito tiene en una tarjeta regalo. Es lo que denominamos incongruencia. Para permitir que el asistente digital identifique y gestione las incongruencias, llame al método context.invalidInput(payload) cuando la respuesta a la expresión del usuario no se entienda en el contexto del componente.

En una conversación digital, el tiempo de ejecución determina si una entrada no válida es una incongruencia buscando coincidencias de respuestas en todas las aptitudes. Si encuentra alguna coincidencia, vuelve a encaminar el flujo. Si no, muestra el mensaje, si se ha proporcionado, solicita al usuario una entrada y, a continuación, vuelve a ejecutar el componente. La nueva entrada se transfiere al componente en la propiedad text.

En una conversación de aptitud independiente, el tiempo de ejecución muestra el mensaje, si se ha proporcionado, solicita al usuario una entrada y, a continuación, ejecuta el componente de nuevo. La nueva entrada se transfiere al componente en la propiedad text.

Este código de ejemplo llama a context.invalidInput(payload) siempre que los datos introducidos no se convierten en un número.

"use strict"
 
module.exports = {
 
    metadata: () => ({
        "name": "AgeChecker",
        "properties": {
            "minAge": { "type": "integer", "required": true }
        },
        "supportedActions": [
            "allow",
            "block",
            "unsupportedPayload"
        ]
    }),
 
    invoke: (context, done) => {
        // Parse a number out of the incoming message
        const text = context.text();
        var age = 0;
        if (text){
          const matches = text.match(/\d+/);
          if (matches) {
              age = matches[0];
          } else {
              context.invalidUserInput("Age input not understood. Please try again");
              done();
              return;
          }
        } else {
          context.transition('unsupportedPayload");
          done();
          return;
        }
 
        context.logger().info('AgeChecker: using age=' + age);
 
        // Set action based on age check
        let minAge = context.properties().minAge || 18;
        context.transition( age >= minAge ? 'allow' : 'block' );
 
        done();
    }
};

A continuación, se muestra un ejemplo de cómo maneja un asistente digital una entrada no válida en el tiempo de ejecución. Con respecto a la primera respuesta a la edad (twentyfive), no hay coincidencias en ninguna aptitud registrada con el asistente digital, por lo que la conversación muestra el mensaje context.invalidUserInput especificado. En la segunda respuesta a la edad (send money), el asistente digital encuentra una coincidencia y pregunta si se debe ejecutar el redireccionamiento hacia este flujo.


Descripción de components-nonsequitur-conversation.png a continuación
Descripción de la ilustración componentes-nonsequitur-conversation.png

Debe llamar a context.invalidInput() o a context.transition(). Si llama a ambas operaciones, asegúrese de que la variable system.invalidUserInput siga definida si se envía algún mensaje adicional. Tenga en cuenta también que los componentes de entrada de usuario (como los componentes Respuesta común y Entidades de resolución) restablecen system.invalidUserInput.

Supongamos, por ejemplo, que modificamos el componente AgeChecker como se muestra a continuación y que llamamos a context.transition() después de context.invalidInput().

if (matches) {  age = matches[0]; } else { 
      context.invalidUserInput("Age input not understood. Please try again"); 
      context.transition("invalid"); 
      context.keepTurn(true);
      done();
      return;
}

En este caso, el flujo de datos debe volver a realizar la transición a askage para que el usuario obtenga dos mensajes de salida: "No se ha entendido la entrada de edad. Vuelva a intentarlo", seguido de "¿Cuántos años tiene?". A continuación, se muestra cómo se puede manejar esto en un flujo de diálogo en modo YAML.

  askage:
    component: "System.Output"
    properties:
      text: "How old are you?"
    transitions:
      next: "checkage"
  checkage:
    component: "AgeChecker"
    properties:
      minAge: 18
    transitions:
      actions:
        allow: "crust"
        block: "underage"
        invalid: "askage"

Ejecución del servicio de componentes en un entorno de desarrollo

Durante la fase de desarrollo, puede iniciar un servicio local para exponer el paquete de componentes personalizados.

  1. Desde la carpeta de nivel superior, abra una ventana de terminal y ejecute estos comandos para iniciar el servicio:
    npm install
    npm start
  2. Para verificar que el servicio se esté ejecutando, introduzca el siguiente URL en un explorador:
    localhost:3000/components

    El explorador muestra los metadatos del componente.

  3. Si tiene acceso directo a Internet, puede acceder al entorno de desarrollo desde una aptitud:
    1. Instale un túnel, como ngrok o Localtunnel.
    2. Si está detrás de un proxy, vaya a http://www.whatismyproxy.com/ para obtener la dirección IP externa del proxy y, a continuación, en la ventana de terminal que vaya a utilizar para iniciar el túnel, introduzca estos comandos:
      export https_proxy=http://<external ip>:80
      export http_proxy=http://<external ip>:80
    3. Inicie el túnel y configúrelo para mostrar el puerto 3000.
    4. En Oracle Digital Assistant, vaya al separador Componentes icono Componentes de la aptitud y agregue un servicio de componente Externo con el URL de metadatos establecido en https://<URL-túnel>/components.
      Puede utilizar cualquier valor para el nombre de usuario y la contraseña.
Ahora, puede agregar estados al flujo de diálogo para los componentes del servicio y probarlos desde la página Vista previa de la aptitud.