When creating bots you have to converse with the users and maintain context of the conversation so you can understand the intent of the users, do the necessary actions and provide users with the information they need.
Doing it all by yourself is a large undertaking as it involves doing Natural Language Process (NLP) and providing yourself with some easy mechanism to improve your conversation models. To solve this problem various services exist e.g. Wit.ai and LUIS. In this tutorial we will look into how to use Wit.ai to create conversation models and use them in Elixir.
This can be used in your messaging bot, IoT device or any other project where you want to take some actions based on conversing with user.
In previous tutorial we learnt how to use Microsoft bot framework to create messaging bots. In this post we will see how we can use Wit.ai to recognize what the user wants to ask and provide them with the correct information.
We will create a bot that tells us the weather of a location that user punches in.
Wit.ai provides a REST API, I created elixir_wit library to talk to Wit.ai API and also provides macros and other helper functions to make it easier to conduct a conversation.
Create story in Wit
First go to Wit.ai and create an app called WeatherBot. Once you create the app you have to add a story. Story in Wit is an example conversation that you think your bot will have with the user. In the story you define what information to extract from the conversation, what actions to run at server and what to reply back with to the user. You can add multiple stories as long as they don't conflict with each other.
Add the story as below
Here we expect the user to say something like "Whats the weather in London?" where London is the name of the city we need to extract. Thankfully Wit already has basic entities like location, datetime, email etc. which it can recognize and extract. Then we have to merge that location in our context and call
fetch_weather action which should add temperature in the context. At the end we reply back to the user with the temperature information we got.
Creating a bot project
You can clone the echo_phoenix_microsoftbot to bootstrap your project. This already contains the echo bot code to communicate with Facebook as discussed in previous tutorial. We will change it instead to send the message from user to Wit and respond to user based on responses from Wit.
Updating message controller
EchoBot.MessagesController module receives message from the user. We will change it to send that message to Wit and responding back just with 200 OK.
Here we create a context map which contains the message id, the user who sent the message and to whom it was sent i.e the bot. We need this information to reply back to the user with the weather information once we figure it out. We spawn a new process that calls Wit API and runs various actions as defined in our story.
Wit action module
Wit provides the
converse API which is responsible for returning back what the bot should do next. The next step can be either answering to the user, performing an action, merging information extracted from the user message in context or waiting for further requests.
elixir_wit provides a helper function called
Wit.run_actions to which you pass in the module which implements the default and custom actions and it will conduct the conversation for you.
The default actions are
say: Called when the bot should send some message back to user.
merge: Contains information extracted from user message that you need to merge into the context.
error: Called when some error occurs. You can do cleanup here. We will just log.
The custom actions are the ones you define in your story which in our case are
fetch_weather to get weather information and put it in context and the
story_ended action which signifies that a story ended and we will just log here. You can leave out
story_ended action from your story and custom action, I usually add it in case I have some locally maintained state of conversation, I can clean it up here.
Writing the Wit action module
For this project create a module
EchoBot.WeatherConversationAction and call
use Wit.Actions which will add the necessary macro to create custom actions and other things required behind the scene. You have to define
error/3 functions in the module, otherwise the project will throw a warning.
You will use
defaction macro to define a custom action, its name should match the action defined in the Wit story.
Let's go over the functions one by one
- say takes in the session id, context and the converse response in message. We will extract the
msgIdfrom the context that we created in
Then we extract the message from converse response that Wit wants us to relay to user, lastly we just call the Microsoft bot framework client to send the message to user.
- merge takes in the same parameters as say. It expects the updated context to be returned from the function. As we only have one story and we know we need to return a merged context with
loci.e location extracted from the message. We get the location from the converse response, add it to the context and return it.
- error takes in the session id, context and error. We log the error here.
Next step is to define the
story_ended actions. Custom actions take in the session id and context and are expected to return the updated context.
fetch_weather we get the location from context (that we earlier put in) and call the Open Weather Map API to get the weather information and add the
temperature in the context in fahrenheit.
That is all whats required to use elixir_wit. Make sure to see the EchoBot.WeatherClient module to see how to communicate with the Open Weather Map API. Deploy the project to heroku or any provider you like and see your bot tell the weather. You can find the complete project on github.
As you can see from the conversation below that Wit.ai is flexible. User can ask about weather in multiple ways and even though we only added one instance of conversation in story but it is smart enough to figure out what the intent of the user is and it still works. This is what makes these language models good as you don't have to think about all the scenarios.