Codice personalizzato e integrazione backend

Ecco alcune best practice per la scrittura di codice personalizzato e l'integrazione backend per gli assistenti digitali.

Al termine di una conversazione, è necessario fare qualcosa con le informazioni raccolte da un utente. In genere tale "qualcosa" richiede l'accesso a un servizio backend per l'esecuzione di query sui dati o sui dati persistenti per i quali è necessario creare componenti personalizzati. Un altro uso per i componenti personalizzati è quello di incorporare una logica personalizzata che gestisce convalide complesse o altre funzioni utility. Oracle Digital Assistant supporta due tipi di componenti personalizzati:

  • Componenti flusso finestra di dialogo personalizzati (CCS)
  • Gestori di eventi entità (EEH)

Durante la fase di pianificazione e progettazione del tuo assistente digitale, devi identificare le risorse backend di cui avrai bisogno e decidere se le API disponibili sono sufficienti o meno.

  • Poiché gli assistenti digitali non sono applicazioni Web, le API esistenti potrebbero dover essere ottimizzate o astratte tramite un livello di ottimizzazione per restituire solo i dati e la quantità di dati richiesti in una conversazione con l'assistente digitale.
  • Se non hai servizi REST per la funzionalità backend da integrare in una conversazione di assistente digitale, devi progettare e attivare un progetto per crearli.

Durante l'implementazione dell'integrazione del servizio backend, è possibile decidere se distribuire i componenti personalizzati in remoto o utilizzare i container di componenti incorporati nelle competenze di Oracle Digital Assistant.

Come indicato nella figura riportata di seguito, l'integrazione backend è una parte necessaria della fase di pianificazione e implementazione.

Componenti flusso finestra di dialogo personalizzata

Con i componenti del flusso di finestre di dialogo personalizzati, è possibile scrivere componenti dell'interfaccia utente personali che è possibile aggiungere al flusso di dialogo per eseguire la logica del codice personalizzata nel contesto di una conversazione. I casi d'uso per la scrittura di tali componenti includono:

  • Esecuzione di query e scrittura di servizi backend remoti tramite servizi REST.

  • Soluzioni pronte all'uso che gestiscono tutte le interazioni degli utenti per un task specifico, come la richiesta di feedback degli utenti al termine di una conversazione, la registrazione e la segnalazione di errori a un amministratore e così via.

  • Supporta la gestione dei dati negli array di oggetti salvati in una variabile di flusso della finestra di dialogo.

Usa buoni nomi per componenti e parametri di input

Non esiste un campo per fornire descrizioni per i componenti personalizzati che spiegano cosa fanno e quali informazioni devono essere trasmesse loro. Quindi i modi migliori per aiutare gli sviluppatori di abilità con l'uso del componente sono utilizzare buoni nomi per i parametri di componente e input e scegliere attentamente le stringhe di azione restituite dal componente.

  • I componenti YAML integrati utilizzano System.<name> come nome. Pertanto, soprattutto per i flussi di dialogo basati su YAML, è possibile utilizzare Custom.<name> per consentire ai revisori degli skill di comprendere che si tratta di un componente personalizzato a cui fa riferimento il flusso della finestra di dialogo. In alternativa, è possibile utilizzare uno spazio dei nomi per fornire il contesto. Per i nostri componenti personalizzati di esempio utilizziamo spesso oracle.sample.<name> per indicare che tali componenti non sono destinati alla qualità di produzione.

  • I parametri di input forniscono dati al componente personalizzato da elaborare. Spesso i dati passati a un componente personalizzato non sono il valore effettivo da utilizzare, ma piuttosto il nome di una variabile che contiene i valori dei dati da elaborare o in cui devono essere scritti i dati sottoposti a query da un servizio remoto. Esaminando i componenti incorporati, utilizzano variable come nome della proprietà per contenere il nome della variabile in cui verrà scritto il risultato del componente oppure <name>Var (ad esempio nlpResultVar) per indicare le proprietà che fanno riferimento a un nome di riferimento di variabile. È possibile migliorare ulteriormente questo aspetto utilizzando i prefissi _in e _out per indicare se una variabile fa riferimento a una variabile che contiene dati o prevede dati dal componente.

  • Le stringhe di azione sono facoltative e possono essere utilizzate dallo sviluppatore delle competenze per determinare lo stato successivo del flusso della finestra di dialogo a cui passare. L'uso di success o failure come stringa di azione non fornisce molto contesto, pertanto si consiglia di utilizzare qualcosa come orderSubmitted, orderRejected o userUnauthorized.

Evita di fare ipotesi nel tuo codice

La realtà è che spesso lo sviluppatore di un componente personalizzato è anche lo sviluppatore di abilità che lo utilizza. Per questo motivo, molti sviluppatori semplificano il loro lavoro con componenti personalizzati facendo ipotesi sulle variabili che sono presenti nell'abilità. Quindi, invece di passare il nome di una variabile al componente, si riferiscono direttamente al nome nella logica del componente personalizzato. Si sconsiglia questo perché tali ipotesi possono facilmente rompere un componente personalizzato. Si consiglia di definire un contratto chiaro e completo tra il componente personalizzato e le competenze che lo utilizzano.

Libreria Think

Una domanda comune riguarda il numero di componenti personalizzati da aggiungere a un pacchetto di servizi componente personalizzato. In generale è sempre una buona idea pensare ai servizi di componenti personalizzati e ai componenti che contiene, come librerie. Pertanto, tutti i componenti correlati a un'attività potrebbero essere salvati in un singolo servizio di componenti personalizzati. Tuttavia, le raccomandazioni devono essere in grado di affrontare la realtà. Pertanto, la domanda su come imballare i componenti personalizzati deve essere risolta in base all'uso previsto dei componenti personalizzati.

  • Riutilizzo non è un'opzione per molti sviluppatori di componenti personalizzati. Quando i componenti personalizzati vengono sviluppati e utilizzati in una competenza specifica, è opportuno raggrupparli in un'unica distribuzione di servizi di componenti personalizzati. L'eccezione a questo è per i componenti che vengono effettivamente riutilizzati in altre competenze.

  • Le Distribuzioni di container di componenti incorporati sono limitate dal numero di servizi di componenti personalizzati per ogni istanza di Oracle Digital Assistant. Pertanto, dovrai utilizzare un singolo servizio di componenti personalizzati per competenza o cercare una distribuzione remota dei componenti personalizzati.

  • Utilizza la distribuzione remota di servizi di componenti personalizzati a Kubernetes in Oracle Cloud Infrastructure, per i seguenti motivi:

    • Per non divulgare informazioni riservate contenute nel codice del componente personalizzato. I servizi di componenti personalizzati distribuiti nel contenitore incorporato possono essere scaricati da chiunque disponga dell'accesso completo all'istanza di Oracle Digital Assistant.

    • Per implementare una migliore segmentazione del codice. I componenti personalizzati devono contenere solo codice necessario per interagire con il bot e per richiamare i servizi REST. Tutti gli altri codici devono essere memorizzati in file JavaScript esterni (se si utilizza un contenitore incorporato) o in livelli di integrazione (servizi REST). I componenti personalizzati includono codice che effettua le operazioni riportate di seguito.

      • leggi parametri di input

      • leggi/imposta valori variabili

      • gestire i messaggi ricevuti da un componente

      • visualizza l'interfaccia utente del componente personalizzato

      • determinare la transizione a uno stato successivo

      • gestire lo stato del componente

      • accedere ai servizi REST

    • Migliorare le prestazioni. Il contenitore incorporato per la distribuzione di componenti personalizzati alle competenze utilizza le funzioni OCI, che hanno un ritardo di avvio a freddo. Per evitare questo ritardo, così come il limite del numero di servizi che possono essere distribuiti, una distribuzione remota di componenti personalizzati fornisce un'alternativa senza preoccupazioni.

    • Condividere componenti comuni. Anche se la nostra esperienza è che il riutilizzo non è altamente classificato tra gli sviluppatori di componenti personalizzati, ha senso creare e distribuire componenti personalizzati comunemente utilizzati su un server remoto. Potresti avere componenti comuni per cose come la gestione degli errori e l'escalation, la gestione delle autorizzazioni OAuth2 a 2 fasi e altro ancora.

Come scrivere i messaggi di log

Il logger predefinito implementato per i componenti personalizzati è il logger della console. È possibile accedere al logger tramite una chiamata a context.logger(). È possibile utilizzare le funzioni di registrazione delle chiamate disponibili per il logger della console come ".info('…') o ".warn('…')".

Nota: l'uso di context.logger() è particolarmente appropriato quando si esegue la distribuzione nel contenitore incorporato, poiché il contenitore incorporato è in grado di visualizzare correttamente questi log. Per i componenti personalizzati distribuiti esternamente, è preferibile utilizzare una libreria di log diversa, ad esempio log4js.

Gestisci stato interno componente

I componenti di flusso della finestra di dialogo personalizzati possono avere un'interazione più lunga con un utente prima che la navigazione passi a uno stato di flusso della finestra di dialogo successivo. Per questa interazione è necessario assicurarsi che il componente gestisca il suo stato interno in modo che possa distinguere tra una chiamata iniziale e le chiamate successive. Sono disponibili due opzioni per eseguire questa operazione:

  • Aggiungere un token al payload del messaggio di postback. Quando il componente personalizzato visualizza un'interfaccia utente in cui gli utenti possono premere un elemento azione, viene inviato un messaggio di postback al componente personalizzato. Il componente personalizzato deve valutare i messaggi di postback ricevuti per determinare se il postback proviene dall'interfaccia utente che sta visualizzando o da un altro componente. Per questo può controllare il messaggio di postback se contiene un token aggiunto dal componente personalizzato durante il rendering dell'elemento azione.

  • Utilizzare una variabile di flusso della finestra di dialogo. Se è necessario gestire uno stato più complesso tra i richiami dei componenti personalizzati, ad esempio per tenere traccia dei valori estratti dai messaggi utente, è possibile utilizzare una variabile di flusso della finestra di dialogo per questo. I componenti personalizzati possono creare variabili di flusso della finestra di dialogo in fase di esecuzione in una chiamata a context.variable('variable name', value). Se una variabile del nome specificato non esiste, verrà creata. L'oggetto "valore" può essere tutto ciò di cui hai bisogno per tenere traccia.

Convalida i parametri di input

Per avere contenuto, è necessario convalidare i parametri di input definiti per un componente personalizzato. Ciò vale anche per i parametri impostati in base alle esigenze. È necessario verificare i seguenti casi:

  • Il parametro di input ha un set di valori.

  • Il valore non inizia con '$ {', in quanto indica un'espressione per la lettura del valore del parametro di input da una variabile o che un oggetto non viene risolto correttamente nel flusso della finestra di dialogo.

Utilizzare la classe MessageFactory per i messaggi dei componenti

Tutte le risposte ai bot inviate da un componente personalizzato devono utilizzare la classe MessageFactory. La classe MessageFactory può essere utilizzata per creare lo stesso tipo di messaggi utente avanzati del componente Risposta comune, che include l'elenco di valori, allegati, layout scheda e messaggi di testo.

Inoltre, i messaggi definiti con la classe MessageFactory sono indipendenti dal canale. Ciò significa che si crea un singolo messaggio di componente, che viene quindi convertito dai connettori specifici del canale nel formato richiesto dal rispettivo canale client.

Per accedere alla classe MessageFactory da un componente personalizzato, utilizzare il riferimento seguente:

let MessageFactory = context.MessageFactory();
Nota

La classe MessageFactory ha sostituito la classe MessageModel, che non è più valida. Entrambe le classi hanno lo stesso scopo generale, ma MessageFactory ha i seguenti vantaggi:
  • Supporta tutti i tipi di messaggio e le proprietà CMM (Common Message Model), anziché solo un sottoinsieme.
  • La sua implementazione è basata sulla classe, fornendo getter pulito, setter e aggiungere metodi per modificare la definizione del messaggio. Il completamento del codice è quando si utilizza Typescript e in JavaScript quando le definizioni di tipo appropriate sono incluse nella parte superiore del gestore di eventi o del componente personalizzato.
  • L'implementazione utilizza il pattern builder, che consente di concatenare un numero di setter o aggiungere metodi, rendendo il codice più leggibile e riducendo il numero di righe che devi codificare

Elenco di controllo per componenti personalizzati

  • ☑ Assicurati che i servizi backend siano ottimizzati o astratti per l'uso con le competenze.
  • ☑ I componenti personalizzati devono contenere solo codice relativo ai bot. Tutti gli altri codici devono essere spostati in classi o librerie di utility oppure distribuiti come singoli servizi REST in un server remoto o in un servizio cloud.
  • ☑ Crea un contratto chiaro e completo tra i componenti personalizzati e le competenze in cui vengono utilizzati.
  • ☑ Utilizzare componenti personalizzati per valutazioni complesse. Evitare Apache FreeMarker in questi casi.
  • ☑ Gestire lo stato dei componenti per le interazioni utente con più richieste.
  • ☑ Convalidare tutti i parametri di input dei componenti personalizzati.
  • ☑ Gestire gli errori restituendo una stringa di azione per consentire allo sviluppatore di skill di gestire i problemi.

Gestori eventi entità

Un handler di eventi entità è un tipo di componente personalizzato che consente di richiamare il codice del componente personalizzato nel contesto della risoluzione delle entità bag composte. I gestori di eventi entità vengono utilizzati in conversazioni basate su modelli per interagire e convalidare l'input utente e per richiamare i servizi backend remoti per l'accesso in lettura e scrittura. A differenza dei componenti del flusso di finestre di dialogo personalizzati, la possibilità di riutilizzo è minima per un gestore eventi, motivo per cui l'implementazione predefinita del gestore eventi entità è per il contenitore skill incorporato.

Aggiungi funzionalità mancanti per risolvere componenti entità

Molte delle funzionalità che è possibile impostare per il componente Risposta comune, ad esempio i pulsanti globali per la Guida e l'annullamento, non sono disponibili per il componente Risolvi entità mediante la configurazione.

Tuttavia, è possibile aggiungere funzionalità mancanti utilizzando i gestori di eventi entità. Ciò consente di sfruttare la semplicità del componente Risolvi entità nel flusso della finestra di dialogo senza sacrificare le funzionalità avanzate.

Gestisci stato

Le funzioni del gestore eventi entità vengono richiamate dai componenti Risolvi entità e Risposta comune durante la risoluzione di un'entità bag composta. Non c'è bisogno di tenere traccia di quale articolo borsa deve essere risolto dopo, in quanto è tutto fatto per voi.

Tuttavia, potresti voler salvare alcune informazioni per un uso successivo. A tal fine sono disponibili due opzioni:

  • Le proprietà di risoluzione del contesto sono variabili create nell'oggetto contesto. Le variabili e i relativi valori esistono finché l'entità bag composita non viene risolta o si lascia lo stato del flusso della finestra di dialogo che risolve un'entità bag composita. Il vantaggio di utilizzare le proprietà di risoluzione del contesto è che non è necessario eseguire operazioni di housekeeping.

    • Per scrivere, utilizzare: context.setCustomProperty(name, value);
    • Per leggere, utilizzare: context.getCustomProperty(name);
  • Le variabili del flusso di dialogo create in runtime o in fase di progettazione possono essere utilizzate per memorizzare i valori che si desidera rendere persistenti oltre la risoluzione dell'entità sacchetto composito. È possibile accedere al contenuto memorizzato nelle variabili di flusso della finestra di dialogo dagli stati di flusso della finestra di dialogo (per le variabili definite solo al momento della progettazione) e da altri gestori di eventi entità.

    • Per scrivere, utilizzare: context.variable(name,value);
    • Per leggere, utilizzare: context.variable(name);

Come scrivere i messaggi di log

Il logger predefinito implementato per i gestori di eventi entità è il logger della console.

È possibile accedere al logger tramite una chiamata a context.logger().

È possibile utilizzare le funzioni di registrazione delle chiamate disponibili per il logger della console, ad esempio .info('…') o .warn('…').

Visualizzazione dei messaggi utente

I messaggi utente personalizzati vengono visualizzati tramite la funzione context.addMessage(). Come per i componenti del flusso di finestre di dialogo personalizzati, si consiglia di utilizzare la classe MessageFactory per creare messaggi indipendenti dal canale invece di eseguire l'output di payload specifici del canale. I gestori di eventi entità supportano anche messaggi di tipo elenco di valori, layout scheda e allegato.

Elenco di controllo per gestori eventi entità

  • ☑ Memorizzare i valori temporanei nel contesto di risoluzione a meno che non sia necessario in uno stato di flusso della finestra di dialogo successivo.
  • ☑ Utilizzare un singolo servizio di componenti personalizzati per tutti i gestori di eventi entità utilizzati in una skill.
  • ☑ Utilizzare la classe MessageFactory per visualizzare i messaggi agli utenti.

Quale componente utilizzare?

I gestori di eventi entità possono essere utilizzati con le entità sacchetto composito, mentre i componenti di flusso della finestra di dialogo personalizzati vengono utilizzati nel contesto della transizione delle conversazioni tra gli stati di flusso della finestra di dialogo. In definitiva, probabilmente userete entrambi. Se si sta seguendo il suggerimento di utilizzare conversazioni basate su modelli, è più probabile che si utilizzino gestori di eventi entità rispetto ai componenti del flusso di finestre di dialogo personalizzati.

Da un punto di vista funzionale, i componenti del flusso di finestre di dialogo personalizzati (CCS) e gli handler di eventi entità (EEH) sono molto simili. Nella tabella seguente vengono confrontati i due tipi di componenti personalizzati.

Funzionalità CCS EEH
Supporto modulo Node.js / sviluppo JavaScript
Supporto TypeScript
Sviluppo basato su browser N
Sviluppo in IDE esterno
Utilizza nei flussi di dialogo N
Usa in entità sacchetto composito N
Parametri di input N
Navigazione programmatica alle transizioni di azione N
Chiama servizi REST
Lettura da / scrittura alle variabili di flusso della finestra di dialogo
Memorizza temporaneamente i valori nel contesto di risoluzione N
Usa bundle di risorse / supporto multilingua
Visualizza interfacce utente avanzate e prompt per interagire con gli utenti
Supporto distribuzione contenitore competenze
Supporto della distribuzione remota
Supporto del debug locale (richiede NGROK o altri tunnel)
Eventi personalizzati N
Supporto azione di postback

Utilizzo di bundle di risorse per CCS ed EEH

I componenti del flusso di finestre di dialogo personalizzate e i gestori di eventi entità che visualizzano i messaggi bot per l'utente devono visualizzare i messaggi nelle lingue supportate dall'assistente digitale.

Fino a poco tempo fa, non esisteva un modo semplice per utilizzare i bundle di risorse definiti in uno skill da componenti personalizzati. Ma ora c'è una nuova interfaccia di programmazione che ti consente di fare riferimento alle chiavi del bundle di risorse nel tuo codice. Ci sono due restrizioni note di cui dovresti essere a conoscenza:

  • L'uso delle stringhe di bundle delle risorse è limitato ai bundle delle risorse senza parametri o con parametri posizionali. I parametri denominati utilizzati con i bundle di messaggi ICU non sono ancora supportati dalla nuova API.

  • L'API produce un'espressione che, quando viene restituita come risposta bot, viene sostituita con la stringa del bundle di messaggi di riferimento per la lingua rilevata.

Per chiamare la nuova API, utilizzare una delle seguenti chiamate oggetto contesto:

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

L'espressione può essere utilizzata nelle risposte di testo, come etichette dei pulsanti e sulle schede utilizzando la classe MessageFactory.

  • Esempio di gestore eventi entità:

    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);
  • Esempio di flusso della finestra di dialogo personalizzata:

    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();

Vedere anche l'articolo TechExchange Utilizzare i parametri di input per passare le stringhe di bundle di risorse tradotte ai componenti personalizzati.

Come utilizzare i parametri denominati

Di seguito viene descritto come accedere ai parametri denominati in un bundle di risorse.

//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);

Raccomandazioni relative a bundle di risorse e componenti personalizzati

L'utilizzo di bundle di risorse ovunque è un tema comune in questa guida. Tuttavia, l'uso di bundle di risorse memorizzati in uno skill crea uno stretto accoppiamento tra il componente di flusso della finestra di dialogo personalizzata o il gestore di eventi e lo skill. Se stai bene con questa dipendenza e apprezzi il vantaggio di avere stringhe di risorse gestite in un unico posto più che evitare il problema dell'accoppiamento stretto, dovresti farlo. Per i gestori di eventi, la possibilità di riutilizzo è comunque minima, motivo per cui non dovrebbero esserci dubbi sull'uso delle stringhe di bundle di risorse nei gestori di eventi entità.

Per i componenti del flusso di finestre di dialogo personalizzati riutilizzati in skill diversi, la funzione di traduzione funzionerà anche se gli skill dispongono dei nomi delle chiavi del bundle di risorse richiesti dal componente personalizzato aggiunto al bundle di risorse.

Utilizzando una soluzione alternativa, è possibile evitare uno stretto accoppiamento di componenti personalizzati a uno skill passando i messaggi letti da un bundle di risorse come parametri di input a un componente flusso finestra di dialogo personalizzato.

Eseguire la migrazione ai gestori di eventi entità?

Se si passa dai componenti di flusso della finestra di dialogo personalizzata ai gestori di eventi entità, questo dovrebbe avvenire per un motivo specifico, non solo perché si tratta di una nuova tecnologia. Cambiare Mi piace per Mi piace non migliora le tue abilità. Se non si è soddisfatti del flusso di conversazione corrente e si sta prendendo in considerazione l'utilizzo di entità sacchetto composito per sostituire parti della conversazione del flusso di dialogo, questo è un buon motivo per spostare la logica del codice dai componenti del flusso di dialogo personalizzati ai gestori di eventi entità.

Procedure ottimali per la migrazione agli handler di eventi entità

Se si decide di spostare la funzionalità esistente dai componenti del flusso di finestre di dialogo personalizzate ai gestori di eventi entità per migliorare il flusso di conversazione, assicurarsi che non si stia solo cercando di imitare il comportamento implementato con il componente del flusso di finestre di dialogo personalizzato e il componente Risposta comune. Iniziare invece a utilizzare il componente Risolvi entità e utilizzare le funzioni del gestore eventi entità per implementare tutta la convalida e la logica necessarie per il caso d'uso conversazionale.