Boty

Podłączenie Wit.ai do bota

Mamy już naszego bota. Mamy też wytrenowany (w mniejszym lub większym stopniu) model w Wit.ai. Przyszedł więc czas na połączenie tych dwóch bytów we wspólnie działającą całość.

Wit.ai ma wystawione na świat zwykłe API. Znajduje się ono pod adresem: api.wit.ai.

Aby się do niego dostać skorzystamy ze zwykłego HttpClient-a. Zanim to zrobimy musimy pobrać token dla naszej aplikacji. Zrobimy to w ustawieniach naszej aplikacji Wit.ai.

Konfiguracja HttpClient-a

Po stworzeniu instancji httpClient-a:

var httpClient = new HttpClient()

musimy dodać ten token jako header requestu:

httpClient.DefaultRequestHeaders.Add("Authorization", "Bearer [token aplikacji Wit.ai]");

Zapytanie w URI

Uri który wysyłamy jako zapytanie ma postać: https://api.wit.ai/message?v=[date]&q=[message], gdzie [date] to data w formacie yyyyMMdd, natomiast [message] to treść wiadomości która skierował użytkownik do naszego bota.

Data

Na początek zajmijmy się datą:

var date = DateTime.Now.ToString("yyyyMMdd");

Konwertujemy aktualną datę na string w formacie yyyyMMdd (4 cyfry roku, 2 miesiąca i 2 na dzień).

Wiadomość

Teraz czas na dodanie wiadomości. Jak już pewnie wiesz z poprzedniej części, wiadomość wyłuskujemy za pomocą activity.Text. Natomiast dodając ją do URI musimy zadbać o to, żeby wszystkie spacje czy inne znaki specjalne zostały przekonwertowane na odpowiedni format. Użyję do tego metody UrlEncode z klasy HttpUtility. Odpowiednie formatowanie wiadomości w kodzie będzie więc wyglądać tak:

var message = HttpUtility.UrlEncode(activity.Text);

Sklejanie całości w URI

Aby skleić powyższe dane w odpowiedni URI posłużę się interpolacją w stringu:

var url = $"https://api.wit.ai/message?v={date}&q={message}";

Teraz wystarczy pobrać odpowiedź na nasze zapytanie:

var response = httpClient.GetStringAsync(new Uri(url)).Result;

Odpowiedź

Jeżeli wszystko poszło zgodnie z planem, otrzymamy odpowiedź w formacie JSON. Jednak żeby skutecznie z nim pracować zdałoby się serializować tą odpowiedź na klasy. W tym celu potrzebny będzie nam JSON który otrzymujemy jako odpowiedź. Możemy pobrać go za pomocą debuggera (kopiując wartość zmiennej response) lub za pomocą np. Postmana. Gdy będziemy mieli już odpowiedź serwera w schowku, tworzymy nową klasę w naszej aplikacji. Następnie kasujemy linijki:

public class NazwaKlasy
{
        
}

Teraz wykorzystamy bardzo pomocną funkcję Visual Studio 2017. W tym celu otwieramy menu Edit -> Paste Special -> Paste JSON as classes. Struktura JSONa zostanie zachowana w klasach. Jakie to wygodne 🙂

Sprawdzanie poprawności

Sposób jaki wymyśliłem na sprawdzenie czy request został zidentyfikowany to sprawdzenie czy znajdują się w nim jakiekolwiek entities. Niestety nie wystarczy sprawdzić, czy propertis public Entities entities jest nullem. Trzeba sprawdzić czy każdy z propertisów klasy Entities jest nullem. Jest to dosyć uciążliwe, ale skuteczne.

Intencja (zamiar) użytkownika

Jak pisałem w poprzednim wpisie, w Wit.ai nie ma już dedykowanego pola Intent. Teraz zamiarem jest nazwa Entity. Żeby dobrze określić zamiar użytkownika należy sprawdzać, które entity są podane, a które nie.

Dla przykładu. Mój bot ma za zadanie:

  • znaleźć drogę z jakiegoś miejsca do innego miejsca
  • znaleźć drogę z lokalizacji użytkownika do wskazanego w zapytaniu miejsca
  • pokazać miejsce na mapie

W wytrenowanym modelu Wit.ai posiadam 3 entities: from, to, location. W pierwszym przypadku wylistowanym wyżej Wit.ai zwraca mi enities from i to.
W drugim przypadku zwracana wartość jest tylko w entity to.
Natomiast w przypadku pokazania miejsca na mapie, w odpowiedzi dostaje entity location.

Obsługiwanie intencji

Obsługą intencji w powyższym przypadku zajmuje się kod:

if (json.entities.from == null && json.entities.to == null)
 {
          return ReturnLocation(json);
}
if (json.entities.from != null && json.entities.to != null)
{
          return RouteFromTo(json);
}
if (json.entities.from == null && json.entities.to != null)
{
          return RouteTo(json);
}
return "Nie rozumiem. Możesz inaczej sformułować pytanie?";

W powyższym kodzie rozważane są przypadki:

  • jeżeli from i to są nullami to znaczy, że użytkownik chce znać położenie jakiegoś miejsca;
  • jeżeli from i to NIE są nullami to znaczy, że użytkownik chce dostać wskazówki dojścia z miejsca from do miejsca to
  • jeżeli from jest nullem a to NIE jest nullem podaj wskazówki dojścia z aktualnego miejsca do wskazanej przez użytkownika lokalizacji.

Jeżeli żaden z powyższych warunków nie jest spełniony, to znaczy, że nie ma przewidzianej dla niego akcji.

Trochę nie podoba mi się taka ifologia, ale nie znalazłem innego – bardziej eleganckiego – rozwiązania.

Moim zdaniem dużo lepsze jest, gdy serwis NLU1 jawnie zwraca zamiar (intent) użytkownika. Dlatego jeżeli nie chcesz koniecznie korzystać z języka polskiego, zalecam skorzystanie np. z LUIS.ai. Jest on dużo przyjemniejszy niż Wit.ai. Ja skorzystałem w tym cyklu z tego drugiego, gdyż chciałem sprawdzić jego możliwości w rozpoznawaniu języka Polskiego.


  1. Natural Language Understanding

Zostaw odpowiedź

Twój adres email nie zostanie upubliczniony.* Pola wymagane *