RASA: FAQ automatisieren und Informationen sammeln mit Chatbots

FAQ mit einem Chatbot automatisieren? Rasa macht es möglich!

In diesem Artikel zeigen wir, wie man einen FAQ Chatbot implementiert, der mit Hilfe von Rasa FAQs beantworten und automatisiert Formulare ausfüllen kann. Für das Arbeiten mit Formularen bietet das Rasa Framework mit dem Form eine einfache Möglichkeit, ohne umfangreiches Erstellen von Dialogen benutzerfreundliche Bots anzufertigen.

Mit dem Artikel "Chatbot erstellen mit Rasa" haben wir bereits beschrieben, wie ein einfacher Chatbot mit Rasa erstellt werden kann. Wir haben veranschaulicht, wie Rasa installiert wird und ein erstes Projekt initialisiert. Auch haben wir gezeigt, wie ein einfacher Dialog mit dem Bot geführt werden kann.

Für diesen Use-Case beschreiben wir einen Chatbot, der es ermöglicht, ein Hotelzimmer zu reservieren und grundlegende Fragen zum Hotel beantwortet. Eine beispielhafte Implementierung finden Sie hier.

Wir haben Rasa 3.0 genutzt. Zunächst beginnen wir damit, dass unser Chatbot Fragen aus einem FAQ beantwortet.

Rasa FAQ Chatbots erstellen: So geht's!

Zu Beginn initialisieren wir in einem leeren Ordner mithilfe der Rasa CLI ein Projekt. Falls Rasa bislang nicht installiert ist und das Aufsetzen eines initialen Projektes noch unbekannt, können die einzelnen Schritte mithilfe unseres vorherigen Tutorials durchgeführt werden.

Nach Initialisierung des Projektes löschen wir die *Intents, Stories und nicht benötigten Konfigurationen der Domain. Anschließend erstellen wir Intents für drei Fragen. Dem automatisierten FAQ-Chatbot können Fragen über den Standort des Hotels, das Aussehen und mögliche Aktivitäten dort gestellt werden.  Diese erstellen wir zunächst innerhalb der NLU (Natural Language Understanding) Daten unter data/nlu.yml.

ResponseSelector

Mit dem ResponseSelector verfügt Rasa über eine Funktionalität, die den Umgang mit Smalltalk und FAQs vereinfacht. Diese Funktionalität wird im Folgenden verwendet. Dazu müssen die Fragen innerhalb der NLU-Daten speziell markiert werden, d. h. die Fragen müssen dem folgenden Muster entsprechen:

## intent: faq/ask_<name>

Die Frage nach dem Aussehen des Hotels wird unten als Beispiel gezeigt. Um den ResponseSelector zu verwenden, müssen mindestens zwei Intents erstellt werden.

    
   - intent: faq_ask_location
     - Wo befindet sich das Hotel?
     - Wo kann ich Sie finden?
     - Wo befindet sich das Hotel?
     - In welcher Stadt befindet sich das Hotel?
     - Wo befindet sich das Hotel 'To speaking bot'? 
    

Nach den Intents legen wir nun die Antworten an, also die Reaktionen des Bots auf den Intent des Benutzers. Dies erfolgt wie im Artikel zuvor beschrieben über Responses innerhalb der Domain.

Damit die Datei domain.yml nicht zu unübersichtlich und überladen mit Konfigurationen wird, legen wir im Ordner data eine neue Datei rules.yml an. Hier werden Rules erstellt, die vergleichbar zu Stories sind, allerdings mit lediglich einem Wechsel im FAQ zwischen Benutzer und Chatbot.

    
     - rule: ask for location
       steps:
       - intent: faq_ask_location
       - action: utter_ask_location
    

Die Antwort utter_ask_location wird in der domain.yml definiert.

    
     utter_ask_location:
       - text: "Das Hotel 'Zum sprechenden Bot' befindet sich im Herzen Münchens mit Panoramablick auf die Alpen."
    

Die Vorteile des ResponseSelector zeigen sich nun in der Erstellung der Story. Es ist nicht nötig mehrere Stories anzulegen, welche jede einzelne Frage behandelt. Innerhalb der Stories unter data/stories.yml werden alle FAQ gleichbehandelt, was sich vor allem bei Chatbots mit sehr vielen verschiedenen FAQ als großer Vorteil herausstellen kann.

    
     - story: Some questions for faq
       steps:
       - intent: faq 
       - action: respond_faq
    

Letztlich fügen wir den Intent faq und die Aktion zur Beantwortung der Fragen der Domäne (Datei: domain.yml) hinzu und der FAQ-Chatbot kann mit dem Befehl rasa train trainiert und anschließend über rasa shell getestet werden.

    
     Bot loaded. Type a message and press enter (use '\stop' to exit):
     Your input -> Where is the hotel?
     The hotel 'to speaking bot' is located in the heart of Munich with a panoramic view of the Alps.
     Your input → What can I do there?
     You can go for a hike or ride a bike in the alps, visit the city with sights such as "Marienplatz" or "Olympiapark" and once a year the "Oktoberfest" takes place in Munich
     Your input ->
    

FAQ Chatbot: Ausfüllen eines Formulars im Dialog

Der Chatbot ist nun im Stande, einfache Fragen zu beantworten. Des Weiteren wird der FAQ-Bot befähigt, Reservierungen anzunehmen. Dafür bietet Rasa die Möglichkeit, Formulare in sogenannten Forms zu füllen. Dieser Artikel beschreibt lediglich ein PoC mit beschränkten Funktionsumfang.

Rasa verfügt mit Entity Extraction über die Möglichkeit, komplexe Datentypen in Formulare einzugeben. So ist es möglich, Angaben des Benutzers wie “Morgen Nachmittag um 14:00 Uhr” korrekt zu deuten. Weitere Informationen finden Sie in der Rasa-Dokumentation.

Um ein Formular in Rasa auszufüllen, müssen wir zunächst die dafür benötigte RulePolicy unter policies in der config.yml mit aufnehmen. Im nächsten Schritt fügen wir das Form der Domain innerhalb der Datei domain.yml hinzu.

    
    forms:  
      hotel_form: 
        required_slot:    
        - number_of_persons
        - nights
        - date
        - room_type
    

Die benötigte Policy und die notwendigen Slots sind hinzugefügt und das Form in die Domain aufgenommen. Wie bereits bei den zu beantwortenden Fragen legen wir nun eine Story an, die den Verlauf des Forms darstellt.

Hierzu erstellen wir zunächst den Intent request_room innerhalb der Domain und legen unter data/nlu.yml Beispielintents für das Training an. Hierbei legen wir zwei unterschiedliche Ausprägungen der Intents an. Der erste Intent enthält lediglich die Information, dass ein Zimmer zu reservieren ist.

    
   - intent: request_room
      examples: |
      - Ich möchte ein Zimmer reservieren
    

Eine weitere Möglichkeit ist es, bereits in diesem Intent Informationen mit zu übergeben. So können bereits innerhalb dieses Intents sogenannte *Entities angegeben werden, wie in diesem Beispiel das Anreisedatum. Dabei ist wichtig, dass der Slot und die Entity die identische Bezeichnung, in diesem Beispiel date aufweisen. Slots und Entities werden im weiteren Verlauf angelegt.

    
    - intent: request_room
        examples: | 
        - Ist ein Zimmer ab [11/02/2020](date) frei.
        - Ich möchte ein Zimmer für den [10/03/2020](date) buchen.
    

Diese beiden Intents ermöglichen es nun, bereits zu Beginn des Forms eines der Felder auszufüllen. Auch sie nehmen wir unter domain.yml in die Domäne auf. Während des weiteren Ausfüllens wird der Benutzer nicht mehr gefragt, welches Datum er wünscht, da er dieses bereits zu Beginn angegeben hat.  

Nun legen wir eine Story an, die den optimalen Fall zum Ausfüllen des Forms wiedergibt.

    
    - story: request room
        steps:
        - intent: request_room
        - action: hotel_form
    

Um das Form im Dialog ausfüllen zu können, erstellen wir die Python Klasse HotelForm innerhalb von actions.py. Diese Klasse erbt von der Klasse FormValidationAction und implementiert die vier Methoden name, requiered_slots, submit und slot_mappings. Der Zweck der jeweiligen Methoden und die Implementierung wird im Weiteren beschrieben.

1. Name

Diese Methode gibt lediglich den Namen des Forms zurück, wie er innerhalb der Domain definiert wurde. Das ermöglicht es Rasa, das *Mapping zwischen Python Klasse und des in der Domain definierten Forms vorzunehmen.

        def name(self) -> Text: return "hotel_form"

2. Benötigte Slots

Diese statische Methode gibt alle Pflichtfelder/Slots des Forms als Liste zurück. Die Reihenfolge der Liste definiert auch die Reihenfolge, in welcher der Chatbot den Benutzer nach den Pflichtfeldern fragen wird. In diesem Beispiel sind die Anzahl der Personen, das Anreisedatum, die Zahl der Nächte und der Raumtyp die Pflichtfelder des Forms.

Es ist auch möglich anhand der Eingaben des Benutzers Felder dynamisch als Pflichtfeld zu definieren. Ein möglicher Anwendungsfall ist beispielsweise – falls Kinder mit im Zimmer schlafen – die Angabe, ob ein Kinderbett benötigt wird. Schläft kein Kind im Zimmer, ist diese Frage unnötig und wird somit auch nicht gestellt. In unserem Beispiel sind dynamische Pflichtfelder jedoch nicht vorgesehen und somit sieht der Code wie folgt aus:

    
    @staticmethod
    def required_slots(tracker: Tracker) -> List[Text]:
        return [
            "number_of_persons",
            "date",
            "nights",
            "room_type" ]
    

Um die Slots zu verwenden, definieren wir sie unter slots im domain. Dies geschieht ebenfalls unter domain.yml. Die Definition eines Slots gibt den Namen, den Datentyp und in speziellen Fällen mögliche Werte des Slots an. In diesem Beispiel zeigen wir nur die Slots number_of_persons und room_type.

Für die zwei weiteren Slots wählen wir einen korrekten Datentyp und verfahren wie bei number_of_persons. Der Slot room_type ist ein Beispiel dafür, dass lediglich eine begrenzte Auswahlmöglichkeit an Werten bestehen kann. Der Benutzer hat hier die Wahl zwischen zwei Varianten, die ihm bei Benutzung des Chatbots als zwei Buttons dargestellt werden.

    
    slots:
        number_of_persons:
            type: float
        room_type:
            type: categorical
                values:
                    - Junior
                    - Senior
    

Fragen für slots

Um mit dieser Methode verbundene Änderungen abzuschließen, müssen wir noch die Fragen vorbereiten, die der FAQ-Chatbot stellen wird, um die Slots zu füllen. Diese müssen ebenfalls innerhalb der Domain erstellt werden (Datei: domain.yml). Sie müssen nach der festgelegten Struktur

utter_ask_<slot_name>.

erstellt werden. Ein besonderes Element ist der Slot room_type. Um dem Benutzer nur die beiden Auswahlmöglichkeiten als Buttons zur Verfügung zu stellen, legen wir diese Antwort wie folgt an:

</slot_name>

    
     utter_ask_room_type:
      - text: "In which room do you want to sleep. In a junior or senior suite?" 
    	buttons:
      - title: "Junior Suite"
      payload: '/choose{"room_type": "Junior"}'
      - title: "Senior Suite" 
      payload: '/choose{"room_type": "Senior"}' 
    

3. Einreichen

Die Methode submit wird ausgeführt, wenn das Form vollständig gefüllt wurde. Sie kann beispielsweise dafür genutzt werden, die Reservierung zu speichern. Dieses Beispiel führt lediglich die Aktion utter_submit aus, die die Eingaben des Benutzers ausgibt und sie damit bestätigt.

    
    def submit(
        self,
        dispatcher: CollectingDispatcher,
        tracker: Tracker,
        domain: Dict[Text, Any],
    ) -> List[Dict]:
        dispatcher.utter_message(template="utter_submit")
        return []
    

Um die Antwort utter_submit auszuführen, definieren wir diese innerhalb der Domain. Den aktuellen Wert können wir ausgeben, indem wir den Namen des Slots innerhalb des Response Textes angeben. Wollen wir beispielsweise den Wert von room_types ausgeben, erfolgt dies so:

"Wir haben die Reservierung für eine {room_type} Suite erhalten."

Innerhalb einer Antwort können beliebig viele Slots mit ihrem aktuellen Wert ausgegeben werden.

4. Slot_mappings

Bevor wir mit slot_mapping die letzte Methode der Klasse definieren, nehmen wir einen weiteren Intent in die Domain mit auf. Es handelt sich um den Intent, den der Benutzer ausführt, um das Form zu füllen. Hierfür fügen wir die Entities number, data, days und room_type und den Intent inform der Domäne hinzu.

    
    intents:
        - inform
    entities:
        - number
        - date
        - days
        - room_type
    

Jetzt fügen wir unter data/nlu.md Beispielinhalte mit den jeweiligen Entities hinzu. Die Entity room_type benötigt das nicht, da diese über eine Auswahlmöglichkeit von zwei Buttons befüllt wird. Wir empfehlen pro Entity mindestens fünf Beispiele für ein korrektes Training zu erstellen.

    
  - intent: request_room
      examples: |
       - Wir sind zu [4](number)
       - Wir suchen ein Zimmer für [2](number)
       - Der [05.03.2020](date) ist mein Anreisedatum
       - Am [15.04.2023](date)
       - [4](days) Tage
       - Ich werde [1](days) Tag im Hotel sein   
    

Wir haben die Entities und Slots der Domain hinzugefügt und die Intents innerhalb von data/nlu.md für das Training angelegt. Nun folgt als letzte Methode das Mapping von Entity zu Slots, hier eine sehr einfache Variante. Es erfolgt ein Mapping mit der Methode form_entity der Klasse FormAction. Als Parameter ist die Entity und eine Liste der möglichen Intents zu übergeben. Das Mapping erfolgt innerhalb des Dictionarys, das durch die Methode zurückgegeben wird.

    
    def slot_mappings(self) -> Dict[Text, Union[Dict, List[Dict[Text, Any]]]]:
        return {
                "number_of_persons":[self.from_entity(entity="number", intent=["inform"])],
                "date": [self.from_entity(entity="date", intent=["inform"])],
                "nights": [self.from_entity(entity="days", intent=["inform"])],
                "room_type": [self.from_entity(entity="room_type", intent=["inform"])],
                }
    

Ausführung des FAQ-Chatbots

Um den Chatbot nun korrekt ausführen zu können, starten wir den Action Server von Rasa. Vom Entwickler erstellte Aktionen werden innerhalb dieses Servers ausgeführt. Innerhalb von Rasa lassen sich noch weit komplexere Aktionen erstellen. So können wir beispielsweise externe Schnittstellen einbinden und innerhalb von Aktionen nutzen. Damit führt ein Fehler innerhalb einer Aktion nicht zum Abbruch des Bots, da dieser unabhängig ausgeführt wird.

Wir starten den Server mit dem Befehl rasa run action. Mit dem Parameter -p

<port>

ist es auch möglich, einen alternativen Port anzugeben, wenn dieser vom Standardport 5055 abweicht.

Startet man nun den Chatbot mit rasa shell, wird es nicht möglich sein, das Form zu füllen. Grund dafür ist, dass wir in der Konfigurationsdatei endpoints.yml die beiden Server, den Action Server und den FAQ-Chatbot selbst, miteinander verknüpfen müssen. Dafür fügen wir die ursprünglich auskommentierten Zeilen wieder hinzu:

    
    action_endpoint: url: http://localhost:5055/webhook
    

Nun können wir den FAQ-Bot nach einem Training über rasa train mittels rasa shell nutzen. Einen Beispieldialog zum Füllen des Dialogs stellen wir im Folgenden dar:

    
    Bot loaded. Type a message and press enter (use '\stop' to exit):
    Your input -> I would like to book a room
    How many will you be?
    Your input -> There are 5 of us
    When do the 5 of you want to come?
    Your input -> We want to come on 12/04/2020 
    How long do you want to stay with us?
    Your input -> For 14 days.
    Which room do you want to sleep in. A junior or senior suite? 2: Senior Suite (\choose{"room_type": "Senior"})
    We have received your reservation for a senior suite. We are looking forward to you!
    Your input ->
    

*Intent = Absicht/Intention des Benutzers. Wenn ein Benutzer z.B. "show me yesterday’s technology news" eingibt, ist die Absicht des Benutzers, eine Liste von Schlagzeilen aus dem Technologiebereich abzurufen. Absichten erhalten einen Namen, oft ein Verb und ein Substantiv, wie z.B. "showNews".

*Entities/Entity = Als Entity wird in der Datenmodellierung ein eindeutig zu bestimmendes Objekt bezeichnet, über das Informationen gespeichert oder verarbeitet werden sollen.

*(Daten-) Mapping = Der Prozess, der Datenelemente zwischen unterschiedlichen Datenmodellen abbildet. Datenmapping wird als ein erster Schritt für verschiedene Aufgaben der Informationsintegration benötigt.

Ausblick

In diesem Artikel haben wir einen Chatbot zum Beantworten einfacher Fragen und zum Durchführen einer Reservierung erstellt. 

Diese Umsetzung beinhaltet noch keine Fehlerbehandlung. So kann der FAQ-Chatbot noch nicht auf unerwartete Fragen reagieren. Auch kann der Benutzer noch keine Fragen während der Reservierung stellen.

Die Möglichkeit, Slots durch externe Schnittstellen oder anhand von Abhängigkeiten zu füllen, bietet dieser Chatbot auch noch nicht. Für all diese Probleme kann Rasa eine Lösung bieten, um den optimalen FAQ-Chatbot für die jeweilige Situation zu erstellen.


Mehr zu technologischen Themen

Ausgewählte Beiträge

Kein Spam, versprochen
Erhalten Sie wertvolle Insights von unserem Expertenteam.