Menu

Ein einfacher Chatbot

30. Januar 2017 - Chatbot, KI

Für einen einfachen Chatbot habe ich die Implementierung auf api.ai ausgesucht, nachdem der schwierigere Weg über eine lokal installierte KI in einer temporären Sackgasse verlaufen ist.

Die Implementation von api.ai besutzt viele ‚one click integrations‘ in verschiedene Chats (z.B. Facebook Messenger, Slack, Twitter, Skye, etc.), von daher habe ich api.ai als erstes ausprobiert.

Der Chat-Client (Messenger, Slack, etc.) dient dabei als Frontend und überstellt die Texte / Daen and api.ai. Diese wertet den Text aus und kann eigenständig darauf antworten oder das Ergebnis als strukturiert Daten an ein Backend schicken, welche dann die Logik übernimmt eine passende Antwort zu senden. Die Antwort wird auch an api.ai geschickt, welche dann an den Client weiterleitet.

Ohne eigenes Backend kann man einfache Konverstationen implementieren, aber über ein eigenes Backend kann man auch Services im Internet benutzen, um die Antwort zu generieren. Als Beispiel bringt api.ai ein Backend mit, welches den Yahoo Wetter Service abfragt, um eine Wettervorhersage im Chat zu ermöglichen.

Details:
https://github.com/api-ai/apiai-weather-webhook-sample

Das Backend in diesem Beispiel wir nach Heroku deployt, das geht auch gut mit einer Rails Anwendung. Von daher die Idee eine kleines Rails Backend (=schnell machbar, läuft bei heroku) zu schreiben, welches Wikipedia (=grosse mächtige Datenbasis, Abfragen benötigen keine Anmeldung / Key) abfragen kann und welches noch eine Zeitansage (=einfach zu implementieren) besitzt, zu schreiben.

Für den ersten Wurf soll auf viel ‚Schnickschnack‘ verzichtet werden. Der vom User eingegebene Text wird einem ‚Intent‘ (Absicht) zugeordnet, also was der User möchte und optional Parameter, bzw. ‚Entities‘, also Verfeinerung / Eigenschaften extrahiert. Beispiel für eine Suche:

'Was ist Astronomie?'

Das soll als Intent eine Suche ‚global_search‘ auslösen, mit dem Wort ‚Astrologie‘ als Suchwort (‚global_word‘).

Der Service api.ai ist teilweise selbsterklärend, somit werde ich nicht auf jeden Schritt der Implementation eingehen und mich auf die Interessanten Teile beschränken, ebenso wie bei dem Rails Service.

API.AI Service

Auf https://api.ai/ kann man sich mit seinem Google Account anmelden.

Einen neuen Agenten anlegen. Nun kann man genau einmal die Sprache des Agenten wählen:

Die Daten können natürlich auch importiert werden. Dazu habe ich meine Testinstallation als zip exportiert:

https://github.com/StMoelter/first-chatbot-backend/blob/api-ai-data-export/api_ai/WikiTest.zip

Der Service kann die .zip Datei importieren. Das erspart doch einiges an Tipparbeit 🙂

Nun kann man die Phrasen definieren, um die Suche Auszulösen. Dazu wird der Intent global_search definiert.

Farbig unterlegt sind hier die Entities, bzw. Parameter. Man kann die Bereiche in den Phrasen durch Doppelklick markieren und einem Parameter zuordnen.

 

Durch Klick auf ‚Entities‘ in der Seitenleiste kann man dann die Parameter verwalten. Die Benennung von Parameter und ‚Entities‘ ist nicht einheiltlich und kann verwirren.

Die Artikel sind eine überschaubare Menge von Worten und somit definierbar:

Dadurch können diese Worte schnell und sicher erkannt werden.

Bei den Parametern für die Wikipedia Suche ist das eher unpraktisch, man möchte nicht die Begriffe für alle Wikipedia Einträge einpflegen. Stattdessen soll der Service diesen von alleine aus dem Text extrahieren:

Das wird hier durch Aktivierung der Checkbox ‚Allow automated expansion‘ erreicht.

Auf der rechten Seite ist ein Bereich, auf dem man den Chat ausprobieren kann:

Wie man sieht ist auch mit einem unbekannten Wort (Radio), der Intent und die Parameter richtig gesetzt worden.

Nun sollte nur noch bei Wikipedia nach ‚Radio‘ nachgeschlagen werden und das Ergebnis in den Chat eingefügt.

Dazu macht es Sinn einen Webservice zu schreiben, welcher Wikipedia abfragt und die Daten an die api.ai wieder übergibt. api.ai kennt dafür Webhooks, um die Abfrage zu gestalten.

Erst Webservice, dann Webhooks, dann sollte es laufen.

Webservice

Um den schnell zu erstellen habe ich ein Rails Projekt aufgesetzt, allerdings nicht API only, wie zu erwarten gewesen wäre, vielleicht soll noch ein kleines Web-Interface dazu.

Das Projekt finden man hier:

https://github.com/StMoelter/first-chatbot-backend

Deployt wird das auf Heroku, dazu sollte der CLient installiert sein, weitere Infos siehe bei Heroku. Heroku kann leider kein sqlite, allerdings brauchen wir erstmal keine Datenbank. Von daher ist dev und test als sqlite und prod(=Heroku) als postgres im Gemfile definiert.

Zur Kommunikation mit der Wikipedia wird das :

gem 'wikipedia-client'

benutzt.

Im Initilializer wird de.wikipedia.org als Endpunkt gewählt, um in in Sprache konsistent zu sein. Wir wollen im deutschen Chatbot ja keine Englischen Ausgaben haben:

https://github.com/StMoelter/first-chatbot-backend/blob/api-ai-data-export/config/initializers/wikipedia_client.rb

Um die Arbeitsweise des Backends zu verstehen schauen wir erstmal auf den Chat-Controller:

https://github.com/StMoelter/first-chatbot-backend/blob/api-ai-data-export/app/controllers/chat_controller.rb

Auf die http Authtentifizierung bietet eine kleine Sicherheit und wurde gewählt, da api.ai das unterstützt und schnell zu implementieren war.

Das Einlesen der PArameter erfolgt über die Zeile:

apiai_response = ApiaiResponse.new(params.to_unsafe_h)

Der Intent wird bestimmt über:

dig(:result, :metadata, :intentName)

auf dem Parameter Hash nach deep_symbolize_keys.

Die Entities über:

dig(:result, :parameters).to_h

Wobei der Hash hier den Namen der Entity (bzw. Parameter)  als Key und die Werte als Array in der Value hat.

Für die Suche wird anhand des Intents über eine Factory der Prozessor bestimmt und mit den Entities aufgerufen:

https://github.com/StMoelter/first-chatbot-backend/blob/api-ai-data-export/app/models/intent_processor/global_search.rb

def self.process(entities)
  entry = Wikipedia.find(Array.wrap(entities[:global_word]).join(' '))
  entry.summary
end

Also recht simpel implementiert, Einfach mal bei Wikipedia nachfragen und die Zusammenfassung komplett unreflektiert zurückliefern. Für einen professionellen Bot muss hier natürlich mehr Logik rein.

Der Rails Service liefert dann api.ai folgendes zurück:

render json: {
  source: 'apiai-wikitest-webhook-sample',
  displayText: output,
  speech: output
}

es muss sowohl ‚displayText‘, als auch ’speech‘ definiert sein, damit api.ai zufrieden ist und das Ergebnis anzeigt.

Mit heroku create (oder ähnlich) wird nun eine stage für den Rails Service erzeugt und liefert die URL zurück welche in api.ai eingetragen wird.

Mit heroku push master findet das Deployment statt und der Service ist bereit.

Webhook

Durch Klick auf ‚Fulfillment‘ auf der linken Seite kommt man in den Screen, um den Webhook zu konfigurieren:

Die Webhooks sollten eingeschaltet werden, URL von heroku, basic auth aus dem Controller werden eingetragen.

zurück zur Ansicht des ‚global_search‘ ‚Intents‘, dort unter Fulfillment auch den Webhook aktivieren:

 

Im Chat-Test auf der rechten Seite sollte die Wikipedia Abfrage erfolgreich sein. Der Service auf Heroku schläft gerne ein und wirft dann beim ersten Versuch einen TimeOut Error. Einfach nochmal probieren, das hilft.

(Text von Wikipedia: https://de.wikipedia.org/wiki/Radio)

Veröffentlichen

Der Bot sollte nun auch von außen erreichbar sein.

Dafür gibt es eine eigene Seite von api.ai unter der der Bot erreicht werden kann. Das erspart erstmal die Integration in Facebook & Co.

Zu finden ist das unter ‚Intergrations‘ auf der linken Seite:

Hier einfach den Schalter auf ‚Publish‘ setzen:

unter der Agent Page ist der Bot dann erreibar und kann auch via iFrame eingebunden werden.

Wenn Ihr den Bot ausprobiert, dort Daten verschickt, denkt dranne, das geht über api.ai (Google) zu heroku und wieder zurück: