Implémenter des composants personnalisés
Pour implémenter des composants personnalisés, utilisez le kit SDK Node.js Oracle Digital Assistant afin d'établir une interface avec le service de composants personnalisé de Digital Assistant.
Pour implémenter des composants personnalisés que vous pouvez déployer vers le conteneur imbriqué de Digital Assistant, Oracle Cloud Infrastructure Functions, un back-end Mobile Hub ou un serveur Node.js, procédez comme suit :
Si vous prévoyez de déployer le package de composants personnalisé vers un service de composant personnalisé imbriqué, chaque brique à laquelle vous ajoutez le package est comptée comme un service distinct. Le nombre de services de composant personnalisés imbriqués d'une instance est limité. Si vous ne connaissez pas cette limite, demandez à l'administrateur de service d'obtenir pour vous la valeur
embedded-custom-component-service-count
comme décrit dans Affichage des limites de service dans la console Infrastructure. Envisagez de réunir plusieurs composants par package afin de diminuer le nombre de services de composant imbriqués que vous utilisez. Si vous essayez d'ajouter un service de composant après avoir atteint cette limite, la création du service échoue.
Etape 1 : installation du logiciel pour créer des composants personnalisés
Pour créer un package de composants personnalisé, vous avez besoin de Node.js, de Node Package Manager et du kit SDK Node.js Oracle Digital Assistant Bots.
Sous Windows, le kit SDK Bots Node ne fonctionne pas sous Windows si l'installation du noeud est de version 20.12.2 ou supérieure en raison d'une modification incompatible en amont dans Node.js. Si la version 20.12.2 ou supérieure du noeud est déjà installée, vous devez la désinstaller, puis installer la version 20.12.1 ou une version antérieure pour que le kit SDK Bots Node fonctionne.
Etape 2 : création du package de composants personnalisés
Pour démarrer un projet, utilisez la commande bots-node-sdk init
dans l'interface de ligne de commande (CLI) du kit SDK afin de créer les fichiers et la structure de répertoire nécessaires pour la structure de votre composant.
La commande init
comprend quelques options, telles que l'utilisation de JavaScript (valeur par défaut) ou de TypeScript, et le nom du fichier JavaScript du composant initial. Ces options sont décrites dans Outils de développement de CLI. Voici la commande de base pour démarrer un projet JavaScript :
bots-node-sdk init <top-level folder path> --name <component service name>
Cette commande effectue les actions suivantes pour un package JavaScript :
-
Créer le dossier de niveau supérieur.
-
Créer un dossier
components
et ajoute un fichier JavaScript de composant d'échantillon nomméhello.world.js
. Il s'agit de l'emplacement où vous placerez vos fichiers JavaScript de composant. -
Ajouter un fichier
package.json
, qui indiquemain.js
en tant que point d'entrée principal et répertorie@oracle/bots-node-sdk
en tant quedevDependency
. Le fichier de package pointe également vers des scriptsbots-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" } }
-
Ajouter un fichier
main.js
, qui exporte les paramètres du package et pointe vers le dossier des composants pour l'emplacement des composants, vers le dossier de niveau supérieur. -
Ajouter un fichier
.npmignore
au dossier de niveau supérieur. Ce fichier est utilisé lors de l'export du package de composants. Il doit exclure du package les fichiers.tgz
. Par exemple :*.tgz
. -
Pour certaines versions de npm, crée un fichier
package-lock.json
. - Installe toutes les dépendances du package dans le sous-dossier
node_modules
.
Si vous n'utilisez pas la commande
bots-node-sdk init
pour créer le dossier du package, assurez-vous que le dossier de niveau supérieur contient un fichier .npmignore
contenant lui-même une entrée *.tgz
. Par exemple :*.tgz
spec
service-*
Sinon, chaque fois que vous packagerez les fichiers dans un fichier TGZ, vous inclurez le fichier TGZ qui existe déjà dans le dossier de niveau supérieur et la taille de votre fichier TGZ continuera de doubler.
Si vous prévoyez un déploiement vers le conteneur imbriqué, votre package doit être compatible avec le noeud 14.17.0.
Etape 3 : création d'un composant personnalisé
Voici les étapes à suivre pour créer chaque composant personnalisé dans votre package :
Création du fichier de composant
Utilisez la commande init component
de l'interface de ligne de commande du kit SDK afin de créer un fichier JavaScript ou TypeScript avec la structure permettant d'utiliser le kit SDK Node.js Oracle Digital Assistant pour écrire un composant personnalisé. Le langage indiqué lors de l'exécution de la commande init
pour créer le package de composants détermine si un fichier JavaScript ou TypeScript est créé.
Par exemple, afin de créer un fichier pour le composant personnalisé, à partir d'une fenêtre de terminal, utilisez la commande CD (Change Directory) pour passer au dossier de niveau supérieur du package et saisissez la commande suivante, en remplaçant <component name>
par le nom du composant :
bots-node-sdk init component <component name> c components
Pour JavaScript, cette commande ajoute <component name>.js
au dossier components
. Pour TypeScript, le fichier est ajouté au dossier src/components
. L'argument c
indique que le fichier est destiné à un composant personnalisé.
Le nom de composant ne doit pas dépasser 100 caractères. Vous pouvez utiliser uniquement des caractères alphanumériques et des traits de soulignement dans le nom. Vous ne pouvez pas utiliser de traits d'union. Le nom ne peut pas non plus comporter le préfixe System.
. Oracle Digital Assistant ne vous permet pas d'ajouter un service de composant personnalisé dont les noms de composant ne sont pas valides.
Pour plus d'informations, reportez-vous à https://github.com/oracle/bots-node-sdk/blob/master/bin/CLI.md
.
Ajout de code aux fonctions metadata et invoke
Votre composant personnalisé doit exporter deux objets :
metadata
: fournit à la brique les informations suivantes sur le composant.- Nom de composant
- Propriétés prises en charge
- Actions de transition prises en charge
Pour les flux de dialogue basés sur YAML, le composant personnalisé prend en charge les propriétés suivantes par défaut. Ces propriétés ne sont pas disponibles pour les briques conçues en mode de dialogue Visual.
autoNumberPostbackActions
: booléen. Non obligatoire. Lorsque la valeurtrue
est définie, les boutons et les options de liste sont numérotés automatiquement. La valeur par défaut estfalse
. Reportez-vous à Numérotation automatique pour les canaux de texte uniquement dans les flux de dialogue YAML.insightsEndConversation
: booléen. Non obligatoire. Lorsque la valeurtrue
est définie, la session arrête l'enregistrement de la conversation pour la génération de rapports d'analyse. La valeur par défaut estfalse
. Reportez-vous à Modélisation du flux de dialogue.insightsInclude
: booléen. Non obligatoire. Lorsque la valeurtrue
est définie, l'état est inclus dans la génération de rapports d'analyse. La valeur par défaut esttrue
. Reportez-vous à Modélisation du flux de dialogue.translate
: booléen. Non obligatoire. Lorsque la valeurtrue
est définie, la traduction automatique est activée pour ce composant. La valeur par défaut est celle de la variable de contexteautotranslation
. Reportez-vous à Services de traduction dans les briques.
invoke
: contient la logique à exécuter. Avec cette méthode, vous pouvez lire et écrire des variables de contexte de brique, créer des messages de conversation, définir des transitions d'état, effectuer des appels REST, etc. En général, vous utilisez le mot-cléasync
avec cette fonction pour gérer les promesses. La fonctioninvoke
accepte l'argument suivant :context
, qui nomme la référence à l'objetCustomComponentContext
dans le kit SDK Node.js Digital Assistant. Cette classe est décrite dans la documentation du kit SDK disponible à l'adresse https://oracle.github.io/bots-node-sdk/. Dans les versions antérieures du kit SDK, son nom étaitconversation
. Vous pouvez utiliser l'un ou l'autre des noms.
Remarque
Si vous utilisez une bibliothèque JavaScript qui ne prend pas en charge les promesses (et n'utilise donc pas le mot-cléasync
), il est également possible d'ajouter un argumentdone
en tant que rappel que le composant appelle une fois le traitement terminé.
Voici un exemple :
'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');
}
}
Pour en savoir plus et découvrir quelques exemples de code, reportez-vous à Ecriture de composants personnalisés dans la documentation du kit SDK Bots Node.
Contrôle du flux avec keepTurn et transition
Utilisez différentes combinaisons des fonctions keepTurn
et transition
du kit SDK Bots Node pour définir comment le composant personnalisé interagit avec l'utilisateur et comment la conversation se poursuit après que le composant renvoie le contrôle du flux vers la brique.
-
keepTurn(boolean)
indique si la conversation doit passer à un autre état sans inviter d'abord l'utilisateur à entrer du texte.Si vous voulez définir
keepTurn
sur True, vous devez appelerkeepTurn
après avoir appeléreply
carreply
définit implicitementkeepTurn
surfalse
. -
transition(action)
fait passer le dialogue à l'état suivant une fois que toutes les réponses, le cas échéant, ont été envoyées. L'argumentaction
facultatif nomme l'action (résultat) renvoyée par le composant.Si vous n'appelez pas
transition()
, la réponse est envoyée mais la boîte de dialogue reste dans l'état et l'entrée utilisateur suivante revient à ce composant. Autrement dit,invoke()
est appelé à nouveau.
invoke: async (context) ==> {
...
context.reply(payload);
context.keepTurn(true);
context.transition ("success");
}
Voici quelques cas d'emploi courants dans lesquels vous pouvez utiliser keepTurn
et transition
pour contrôler le flux de dialogue :
Cas d'emploi | Valeurs définies pour keepTurn et transition |
---|---|
Composant personnalisé qui passe à un autre état sans inviter d'abord l'utilisateur à entrer du texte. |
Par exemple, ce composant personnalisé met à jour une variable avec la liste des valeurs à afficher immédiatement dans l'état suivant du flux de dialogue.
|
Composant personnalisé qui permet à la brique d'attendre une saisie après le retour du contrôle à la brique et avant le passage de la brique à un autre état. |
Par exemple :
|
Composant personnalisé qui obtient l'entrée utilisateur sans renvoyer le contrôle de flux à la brique. Par exemple :
|
Par exemple, ce composant personnalisé génère un devis, puis affiche les boutons
Yes et No pour demander un autre devis. Il revient à la brique lorsque l'utilisateur clique sur No .
Si un composant ne passe pas à un autre état, il doit suivre son propre état, comme le montre l'exemple ci-dessus. Pour une gestion d'état plus complexe, par exemple pour donner à l'utilisateur la possibilité d'annuler l'opération si une extraction de données prend trop de temps, vous pouvez créer et utiliser une variable de contexte. Par exemple : Tant que vous n'avez pas de modification d'état, toutes les valeurs transmises en tant que propriétés de composant sont disponibles. |
L'appel de composant se répète sans entrée utilisateur. Par exemple :
|
Voici un exemple conçu spécialement pour vous montrer comment répéter l'appel sans attendre l'entrée utilisateur, puis comment réaliser la transition ensuite :
|
Accès au back-end
Vous constaterez que plusieurs bibliothèques Node.js ont été créées pour faciliter les demandes HTTP et que la liste change fréquemment. Examinez les avantages et les inconvénients des bibliothèques actuellement disponibles, et déterminez lesquelles fonctionnent le mieux pour vous. Nous vous recommandons d'utiliser une bibliothèque qui prend en charge les promesses afin de tirer parti de la version async
de la méthode invoke
, introduite dans la version 2.5.1, et d'utiliser le mot-clé await
pour écrire vos appels REST de façon synchrone.
Vous avez la possibilité d'utiliser l'API node-fetch préinstallée avec le kit SDK Bots Node. Vous trouverez des exemples de code dans Accès au back-end à l'aide d'appels REST HTTP dans la documentation du kit SDK Bots Node.
Utilisation du kit SDK pour accéder à la charge utile de demande et de réponse
Les méthodes d'instance CustomComponentContext
permettent d'obtenir le contexte de l'appel, de modifier les variables et d'y accéder, et de renvoyer les résultats au moteur de dialogue.
Vous trouverez plusieurs exemples de code pour l'utilisation de ces méthodes dans Ecriture de composants personnalisés et Messagerie de conversation dans la documentation du kit SDK Bots Node.
La documentation de référence du kit SDK est disponible à l'adresse suivante : https://github.com/oracle/bots-node-sdk
.
Composants personnalisés pour les briques multilingues
Lorsque vous concevez un composant personnalisé, vous devez déterminer si ce composant sera utilisé par une brique qui prend en charge plusieurs langues.
Si le composant personnalisé doit prendre en charge les briques multilingues, vous devez savoir si les briques sont configurées pour la prise en charge des langues natives ou le service de traduction.
Lorsque vous utilisez un service de traduction, vous pouvez traduire le texte de la brique. Vous disposez des options suivantes :
-
Définissez la propriété
translate
dans l'état du composant personnalisé sur True pour traduire la réponse du composant, comme décrit dans Envoyer des réponses directement au service de traduction. -
Renvoyez les données brutes à la brique dans les variables et utilisez les valeurs des variables dans un composant système qui compose la sortie. Définissez la propriété
translate
de ce composant sur True. Reportez-vous à Utilisation d'un composant système pour transmettre le message au service de traduction. -
Renvoyez les données brutes à la brique dans les variables et utilisez les valeurs des variables dans un composant système qui utilise la clé de regroupement de ressources pour la langue. Reportez-vous à Utiliser un composant système pour référencer un groupe de ressources.
Pour les compétences en langue maternelle, vous avez les options suivantes :
-
Transmettez les données à la brique dans des variables, puis générez le texte à partir d'un composant système en transmettant les valeurs des variables à une clé de groupe de ressources, comme décrit dans Utilisation d'un composant système pour référencer un groupe de ressources. Avec cette option, le composant personnalisé doit avoir des propriétés de métadonnées pour que la brique transmette les noms des variables dans lesquelles stocker les données.
-
Utilisez le groupe de ressources du composant personnalisé pour composer la réponse du composant personnalisé, comme décrit dans Groupes de ressources de référence à partir du composant personnalisé. Utilisez la méthode
conversation.translate()
pour obtenir la chaîne de groupe de ressources à utiliser pour votre appel verscontext.reply()
. Cette option n'est valide que pour les définitions de groupe de ressources qui utilisent des paramètres positionnels (numérotés). Elle ne fonctionne pas pour les paramètres nommés. Avec cette option, le composant personnalisé doit avoir une propriété de métadonnées pour le nom de la clé de groupe de ressources, et les paramètres de la clé de groupe de ressources nommée doivent correspondre à ceux utilisés dans l'appel decontext.reply()
.
Voici un exemple d'utilisation du regroupement de ressources à partir du composant personnalisé. Dans cet exemple, fmTemplate
serait défini sur quelque chose comme ${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();
}
};
Vérification du fonctionnement du composant dans les assistants numériques
Dans une conversation avec assistant numérique, l'utilisateur peut interrompre un flux de conversation en modifiant le sujet. Par exemple, si un utilisateur lance un flux pour effectuer un achat, il peut interrompre ce flux afin de demander le montant des crédits dont il dispose sur une carte-cadeau. Nous appelons ce phénomène non sequitur. Pour permettre à l'assistant numérique d'identifier et de gérer des non sequiturs, appelez la méthode context.invalidInput(payload)
lorsqu'une réponse de variation utilisateur n'est pas comprise dans le contexte du composant.
Dans une conversation numérique, l'exécution détermine si une entrée non valide est un non sequitur en recherchant les correspondances de réponse dans toutes les briques. Si des correspondances sont trouvées, le flux est réacheminé. Dans le cas contraire, un message s'affiche, s'il est fourni, pour inviter l'utilisateur à saisir des données, puis le composant est exécuté de nouveau. La nouvelle entrée est transmise au composant dans la propriété text
.
Dans une conversation de brique autonome, l'exécution affiche le message, s'il est fourni, qui invite l'utilisateur à effectuer une saisie, puis exécute à nouveau le composant. La nouvelle entrée est transmise au composant dans la propriété text
.
Cet exemple de code appelle context.invalidInput(payload)
lorsque l'entrée n'est pas convertie en nombre.
"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();
}
};
Voici un exemple de la façon dont un assistant numérique gère les entrées non valides au moment de l'exécution. Pour la première réponse relative à l'âge (twentyfive
), il n'y a aucune correspondance dans les briques inscrites auprès de l'assistant numérique, donc la conversation affiche le message context.invalidUserInput
indiqué. Dans la deuxième réponse relative à l'âge (send money
), l'assistant numérique trouve une correspondance ; il demande ainsi s'il doit réacheminer vers ce flux.

Description de l'illustration components-nonsequitur-conversation.png
Vous devez appeler context.invalidInput()
ou context.transition()
. Si vous appelez les deux opérations, assurez-vous que la variable system.invalidUserInput
est toujours définie si un message supplémentaire est envoyé. Notez également que les composants d'entrée utilisateur (tels que les composants Réponse commune et Résoudre les entités) réinitialisent system.invalidUserInput
.
Par exemple, supposons que vous modifiez le composant AgeChecker comme indiqué ci-dessous, et que vous appelez context.transition()
après 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;
}
Dans ce cas, le flux de données doit revenir à askage
pour que l'utilisateur obtienne deux messages de sortie : "Age input not understood. Please try again", suivi de "How old are you?". Voici comment cela peut être géré dans un flux de dialogue en mode 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"
Exécution du service de composant dans un environnement de développement
Pendant la phase de développement, vous pouvez démarrer un service local pour afficher le package de composants personnalisé.