TSM - Dezvoltarea agenților virtuali cu Microsoft Bot Framework

Claudiu Mera - Software Developer


Agenții virtuali au devenit din ce în ce mai utilizați și populari în ultima vreme datorită funcțiilor crescute de inteligență artificială și a capacității de a integra tot mai multe dispozitive. De aceea, s-au lansat diverse platforme care mai de care mai avansate pentru crearea unor asemenea programe, printre care Microsoft Bot Framework.

Evoluția și aplicabilitatea agenților

Asistenți virtuali, agenți conversaționali, chat bots, toate sunt denumiri care au fost atribuite aplicațiilor software capabile să realizeze acțiuni în numele unei persoane. La urma urmei, nici nu contează atât de mult cum ne referim la aceștia, ci serviciile pe care le pot oferi și mai mult, cât de naturală este percepută interacțiunea cu un asemenea program. Modurile de interacțiune cu un agent sunt de regulă fie sub formă textuală, fie prin comenzi vocale.

Dispozitivele cu rol de asistenți au existat de mult timp sub o formă sau alta. IBM ShoeBox, lansat la începutul anilor '60, era capabil să recunoască cifrele și un set predefinit de 16 cuvinte. Mai târziu, a fost introdus suportul pentru recunoașterea vocală. Acesta a devenit tot mai avansat, culminând cu apariția programelor de dictare în anii '90. De asemenea, multe magazine de retail au introdus asistenți conversaționali pentru a automatiza partea de suport oferită clienților.

Odată cu îmbunătățirile legate de inteligența artificială și cu integrarea în dispozitivele mobile a început o nouă eră a agenților virtuali. Siri, Cortana, Alexa sunt numai câteva exemple care s-au bucurat de un succes enorm în rândul utilizatorilor. Acestea au devenit populare datorită simplității în utilizare, a înțelegerii limbajului natural și a multitudinii de activități în care ne pot asista.

Un factor important în utilizarea unui chat bot este dat de aplicabilitatea acestuia în viața de zi cu zi, fie că este vorba de planificarea agendei personale, de realizarea predicțiilor meteo sau de rezervări online. Din acest motiv au fost lansate o serie de platforme de dezvoltare care sunt menite să sporească numărul de servicii oferite și implicit, rata de adopție a unui asistent conversațional. De ce am realiza asemenea aplicații? Pentru că ele pot fi introduse cu ușurință în diverse canale de comunicare (Facebook, Skype) și prin urmare, pot avea o bază mare de utilizatori indiferent dacă ei folosesc Windows, iOS sau Android. Microsoft a lansat o asemenea platformă de dezvoltare caracterizată prin integrarea cu diverse servicii cognitive și mai ales o simplitate sporită în realizarea aplicațiilor, platformă denumită Microsoft Bot Framework.

Arhitectura soluțiilor chat bot

Microsoft Bot Framework este o platformă ușor de folosit, intuitivă, care ne permite să dezvoltăm chat bots și să îi publicăm în cadrul multiplelor canale de comunicare: medii sociale (Facebook), aplicații de mesagerie instantă (Skype, Slack, Twilio) sau skill-uri Cortana. Mai mult, aceștia pot fi conectați la Office pentru a răspunde automat la emailurile primite.

Arhitectura aplicațiilor chat bot

Aplicațiile pentru boți sunt în sine niște servicii REST bazate pe frameworkul Web API. Schimbul de mesaje cu utilizatorul are la bază formatul JSON. Dezvoltarea efectivă a unui program de tip chat bot se face prin Bot Builder SDK care ne pune la dispoziție C# și Node.js ca limbaje de implementare.

Prin Bot Builder SDK avem acces la diverse alte API-uri care fac parte din Microsoft Cognitive Services. Acestea ne permit să adăugăm funcționalități agentului virtual cum ar fi procesarea de imagini, interpretarea limbajului natural, recunoaștere vocală, traduceri sau căutări online, iar lista de exemple poate continua. Toate acestea conferă un grad avansat de inteligență, dar este de ajuns acest lucru dacă agentul virtual nu are o vizibilitate suficient de mare în exterior? Pentru a expune aplicația realizată, Bot Builder SDK folosește o componentă denumită Bot Connector Service ce are rolul de a stabili o legătură între aplicație și canalele de comunicare. Mai exact, această componentă se ocupă de traducerea mesajelor trimise prin canalul de comunicare (Facebook, Skype etc.) în mesaje JSON primitive, care pot fi interpretate de serviciul REST aflat la baza aplicației.

Bineînțeles, testarea unui asemenea agent folosind tooluri pentru servicii REST ar fi dificilă și consumatoare de timp. Aceasta, deoarece răspunsurile JSON primite de la un chat bot pot ajunge la o formă foarte complexă. De aceea, cei de la Microsoft au introdus o aplicație client denumită Bot Framework Emulator ce ne permite să testăm agentul conversațional pe mediile locale. Emulatorul oferă o reprezentare vizuală a mesajelor transmise de la și către bot în cadrul unei secțiuni de mesagerie instantă. De asemenea, există o secțiune specializată pentru afișarea răspunsului JSON, respectiv una prin care se poate urmări statusul HTTP de la fiecare request și response către serviciul REST.

Soluțiile dezvoltate sunt înregistrate în cadrul unui portal denumit Bot Framework Portal. Acesta permite fiecărui utilizator să-și gestioneze aplicațiile și canalele de comunicare aferente. Înainte de adăugarea unui agent nou, acesta trebuie publicat în Microsoft Azure. În momentul înregistrării unui bot este necesar să specificăm endpointul serviciului hostat în Azure la pasul precedent.

Odată înregistrat, agentul nou creat este adăugat automat la două canale de comunicare: Web Chat, respectiv Skype. Mesajele transmise de un agent pot avea o formă vizuală diferită de la un canal la altul. De exemplu, un mesaj care include imagini și butoane pentru acțiuni poate arăta într-un anumit fel pe Facebook și în alt fel pe Skype. O resursă utilă în acest sens este Channel Inspector prin care putem previzualiza felul în care sunt afișate diferite tipuri de mesaje pe diverse canale. Prin urmare, ne putem face o imagine despre cum sunt redate mesajele bogate în conținut pe un anumit mediu.

Radiografia unui bot

Pentru implementarea unui agent conversațional folosind Bot Builder SDK sunt necesare înțelegerea anumitor concepte de bază.

În primul rând, întreaga logică din spatele comunicării dintre un bot și consumator este gestionată de Dialoguri. Ele sunt invocate în momentul în care se trimite un mesaj sau, în termeni tehnici, când se realizează un POST către serviciul REST al aplicației. Primul dialog care este invocat când începe o nouă conversație poartă denumirea de Root Dialog. Celelalte dialoguri sunt ordonate sub forma unei stive, iar dialogul aflat în top este responsabil cu procesarea tuturor mesajelor de la client până când se invocă un alt dialog sau se termină conversația.

Orice client care se conectează este identificat în mod unic. Etapele sesiunii conversaționale sunt tratate în cadrul controllerului Web API prin următoarele evenimente: Conversation Update (adăugarea sau scoaterea membrilor dintr-o conversație), Contact Relation Update (modificări în lista de contacte), Typing (scrierea unui mesaj ce nu a fost recepționat încă), Ping (verificare status), Delete User Data (ștergerea completă a unui utilizator).

Fiecare informație transmisă între agent și canalul de comunicare poartă denumirea de Activitate. De menționat că unele activități sunt invizibile pentru utilizator, cum ar fi notificările pentru adăugarea botului în lista de contacte. Cele mai des întâlnite activități sunt Mesajele. Ele pot avea un conținut foarte variat, de la text la imagini, sunete, video sau facturi generate pentru plăți. Mai mult, pot include o serie de butoane pentru acțiuni din partea clientului. Acest tip de mesaje complexe poartă denumirea de carduri.

Model de card informativ

Din moment ce sunt invocate o succesiune de activități pe parcursul conversației, o întrebare este cum se persistă datele utilizatorului pentru refolosire? O variantă este bineînțeles, salvarea lor într-o bază de date. O altă soluție oferită de Bot Framework este folosirea obiectului UserData care menține un dicționar intern pentru stocarea proprietăților. Acest obiect este accesibil în cadrul contextului dialogului curent și asigură persistența pentru întreaga sesiune.

De cele mai multe ori apelăm la un bot pentru a ne asista în efectuarea unor procese ce cuprind mai mulți pași, cum ar fi rezervările online. Acești pași ar putea fi implementați prin dialoguri, însă timpul de dezvoltare ar crește considerabil în cazul scenariilor mai avansate. Bot Builder SDK simplifică enorm de mult acest aspect prin introducerea Form Flows. Aceasta este o funcționalitate ce generează automat dialoguri pentru o conversație ghidată.

Primul pas este definirea modelului cu proprietăți într-o clasă serializabilă. Cel de- al doilea pas este apelarea metodei Build din cadrul obiectului FormBuilder. Aceasta asigură generarea de dialoguri pentru fiecare din proprietățile definite în modelul anterior.

Definirea modelului pentru colectarea datelor de la utilizator

[Serializable]
    public class FlightReservation
    {
        [Prompt("Please enter the Origin you are flying from:")]
        public string Origin;

        [Prompt("Please enter the Destination you are flying to:")]
        public string Destination;

        [Prompt("Please enter a Departure date:")]
        public DateTime DepartureDate;

        [Prompt("Please enter an Arrival date:")]
        public DateTime? ArrivalDate;

        public string PassengerName;

        public CheckInOptions? CheckIn;

        public static IForm BuildForm()
        {
            return new FormBuilder()
                .Message("Welcome to Flight Booking assistant!")
                .Build();
        }    
     }

Dialogurile generate suportă diverse interogări în cadrul procesului de introducere a câmpurilor: vizualizarea stadiului completării, revenirea la pasul precedent, renunțarea la completare. La acestea se pot adăuga mecanisme de validare și logică specifică pentru popularea fiecărui field. În plus, se pot introduce și mecanisme speciale pentru conotațiile semantice ale unor câmpuri, lucru ce sporește flexibilitatea completării.

Procesarea limbajului natural

Cum ar fi dacă agentul conversațional ar răspunde doar la un set predefinit de comenzi? Ne-ar lua destul de mult timp să învățăm aceste comenzi, iar posibilitățile de interacțiune ar fi extrem de limitate. Înțelegerea limbajului natural este unul dintre atuurile asistenților de azi și ceea ce îi diferențiază în mare parte de agenții de acum două decenii.

Microsoft Cognitive Services este o suită de servicii de inteligență artificială și machine learning care sunt puse la dispoziția dezvoltatorilor pentru integrare în aplicații. O componentă a acestuia, denumită LUIS (Language Understanding Intelligent Service) este responsabilă pentru înțelegerea limbajului natural.

Printre avantajele LUIS se numără: mecanismul de învățare activă, rapiditatea, procesul simplu de configurare, recunoașterea unor domenii predefinite, un API puternic și flexibil pentru programatori și nu în ultimul rând, suportul pentru limbi multiple.

Cum reușeste LUIS să interpreteze limbajul natural? Acesta are la bază trei concepte esențiale: Intenții, Entități, respectiv Exprimări (Utterances). Scopul lui este să identifice Intenția și să extragă Entitățile din fiecare Exprimare.

Așadar, primul pas în construirea modelului LUIS constă în definirea intențiilor. Acestea sunt de obicei niște acțiuni generice la care utilizatorul se așteaptă de la un bot (inițierea unui proces de rezervare online, interogarea pentru un anumit serviciu). Cel de-al doilea pas este ca pentru fiecare asemenea intenție definită să specificăm una sau mai multe exemple de exprimare (formulări de genul "vreau să fac o rezervare către...", "pot călători spre locația... ?" sau "aveți disponibile zboruri către destinația... ?").

Cel de-al treilea pas este stabilirea entităților. Acestea sunt obiecte asociate cu o acțiune. De exemplu, dacă ne definim o entitate numită Place și interogăm agentul dacă are zboruri spre o anumită destinație, cea din urmă va fi extrasă din context și asociată entității Place. Pe lângă entitățile specificate de noi, mai există și o serie de entități gata predefinite (locații geografice, calendar, muzică etc.).

Afișarea rezultatelor pentru o interogare LUIS

De fiecare dată când utilizatorul transmite o comandă (exprimare), LUIS încearcă să o încadreze într-una dintre intenții. Pentru fiecare asociere se stabilește un scor între 0 și 1. Intenția cu scorul cel mai apropiat de 1 iese câștigătoare. Mecanismul de matching este destul de performant și nu trebuie ca exprimarea să fi fost configurată înainte în mod explicit. Prin urmare, chiar dacă ordinea cuvintelor din interogare diferă sau apar sinonime, sunt șanse mari ca exprimarea să fie încadrată corect. Atenție trebuie acordată totuși ambiguităților, iar în cazul în care o exprimare este asociată greșit, să o atribuim intenției corecte.

După testarea aplicației, aceasta se publică și devine accesibilă printr-un endpoint HTTPS. La fiecare apel se returnează un rezultat JSON ce conține scorurile încadrărilor în fiecare intenție. Aceste rezultate pot fi folosite ulterior de aplicații client, cum ar fi chat bots.

Bot Builder SDK oferă suport pentru integrarea cu LUIS. Pentru aceasta trebuie să realizăm o clasă care moștenește LuisDialog. Clasa respectivă trebuie decorată cu un atribut ce specifică ID-ul aplicației LUIS expusă anterior la endpointul HTTPS.

La fiecare intenție trebuie să precizăm o acțiune, fie că e vorba de invocarea unui dialog nou sau de afișarea unor mesaje către utilizator. Pentru implementarea unei asemenea acțiuni, trebuie să definim o metodă decorată cu un atribut ce coincide cu numele intenției. Așadar, când LUIS face matching la o exprimare cu o anumită intenție, va fi invocată din cod metoda care are atributul intenției respective.

[LuisIntent("QueryRoutes")]
public async Task QueryRoutes(IDialogContext context, LuisResult result)
{
   foreach (var entity in result.Entities.Where(e => e.Type == "Place"))
   {
     var entityValue = entity.Entity.ToLower();

     if (availableRoutes.Exists(p => p.Equals(entityValue)))
     {
       await CreateHeroCardReply(context, _message, entityValue);
       context.Wait(MessageReceived);
       return;
     }
     else
     {
      await context.PostAsync("We don't provide routes to that place.");
      context.Wait(MessageReceived);
      return;
     }
  }

 await context.PostAsync("I'm sorry. No places have been specified.");
 context.Wait(MessageReceived);
 return;
}

Definirea acțiunilor pentru o intenție identificată de LUIS

Obiectul LUISResult, care se transmite de regulă ca parametru metodelor, oferă acces la lista de entități identificate în cadrul unei exprimări. De aici, nu ne rămâne decât să prelucrăm datele respective și să afișăm mesaje relevante către client. Odată integrat, LUIS va face ca interacțiunea cu agentul virtual să fie percepută mult mai natural, întrucât utilizatorul își poate exprima liber intențiile, fără constrângeri legate de forma și conținutul mesajelor.

Concluzie

Aceste platforme de dezvoltare, cum este și Microsoft Bot Framework, au în primul rând rolul de a stimula creativitatea, de a ajuta lumea să identifice noi scenarii de utilizare ale agenților. Cu părți bune sau rele, asistenții inteligenți ne vor putea ușura multe dintre activități pe viitor și mai presus de toate, vor deschide noi orizonturi.