Persisting Data for a Smarter Chatbot

With Watson Conversation Service and Cloudant

In a previous blog post Josh Zheng showed us how to build a Slack bot that uses Watson Conversation and a 3rd party API called Spoonacular to provide recipe suggestions and instructions based on ingredients and cuisines.

In this blog post we’ll show you how we improved the Watson Recipe Chatbot by integrating Cloudant to cache 3rd party API calls, provide a more personal experience to the user, and perform analysis on the conversations users have with the bot.

The source code and setup instructions for the new version of the Watson Recipe Bot can be found at https://github.com/ibm-cds-labs/watson-recipe-bot-python-cloudant.

In Review: Watson Recipe Bot

Before we start, let’s review how the Watson Recipe Bot works.

  • First, you start a conversation with the sous-chef bot by sending it a direct message:
  • Next, you tell the sous-chef what you would like to do. You can say you want to cook something, you want to eat something, etc:
  • Next, you reply to the sous-chef whether or not you would like to use specific ingredients (if you say no sous-chef will ask you to specify a cuisine):
  • Next, you specify the ingredients or cuisine you would like to cook with, and sous-chef will return a list of recipes:
  • Finally, you select the recipe you’d like to cook, and sous-chef will tell you how:

So, what’s actually going on here?

How It Works

Here is a high-level architecture diagram:

Here’s what’s happening behind the scenes:

  1. sous-chef is a Slack bot that has been registered with a Slack team. Slack users send direct messages to the sous-chef bot.
  2. Messages are immediately sent from Slack to the application over a WebSocket connection.
  3. The application forwards the message along with the current state of the conversation for the active user to Watson Conversation.
     Watson Conversation uses natural language processing to determine the intent of the message. For example, is the user asking to cook something? Did the user specify a cuisine?
  4. Watson Conversation returns a response to the application which includes the identified intent and any other information related to the current step in the conversation.
  5. The application uses the response to determine it’s next step. For example, if the user specifies a cuisine the application will query Spoonacular (a 3rd party API) for a list of recipes.
  6. Spoonacular returns a response to the application.
  7. The application formulates a message and sends it to Slack.
  8. Slack delivers the message to the user.

Adding Persistence with Cloudant

The new version of the application uses Cloudant to store users, ingredients, cuisines, and recipes.

Here’s how it’s different:

  1. A document for each user that interacts with the bot is stored in Cloudant.
  2. A document for each ingredient, cuisine, or recipe is also stored in Cloudant.
  3. The application looks for ingredients, cuisines, and recipes in Cloudant before querying Spoonacular.
     If one of these already exists in Cloudant, then it is returned immediately and there is no need for the application to query Spoonacular.
  4. Every request for an ingredient, cuisine, or recipe made by a user is stored in Cloudant.
  5. In addition, the number of times the user has requested a particular ingredient, cuisine, or recipe is stored in the user document.
  6. A new Watson Conversation intent has been created that allows a user to request their favorite recipes.
     The application queries Cloudant to find the recipes requested most by the user.

How It Works Now

Here is what the new architecture looks like:

Most of the application logic has remained the same. We have simply added a Cloudant instance and a little bit of code.

Let’s rehash some of the benefits of the new implementation:

  1. Reduce 3rd party API calls by caching entities in Cloudant.
  2. Provide a more personal experience for users (favorite recipes) using the data in Cloudant.
  3. Perform analysis on ingredients, cuisines, and recipes.

Let’s dive into these points in a little more detail…

Reduce 3rd Party API Calls

Many 3rd Party APIs limit the number of times you can call them, or charge you based on the number of API calls you make. In our example
 we make the following calls to Spoonacular:

  1. Request a list of recipes based on an ingredient or comma-separated list of ingredients.
  2. Request a list of recipes based on a cuisine.
  3. Request a specific recipe and the steps required to cook it.

Multiple users may request the same recipe, cuisine, or ingredient from Spoonacular, and if the data we receive from Spoonacular never changes, or changes infrequently, we would be making the same API calls over and over again. By querying Cloudant first, and storing the results of the API calls in Cloudant, we can reduce the number of API calls made to Spoonacular. This could result in cost savings and help us avoid rate limits.

We could also see performance improvements. Your application performance may suffer if the 3rd party API experiences high load from other users. In many cases you will experience lower latency querying your Cloudant instance vs. a 3rd party API.

Note: If you are using a 3rd party API consult their Terms of Service to make sure you are allowed to cache results. Spoonacular does allow caching as part of their Terms of Service.

Provide a More Personal Experience

As users request ingredients, cuisines, and recipes we can learn about each user’s habits. Do they cook Chinese food more than Italian food? Do they cook with chicken more than beef? What are their favorite recipes?

With just a few lines of code we can track every ingredient, cuisine, and recipe requested by a user. We can then use that information to personalize a user’s experience. The application currently stores each ingredient, cuisine, and recipe selected by a user in a "type": "user" document in Cloudant. Here’s an example:

Using this information we can give a user their favorite recipes sorted by count. We created a new intent and dialog path in the Watson Conversation workspace to support favorites:

When the application receives the favorite_recipes intent from Watson Conversation, it immediately looks up and returns the recipes stored in Cloudant:

This is a very simple example, but with more data we could make the bot even better. You can see in the user document that this user has requested Chinese food more often than Italian. We also store every request for a cuisine, including the date of the request. It looks something like this:

We could look for patterns in this data. Maybe the user makes a request for Chinese food every Friday. If the user messages the bot on a Friday it could immediately ask if they want Chinese food.

By persisting a user’s behavior we can make simple improvements to our application to provide a better, more personal experience for our users.

Analyzing the Data

Now that we are storing every user request for an ingredient, cuisine, and recipe, we can do some simple analytics on our data. The new application automatically creates views in Cloudant that allow you to find the most popular ingredients, cuisines, and recipes requested by all users. The views are a part of the by_popularity design doc and are named ingredients, cuisines, and recipes.

For example, you can easily find the most popular cuisines by requesting the cuisines view in the by_popularity design doc. The view looks like this:

You can query the view in your browser at the following URL:

https://CLOUDANT-USERNAME.cloudant.com/watson_recipe_bot/_design/by_popularity/_view/cuisines?group=true

Here’s an example of the output:

Here you can see that your users as a whole are requesting Italian recipes more often than Chinese, unlike the user we looked at earlier.

How about finding out what days people tend to use the bot more? The application includes a design doc to do that as well. The following recipes view of the by_day_of_week design doc shows you the total number of recipes requested by day:

Here’s an example of the output:

These are fairly simple examples of the analysis you can perform on this type data, but once you start storing the data the sky is the limit. You could correlate weather information with ingredient or cuisines requests. You could determine which cuisines or ingredients result in the most drop off and, hence, the least desirable recipes. You can do this analysis in Cloudant or import your data into other tools like Apache Spark or dashDB for further analysis.

Performing analysis on your data can help you improve your product, which can lead to better experiences for your users and reduce your user drop off rate.

What’s Next?

In this blog we showed you how we could make simple, but valuable improvements to the Watson Recipe Bot by adding persistence with Cloudant and a little bit of code. We showed you how we could cut down on 3rd party API calls, personalize the user’s experience, and perform some basic analysis on the data.

Try a deployment for yourself. The README at https://github.com/ibm-cds-labs/watson-recipe-bot-python-cloudant has step-by-step instructions for completing your first deployment. Alternatively, if you’re already using Watson Conversation in your applications and want to use Cloudant to persist this data, we hope the source code in the repo above gives you some ideas to improve your apps. Happy coding!