Building a Rasa Chatbot to perform movie search

Eleonora Fontana
Betacom
Published in
6 min readMay 31, 2021
Photo by GR Stocks on Unsplash

Introduction

Welcome to the second article of a series covering the Rasa framework. Today we will improve the chatbot built in the previous article which you can find at Introduction to Rasa Open Source. In particular, we would like to add a search mechanism: given a movie plot, the bot will return the top 5 movies matching it. To do so, we will use the Machine Learning model discuss in a previous article: Unsupervised NLP task in Python with doc2vec.

In the first section we will go through Rasa Custom Actions and learn how to implement them, whereas in the second section we work at the chatbot add a custom action to perform the movie search.

Before reading this article, we strongly recommend to take a look at the previous one (Introduction to Rasa Open Source) since we will refer to it a lot.

Custom actions

In Rasa a Custom Action is an action that can run any code you want. For example, you can use it to make an API call, to query a database, or to recall a Machine Learning algorithm. In this section we will learn how to add a custom action to your bot.

Once you define what the action should do, the first thing to do is to give it a name. Let’s say we call the action “action_hello_world”.

Let’s now open the actions file which is available at “..\actions\actions.py” and take a look at it. It is a Python file which contains a class for each action. You probably will find all the lines commented as by default there are no custom actions . It should show you a simple example for a custom action which utters “Hello World!”. Let’s see how to build such an action.

The first thing to do is to import all the libraries we need:

from typing import Any, Text, Dict, List
from rasa_sdk import Action, Tracker
from rasa_sdk.executor import CollectingDispatcher

Now we need to add a class for our new action. We also have to define a class method name which returns the action name, i.e. “action_hello_world”.

class ActionHelloWorld(Action):
def name(self) -> Text:
return “action_hello_world”

What the action does will then be written in the run method. It takes three parameters in input: dispatcher, tracker and domain. Here’s more details about them.

  • dispatcher is an instance of the CollectingDispatcher class which is used to generate responses to send back to the user via the utter_message method.
  • tracker is an instance of the Tracker class which represents a Rasa conversation tracker and lets you access the bot’s memory. You can get information about past events and the current state of the conversation through Tracker attributes and methods. For example, you can access slot values using tracker.get_slot(slot_name), and the most recent user message is stored into tracker.latest_message['text'].
  • domain is the bot’s domain.

The example custom action you should find in your actions.py file is the following:

def run(self,
dispatcher: CollectingDispatcher,
tracker: Tracker,
domain: Dict[Text, Any]) -> List[Dict[Text, Any]]:
dispatcher.utter_message(text=”Hello World!”)
return []

Obviously, you can add some code to your action in order to let it perform what you need. In the next section we will add a Machine Learning algorithm to it.

Now we need to add the action we just built into the actions section of the domain.yml file:

The last thing to do is to add the action to the stories and\or the rule files and to activate an endpoint for the action server in the endpoints.yml file:

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

Movie search

Recall the chatbot we built in the previous article: it is able to ask the user how they feel and if they want to search for a movie. The movie search is not actually available though. Indeed, when a user asks to look for a movie, the bot simply answers something like “This function is not available yet”.

Our custom action will be called “action_movie_search” and will return the top 5 matching movie titles of a given plot.

In the action file we will need to load the Machine Learning model. You can either build it following our article or download it from here.

At the top of the actions.py file we will need to add the following lines to import all the modules and load both the model and the dataset (the Wikipedia Movie Plots Dataset is available at this page):

import pandas as pd
from gensim.models.doc2vec import Doc2Vec
from gensim.parsing.preprocessing import preprocess_string
# Load ML model
model = Doc2Vec.load(‘movies_doc2vec’) # use the full path
# Load dataset to get movie titles
df = pd.read_csv(‘wiki_movie_plots_deduped.csv’, sep=’,’, usecols = [‘Release Year’, ‘Title’, ‘Plot’]) # use the full path
df = df[df[‘Release Year’] >= 2000]

The next step is to find movies that match the user input description. To do so, we start by preprocessing the input to perform embedding on the new sentence. It will give the sentence the vector representation needed to apply the model to it and look for similar vectors. We lastly select the top 5 most similar movies and return their titles in the bot answer.

The run method of the action we will then look as follows:

def run(self, 
dispatcher: CollectingDispatcher,
tracker: Tracker,
domain: Dict[Text, Any]) -> List[Dict[Text, Any]]:
# get last user message
userMessage = tracker.latest_message['text']
# find matching movies
new_doc = preprocess_string(userMessage)
test_doc_vector = model.infer_vector(new_doc)
sims = model.dv.most_similar(positive = [test_doc_vector])
# get the top 5 matches
movies = [df['Title'].iloc[s[0]] for s in sims[:5]]
# send the bot answer to the user
botResponse = f"I found the following movies: {movies}.".replace('[','').replace(']','')
dispatcher.utter_message(text=botResponse)
return []

We now have to add “action_movie_search” to the domain file.

Let’s take a look at the stories.yml file and try to guess what we need to change. All the stories where there is an intent “affirm” after the action “utter_movie”, have the “utter_not_yet” action after that. So here’s what we need to do:

  1. Delete the action “utter_not_yet” from both the stories and the domain files;
  2. Add the action “utter_plot” in the domain file, with a text such as “Okay! Write the plot of the movie title you are looking for.”;
  3. Add the intent “movie_plot” in both the nlu and the domain file, writing some movie plot examples.

We can now edit the stories, replacing the sub-path

- action: utter_movie
- intent: affirm
- action: utter_not_yet

with

- action: utter_movie
- intent: affirm
- action: utter_plot
- intent: movie_plot
- action: action_movie_search

Lastly, remember to activate the action endpoint via the endpoints.yml file and then train the chatbot as explained in the previous article. Once the model is trained, you can chat with the bot and try to improve it.

Here’s an example of a conversation with the bot we just built:

As you can see, the correct movie title “Wreck-It Ralph” is one of the movies returned from the bot.

Conclusion

In this article we went through Rasa Custom Actions and you should now be able to write them on your own. In the next article we will discuss the chatbot Pipeline, Rules, Policies and how to customize them to better control and steer the conversation based on user input.

The chatbot code is available here. Enjoy!

--

--