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:
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.
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.
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 denominadohello.world.js
. Aquí puede colocar los archivos JavaScript del componente. -
Agrega un archivo
package.json
, que especificamain.js
como punto de entrada principal y muestra@oracle/bots-node-sdk
comodevDependency
. El archivo de paquete también apunta a algunas secuencias de comandosbots-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
.
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
A continuación, se indican los pasos para crear cada componente personalizado en el paquete:
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 estrue
, los botones y las opciones de lista se enumeran automáticamente. El valor por defecto esfalse
. Consulte Numeración automática para canales de solo texto en flujos de diálogo de YAML.insightsEndConversation
: booleano. No necesario. Cuando el valor estrue
, la sesión deja de grabar la conversación para la generación de informes de estadísticas. El valor por defecto esfalse
. Consulte Modelo del flujo de diálogo.insightsInclude
: booleano. No necesario. Cuando el valor estrue
, el estado se incluye en los informes de estadísticas. El valor por defecto estrue
. Consulte Modelo del flujo de diálogo.translate
: booleano. No necesario. Cuando el valor estrue
, se activa la traducción automática para este componente. El valor por defecto es el valor de la variable de contextoautotranslation
. 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 claveasync
con esta función para gestionar promesas. La funcióninvoke
toma el siguiente argumento:context
, que asigna un nombre a la referencia al objetoCustomComponentContext
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 eraconversation
. Puede utilizar cualquiera de los nombres.
Nota
Si utiliza una biblioteca JavaScript que no soporta promesas (y, por lo tanto, no utiliza la palabra claveasync
), también es posible agregar un argumentodone
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 akeepTurn
después de llamar areply
porquereply
define implícitamentekeepTurn
enfalse
. -
transition(action)
hace que el cuadro de diálogo pase al siguiente estado después de enviar todas las respuestas, si las hay. El argumentoaction
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 ainvoke()
.
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. |
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.
|
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. |
Por ejemplo:
|
Componente personalizado que obtiene la entrada del usuario sin devolver el control de flujo a la aptitud. Por ejemplo:
|
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 .
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: 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:
|
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:
|
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:
-
Defina la propiedad
translate
en el estado del componente personalizado en true para traducir la respuesta del componente, como se describe en Enviar respuestas directamente al servicio de traducción. -
Devolver los datos raw a la aptitud en variables y utilizar los valores de las variables en un componente del sistema que compone la salida. Defina la propiedad
translate
de ese componente en true. Consulte Uso de un Componente del Sistema para Pasar el Mensaje al Servicio de Traducción. -
Vuelva a enviar los datos raw a la aptitud en variables y utilice los valores de las variables en un componente del sistema que utilice la clave de grupo de recursos para el idioma. Consulte Use a System Component to Reference a Resource Bundle.
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 acontext.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 acontext.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 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.