Implementa componenti personalizzati
Per implementare i componenti personalizzati, utilizzare l'SDK Node.js di Oracle Digital Assistant per interfacciarsi con il servizio di componenti personalizzati di Digital Assistant.
Di seguito viene descritto come implementare i componenti personalizzati che è possibile distribuire nel contenitore incorporato Digital Assistant, in Oracle Cloud Infrastructure Functions, in un backend Mobile Hub o in un server Node.js.
Se si prevede di distribuire il package di componenti personalizzati in un servizio di componenti personalizzati incorporato, ogni skill a cui si aggiunge il package viene conteggiato come servizio separato. È previsto un limite al numero di servizi di componenti personalizzati incorporati che un'istanza può avere. Se non si conosce il limite, chiedere all'amministratore del servizio di ottenere automaticamente
embedded-custom-component-service-count
come descritto in Visualizza limiti del servizio nella console dell'infrastruttura. Prendere in considerazione l'inserimento di più componenti per pacchetto per ridurre al minimo il numero di servizi dei componenti incorporati che si utilizzano. Se si tenta di aggiungere un servizio componente dopo aver raggiunto tale limite, la creazione del servizio non riesce.
Passo 1: Installare il software per la creazione di componenti personalizzati
Per creare un pacchetto di componenti personalizzato, sono necessari Node.js, Node Package Manager e l'SDK Node.js dei bot di Oracle Digital Assistant.
In Windows, l'SDK del nodo bot non funziona in Windows se l'installazione del nodo è la versione 20.12.2 o successiva a causa di una modifica non compatibile con le versioni precedenti in Node.js. Se hai già installato Node versione 20.12.2 o successiva, devi disinstallarlo e quindi installare la versione 20.12.1 o precedente per consentire il funzionamento dell'SDK del nodo bot.
Passo 2: Creare il pacchetto di componenti personalizzati
Per avviare un progetto, utilizzare il comando bots-node-sdk init
dell'interfaccia della riga di comando (CLI) dell'SDK per creare i file e la struttura di directory necessari per la struttura del componente.
Il comando init
include alcune opzioni, ad esempio l'uso di JavaScript (impostazione predefinita) o TypeScript e il nome del file JavaScript del componente iniziale. Queste opzioni sono descritte in Strumenti per sviluppatori CLI. Di seguito è riportato il comando di base per l'avvio di un progetto JavaScript.
bots-node-sdk init <top-level folder path> --name <component service name>
Questo comando completa le seguenti azioni per un pacchetto JavaScript:
-
Crea la cartella di livello superiore.
-
Crea una cartella
components
e aggiunge un file del componente di esempio JavaScript denominatohello.world.js
. Qui è dove mettere i file del componente JavaScript. -
Aggiunge un file
package.json
, che specificamain.js
come punto di ingresso principale ed elenca@oracle/bots-node-sdk
comedevDependency
. Il file del pacchetto punta anche ad alcuni scriptbots-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" } }
-
Aggiunge alla cartella di livello superiore un file
main.js
che esporta le impostazioni del package e punta alla cartella dei componenti per la posizione dei componenti. -
Aggiunge un file
.npmignore
alla cartella di livello superiore. Questo file viene utilizzato quando si esporta il package di componenti. Deve escludere i file.tgz
dal pacchetto. Ad esempio:*.tgz
. -
Per alcune versioni di npm, crea un file
package-lock.json
. - Installa tutte le dipendenze del pacchetto nella sottocartella
node_modules
.
Se non si utilizza il comando
bots-node-sdk init
per creare la cartella del pacchetto, assicurarsi che la cartella di livello superiore contenga un file .npmignore
contenente una voce *.tgz
. Ad esempio:*.tgz
spec
service-*
In caso contrario, ogni volta che si inseriscono i file in un file TGZ, si include il file TGZ già esistente nella cartella di livello superiore e il file TGZ continuerà a raddoppiare le dimensioni.
Se si prevede di eseguire la distribuzione nel contenitore incorporato, il pacchetto deve essere compatibile con il nodo 14.17.0.
Passo 3: Creare e creare un componente personalizzato
Di seguito sono riportati i passi per la creazione di ogni componente personalizzato nel package.
Creazione del file del componente
Utilizzare il comando init component
dell'interfaccia CLI dell'SDK per creare un file JavaScript o TypeScript con la struttura per utilizzare l'SDK Node.js di Oracle Digital Assistant per scrivere un componente personalizzato. La lingua specificata al momento dell'esecuzione del comando init
per creare il pacchetto del componente determina se viene creato un file JavaScript o TypeScript.
Ad esempio, per creare un file per il componente personalizzato, da una finestra di terminale, dal CD alla cartella di livello superiore del pacchetto e digitare il comando seguente, sostituendo <component name>
con il nome del componente:
bots-node-sdk init component <component name> c components
Per JavaScript, questo comando aggiunge <component name>.js
a components folder
. Per TypeScript, il file viene aggiunto alla cartella src/components
. L'argomento c
indica che il file si riferisce a un componente personalizzato.
Il nome del componente non può superare i 100 caratteri. Nel nome è possibile utilizzare solo caratteri alfanumerici e caratteri di sottolineatura. Non è possibile utilizzare trattini. Né il nome può avere un prefisso System.
. Oracle Digital Assistant non consentirà di aggiungere un servizio componente personalizzato con nomi componente non validi.
Per ulteriori dettagli, vedere https://github.com/oracle/bots-node-sdk/blob/master/bin/CLI.md
.
Aggiungi codice ai metadati e richiama funzioni
Il componente personalizzato deve esportare due oggetti:
metadata
: fornisce allo skill le informazioni sui componenti riportate di seguito.- Nome componente
- Proprietà supportate
- Azioni di transizione supportate
Per i flussi di dialogo basati su YAML, il componente personalizzato supporta per impostazione predefinita le seguenti proprietà. Queste proprietà non sono disponibili per gli skill progettati in modalità finestra di dialogo Visual.
autoNumberPostbackActions
: booleano. Non necessaria. Quando si utilizzatrue
, i pulsanti e le opzioni di elenco vengono numerati automaticamente. L'impostazione predefinita èfalse
. Vedere Numerazione automatica per i canali di solo testo nei flussi di finestre di dialogo YAML.insightsEndConversation
: booleano. Non necessaria. Quandotrue
, la sessione interrompe la registrazione della conversazione per la generazione di report degli approfondimenti. L'impostazione predefinita èfalse
. Vedere Modellare il flusso di finestre di dialogo.insightsInclude
: booleano. Non necessaria. Quando si utilizzatrue
, lo stato viene incluso nel report degli approfondimenti. L'impostazione predefinita ètrue
. Vedere Modellare il flusso di finestre di dialogo.translate
: booleano. Non necessaria. Quando si utilizzatrue
, la traduzione automatica è abilitata per questo componente. L'impostazione predefinita è il valore della variabile di contestoautotranslation
. Vedere Translation Services in Skills.
invoke
: contiene la logica da eseguire. In questo metodo è possibile leggere e scrivere variabili di contesto delle competenze, creare messaggi di conversazione, impostare transizioni di stato, effettuare chiamate REST e altro ancora. In genere, questa funzione consente di utilizzare la parola chiaveasync
per gestire le promesse. La funzioneinvoke
accetta il seguente argomento:context
, che assegna un nome al riferimento all'oggettoCustomComponentContext
nell'SDK Node.js di Digital Assistant. Questa classe è descritta nella documentazione dell'SDK all'indirizzo https://oracle.github.io/bots-node-sdk/. Nelle versioni precedenti dell'SDK, il nome eraconversation
. È possibile utilizzare uno dei due nomi.
Nota
Se si utilizza una libreria JavaScript che non supporta le promesse, quindi non si utilizza la parola chiaveasync
, è anche possibile aggiungere un argomentodone
come callback richiamato dal componente al termine dell'elaborazione.
Esempio:
'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');
}
}
Per ulteriori informazioni ed esplorare alcuni esempi di codice, consulta la sezione relativa alla scrittura di componenti personalizzati nella documentazione relativa all'SDK del nodo bot.
Controllare il flusso con keepTurn e la transizione
È possibile utilizzare diverse combinazioni delle funzioni keepTurn
e transition
del kit SDK nodo bot per definire il modo in cui il componente personalizzato interagisce con un utente e il modo in cui la conversazione continua dopo che il componente restituisce il controllo del flusso alla competenza.
-
keepTurn(boolean)
specifica se la conversazione deve passare a un altro stato senza prima richiedere l'input dell'utente.Tenere presente che se si desidera impostare
keepTurn
su true, è necessario chiamarekeepTurn
dopo aver chiamatoreply
perchéreply
imposta in modo implicitokeepTurn
sufalse
. -
transition(action)
fa sì che la finestra di dialogo passi allo stato successivo dopo l'invio di tutte le eventuali risposte. L'argomento facoltativoaction
indica l'azione (risultato) restituita dal componente.Se non si chiama
transition()
, la risposta viene inviata, ma la finestra di dialogo rimane nello stato e il successivo input utente torna a questo componente. Cioè,invoke()
viene chiamato di nuovo.
invoke: async (context) ==> {
...
context.reply(payload);
context.keepTurn(true);
context.transition ("success");
}
Di seguito sono riportati alcuni casi d'uso comuni in cui utilizzare keepTurn
e transition
per controllare il flusso della finestra di dialogo.
caso d'uso | Insieme di valori per keepTurn e transizione |
---|---|
Componente personalizzato che passa a un altro stato senza prima richiedere l'input all'utente. |
Ad esempio, questo componente personalizzato aggiorna una variabile con un elenco di valori da visualizzare immediatamente in base allo stato successivo nel flusso della finestra di dialogo.
|
Componente personalizzato che consente allo skill di attendere l'input dopo che il controllo torna allo skill e prima che lo skill passi a un altro stato. |
Ad esempio:
|
Componente personalizzato che recupera l'input dell'utente senza restituire il controllo del flusso allo skill. Ad esempio:
|
Ad esempio, questo componente personalizzato restituisce un preventivo, quindi visualizza i pulsanti
Yes e No per richiedere un altro preventivo. Esegue la transizione allo skill quando l'utente fa clic su No .
Se un componente non passa a un altro stato, deve tenere traccia del proprio stato, come mostrato nell'esempio precedente. Per una gestione dello stato più complessa, ad esempio per consentire all'utente di annullare se il recupero dei dati richiede troppo tempo, è possibile creare e utilizzare una variabile di contesto. Ad esempio: Tenere presente che, finché non si esegue la transizione, sono disponibili tutti i valori passati come proprietà del componente. |
Il richiamo del componente si ripete senza l'input dell'utente. Ad esempio:
|
Di seguito è riportato un esempio in qualche modo elaborato che mostra come ripetere il richiamo senza attendere l'input dell'utente e quindi come eseguire la transizione al termine:
|
Accedi al backend
Verranno trovate diverse librerie Node.js create per semplificare le richieste HTTP e l'elenco cambia frequentemente. Dovresti rivedere i pro e i contro delle librerie attualmente disponibili e decidere quale funziona meglio per te. Si consiglia di utilizzare una libreria che supporti le promesse in modo da poter utilizzare la versione async
del metodo invoke
, introdotta nella versione 2.5.1, e la parola chiave await
per scrivere le chiamate REST in modo sincrono.
Un'opzione è l'API node fetch preinstallata con l'SDK del nodo bot. Accedere al backend mediante chiamate REST HTTP nella documentazione relativa all'SDK del nodo bot contiene alcuni esempi di codice.
Utilizzare l'SDK per accedere ai payload delle richieste e delle risposte
I metodi di istanza CustomComponentContext
consentono di ottenere il contesto per le variabili di richiamo, accesso e modifica e di inviare nuovamente i risultati al motore della finestra di dialogo.
È possibile trovare diversi esempi di codice per l'uso di questi metodi in Scrittura di componenti personalizzati e Messaggistica di conversazione nella documentazione dell'SDK del nodo bot.
La documentazione di riferimento sull'SDK è disponibile all'indirizzo https://github.com/oracle/bots-node-sdk
.
Componenti personalizzati per skill multilingua
Quando si progetta un componente personalizzato, è necessario valutare se il componente verrà utilizzato da uno skill che supporta più lingue.
Se il componente personalizzato deve supportare competenze multilingue, è necessario sapere se le competenze sono configurate per il supporto della lingua nativa o il servizio di traduzione.
Quando si utilizza un servizio di traduzione, è possibile tradurre il testo dalla competenza. Sono disponibili le opzioni seguenti:
-
Impostare la proprietà
translate
nello stato del componente personalizzato su true per tradurre la risposta del componente, come descritto in Invia risposte direttamente al servizio di traduzione. -
Invia dati grezzi alla competenza nelle variabili e utilizza i valori delle variabili in un componente di sistema che compone l'output. Impostare su true la proprietà
translate
del componente. Vedere Utilizzare un componente di sistema per passare il messaggio al servizio di traduzione. -
Inviare i dati non elaborati allo skill nelle variabili e utilizzare i valori delle variabili in un componente di sistema che utilizza la chiave bundle di risorse per la lingua. Vedere Utilizzare un componente di sistema per fare riferimento a un bundle di risorse.
Per le competenze linguistiche native, sono disponibili le seguenti opzioni:
-
Trasmettere i dati alla competenza nelle variabili e quindi eseguire l'output del testo da un componente di sistema passando i valori delle variabili a una chiave bundle di risorse, come descritto in Utilizzare un componente di sistema per fare riferimento a un bundle di risorse. Con questa opzione, il componente personalizzato deve avere proprietà di metadati per consentire allo skill di passare i nomi delle variabili in cui memorizzare i dati.
-
Utilizzare il bundle di risorse del componente personalizzato per comporre la risposta del componente personalizzato, come descritto in Bundle di risorse di riferimento del componente personalizzato. Utilizzare il metodo
conversation.translate()
per ottenere la stringa del bundle delle risorse da utilizzare per la chiamata acontext.reply()
. Questa opzione è valida solo per le definizioni di bundle di risorse che utilizzano parametri posizionali (numerati). Non funziona per i parametri denominati. Con questa opzione, il componente personalizzato deve avere una proprietà di metadati per il nome della chiave del bundle di risorse e i parametri della chiave del bundle di risorse denominato devono corrispondere a quelli utilizzati nella chiamata acontext.reply()
.
Di seguito è riportato un esempio di utilizzo del bundle di risorse dal componente personalizzato. In questo esempio, fmTemplate
verrà impostato su un valore simile a ${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();
}
};
Assicurarsi che il componente funzioni negli assistenti digitali
In una conversazione dell'assistente digitale, un utente può interrompere un flusso di conversazione modificando l'oggetto. Ad esempio, se un utente avvia un flusso per effettuare un acquisto, potrebbe interrompere tale flusso per chiedere quanto credito ha su una carta regalo. Lo chiamiamo non sequitur. Per consentire all'assistente digitale di identificare e gestire i non sequiturs, chiamare il metodo context.invalidInput(payload)
quando una risposta di espressione utente non è compresa nel contesto del componente.
In una conversazione digitale, il runtime determina se un input non valido è un non sequitur mediante la ricerca di corrispondenze di risposta in tutte le competenze. Se trova corrispondenze, reindirizza il flusso. In caso contrario, viene visualizzato il messaggio, se fornito, che richiede all'utente l'input, quindi esegue di nuovo il componente. Il nuovo input viene passato al componente nella proprietà text
.
In una conversazione di skill standalone, il runtime visualizza il messaggio, se fornito, che richiede all'utente l'input, quindi esegue di nuovo il componente. Il nuovo input viene passato al componente nella proprietà text
.
Questo codice di esempio chiama context.invalidInput(payload)
ogni volta che l'input non viene convertito in un numero.
"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();
}
};
Di seguito è riportato un esempio di come un assistente digitale gestisce l'input non valido in runtime. Per la prima risposta età (twentyfive
), non ci sono corrispondenze in alcuna competenza registrata con l'assistente digitale in modo che la conversazione visualizzi il messaggio context.invalidUserInput
specificato. Nella risposta alla seconda età (send money
), l'assistente digitale trova una corrispondenza in modo da chiedere se deve reindirizzare a tale flusso.

Descrizione dell'immagine components-nonsequitur-conversation.png
È necessario chiamare context.invalidInput()
o context.transition()
. Se si richiamano entrambe le operazioni, assicurarsi che la variabile system.invalidUserInput
sia ancora impostata se viene inviato un messaggio aggiuntivo. Tenere inoltre presente che i componenti di input dell'utente, ad esempio i componenti delle entità di risposta comune e di risoluzione, reimpostano system.invalidUserInput
.
Si supponga, ad esempio, di modificare il componente AgeChecker come illustrato di seguito e di chiamare context.transition()
dopo 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;
}
In questo caso, il flusso di dati deve tornare a askage
in modo che l'utente riceva due messaggi di output: "Input età non compreso. Si prega di riprovare" seguito da "Quanti anni hai?". Ecco come potrebbe essere gestito in un flusso di dialogo in modalità 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"
Eseguire il servizio del componente in un ambiente di sviluppo
Durante la fase di sviluppo, è possibile avviare un servizio locale per esporre il package di componenti personalizzati.