Close

12 ottobre 2019

Guida all’utilizzo del framework Rasa e sviluppo applicativo del chatbot all’interno di un progetto

chatbot-technology

Indice

  • Introduzione – cos’è un chat bot?
  • Rasa
  • Installare Rasa su Windows
  • Come funziona Rasa
  • Come funziona Rasa
  • Interactive learning
  • Train init
  • Dialogare con il chat bot
  • Presentazione del progetto di applicazione
  • Architettura del progetto eServant
  • Integrazione con Docker
  • Conclusioni

Introduzione – cos’è un chat bot?

Un chat bot è un software capace di replicare una conversazione con un umano. Nel loro utilizzo più comune lo possiamo trovare usato come interfaccia per interagire con le applicazioni in modo diretto. L’esperienza dell’utente viene così migliorata e, soprattutto, semplificata: l’ormai comune click sullo schermo di un cellulare può essere rimpiazzato da un semplice comando vocale. In esso è possibile racchiudere più di una funzione, accorciando i passi necessari e abbreviando i percorsi che, talvolta, possono risultare labirintici.

I chat bot non sono solo questo. Sebbene questa sia la loro funzione più pratica, essi possono anche offrire un’interazione più o meno elaborata con il proprio interlocutore. Quelli più avanzati sono ormai in grado di offrire un livello di dialogo abbastanza elevato, senza blocchi o risposte eccessivamente sconnesse. La loro implementazione è estremamente complessa e richiede uno studio accurato non solo della struttura della linguistica ‘ufficiale’, ma contempla anche quello comunemente noto come ‘slang’, ovvero del gergo parlato quotidianamente.

Un’applicazione estremamente interessante di questa funzionalità dei chat bot la possiamo trovare nella robotica. Gli automi ‘parlanti’ infatti non sono più una fantascienza, ma una tecnologia esistente e in continuo sviluppo. La lingua è un’entità estremamente vasta e intricata. Essa presenta un’infinità di sfaccettature e le sue regole e dizionari sono in continuo mutamento, rendendo sostanzialmente impossibile poterla mappare interamente.

È qui che introduciamo il machine learning.

Il machine learning (in italiano ‘apprendimento automatico’) è una branca dell’intelligenza artificiale, che si pone il compito di migliorare, in modo progressivo, le prestazioni di un algoritmo attraverso l’analisi di una serie di dati senza essere stato esplicitamente e preventivamente implementato.

Esistono tre approcci distinti: supervisionato, non supervisionato e per rinforzo.

Il primo consiste nel fornire alla macchina una serie di modelli ed esempi che permettono di costruire un database di ‘esperienze’. La macchina posta davanti a un nuovo problema eseguirà una ricerca al suo interno, attenendosi al modello più vicino alla richiesta. È possibile ricondurre questo approccio a quello che in algebra viene chiamata ‘induzione’.

Il secondo invece fornisce i dati direttamente alla macchina, senza l’intervento del programmatore. In questo metodo non c’è un “insegnante”, dovrà essere la macchina stessa ad elaborare i dati, organizzarle e imparare il loro significato. In questo modo si offre una maggiore libertà alla macchina, ma anche un margine di errore maggiore se i dati non sono sufficienti.

L’ultimo è probabilmente il più complesso poiché è la macchina stessa a ricavare i dati da cui apprendere. In particolare essa viene dotata di un sistema capace di rilevare l’ambiente circostante, come ad esempio una telecamera, e da esso ricavare ciò che serve. La macchina deve quindi essere dotata non solo del supporto per la raccolta, ma anche per l’elaborazione. Questa tipologia di machine learning è tipico delle auto senza pilota.

Il machine learning è un argomento così complesso che richiederebbe un approfondimento ben maggiore di quello che possiamo trattare qui.

Rasa

Rasa è un framework open source in Python per la creazione di un chat bot basato sul machine learning supervisionato, che abbiamo usato nella nostra applicazione.

Esistono molti altri framework per creare chat bot, tra cui i più famosi sono quelli di Google (Dialogflow), Amazon (Amazon Lex) e Microsoft (Luis), i quali permettono uno sviluppo rapido e intuitivo del bot attraverso interfaccia. Dunque perché abbiamo scelto proprio Rasa?

Per prima cosa Rasa è open source. Questo significa avere a disposizione il codice e poter intervenire manualmente sul bot in caso di necessità. Questo è un evidente vantaggio rispetto ad altri framwork, come Dialogueflow, i quali permettono la modifica del bot esclusivamente attraverso interfaccia.

Inoltre Rasa è un progetto in continuo sviluppo, con una vasta community di sviluppatori a supporto. Gli stessi creatori sono disponibili a fornire chiarimenti e alla risoluzioni di bug e problemi vari.

Infine ha il vantaggio di non essere un servizio cloud e poter quindi essere ospitato in locale sulle macchine aziendali. Nella chat con l’utente potrebbero passare dati sensibili che, per motivi di privacy, non dovrebbero essere esposti all’esterno. La privacy dell’utente viene così garantita, avendo tutti i dati in locale.

Quest’ultima ragione è stata la motivazione principale che ci ha spinto a scegliere Rasa per il nostro progetto.

Installare Rasa su Windows.

Il primo step per poter usare Rasa è ovviamente l’installazione.

Il sistema operativo che abbiamo usato è Windows, il quale richiede alcuni passaggi aggiuntivi rispetto ad altri ambienti. Per questo motivo riportiamo in seguito i dettagli dell’operazione.

Come prerequisito necessario allo sviluppo del nostro bot, dobbiamo aver installato sulla nostra macchina Python 3.6.1.

Creiamo una nuova cartella nella quale inserire il nostro virtual envirorment. Per farlo, una volta entrati nella cartella da terminale, digitare ‘virtualenv <nome enviorment>’. È necessario assicurarsi che si stia usando Python 3.6 e non versioni successive.

Per attivare l’ambiente appena creato aprire la path ‘<nome enviorment>/Script/’ e digitare ‘activate’.

Adesso possiamo procedere all’installazione. Tutti i programmi necessari sono stati scritti in una lista ‘requirements.txt’ dentro la cartella ‘rasa’ nel pacchetto fornito dal sito ufficiale di Rasa. Per installarli basterà eseguire il comando ‘python pip install –r requirements.txt’.

Procediamo installando la lingua desiderata, in questo caso italiano, attraverso il comando ‘python -m spacy download it’. Questo particolare comando richiede autorizzazione, dunque prima di eseguirlo dobbiamo assicurarci che la shell sia stata aperta come amministratore.

Infine scarichiamo Rasa vero e proprio attraverso il comando ‘pip install rasa_Core’.

Queste installazioni sono sufficienti a far funzionare il nostro chat bot, tuttavia ai fini del nostro progetto è necessario che Rasa possa comunicare con la nostra applicazione. Per farlo passeremo da un altro framework in Python: Django.

Installare Django attraverso ‘pip’ è semplice e ben documentato, dunque non ci soffermeremo troppo su questo passaggio.

Infine per collegare tutte le componenti usiamo un programma chiamato Docker. Tale programma può essere scaricato e installato da interfaccia, seguendo le istruzioni sul sito ufficiale.

Tutti questi passaggi verranno automatizzati attraverso dei Dockerfile che vedremo più avanti.

 

Come funziona Rasa

Rasa è composto da due parti: Rasa Core e Rasa Nlu.

La prima, come suggerisce il nome, è il cuore del bot, ovvero la parte che gestisce l’apprendimento e sceglie le risposte secondo i modelli che crea durante “l’allenamento”.

Rasa Nlu è invece il motore di comprensione del linguaggio. Appoggiandosi ad altri framework quali tensorflow, spacy e sklearn, Rasa Nlu riesce ad analizzare la grammatica e la logica della frase. Da essa estrapola le parti che interessano a Rasa Core per elaborare la risposta.

 

Dominio del bot

Il bot possiede un un file chiamato domain.yml, presente nella path ‘rasa/models/current/dialogue/’. In esso sono salvati gli elenchi delle risposte, delle azioni, degli argomenti di conversazione, etc… ovvero, tutto ciò che necessita per imparare a comprendere il linguaggio e a rispondere.

Andiamo adesso a vedere nel dettaglio quali parti costituiscono il dominio del bot:

  • Intents
  • Entities
  • Slots
  • Templates
  • Actions

Analizziamo in ordine queste componenti.

 

Intents:

Gli intents costituiscono il significato della frase, ovvero la finalità della richiesta dell’utente. Per fare un esempio: “Buongiorno” ha come intent ‘saluto’. Possiamo dichiarare un qualsiasi numero di intent dentro domain.

Quando il bot analizza la frase in entrata, cerca di estrapolarne lo scopo, riconducendolo a uno degli intents presenti all’interno del file domain.yml. Per farlo utilizza un algoritmo di machine learning supervisionato con esempi forniti dentro il file nlu.md nella path ‘rasa/data/’, che vedremo più avanti.

 

Entities:

Le entities rappresentano un oggetto specifico dell’intent. Ad esempio: “Sto cercando un ristorante giapponese”, ‘giapponese’ è l’entity, ovvero una specifica dell’intent ‘restourant_research’. Ovviamente una frase può non avere al suo interno alcuna entity, come nel primo esempio (“Buongiorno”) o può averne più di uno (ad esempio: “Cerco un ristorante giapponese a Firenze”, dove le entities sono ‘giapponese’ e ‘Firenze’).

 

Slots:

Gli slots sono ‘memoria’ preposta a ricordare alcuni valori delle entità, necessari a seguire il filo logico del discorso. Facciamo un esempio: all’input “Voglio prenotare un tavolo per due al ristorante.” Il bot potrebbe rispondere “A che ora?”. La conversazione a questo punto procede, aggiungendo altri parametri come: nome del ristorante, orario, tipo del menù… Tutte queste informazioni devono essere salvate negli slots affinché la prenotazione possa infine essere fatta correttamente. Possiamo quindi dire che gli slots rappresentano i valori del filo conduttore della conversazione.

 

Templates:

I templates sono semplici frasi testuali di risposta che il bot può utilizzare. A tali risposte possono essere associate anche immagini. Ogni template per convenzione ha un nome nella forma “utter_<nome_template>”. Ad un singolo template può corrispondere più di una risposta. Ad esempio per il template “utter_greet” possiamo inserire “Salve” e “Bentornato”. In tal caso il bot di volta in volta sceglierà in modo casuale una tra le risposte fornite. I templates sono risposte statiche, dunque non modificabili in stato di esecuzione.

 

Actions:

Qui risiede una lista delle azioni a disposizione del bot.

Un’azione è una funzione definita in un file apposito, come descritto in seguito, che il chat bot esegue al manifestarsi di uno o più intent. Essa può eseguire una serie di istruzioni anche complesse, come ad esempio la prenotazione a un ristorante, poi fa un return di una stringa che contiene la risposta da dare.

Un esempio di action molto semplice è un echo della frase inserita dall’utente. In questo caso abbiamo solo un return della stringa di risposta, ma possiamo aggiungere della logica al suo interno usando istruzioni in linguaggio Python.

Riprendendo l’esempio dell’echo, possiamo intuire che i templates, sostanzialmente, non sono altro che casi particolari di actions, dove ci limitiamo a fare un return di una stringa statica, senza alcuna logica aggiuntiva. Per necessità architetturali dobbiamo inserire i nomi di tutti i templates tra le actions. A questo punto si possono aggiungere nuove azioni personalizzate (per convenzione chiamate “action_<nome_azione>”).

Una volta dichiarate, possiamo definirle in un file ‘actions.py’ nella cartella ‘rasa/’. Ogni azione viene associata a una classe (“class ActionNomeAzione (Action):”) in cui è necessario definire almeno due metodi: il primo è name(self), il quale fa un semplice return di una stringa contente il nome dell’azione (lo stesso nome che abbiamo definito in domain.yml); il secondo è “run (self, dispatcher, tracker, domain)” dentro al quale possiamo scrivere il codice da eseguire quando l’azione viene chiamata dal bot.

I tre parametri in entrata rappresentano rispettivamente: il dispatcher che permette di mandare messaggi di risposta all’utente, il tracker che tiene in memoria l’intent e l’entity dell’input e infine domain, che rappresenta il percorso del file domain.yml.

 

Le azioni vengono eseguite attraverso un server chiamato action_endpoints, che specifichiamo nel file endpoints.yml nella cartella ‘rasa/’. Esso viene eseguito su ‘localhost:5055/weebhook’. Affinché il bot possa eseguire le custom actions è dunque necessario far partire il server weebhook attraverso il comando ‘python –m rasa_Core_sdk.endpoint –actions <file_actions>’, dove ‘file_actions’ è il nome del file ‘actions.py’ che contiene il codice delle azioni da eseguire.

 

Comprensione del linguaggio

Come fa Rasa a riconoscere l’intent e sapere quale azione eseguire?

Tali informazioni vengono rispettivamente fornite nei file ‘nlu.md’ e ‘stories.md’, entrambi presenti all’interno della cartella ‘rasa/data/’.

Nlu.md

In questo file vengono specificate le linee di dialogo alle quali poter ricondurre l’input dell’utente.

In sostanza, per ogni intent forniamo una pool di esempi a cui il bot può attingere per imparare a riconoscere la frase dell’utente.

Ogni intent ha i propri esempi.

Nella pratica si scrive “## intent: <nome_intent>” e sotto di esso possiamo inserire un qualsiasi numero di esempi di frase per quel specifico intent. Nel caso la frase contenga delle entities è necessario specificare dove esse si trovano, racchiudendole tra parentesi quadre e specificando subito dopo di quali entities si trattano con le parentesi tonde. Un esempio renderà tutto più chiaro:

## intent: restourant_search

  • Sto cercando un ristorante [messicano](cusine) in [centro](location).
  • Dov’è un ristorante [francese](cusine) qui [vicino](location)?

Nell’esempio vediamo l’intestazione che rappresenta l’intent della frase (restourant_research), seguito da due esempi di frasi legate a quell’intent. Nelle parentesi tonde troviamo le entities, mentre in quelle quadrate un esempio dei loro possibili valori. Non esiste un numero massimo di esempi che possiamo inserire, al contrario, più esempi forniamo, più saranno le interazioni a disposizione del bot.

Apriamo una piccola parentesi sulla punteggiatura. Possiamo dire che le virgole sono abbastanza ininfluenti. Il bot riesce a comprendere l’intent della frase nella maggior parte dei casi con o senza virgole. Il punto e il punto esclamativo di fine frase non hanno alcun peso e la loro presenza o assenza non ha alcun effetto sulla comprensione linguistica. Arriviamo adesso al punto interrogativo. È necessario inserirlo, altrimenti il bot non potrà riconoscere un’affermazione da una domanda. La stessa frase con o senza punto interrogativo potrebbe appartenere a intent diversi.

Come consiglio generale, suggeriamo di essere precisi e inserire sempre la punteggiatura in modo grammaticalmente corretto, nonostante non sia del tutto necessaria.

Sinonimi

Sempre all’interno del file nlu.md abbiamo una sezione dedicata ai sinonimi. Cosa sono i sinonimi?

Per motivi interni alla logica del proprio bot alcune parole potrebbero avere lo stesso valore.

Riprendendo l’esempio di prima, “Dov’è un ristorante francese qui vicino?” coincide con “Cerco un ristorante con piatti della Francia qui vicino”. In questo caso francese e Francia sono entrambe entities, le quali dovrebbero avere lo stesso valore e significato.

Non è necessario che due sinonimi abbiano lo stesso significato grammaticale. Da un punto di vista di sviluppo del bot anche “autobus” e “metropolitana” potrebbero avere lo stesso significato, nel senso che quando vengono rilevate come entity il bot deve reagire a entrambe nello stesso modo.

Per specificare i sinonimi possiamo inserire “## synonym: <valore entity>” seguito da una lista di parole che hanno lo stesso valore.

Nel nostro esempio avremo:

## synonym: francese

  • Francia

In questo modo, quando Rasa trova l’entity ‘Francia’, le associa il valore ‘francese’.

 

Stories.md

Per quanto invece riguarda il file stories.md, qui vengono definite le ‘storie’, ovvero le sequenze di dialogo da cui Rasa comprende come rispondere.

Per prima cosa serve dare un nome alla storia. Il nome non è di per sé importante e modificarlo non comporta alcun vantaggio, oltre alla leggibilità del codice stesso. Esso si presenta nella forma “## story <nome storia>”.

Sotto possiamo specificare una sequenza di dialogo, identificando ogni frase con il proprio intent. In particolare sarà nella forma ‘* <nome_intent>{“<nome_entity>”: “<valore_entity>”}’, dove la parte tra parentesi graffa è ripetibile o omissibile a seconda dell’intent.

Infine sotto all’intent si trova la risposta che il bot deve dare, nella forma “- <nome_azione>”.

Presentiamo un esempio per rendere più chiara la forma di una story:

 

## story restourant reserch

* restourant_search{“cousine”: “francese”}{“location”: “vicino”}

– utter_how_many_people

* number_of_people{“number”: “2”}

– action_show_research

 

Nell’esempio sopra, abbiamo riportato una linea di dialogo dove l’utente chiede un ristorante francese nelle vicinanze (restourant_search), il bot risponde chiedendo per quante persone (utter_how_many_people). A questo punto l’utente indica quante persone sono presenti (number_of_people) e il bot effettua una ricerca e mostra i ristoranti con le specifiche richieste (action_show_research).

In teoria potremmo continuare questa linea di dialogo all’infinito, ma per l’apprendimento del bot risulta più efficiente creare delle storie brevi ma significative, piuttosto che una lunga omnicomprensiva.

Anche in questo file, più esempi forniamo più miglioriamo la comprensione del dialogo. Tuttavia vogliamo ricordare che il bot non è in grado di discernere da un buon esempio e uno non ottimo. Ogni esempio è allo stesso livello di tutti gli altri, dunque sta a noi fornire delle stories che semplifichino l’apprendimento invece di complicarlo.

 

Interactive learning

Scrivere questi stories.md e nlu.md interamente a mano può risultare tedioso e difficile. Fortunatamente viene in nostro aiuto un tool di Rasa chiamato ‘interactive learning’. I comandi per far partire questo tool sono stati scritti in un file Python, ‘train_interactive.py’ nella cartella ‘rasa/’. Per farlo partire basterà dunque eseguire tale file usando ‘python train_interactive.py’.

A questo punto comparirà un’interfaccia sul terminale, dove potremo iniziare a interagire con il bot. Da qui possiamo digitare da tastiera e inserire un frase. Il bot la elabora e ci fa vedere quali entities sono presenti al suo interno e a quale intent appartiene. Nel caso sia tutto corretto, diamo conferma al bot e proseguiamo. Altrimenti possiamo segnalare se l’intent non è corretto (e in tal caso dirgli quale intent è quello corretto) e/o se le entities trovate sono sbagliate (e in tal caso segnare quali sono).

Fatto questo il bot prosegue dando una risposta (ricordiamo che per eseguire una custom action è necessario che il server weebhook sia attivo). Prima di eseguirla, il bot ci chiede se l’action scelta è quella corretta. Se lo è, confermiamo e il bot è libero di eseguirla, facendoci vedere la risposta risultante. Altrimenti possiamo segnalargli quale azione è quella giusta e fargliela eseguire.

A questo punto il bot torna in ascolto dell’utente, aspettando un nuovo input.

Il train può essere interrotto in qualsiasi momento con ‘ctrl + c’, salvandolo o scartandolo. Nel caso in cui venga salvato, vedremo che i file ‘nlu.md’ e ‘stories.md’ sono stati automaticamente modificati seguendo la sequenza di dialogo del train appena fatto.

Esempio di interactive learning

esempio interactive learning

Si consiglia di non fare sessioni di allenamento troppo lunghe o troppo specifiche. A Rasa bastano poche storie per funzionare correttamente, purché siano significative.

 

Train init

Una volta definiti i file descritti finora, siamo pronti a far partire il vero allenamento di Rasa. I comandi per farlo iniziare sono stati raccolti nel file ‘train_init.py’ nella cartella ‘rasa/’. Dunque come per l’interactive learning, basta eseguire questo script per far iniziare il train (‘python train_init.py’). Durante l’esecuzione il server weebhook per le actions deve essere chiuso e riavviato alla fine dell’allenamento.

Schermata di allenamento del bot

schermata di allenamento del bot

Una volta terminato possiamo notare che alcuni file sono stati modificati o generati, nel caso sia il primo train. Tali file non vanno in alcun modo modificati a mano. Successive modifiche ai file stories.md e nlu.md richiedono un nuovo train del bot.

Dalla schermata nell’immagine possiamo notare alcuni valori interessanti. Il primo è epoch: questo è il numero di sessioni di cui è composto l’allenamento. Di base è impostato a 100, tuttavia è possibile modificare questo valore nel file train_init usato per avviarlo. Tuttavia un aumento delle sessioni, non porterà necessariamente a una migliore precisione. Possiamo dire che la curva di apprendimento del bot è una funzione logaritmica, per cui, superata una certa soglia, la crescita del miglioramento sarà sempre più ininfluente. Inoltre, sebbene non sia molto, ogni sessione richiede un certo tempo di attesa, dunque aumentarle comporta una crescita del tempo di build.

I valori alla destra della percentuale di avanzamento rappresentano il tempo di attesa, le perdite e la precisione. Man mano che le sessioni avanzano, vedremo scendere il valore delle perdite, mentre l’accuratezza cresce.

 

Dialogare con il chat bot

Possiamo far partire il bot vero e proprio attraverso il comando ‘python -m rasa_core.run –auth_token rasabot -d models\current\dialogue -u models\current\Nlu -o out.log –endpoints endpoints.yml –enable_api’.

Qui possiamo parlare direttamente con il nostro bot e controllarne il corretto funzionamento.

Bot in esecuzione

bot in esecuzione

Esiste un altro metodo per ottenere una risposta da Rasa, ovvero attraverso un server che risponde all’indirizzo ‘http://127.0.0.1:5000/’. A tale indirizzo il server di Rasa risponde con un semplice ‘hello from Rasa Nlu: 0.13.8’, utile per controllare che il server sia operativo, ma fine a sé stesso per il resto.

Per poter ottenere una risposta a un input dobbiamo andare all’indirizzo:

‘http://127.0.0.1:5000/parse?q=<testo di input per Rasa>&project=current&model=Nlu’

Stavolta il bot rende un file di tipo json dentro al quale sono contenute tutte le informazioni sulla risposta del bot: intent, entities, confidenza (una percentuale che rappresenta l’accuratezza della predizione sugli intents e sulle entities), il testo ricevuto come input e infine il progetto e il modello usati (ovvero i file auto-generati nelle cartelle rispettivamente ‘rasa/models/current/dialogue/’ e ‘rasa/models/current/nlu/’).

Notiamo che non è compresa la risposta. Infatti questo server espone soltanto la parte di Rasa Nlu (responsabile della comprensione del linguaggio) e non quella di Rasa Core (responsabile della gestione delle risposte).

Per avviare questo server, si usa il comando ‘python -m rasa_nlu.server -c nlu_config.yml –path models/’.

Nel nostro progetto useremo questo server.

 

Presentazione del progetto di applicazione

Adesso che abbiamo fornito una descrizione del funzionamento di Rasa, andiamo a vedere l’applicazione in cui verrà implementato.

Il progetto, finanziato dalla Regione Toscana, è stato chiamato eServant ed è un’applicazione per la gestione di grandi eventi, sia dal lato dell’organizzatore che dal lato del partecipante.

 

In questo contesto, il chat bot è stato pensato come aiuto nell’utilizzo dell’app: un nuovo utente potrebbe aver bisogno di una mano a trovare alcune funzionalità che sta cercando e a scoprirne di nuove.

Invece di inserire una lunga lista di tutorial, abbiamo pensato fosse più pratico per il cliente poter chiedere “direttamente”, usando linguaggio naturale.

Per il momento il bot è in grado di riconoscere la richiesta e rispondere con un tutorial di tipo testuale, spiegando dettagliatamente i passaggi da eseguire.

Nel futuro si progetta di sostituire le risposte testuali con clip animate, rendendo ancora più intuitiva la risposta. Inoltre si potrebbero inserire delle nuove funzioni, in modo tale che sia il bot stesso ad aprire il percorso corretto nell’app, senza bisogno di tutorial. Infine stiamo progettando un inserimento vocale, oltre che testuale, in modo tale che l’utente possa letteralmente “parlare” con il bot.

 

Architettura del progetto eServant

Rappresentazione dell’architettura del progetto

rappresentazione dell'architettura dell'applicazione

  

L’utente può aprire il chat bot in qualsiasi momento, grazie a un pulsante in basso a destra presente in ogni schermata. Una volta aperto è possibile inserire una frase per interagire con esso. Il testo viene messo nel body di una richiesta POST diretta al server di Django, del quale parleremo più avanti. Dunque Django attiva la view ‘rasaRequest’, la quale prende il testo contenuto nel body e chiama attraverso una get il server di Rasa Nlu, di cui abbiamo parlato nella parte di presentazione di Rasa.

A questo punto riceve il file json di risposta dal bot da cui estrae l’intent e le entities.

Ricordiamo che, con questo server, Rasa non fornire alcuna risposta. È stato quindi necessario simulare la scelta della risposta di Rasa Core attraverso una query su database.

Questo aspetto, per quanto a prima vista possa sembrare uno svantaggio, risulta invece essere molto efficace per il mantenimento futuro del bot. Infatti per aggiungere, modificare o rimuovere una risposta basterà modificare direttamente il database da lato back-end, senza dover interagire direttamente sul codice di Rasa.

Dobbiamo però aggiungere che questa soluzione risulta inefficiente per dei bot molto complessi, poiché bypassa completamente l’algoritmo di scelta di Rasa Core. Nella nostra applicazione, che si limita a fornire un tutorial testuale su richiesta specifica, non c’è alcun bisogno di questo tipo di algoritmo, che, al contrario, rappresenterebbe solamente un over-head per il nostro caso d’uso. Un chat bot molto più complesso, che magari possiede più actions legate allo stesso intent, oppure possiede linee di dialogo molto lunghe e ramificate, richiede sicuramente l’intervento di Rasa Core.

Una volta ottenuta la risposta, la view ‘rasaRequest’ ha adesso tutto quel che le serve per fare un return a eServant. In particolare, vengono restituiti quattro fattori in formato json: il nome dell’intent, i valori delle entities, la confidenza sull’intent e il testo di risposta.

L’applicazione mostrerà all’utente la risposta e userà i restanti tre valori a fine di debug.

Affinché Rasa possa interagire con la nostra applicazione è necessario un intermediario. Tale intermediario è Django (https://www.djangoproject.com/).

 

Funzionamento di rasaRequest

Passiamo adesso ad analizzare il funzionamento della view che simula Rasa Core, ovvero ‘rasaRequest’, definita nel file views.py di cui abbiamo precedentemente parlato.

Per prima cosa vogliamo che si possa accedere all’indirizzo di questa view, soltanto attraverso il metodo POST. Aggiungiamo in testa alla definizione della funzione ‘@api_view([‘POST’])’, che è un decorator dal pacchetto rest_framework (installato attraverso il file requirements.txt di Django).

Si procede adesso decodificando (in utf8) il body della request di POST arrivata.

Per rendere più comprensibile la frase a Rasa, vengono eliminate le punteggiature in eccesso (ad esempio ‘?????’ diventa ‘?’). Lo stesso viene fatto anche per gli spazi in eccesso.

A questo punto effettuiamo una HttpRequest (get) al server di Rasa Nlu, il quale risponderà dandoci l’intent compreso. L’indirizzo al quale ci riferiamo non è ‘http://localhost:5000…’, ma ‘ “http://” + os.environ.get(“RASAHOST”) + “:5000…” ’. Questo perché il server non verrà eseguito in locale, ma su una path definita nell’envirorment di Docker (che vedremo successivamente).

Una volta ricevuta la risposta di Rasa Nlu, la trasformiamo in json e da essa ricaviamo 3 valori: intent, confidence, entities.

Per prima cosa verifichiamo che la confidenza superi la soglia minima (definita all’inizio della funzione come costante default_action_fallback_treshold = 0.35). In caso contrario la risposta diventa ‘Mi dispiace, non ho capito cosa mi hai detto’.

Un dettaglio degno di nota è che una bassa soglia di confidenza non corrisponde necessariamente a un errore di comprensione di Rasa. Se, infatti, un utente che scrive una frase insensata o completamente fuori contesto, Rasa cerca comunque di ricondurla a un intent esistente, ma con “scarso successo”. In modo empirico abbiamo notato che la soglia di confidenza per questo tipo di frase non supera quasi mai il 20%. Allo stesso modo visto che la confidenza sulle richieste ben formate supera quasi sempre il 45%. Impostando la soglia limite a 35% ci assicuriamo il corretto funzionamento, eliminando frasi senza senso e includendo quelle formulate correttamente.

Se la soglia viene rispettata, si passa all’elaborazione e alla ricerca dell’intent. Prima di effettuare la ricerca sul database, dobbiamo trasformarlo nella “corretta forma”, descritta precedentemente.

Controlliamo che l’intent sia service e che abbia almeno un’entity. In tal caso possiamo procedere: sostituiamo gli eventuali spazi nel nome dell’entity con ‘_’ (così l’entity ‘posti liberi’ diventa ‘posti_liberi’), poi congiungiamo intent e entity con ‘_’ (così l’intent ‘service’ con entity ‘posti_liberi’ diventa ‘service_posti_liberi’).

Adesso siamo pronti a fare una ricerca sul database.  Se l’intent esiste ed è stato compreso con la giusta confidenza, scegliamo casualmente una delle risposte offerte dal database. Nel caso di un tutorial, che ha risposta univoca, viene selezionata sempre l’unica risposta disponibile. Nel caso invece di una conversazione, che prevede quattro possibili risposte, viene scelto uno qualsiasi tra i risultati della query.

Infine facciamo un return in formato json, dove abbiamo memorizzato intent, entity, confidenza e risposta.

Da esso eServant potrà ricavare la risposta da dare all’utente e utilizzare i restanti dati per debug.

 

Integrazione con Docker.

Docker è un’applicazione per l’utilizzo di container. Un container è un gruppo, un agglomerato di programmi che lavorano insieme. Docker riesce a far ‘collaborare’ i programmi all’interno di un container.

Nella pratica questo significa che Docker fornisce l’ambiente virtuale per far girare i nostri programmi, esegue automaticamente una serie di comandi di inizializzazione per Rasa e per Django (definiti nei rispettivi run.sh) e, infine, ospita contemporaneamente tutti i server necessari a questa applicazione. Il tutto su un’unica console, attraverso un singolo comando da testiera.

Non entreremo eccessivamente nel dettaglio di questo programma, ci limiteremo a spiegare quali sono le funzioni dei vari file all’interno del nostro progetto.

Sia la cartella ‘django’ che ‘rasa’ hanno al loro interno un file chiamato ‘Dockerfile’. Dentro di esso si trova l’ambiente dentro il quale i due servizi devono girare. Questi file, oltre a definire l’ambiente, come ad esempio il comando ‘FROM python:3.6.1’, eseguono alcune istruzioni da linea di comando attraverso la parola chiave ‘RUN’, tra cui l’installazione dei rispettivi requirements.text’. In più vengono esposte le porte necessarie a far funzionare i server. Una volta creato l’ambiente, esso verrà mantenuto, a meno di non distruggerlo attraverso linea di comando.

Entrambe le cartelle inoltre contengono un file ‘run.sh’. Dentro si trovano le istruzioni da eseguire ogni volta che l’ambiente viene riavviato. In particolare per Rasa troviamo l’istruzione per il train e quella per avviare il server di Rasa Nlu. Per Django viene eseguito makemigrations e migrate (comandi di Django che servono ad aggiornare il database in caso di modifica), la creazione di un superuser per accedere all’admin di Django e infine l’avvio del suo server.

A collegare tutti i file ci pensa ‘docker-compose.yml’, presente nella cartella principale del progetto.

Se lo apriamo possiamo vedere che espone tre servizi: djangodb, django e rasa. Per ognuno di questi servizi viene specificato, se esistono, dove si trova il corrispettivo compose, dove si trova il corrispettivo run.sh, la porta sul quale girare, le sue dipendenze e le eventuali variabili d’ambiente usate. Un esempio di variabile d’ambiente usata nel progetto è ‘os.envirion(“RASAHOST”)’, che si trova nella view rasaRequest. Questa variabile rappresenta la path del server di Rasa Nlu, che, come abbiamo già spiegato, non verrà eseguito nella stessa macchina del server di Django.

Infine vengono definiti i volumi, ovvero quali dati nel database devono essere persistenti al riavvio del container.

Tutto questo permette di far girare tutti i server, i comandi di inizializzazione, etc. attraverso la semplice esecuzione del file ‘docker-compose.yml’, usando il comando ‘docker-compose up’.

 

Conclusioni

In questo progetto abbiamo toccato molte tecnologie interessanti, non solo riguardanti la creazione di chat bot, ma anche lo sviluppo di back-end, la comunicazione tra server e la gestione di database e di container.

Django è uno strumento molto efficiente, capace di semplificare nettamente la scrittura e la gestione di un database. Questo grazie innanzitutto al linguaggio usato, ovvero Python, che risulta molto più efficace e meno contorto di altri linguaggi come Java. Inoltre la corrispondenza tra classe e tabella attraverso l’uso di modelli risulta facilmente mantenibile e comprensibile. In particolare l’utilizzo dei campi, Fields, forniti dal framework permettono una classificazione precisa di ogni colonna. Infine Django fornisce una pagina di Admin abbastanza banale, ma anche molto semplice e rapida nell’utilizzo, perfetta per gestire database di piccole dimensioni come il nostro.

Docker è una tecnologia relativamente nuova, ma molto performante. Poter gestire la routine di avvio e la creazione di ambienti attraverso un unico file permette, in primo luogo, una maggiore leggibilità, ma anche una manutenzione più funzionale e una migliore praticità per lo sviluppatore, al quale basterà un singolo comando per eseguire tutto il container.

Infine parliamo di Rasa, il cuore del nostro progetto. Come già accennato nel confronto con altri framework per lo sviluppo di chat bot, esso risulta sicuramente più complicato e meno intuitivo. Spesso ci sono stati dei problemi che hanno richiesto diverso tempo e studio per essere risolti. Tuttavia, la larga community e il team di sviluppo alle spalle di questo progetto, ha sempre fornito tutti i chiarimenti necessari in modo esaustivo e repentino.

Questa maggiore complessità è, tuttavia, compensata da alcuni pregi assolutamente da non sottovalutare. Innanzitutto permette una gestione a un livello superiore. Esaminare più da vicino il funzionamento di questo framework, ci ha permesso di interagire e amministrare le sue parti in maniera più libera e consapevole. Ne è un esempio l’integrazione con Django che abbiamo apportato.

Inoltre ricordiamoci che può essere eseguito in locale, senza affidarsi a un servizio cloud. Per un’azienda, che deve rispettare le norme sulla privacy, sempre più precise e restrittive, Rasa permette una totale direzione delle informazioni esposte all’esterno, prevenendo in questo modo qualsiasi problema.

 

Domande/osservazioni/commenti a d.risaliti@quidinfo.it

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *