How to create a FAQ Chatbot with Rasa?

February 2022
November 2020

A Rasa FAQ chatbot will bring your First-Level Support to a new level!

In this tutorial we will show how to implement a FAQ chatbot with Rasa to answer FAQs and fill in forms. For working with forms, the Rasa framework with the FormPolicy offers a simple way to create simple yet user-friendly bots for this task without the need to write extensive dialogs.

Our tutorial "Create a chatbot using Rasa" describes how to create a simple chatbot with Rasa. In that earlier article, we showed how to install Rasa and initialize a first project. We also illustrated how to have a simple dialog with the bot.

In order to demonstrate a use case, we describe a chatbot which allows to reserve a hotel room and answers basic questions about the hotel. You can find an exemplary implementation here. This article describes the steps of how to create the chatbot using the implementation found there.

Please note that we used Rasa 3.0 for the creation. Now, we will start with our chatbot answering questions.

Building the Rasa FAQ Chatbot

At first, initialize a project in an empty folder using the Rasa CLI. In case you haven't installed Rasa yet and you don’t know how to set up an initial project, you can follow the steps in our previous tutorial.

After initialization of the project we delete the *intents, stories and unnecessary configurations of the domain. Now, we create intents for three questions. We want to ask the Rasa FAQ chatbot questions about the location of the hotel, its appearance and possible activities there. We first create these within the NLU data under data/nlu.yml.

ResponseSelector

With the ResponseSelector Rasa has a functionality that simplifies the handling of small talk and FAQs. We use this functionality below. For this purpose, the questions within the NLU data have to be specially marked, i.e. the questions need to follow the pattern:

## intent: faq/ask_<name>

We show the question about the appearance of the hotel below as an example. In order to use the ResponseSelector, we need to create at least two intents.

    
   - intent: faq_ask_location
     - Where is the hotel?
     - Where can I find you?
     - The hotel is where?
     - In which city is the hotel?
     - Where is the hotel 'To speaking bot'? 
    

After creating the intents, the responses, i.e. the reactions of the bot to the user’s intent, must be prepared. This is done via responses within the domain as described in the previous article.

To avoid the domain.yml file becoming too confusing and overloaded with configurations, we add a new rules.yml to the data folder. In this file the rules to reply to the previously defined questions are generated. Responses are created similarly to stories, but with only one switch between user and chatbot.

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

The answer utter_ask_location is defined in the domain.yml

    
     utter_ask_location:
       - text: "The hotel 'To speaking bot' is located in the heart of Munich with a panoramic view of the Alps."
    

The advantages of the ResponseSelector are now evident in the creation of the story. It is not necessary to create multiple stories, which deal with each individual question. Within the stories under data/stories.yml all FAQs are treated in the same way and there is no distinction between them. This can be a major advantage especially for chatbots with a lot of different FAQs.

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

Ultimately we need to add the intent faq and the action to answer the questions of the domain (file: domain.yml). Consequently, we can train the Rasa FAQ chatbot with the command rasa train and then test via rasa shell.

    
     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 ->
    

Rasa FAQ Chatbot: Filling a form through dialog

The FAQ chatbot is now able to answer simple questions. In the following we describe how to enable the Rasa bot to accept reservations. In order to do this, Rasa offers the possibility to fill in Forms. This article describes only a PoC with limited functionality.

With Entity Extraction Rasa offers the possibility to enter complex data types into forms. Thus, it is possible to correctly interpret user data, such as "Tomorrow afternoon at 14:00". For more information please refer to the Rasa documentation.

To fill a form in Rasa you first have to add the required RulePolicy under policies in config.yml.
The next step is to add the form, that we want to fill, to the domain within the domain.yml file.

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

We have added the required policy, and included the form in the domain. As with the questions to be answered, we generate a story, which shows the process of filling the form. For this we first set up the intent request_room within the domain and define sample contents for the training under data/nlu.yml.

Here we add two different versions of the intents. The first intent only contains the information that a room must be reserved.

    
   - intent: request_room 
      examples: |
      - I would like to book a room
    

Another possibility is to transfer information already in this intent. So, you can specify so-called entities within this intent. In this example, we use the arrival date for this purpose. It is important that the slot and the entity have an identical name, in this example date. Slots and entities will be specified in the further steps.

    
    - intent: request_room 
        examples: |  
        - Is a room available from the [11/02/2020](date).
        - I would like to book a room for the [10/03/2020](date).
    

These two intents now make it possible to fill one of the fields at the beginning of the form. We also include them in the domain under domain.yml. During the further completion of the form, the chatbot doesn't ask the user which date he or she wants, since this date was already specified at the beginning.  

Now we will build a story that shows the optimal case for filling the form.

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

In order to fill the form in a dialog, we write the Python class HotelForm within actions.py. This class inherits from the class FormAction and implements the four methods name, requiered_slots, submit and slot_mappings. We describe the purpose of each method and its implementation below.

1. Name

This method simply returns the name of the form as defined within the domain. This allows Rasa to perform *mapping between the Python class and the form defined within the domain.

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

2. Required slots

This static method returns all mandatory fields/slots of the form as a list. The order of the list also defines the order in which the chatbot will ask the user for the mandatory fields. In this example, the number of people, the date of arrival, the number of nights and the room type are the required fields of the form.

It would also be possible to dynamically define fields as mandatory based on the user’s input. A use case might be the indication whether the user needs a crib - if children sleep in the room. If no child sleeps in the room, this question is unnecessary and therefore the chatbot doesn't ask it.  In our example, however, we don't include dynamic mandatory fields and therefore the code looks like this:

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

To use the slots we define them under slots within the domain in the domain.yml. The definition of a slot specifies the name, the data type and in special cases the possible values of the slot. In this example we only show the slots number_of_persons and room_type.

For the two other slots, we need to select a correct data type and then the procedure is the same as for number_of_persons. The slot room_type is an example of a slot with a limited choice of values. At this point the user has the choice between two variants, which are displayed in the chat with the bot as two buttons.

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

Questions for slots

To complete the changes associated with this method we still need to prepare the questions that the FAQ chatbot will ask to fill the slots. We must also create these within the domain (file: domain.yml). They have to be set up according to the fixed structure utter_ask

utter_ask_<slot_name>.

A special element is the slot room_type. In order to provide the user with only the two selection options as buttons, we create this response as follows:

</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. Submit

The submit method is executed when the form is completely filled. It may serve to save the reservation. This example only executes the action utter_submit. This action prints the user’s input and thus confirms it.

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

In order to execute the response utter_submit, it must be defined within the domain. By specifying the name of the slot within the response text, the current value can be output. For example, if we want to output the value of room_types, we do as follows:

"We have received the reservation for a {room_type} suite."

Within a response any number of slots can be output with their current value.

4. Slot_mappings

Before we define the last method of the class called slot_mapping, we have to add another intent to the domain. This is the intent that the user executes to fill the form. For this we add the entities number, data, days and room_type and the intent inform to the domain.

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

Now we add sample contents with the respective entities under data/nlu.md. The entity room_type does not need this, because it is filled by a choice of two buttons. We recommend to provide at least 5 examples per entity for a correct training.

    
  - intent: request_room 
      examples: |
       - There are [4](number) of us
       - We are looking for a room for [2](number)
       - The [03/05/2020](date) is my date of arrival
       - On [04/15/2023](date)
       - [4](days) days 
       - I will be in the hotel for [1](days) day   
    

We added the entities and slots to the domain and set up the intents within data/nlu.md for the training. Now the last method is the mapping from entities to slots, in this example a very simple version. We do the mapping with the method form_entity of the class FormAction. The entity and a list of possible intents is passed as parameters. We map within the dictionary, which the method returns.

    
    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"])],
                }
    

Executing the Rasa FAQ Chatbot

To execute the chatbot correctly, we now start the action server of Rasa. Actions created by the developer are executed within this server. We can create even more complex actions within Rasa. For example, we can integrate external interfaces and use them within actions. Thus, an error within an action does not lead to the termination of the bot itself, since it is executed independently.

We start the server with the command rasa run action. With the parameter –p

<port>

it is also possible to specify an alternative port if it differs from the standard port 5055.

If you now start the chatbot with rasa shell, filling the form won’t work. The reason for this is that in the configuration file endpoints.yml we have to link the two servers, the action server and the chatbot itself. Therefore we have to add the initially commented lines:

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

Now we can use the chatbot after a training via rasa train using rasa shell. Below we show an example for filling the dialog.

    
    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 = Intention of the user. For example, if a user enters "show me yesterday’s technology news", the user’s intention is to retrieve a list of technology headlines. We name the intentions, often using a verb and a noun, such as "showNews".

*Entities/Entity = In data modeling, an entity is a clearly identifiable object which possesses information that we want to store or process.

*(Data-) Mapping = The process that maps data elements between different data models. Data mapping is a first step for various information integration tasks.

Prospects

In this tutorial we have built a Rasa chatbot for answering simple FAQs and making a reservation.

This implementation does not yet include error handling. So far, the bot can’t react to unexpected questions. Also, the user can’t ask any questions during the reservation. This chatbot doesn’t allow to fill slots through external interfaces or based on dependencies either.

Still, for all these problems Rasa can offer a solution to create the optimal chatbot for each situation.


You want to see more?

Featured posts

Show more
No spam, we promise
Get great insights from our expert team.