Zgodnie z definicją, bot to skrót od robot. Jest maszyną mającą na celu naśladowanie pewnych ludzkich zachowań. W przypadku tego zadania przeprowadzenie prostego dialogu z rozmówcą.
Historia botów, zdolnych do rozmowy z człowiekiem, sięgalat 60 (patrz ELIZA) i jest nierozerwalnie związana z testem Turinga określającym inteligencję maszyny. Według Turinga, maszyna, która przy rozmowie z człowiekiem w żaden sposób nie zdradziłaby, że nim nie jest, to maszyna inteligentna.
Od początku lat '60, kiedy stworzono pierwszego w miarę wiarygodnego bota, minęło wiele czasu. Powstały wybitne projekty (patrz A.L.I.C.E). Na potrzeby bota Alice stworzono specjalny język programowania, (A.I.M.L). Jest to chyba największy do tej pory projekt mający na celu implementację wiarygodnego bota. Pracuje nad nim już od 10 lat sztab ludzi. Mimo tak wielkich wysiłków i potężnych funduszy żadna maszyna nie przeszła jeszcze testu Turinga. Obrazuje to jak trudne jest to zadanie.
Każdy poprawnie zaprojektowany bot składa się z 2 części. Pierwsza z nich to baza danych, która stanowi całą wiedzę bota. Zawiera wszystkie odpowiedzi (lub dane konieczne do ich konstrukcji) oraz kluczowe zwroty służące do interpretacji zdania wpisanego przez człowieka. Druga to program, będący mechanizmem przetwarzającym informacje zawarte w bazie danych.
Baza wiedzy powinna, w miarę możliwości, nie stanowić integralnej części kodu programu (umożliwia to łatwą rozbudowę bota). Rysunek 1 przedstawia ogólny schemat działania botów konwersacyjnych.
Wybór języka, w którym miałby powstać bot Edi odbył się już na etapie wyboru tematu projektu. Nie było to wcale łatwe. Jak już wspomniałem, specjalnie na potrzeby projektu A.L.I.C.E stworzony został język A.I.M.L. Narzędzie to zostało napisane w sposób ułatwiający implementację botów. Zawiera wiele gotowych funkcji i schematów. Wykonanie projektu w A.I.M.L zwolniłoby mnie z pracy koncepcyjnej (wszystko już zostało wymyślone i wystarczy się zapoznać z dokumentacją i przykładami).
Tworzenie wyrazów błiskoznacznych wymaga wklepania dość długiego kodu. Czyni to program nieczytelnym i mniej wygodnym w pisaniu. Język polski ze swoimi licznymi odmianami i obocznościami zmusza do nieustannego stosowania mechanizmów bliskoznaczności. W przypadku C++ wystarczy napisać prostą funkcję wyszukiwania przybliżonego i umieścić w bazie danych tylko temat wyrazu.
Wiekszość przykładów botów w A.I.M.L na jakie się natknąłem były to "sztywne" programy, które oczekiwały od użytkownika wpisania konkretnej wypowiedzi. Były to programy bardzo wrażliwe na składnię zdania. W C++ można bardzo prosto (opisane w dalszej części opracowania) stworzyć dość prostego i niezwykle elastycznego bota.
Zwolnienie z wymyślenia bota od podstaw nie jest zaletą. Zawsze chciałem zmierzyć się z podobnym problemem;
Ostatnim, decydującym za wyborem C++ argumentem, była dużo lepsza znajomość tego języka niż A.I.M.L. Dało to więcej czasu na stworzenie bota, gdyż nie musiałem instalować nowego narzędzia i się z nim zapoznawać.
Ostatecznie Ediego napisałem w "Bloodshed Dev-C++". Narzędzie to jest dostępne na licencji GNU.
Zadanie okazało się dosyć obszerne. Rozmowy, które można prowadzić z Edim można podzielić na kilka tematów (noclegi, rozrywka, zakupy, jedzenie i zwiedzanie). Każdy z wymienionych tych tematów mógłby z powodzeniem być podstawą do skonstruowania osobnego bota.
Dlatego postanowiłem podzielić bazę danych na kilka plików. Jeden z nich zawiera słowa-klucze z przyporządkowanymi im liczbami, oraz numery tematów. Te liczby przyporządkowane wyrazom-kluczom to pewne punkty. Kiedy użytkownik wpisze zdanie, komputer porównuje wyrazy ze zdania (wyszukiwanie przybliżone) z wyrazami-kluczami każdego tematu. W przypadku dopasowania, program przydziela tematowi ilość punktów zapisaną w bazie danych przy wyrazie. Temat, który zdobędzie najwięcej punktów jest uważany przez program za obowiązujący. Najlepiej byłoby to zobrazować na przykładzie:
Zastosowanie takiej strategii daje maksymalną tolerancję na różnice w składni zdania. Rozważmy zwroty "Znajomi wyciągają mnie na piwo. Gdzie moglibyśmy pójść?" i "Gdzie mógłbym pójść ze znajomymi na piwo?". Składają się one z podobnych wyrazów i oznaczają dokładnie to samo. Zastosowany w bocie Edim mechanizm jest prosty i jednocześnie potrafiłby rozpoznać oba przypadki. Prawidłowe działanie zależy oczywiście od dobrze skonstruowanej bazy danych. Przy jej tworzeniu potrzebna jest wiedza eksperta, ale każdy Polak zna swój język na tyle dobrze, że byłby w stanie napisać poprawny plik znając jego składnie.
Po poprawnym zaklasyfikowaniu tematu, program otwiera odpowiedni plik z reakcjami językowymi (znajdują się w folderze "reakcje").
Każdą rozmowę prowadzoną z osobą "urzędową" (na przykład z policjantem, lekarzem, ale też z kelnerem i przewodnikiem) da się zamknąć w pewnych szablonach. W niektórych przypadkach jest tych szablonów więcej, w innych mniej, ale zawsze jest to skończona ilość.
Zjawisko to zostało wykorzystane w "Edim". Bot może udzielić na każdy temat jednej z kilku odpowiedzi. Wybór prawidłowej jest dokonywany na podobnej zasadzie, jak wybór tematu (tzn. konkurs punktów przydzielonych odpowiedziom za wagi wyrazów kluczy). Załóżmy, że rozmowa dotyczy noclegu. O co potencjalny rozmówca mógłby pytać?
1) O miejsce, gdzie można przenocować,
2) O cenę doby w zaproponowanym hotelu,
3) O inne możliwe opcje noclegu,
4) O ceny innych opcji.
Oczywiście ilość opcji zależy od wyobraźni osoby konfigurującej bota. Na każdy z tych tematów można udzielić kilku odpowiedzi, które są losowo wybierane. Odpowiedzi te można również sztucznie rozbijać (na przykład osobno te, dotyczące hotelu :Wrocław" a osobno dotyczące stancji u pani Krysi). Każda odpowiedź dotyczy konkretnego pytania, które jest charakteryzowane przy pomocy wyrazów kluczy. Przykładowy plik do klasyfikacji odpowiedzi przedstawia rysunek 3.
Składnia plików bazy nie dotyczy w sposób bezpośredni algorytmu, więc zostanie omówiona później.
Rysunek 4 przedstawia schemat blokowo-ideowy działania bota.
Program został stworzony w Dev C++. Implementację bota dodatkowo ułatwiła standawdowa biblioteka "string" (umożliwia łatwe operowanie na napisach).
Dodatkowo program korzysta ze standardowych klas obsługujących strumienie wejścia i wyjścia oraz statyczne tablice deklarowane dynamicznie.
Tego typu podejście daje bardzo duże możliwości rozbudowy bota (własna konfiguracja ogranicza się prawie tylko do zmiany bazy wiedzy) przy małym nakładzie pracy na pisanie programu.
Składni plików z danymi i algorytmowi poświęciłem osobne akapity, więc nie będe się rozwodził.
Przykład takiego pliku ilustruje rysunek 2.
Pierwszym jego znakiem (zbiorem znaków) musi być liczba określająca ilość tematów, na które bot może dyskutoweć. Potem następuje dowolna ilość komentarzy. Potem są sekcje tematów. Każda z nich rozpoczyna się od znaku "$" i numeru tematu (numeracja musi być w kolejności rosnącej i nie przypadkowej). Kolejna liczba mówi o przewidzianej ilości wyrazów-kluczy. Składnię zamyka lista wyraz-punkty. Między sekcjami tematów mogą wystąpić komentarze.
Przykład takiego pliku przedstawia rysunek 3. Jest on bardzo podobny do pliku rozpoznawania tematu. Różnice polegają na tym, że plików odpowiedzi jest wiele (dla każdego tematu osobny), muszą się znajdować w folderze "reakcje" i po sekcji rozpoczynającej się od "$" mają jeszcze sekcję możliwych odpowiedzi (znak "#", numer tematu, ilość odpowiedzi i ich lista przedzielana separatorem "&"). W odpowiedziach, zamiast spacji trzeba wstawiać podkreślenie dolne "_".
Nazwy omawianych plików zdefiniowane są na sztywno w kodzie programu. Nie byłoby jednak problemu z przerzuceniem ich do pliku konfiguracyjnego.
Przykład 1 obrazuje nie do końca poprawne działanie bota. Baza danych zawierała jeszcze błędy.
Zamieściłem go jednak celowo, chcąc pokazać wpływ drobnych zmian w punktowaniu wyrazów-kluczy na działanie programu.
Zaraz po powitaniu zostało zadane pytanie o imię bota. Odpowiedź udzielona zawiera zwrot witam. Jest to błąd wynikający z nie rozwinięcia bazy danych.
Kolejny błąd (oznaczony 1) wydaje się być poprawną odpowiedzią. Jest to jednak przypadek. W rzeczywistości popełniono błąd taki jak w 2. W punkcie oznaczonym jako 3 poprawiono wartości punktowe niektórych wyrazów i otrzymano poprawny wynik. (bazy związane z odpowiedziami można poprawiać w trakcie działania programu. Baza "tematy.txt" natomiast jest przy każdym uruchomieniu wczytywana do RAMu, jej zmiana wymaga restartu programu).
Kolejne będy w działaniu daje się zauważyć dopiero przy pożegnaniu.
Program ma zwroty kończące działanie wpisane na stełe w postaci makrodefinicji. Istotna jest więc wielkość liter. Edi reaguje na wyrazy: "zegnam", "warka", "nara" i "dzieki". Przyszłe wersje bota będą miały ten błąd poprawiony.
Przykład 2 jest najzupełniej poprawnie przeprowadzoną rozmową. Edi nie pomylił się przy odpowiedzi na ani jedno pytanie.
Jest to rozmowa przeprowadzona dość niedbale. Widać kilka wad Ediego. Większość dała by się wyeliminować poprzez rozbudowę bazy danych bota, ale nie wszystkie.
Na przypadki (1) i (2) Edi nie był przygotowany. Da się je wyeliminować rozwijając bazę danych. Gorzej jest z (3) i (5). Ediego cechuje brak pamięci i brak obsługi podmiotów domyślnych. Poprawa tego wymagała by sporego nakładu czasowego.
Punkt 4 to błąd ortograficzny. Edi nie zrozumiał.
Na koniec przykład rozmowy Ediego z moim kolegą. Nie wiedział zupełnie nic na temat możliwości bota, więc nie pogadali sobie za dużo.
Rozmowy przeprowadzane ze mną wypadły bardzo dobrze, bo wiem o czym można z Edim pogadać. Dodatkowo nie starałem się go specjalnie wysypać.
Zdecydowanie gorzej wyszła większość rozmów z ludźmi przypadkowymi.
Większość dialogów była tak prowadzona, żeby jak najszybciej pokazać botowi, że jednak nie jest inteligentny (dlatego przytoczyłem tylko jeden przykład).
Napisanie prostego bota, który reagowałby na język naturalny jest zadaniem łatwym. Problemy pojawiają się przy tworzeniu bazy wiedzy. Zaprojektowanie odpowiedniej (umożliwiającej botowi reakcje na wszystkie wymyślone przez człowieka sytuacje) jest zadaniem niezwykle trudnym i czasochłonnym.
W przypadku Ediego problem polega nie tylko na dobraniu odpowiednich wyrazów kluczy, ale też na przypisaniu im wag. Mogą się przytrafić sytuacje, w których to samo słowo musi być przypisane kilku tematom.
Dodatkowy problem pojawia się, kiedy chcemy żeby nasz bot był trochę bardziej inteligentny i umiał stosować się do podmiotu domyślnego i wcześniejszych wypowiedzi rozmówcy.
Niestety Edi nie jest w stanie rozpoznać nawet imienia rozmówcy. Nie zadaje pytańi jest w stanie tylko na nie odpowiadać.
Edi, pomimo naprawdę prostej konstrukcji mógłby być bardzo mądrym botem. Trzeba by mu tylko napisać odpowiednią bazę wiedzy. Na to niestety nie starczyło już czasu.
Było by go dużo więcej, gdyby nie konieczność spełnienia wszystkich wymogów formalnych związanych z projektem.
Zeszłoroczne projekty,
Strona projektu A.L.I.C.E bot,
Programowanie w C++;
Wikipedia.