Integración de backend y código personalizado

Estas son algunas de las mejores prácticas para escribir código personalizado y realizar la integración de backend para asistentes digitales.

Al final de una conversación, debe hacer algo con la información recopilada de un usuario. Ese "algo" normalmente requiere acceso a un servicio de backend para consultar datos o datos persistentes para los que necesita crear componentes personalizados. Otro uso de los componentes personalizados es incorporar una lógica personalizada que maneje validaciones complejas u otras funciones de la utilidad. Oracle Digital Assistant soporta dos tipos de componentes personalizados:

  • Componentes del flujo de diálogo personalizado (CCS)
  • Controladores de eventos de entidades (EEH)

Durante la fase de planificación y diseño del asistente digital, debe identificar los recursos de backend que necesitará y decidir si las API que tiene disponibles son suficientes o no.

  • Puesto que los asistentes digitales no son aplicaciones web, puede que sea necesario optimizar o abstraer las API existentes a través de una capa de optimización para devolver solo los datos y la cantidad de datos necesarios en una conversación de asistente digital.
  • Si no tiene servicios REST para que la funcionalidad de backend se integre en una conversación de asistente digital, debe diseñar y disparar un proyecto para crearlos.

Al implantar la integración de servicios de backend, debe tomar decisiones sobre si desplegar componentes personalizados de forma remota o utilizar los contenedores de componentes embebidos en las aptitudes de Oracle Digital Assistant.

Como se indica en la siguiente figura, la integración de backend es una parte necesaria de la fase de planificación y de implantación.


Descripción de implemento-backend-integration.png

Componentes del Flujo de Diálogo Personalizado

Con los componentes de flujo de diálogo personalizados, puede escribir sus propios componentes de interfaz de usuario que puede agregar al flujo de diálogo para ejecutar la lógica de código personalizado en el contexto de una conversación. Entre los casos de uso para escribir estos componentes se incluyen:

  • Consulta y escritura de servicios de backend remotos mediante servicios REST.

  • Soluciones listas para usar que gestionan todas las interacciones de usuario para una tarea específica, como solicitar comentarios de usuario al final de una conversación, registrar e informar errores a un administrador, etc.

  • Soporta la gestión de datos en matrices de objetos guardadas en una variable de flujo de diálogo.

Usar buenos nombres para componentes y parámetros de entrada

No hay un campo para proporcionar descripciones de componentes personalizados que expliquen lo que hacen y qué información se debe transferir a ellos. Por lo tanto, las mejores formas de ayudar a los desarrolladores de aptitudes a utilizar el componente son utilizar nombres correctos para el componente y los parámetros de entrada y seleccionar cuidadosamente las cadenas de acción que devuelve el componente.

  • Los componentes YAML incorporados utilizan System.<name> como nombre. Por lo tanto, especialmente para los flujos de diálogo basados en YAML, puede que desee utilizar Custom.<name> para que los revisores de aptitudes comprendan que es un componente personalizado al que hace referencia el flujo de diálogo. O bien, puede utilizar un espacio de nombres para proporcionar contexto. Para nuestros componentes personalizados de muestra, a menudo utilizamos oracle.sample.<name> para indicar que esos componentes no están destinados a ser de calidad de producción.

  • Los parámetros de entrada proporcionan datos al componente personalizado que se va a procesar. A menudo, los datos transferidos a un componente personalizado no son el valor real con el que trabajar, sino el nombre de una variable que contiene los valores de datos que procesar o en la que se deben escribir los datos consultados desde un servicio remoto. Si observan los componentes incorporados, utilizan variable como nombre de propiedad para contener el nombre de la variable en la que se escribirá el resultado del componente o <name>Var (por ejemplo, nlpResultVar) para indicar propiedades que hacen referencia a un nombre de referencia de variable. Puede mejorarlo aún más mediante los postfijos _in y _out para indicar si una variable hace referencia a una variable que contiene datos o está esperando datos del componente.

  • Las cadenas de acción son opcionales y las puede utilizar el desarrollador de aptitudes para determinar el siguiente estado de flujo de diálogo al que navegar. El uso de success o failure como cadena de acción no proporciona mucho contexto, por lo que se recomienda utilizar algo como orderSubmitted, orderRejected o userUnauthorized en su lugar.

Evite hacer suposiciones en su código

La realidad es que a menudo el desarrollador de un componente personalizado también es el desarrollador de aptitudes que lo utiliza. Por este motivo, muchos desarrolladores simplifican su trabajo con componentes personalizados realizando suposiciones sobre variables que están presentes en la aptitud. Por lo tanto, en lugar de transferir el nombre de una variable al componente, hacen referencia directamente al nombre en la lógica del componente personalizado. No recomendamos esto porque estas suposiciones pueden romper fácilmente un componente personalizado. Recomendamos definir un contrato claro y completo entre el componente personalizado y las aptitudes que lo utilizan.

Biblioteca Think

Una pregunta común es cuántos componentes personalizados se deben agregar a un paquete de servicios de componentes personalizados. En general, siempre es una buena idea pensar en los servicios de componentes personalizados, y los componentes que contiene, como bibliotecas. Por lo tanto, todos los componentes relacionados con una tarea se pueden guardar en un único servicio de componentes personalizados. Sin embargo, las recomendaciones deben ser capaces de hacer frente a la realidad. Por lo tanto, la pregunta sobre cómo empaquetar componentes personalizados debe responderse en función del uso previsto de los componentes personalizados.

  • La reutilización no es una opción para muchos desarrolladores de componentes personalizados. Cuando los componentes personalizados se desarrollan y utilizan en una aptitud específica, tiene sentido agrupar todos esos componentes en un único despliegue de servicio de componentes personalizados. La excepción a esto es para los componentes que se reutilizan realmente en otras aptitudes.

  • Los despliegues de contenedores de componentes incrustados están restringidos por el número de servicios de componentes personalizados por instancia de Oracle Digital Assistant. Por lo tanto, deseará utilizar un único servicio de componentes personalizados por aptitud o buscar un despliegue remoto de los componentes personalizados.

  • Utilizar el despliegue remoto de servicio de componentes personalizados en Kubernetes en Oracle Cloud Infrastructure, por los siguientes motivos:

    • No divulgar información confidencial incluida en el código de componente personalizado. Los servicios de componentes personalizados desplegados en el contenedor embebido los puede descargar cualquier persona que tenga acceso completo a la instancia de Oracle Digital Assistant.

    • Para implantar una mejor segmentación del código. Los componentes personalizados solo deben contener el código necesario para interactuar con el bot y llamar a los servicios REST. El resto del código se debe almacenar en archivos JavaScript externos (si se utiliza un contenedor embebido) o en capas de integración (servicios REST). Los componentes personalizados incluyen código que realiza las siguientes acciones:

      • parámetros de entrada de lectura

      • valores de variable read/set

      • manejar mensajes recibidos por un componente

      • Representar la interfaz de usuario del componente personalizado

      • determinar la transición al siguiente estado

      • gestionar el estado de los componentes

      • acceso a servicios REST

    • Mejorar el rendimiento. El contenedor embebido para desplegar componentes personalizados en aptitudes utiliza funciones de OCI, que tienen un retraso de inicio en frío. Para evitar este retraso, así como el límite en el número de servicios que se pueden desplegar, un despliegue remoto de componente personalizado le proporciona una alternativa sin preocupaciones.

    • Compartir componentes comunes. Aunque nuestra experiencia es que la reutilización no está muy clasificada entre los desarrolladores de componentes personalizados, tiene sentido crear y desplegar componentes personalizados de uso común en un servidor remoto. Puede que tenga componentes comunes para aspectos como el manejo y la escalada de errores, el manejo de autorizaciones OAuth2 de 2 partes y mucho más.

Cómo escribir mensajes de log

El registrador por defecto implantado para componentes personalizados es el registrador de consola. Puede acceder al registrador mediante una llamada a context.logger(). Puede utilizar las funciones de registro de llamadas disponibles para el registrador de consola, como ".info('…') o ".warn('…')".

Nota: el uso de context.logger() tiene más sentido al desplegar en el contenedor embebido, ya que el contenedor embebido sabe cómo mostrar correctamente estos logs. Para los componentes personalizados que despliega externamente, es mejor utilizar una biblioteca de registro diferente, como log4js.

Gestionar el estado interno del componente

Los componentes del flujo de diálogo personalizado pueden tener una interacción más larga con un usuario antes de que la navegación pase a un siguiente estado de flujo de diálogo. Para esta interacción, debe asegurarse de que el componente maneja su estado interno para que pueda distinguir entre una llamada inicial y las llamadas posteriores. Para ello, existen dos opciones:

  • Agregue un token a la carga útil del mensaje de devolución. Cuando el componente personalizado representa una interfaz de usuario en la que los usuarios pueden pulsar un elemento de acción, se envía un mensaje de devolución al componente personalizado. El componente personalizado debe evaluar los mensajes de devolución que recibe para determinar si dicha devolución procede de la interfaz de usuario que está representando o de otro componente. Para ello, puede comprobar el mensaje de devolución en cuanto a si contiene un token que el componente personalizado agregó al representar el elemento de acción.

  • Utilice una variable de flujo de diálogo. Si necesita gestionar un estado más complejo entre llamadas a componentes personalizados, por ejemplo, para realizar un seguimiento de los valores extraídos de los mensajes de usuario, puede utilizar una variable de flujo de diálogo para ello. Los componentes personalizados pueden crear variables de flujo de diálogo en tiempo de ejecución en una llamada a context.variable('variable name', value). Si no existe una variable con el nombre especificado, se creará. El objeto "value" puede ser cualquier cosa que necesite para realizar un seguimiento.

Validar Parámetros de Entrada

Los parámetros de entrada que defina para un componente personalizado deben validarse para tener contenido. Esto también se aplica a los parámetros que defina como necesarios. Es necesario comprobar los siguientes casos:

  • El parámetro de entrada tiene un valor definido.

  • El valor no empieza por '$ {', ya que indica una expresión para leer el valor del parámetro de entrada de una variable o que un objeto no se ha resuelto correctamente en el flujo de diálogo.

Uso de la Clase MessageFactory para Mensajes de Componente

Todas las respuestas de bot que envíe desde un componente personalizado deben utilizar la clase MessageFactory. La clase MessageFactory se puede utilizar para crear el mismo tipo de mensajes de usuario enriquecidos que el componente Respuesta común, que incluye una lista de valores, anexos, diseños de tarjetas y mensajes de texto.

Además, los mensajes que se definen con la clase MessageFactory son independientes del canal. Esto significa que se crea un mensaje de componente único, que luego los conectores específicos del canal convierten en el formato requerido por el canal de cliente correspondiente.

Para acceder a la clase MessageFactory desde un componente personalizado, utilice la siguiente referencia:

let MessageFactory = context.MessageFactory();
Nota

La clase MessageFactory sustituye a la clase MessageModel, que ha quedado en desuso. Ambas clases tienen la misma finalidad general, pero MessageFactory tiene las siguientes ventajas:
  • Soporta todos los tipos de mensajes y propiedades del modelo de mensajes común (CMM), en lugar de solo un subjuego.
  • Su implantación está basada en clases, lo que proporciona getter, setter y métodos limpios para cambiar la definición del mensaje. La finalización de código se produce cuando se utiliza Typescript, así como en JavaScript, cuando se incluyen las definiciones de tipo adecuadas en la parte superior del manejador de eventos o componente personalizado.
  • La implantación utiliza el patrón de creador, que permite encadenar un número de setter o agregar métodos, haciendo que el código sea más legible y reduciendo el número de líneas que tiene que codificar

Lista de comprobación para componentes personalizados

  • ☑ Asegúrese de que los servicios de backend estén optimizados o abstraídos para su uso con aptitudes.
  • ☑ Los componentes personalizados solo deben contener código relacionado con bots. El resto del código se debe mover a bibliotecas o clases de utilidades, o bien se debe desplegar como servicios REST individuales en un servidor remoto o servicio en la nube.
  • ☑ Cree un contrato claro y completo entre los componentes personalizados y las aptitudes en las que se utilizan.
  • ☑ Utilice componentes personalizados para evaluaciones complejas. Evite Apache FreeMarker en esos casos.
  • ☑ Gestione el estado de los componentes para interacciones de usuario de varias solicitudes.
  • ☑ Valide todos los parámetros de entrada de componentes personalizados.
  • ☑ Gestione los errores devolviendo una cadena de acción para que el desarrollador de aptitudes gestione los problemas.

Manejadores de eventos de entidades

Un manejador de eventos de entidad es un tipo de componente personalizado que permite llamar al código de componente personalizado en el contexto de la resolución de entidades de bolsa compuesta. Los manejadores de eventos de entidades se utilizan en conversaciones controladas por modelos para interactuar con la entrada de usuario y validarla, y para llamar a servicios de backend remotos para acceder a lectura y escritura. A diferencia de los componentes del flujo de diálogo personalizado, la posibilidad de reutilización es mínima para un manejador de eventos, por lo que la implantación por defecto del manejador de eventos de entidad se realiza en el contenedor de aptitudes embebido.

Adición de Funcionalidad que Falta para Resolver Componentes de Entidades

Gran parte de la funcionalidad que se puede definir para el componente Respuesta común, como botones globales de ayuda y cancelación, no está disponible para el componente Resolver entidades mediante la configuración.

Sin embargo, puede agregar la funcionalidad que falta mediante manejadores de eventos de entidad. Esto le permite aprovechar la simplicidad del componente Resolver entidades en el flujo de diálogo sin sacrificar la funcionalidad avanzada.

Gestionar estado

Los componentes Resolve Entities y Common Response llaman a las funciones del manejador de eventos de entidad al resolver una entidad de bolsa compuesta. No es necesario que realice un seguimiento de qué artículo de bolsa debe resolverse a continuación, ya que todo se realiza por usted.

Aún así, puede que desee guardar información para su uso posterior. Para ello, tiene dos opciones:

  • Las propiedades de resolución de contexto son variables que crea en el objeto de contexto. Las variables y sus valores existen hasta que se resuelve la entidad de bolsa compuesta o se deja el estado del flujo de diálogo que resuelve una entidad de bolsa compuesta. La ventaja de usar las propiedades de resolución de contexto es que no hay servicio de limpieza que deba hacer.

    • Para escritura, utilice: context.setCustomProperty(name, value);
    • Para la lectura, utilice: context.getCustomProperty(name);
  • Las variables de flujo de diálogo creadas en tiempo de ejecución o en tiempo de diseño se pueden utilizar para almacenar los valores que desea mantener más allá de la resolución de la entidad de bolsa compuesta. Se puede acceder al contenido almacenado en variables de flujo de diálogo desde estados de flujo de diálogo (solo para variables definidas en tiempo de diseño) y desde otros manejadores de eventos de entidad.

    • Para escritura, utilice: context.variable(name,value);
    • Para la lectura, utilice: context.variable(name);

Cómo escribir mensajes de log

El registrador por defecto implantado para los manejadores de eventos de entidad es el registrador de consola.

Puede acceder al registrador mediante una llamada a context.logger().

Puede utilizar las funciones de registro de llamadas disponibles para el registrador de consola, como .info('…') o .warn('…').

Visualización de Mensajes de Usuario

Los mensajes de usuario personalizados se muestran mediante la función context.addMessage(). Al igual que con los componentes de flujo de diálogo personalizados, nuestra recomendación es utilizar la clase MessageFactory para crear mensajes independientes del canal en lugar de generar cargas útiles específicas del canal. Los gestores de eventos de entidad también admiten mensajes de tipo lista de valores, diseño de tarjeta y anexo.

Lista de comprobación para controladores de eventos de entidades

  • ☑ Almacene valores temporales en el contexto de resolución a menos que sea necesario en un estado de flujo de diálogo posterior.
  • ☑ Utilice un único servicio de componente personalizado para todos los manejadores de eventos de entidad utilizados en una aptitud.
  • ☑ Utilice la clase MessageFactory para que los mensajes se muestren a los usuarios.

¿Qué componente debería utilizar?

Los manejadores de eventos de entidades se utilizan con entidades de bolsa compuesta, mientras que los componentes de flujo de diálogo personalizados se utilizan en el contexto de conversaciones en transición entre estados de flujo de diálogo. En última instancia, es probable que estés usando ambos. Si sigue la recomendación de utilizar conversaciones controladas por modelo, será más probable que utilice manejadores de eventos de entidad que componentes de flujo de diálogo personalizados.

Desde un punto de vista funcional, los componentes del flujo de diálogo personalizado (CCS) y los manejadores de eventos de entidad (EEH) son muy similares. La siguiente tabla compara los dos tipos de componentes personalizados.

Funcionalidad CCS EEH
Soporte del módulo Node.js / Desarrollo de JavaScript
Soporte de TypeScript
Desarrollo basado en explorador No
Desarrollo en IDE externo
Usar en flujos de diálogo No
Utilizar en entidades de bolsa compuesta No
Parámetros de Entrada No
Navegación programática a transiciones de acción No
Llamar a servicios REST
Leer de / escribir en variables de flujo de diálogo
Almacenar valores temporalmente en contexto de resolución No
Uso de grupos de recursos/soporte multilingüe
Representar interfaces de usuario enriquecidas y peticiones de datos para interactuar con los usuarios
Soporte de despliegue de contenedor de aptitudes
Soporte de despliegue remoto
Soporte de depuración local (requiere NGROK u otros túneles)
Eventos Personalizados No
Soporte de acciones de devolución

Uso de grupos de recursos para CCS y EEH

Los componentes del flujo de diálogo personalizado y los manejadores de eventos de entidad que muestran mensajes de bot al usuario deben mostrar mensajes en los idiomas soportados por el asistente digital.

Hasta hace poco, no había una forma sencilla de utilizar grupos de recursos definidos en una aptitud a partir de componentes personalizados. Pero ahora hay una nueva interfaz de programación que le permite referir claves de paquetes de recursos en su código. Hay dos restricciones conocidas que debe conocer:

  • El uso de cadenas de grupos de recursos está limitado a grupos de recursos sin parámetros o con parámetros posicionales. La nueva API aún no admite los parámetros con nombre utilizados con los paquetes de mensajes de ICU.

  • La API produce una expresión que, cuando se devuelve como respuesta de bot, se sustituye por la cadena de grupo de mensajes a la que se hace referencia para el idioma detectado.

Para llamar a la nueva API, utilice una de las siguientes llamadas de objeto de contexto:

  • let expression = context.translate('resource_bundle_key_name');
  • let expression = context.translate('resource_bundle_key_name', param1, param2);

La expresión se puede utilizar en respuestas de texto, como etiquetas de botón y en tarjetas con la clase MessageFactory.

  • Ejemplo de manejador de eventos de entidades:

    const messageModel = context.getMessageFactory();
    //create a conversation message format text object that references a key name
    const message = messageModel.createTextMessage(context.translate('resource_bundle_key'));
    //display the message to the user keeping the turn, which means the composite bag entity
    //proceeds with the next bag item to resolve
    context.addMessage(message,true);
  • Ejemplo de flujo de diálogo personalizado:

    const messageModel = context.getMessageFactory();
    //create a conversation message format text object that references a key name
    const message = messageModel.createTextMessage(context.translate('resource_bundle_key'));
    //display the message to the user keeping the turn, which means the composite bag entity
    //proceeds with the next bag item to resolve
    context.reply(message); context.keepTurn(true);
    context.transition(); done();

Consulte también el artículo TechExchange Uso de parámetros de entrada para transferir cadenas de grupos de recursos traducidas a componentes personalizados.

Cómo utilizar parámetros con nombre

A continuación, se muestra cómo acceder a parámetros con nombre en un grupo de recursos:

//Entity event handler sample
let expression = "${rb('key_name','param_name1,param_name2',"+value1+","+value2+")}";
let message = messageFactory.createTextMessage(expression);
context.addMessage(message,true);
//Custom dialog flow component sample
let expression = "${rb('key_name','param_name1,param_name2',"+value1+","+value2+")}";
let message = messageFactory.createTextMessage(expression);
context.reply(message);

Recomendaciones sobre grupos de recursos y componentes personalizados

El uso de grupos de recursos en todas partes es un tema común en esta guía. Sin embargo, el uso de grupos de recursos almacenados en una aptitud crea un acoplamiento estricto entre el componente de flujo de diálogo personalizado o el manejador de eventos y la aptitud. Si está de acuerdo con esta dependencia y valora la ventaja de tener cadenas de recursos gestionadas en un solo lugar más que evitar el problema del acoplamiento estricto, debe hacerlo. En el caso de los gestores de eventos, la posibilidad de reutilización es mínima, por lo que no debe haber ninguna duda sobre el uso de cadenas de grupos de recursos en los gestores de eventos de entidades.

Para los componentes de flujo de diálogo personalizados que se reutilizan en diferentes aptitudes, la función de traducción también funcionará si las aptitudes tienen los nombres de clave de grupo de recursos requeridos por el componente personalizado agregado a su grupo de recursos.

Con una solución alternativa, puede evitar el acoplamiento estricto de componentes personalizados a una aptitud transfiriendo los mensajes leídos de un grupo de recursos como parámetros de entrada a un componente de flujo de diálogo personalizado.

¿Debe migrar a manejadores de eventos de entidad?

Si pasa de los componentes del flujo de diálogo personalizado a los manejadores de eventos de entidades, debe ser por un motivo específico, no solo porque se trata de una nueva tecnología. Cambiar los gustos por gustos no mejora tus habilidades. Si no está satisfecho con el flujo de conversación actual y está considerando el uso de entidades de bolsa compuesta para reemplazar partes de la conversación de flujo de diálogo, esa es una buena razón para mover la lógica de código de componentes de flujo de diálogo personalizados a manejadores de eventos de entidad.

Mejores prácticas al migrar a manejadores de eventos de entidades

Si decide mover la funcionalidad existente de los componentes del flujo de diálogo personalizado a los manejadores de eventos de entidad para mejorar el flujo de conversación, asegúrese de que no solo está intentando imitar el comportamiento que ha implantado con el componente del flujo de diálogo personalizado y el componente de respuesta común. En su lugar, empiece a utilizar el componente Resolve Entities y utilice las funciones de manejador de eventos de entidad para implantar toda la validación y la lógica necesarias para su caso de uso conversacional.