In the last chapter we implemented a virtual shopping assistant bot to interact with the users’ offers. Athough the structured messages make the conversation easier by allowing users to quickly make selections instead of typing a message, it doesn’t make our bot conversational.
In our example, users only have to select two options, gender and size, to see matching t-shirts, but what if we have more options. Instead of exchanging multiple messages to make a selection, what if a user can simply type “I am looking for large size t-shirts for men,” and our bot responds with matching t-shirts. Or, what if a user types, “I’m looking for a men’s t-shirt,” and then we can guide them to select a size. Instead of having a predefined workflow, a conversational interface can allow us to change our interaction based on a user’s message. This not only provides a better user experience to our customers, but also makes the interaction more human like.
In this chapter, we will learn how to make our bot smarted and conversational. We will learn about wit.ai, which offers a natural language processing API to convert text and voice into actionable data, and also update our bot engine to use wit.ai to process a user’s text messages.
Natural Language Processing uses software to analyze, understand, and synthesize language that humans use. It allows human machine interaction using natural language by enabling machines to derive meaning for the natural language. Natural language processors are used for many things like translation, sentimental analysis, automated summarization of books/text, and information extraction.
There are many libraries and tools availale for natural language processing, and wit.ai is one of them. Wit.AI is an AI start-up that aims to help developers with NLP tasks through the API. Facebook acquired it in January 2015. At the F8 conference in April 2016, Facebook introduced the major update to their platform and deployed their own version of the Bot Engine.
Wit.ai offers a SaaS platform that allows developers to create conversational interfaces using natural language processing. Wit.ai works with either text or speech inputs, helps developers manage conversational intent, and allows custom business logic to be implemented using JavaScript.
The platform is free for commercial and noncommercial use and encourages the creation of open applications in 39 different languages. Wit.ai can help you parse a message into structured data (Understand) or predict the next action your bot should perform (Converse). While at first glance it may look easier to just use scripts, AIML, or slot-based workflow templates to build bots, we believe that these approaches soon lead to bottlenecks.
A cool feature in Wit.ai is that you can test and talk to your bot without leaving the Wit Console. Press ~ or click on the launcher at the bottom-right corner of the screen to start a conversation with your bot.
Stories in Wit are conversations’ instances. Bot Engines trains with these stories. Your bot must have conversations paths to start with. When you create a bot, you can train it with your most frequent stories.
At this level, Bot Engines create a machine-learning model that intentionally over-learn the stories dataset (Overfitting). The more you train your bot the more conversations are built and the machine learning will acquire stories, which lead to a better model.
Unlike rules, which are imperative approaches, stories are independent. If you have a new use case, you can add the story matching it without taking into consideration the predecessors.
Create your first story: Say "Hi, I am looking for large size men’s t-shirts"
In this story, we have taught several things:
• Steps 2 to 5: the natural language sentence “Hi, I am looking for large size men’s t-shirts” should be understood as an intent “shop”, product: “t-shirt”, size “large” and user “men”. Product, size and user are intents too, but they don’t follow the same strategy as “shop”. Strategies are explained in a forthcoming section. • Step 6: when an intent “shop” is detected, the bot send a message back to the user with the confirmation of what the user just asked for.
Note that the more expressions you validate for each value, the better it will work.
Actions serve to modify the context of a conversation when you need to predict the next action. They also help Wit train efficient models before you have hundreds of Stories, because they can encapsulate some business logic that requires a huge dataset to acquire.
Whenever a user converses with your bot, you will receive all of the messages in the Wit Inbox. This will help you discover your users’ expectations and logic. Wit advises developers to make the first version of their bots as lightweight as possible.
When you check your messages in the Inbox, you can validate, or not, the result of Wit processing on a message. Each time you validate a processing, it is added automatically to your learning dataset and your bot’s model is rebuilt in real time.
Some built-in entities are already defined in the Wit AI like temporal expressions, locations, temperatures, etc. Apps in Wit are already trained to recognise these entities. It’s more practical and recommended to use them whenever it is possible. All built-in entities are prefixed by wit/.
When the entities we need are not built-in, we can create our own. The Wit AI provides three Search Strategies for entities:
In this section, we will update our bot engine to use wit.ai to process a user’s text messages. We will use Wit.ai’s Node.js SDK.
Let’s start by installing and adding the SDK to package.json.
npm install --save node-wit
To talk to the Wit API, our bot engine will need a Wit server access token. This is available from the Wit app’s setting page. Create a new environment variable, WIT_TOKEN
, in Heroku and set the value as our server access token.
Next, we update bot-engine.js to use Wit.ai for processing all incoming text messages from our bot users.
Add the following code to bot-engine.js:
const
Wit
=
require
(
'node-wit'
).
Wit
;
const
WIT_TOKEN
=
process
.
env
.
WIT_TOKEN
;
const
actions
=
{
send
({
sessionId
},
{
text
})
{
return
postMessage
(
sessionId
,
{
text
:
text
});
},
getMatchingProducts
(
request
)
{
const
{
sessionId
,
entities
}
=
request
;
const
newFilter
=
{};
if
(
!!
entities
.
size
&&
entities
.
size
[
0
]
&&
entities
.
size
[
0
].
value
)
{
newFilter
.
size
=
storeApi
.
getSizeValue
(
entities
.
size
[
0
].
value
);
}
if
(
!!
entities
.
gender
&&
entities
.
gender
[
0
]
&&
entities
.
gender
[
0
].
value
)
{
newFilter
.
gender
=
storeApi
.
getGenderValue
(
entities
.
gender
[
0
].
value
);
}
return
sendMessageBasedOnFilter
(
sessionId
,
newFilter
);
}
};
const
wit
=
new
Wit
({
accessToken
:
WIT_TOKEN
,
actions
});
Here, we imported the Wit SDK and created a new constant for a Wit server access token. Then we defined the actions object, which has an action name as key, and an action function as the value. Our action object has a send action, which is a mandatory action that takes request and response parameters. We also have our custom getMatchingProducts
action, which we defined in our Wit.ai stories. A custom action takes only a request parameter.
Our send action takes the response text from Wit and sends it to the user using the existing postMessage
function. Action functions should return a promise, so we will update the postMessage
to return a promise.
The getMatchingProducts
action will be invoked by Wit.ai if the user message matches the story to show matching products. Wit.ai will send a list of entities as part of the request. We will check if entities size and gender are defined and have a value. Then we call the sendMessageBasedOnFilter
function to take action based on the new filter value.
We also construct a new instance of Wit by passing our server accesss token and actions. We will use this to process incoming text messages.
Next, we add the following functions to the bot-engine.js:
sendMessageBasedOnFilter
=
(
recipientId
,
newFilter
)
=>
{
return
updateUserFilter
(
recipientId
,
newFilter
)
.
then
(
filter
=>
{
if
(
filter
.
size
&&
filter
.
gender
)
{
sendMatchingProducts
(
recipientId
,
filter
);
}
else
if
(
filter
.
gender
)
{
sendSizeOptions
(
recipientId
);
}
else
{
sendGenderOptions
(
recipientId
);
}
});
}
const
sendGenderOptions
=
(
sender
)
=>
{
postMessage
(
sender
,
buildButtonTemplateMessage
(
'What are you looking for ?'
,
getGenderOptionButtons
()));
}
const
handleTextMessage
=
(
sender
,
text
)
=>
{
wit
.
runActions
(
sender
,
text
,
{}
).
then
((
context
)
=>
{
console
.
log
(
'Context recieved from wit'
,
context
);
}).
catch
((
error
)
=>
{
console
.
error
(
'Failed to run wit action'
,
error
);
})
}
The sendMessageBasedOnFilter
will take the new filter value and update user session data. After updating session data, we will send a response message to the user based on current filters. If the user has selected both gender and size, we will send the matching products to the user using the sendMatchingProducts
function. If the size or gender is missing, we will prompt the user to make a selection by sending them the options using the sendSizeOptions
and sendGenderOptions
functions.
We also moved the logic to send gender options to the user in a separate function, sendGenderOptions
, to allow reuse without duplication. We implement the handleTextMessage
function, which will pass all of the incoming text message to the Wit runActions
function for processing. The runActions
function take following parameters:
In our bot, we are using Redis for session management, and we use sender’s id as a unique key and store the session state as a value. So in our case, we can use a sender’s id as a sessionId
to uniquely identify a user, and since we manage our session outside our Wit interaction, we can simply send an empty object as the context.
Finally, we update the handleIncomingMessage
function to pass all of the incoming text message to the handleTextMessage
function for processing.
const
handleIncomingMessage
=
(
entries
)
=>
{
entries
.
forEach
(
function
(
entry
)
{
entry
.
messaging
.
forEach
(
function
(
event
)
{
let
sender
=
event
.
sender
.
id
if
(
event
.
message
&&
event
.
message
.
quick_reply
)
{
handleQuickReplies
(
sender
,
event
.
message
.
quick_reply
);
}
else
if
(
event
.
postback
)
{
handlePostback
(
sender
,
event
.
postback
);
}
else
if
(
event
.
message
&&
event
.
message
.
text
)
{
handleTextMessage
(
sender
,
event
.
message
.
text
);
}
});
});
}
Since the new sendMessageBasedOnFilter
function is responsible to update user session data and send a response to the user based on their selection, we can update handleQuickReplies
and handlePostback
to use the sendMessageBasedOnFilter
function. Our previous implementation always expected the user to select a gender before selecting a size. Using the sendMessageBasedOnFilter
function not only removes some duplicate code but also allows us to handle the user selection without enforcing this workflow.
const
handleQuickReplies
=
(
sender
,
quickReply
)
=>
{
if
(
quickReply
&&
quickReply
.
payload
)
{
const
value
=
JSON
.
parse
(
quickReply
.
payload
);
sendMessageBasedOnFilter
(
sender
,
value
);
}
}
const
handlePostback
=
(
sender
,
postback
)
=>
{
const
{
payload
}
=
postback
;
if
(
payload
===
'GET_STARTED_BUTTON_CLICKED'
)
{
sendWelcomeMessage
(
sender
);
}
else
if
(
payload
===
'START_NEW_SEARCH'
)
{
session
.
setData
(
sender
,
{});
sendGenderOptions
(
sender
);
}
else
{
const
value
=
JSON
.
parse
(
payload
);
sendMessageBasedOnFilter
(
sender
,
value
);
}
}
Also, since our action functions should return a promise, update the postMessage
function to return the promise.
const
postMessage
=
(
recipientId
,
message
)
=>
{
return
axios
.
post
(
FB_MESSENGER_URL
,
{
recipient
:
{
id
:
recipientId
},
message
})
.
catch
(
function
(
error
)
{
console
.
error
(
`Unable to post message to Facebook
${
message
}
`
,
error
);
});
}
In our store-api.js, we have defined gender and size options, each with a label to display to the user, and the value that is expected by our store API. In the user session, we store the value of size and gender instead of the labels. When a user sends a text message to search for t-shirts, they will use a value for gender and size, which will match the labels for these options.
For example, if the user enters the following text, “I am looking for medium size women’s tshirt,” the entities size and gender will have a value ‘medium’ and ‘women’ respectively. We need to map these values to the values our backend expects, which in this case will be ‘M’ and ‘W.’
So, let’s add the following two methods to our store-api.js to do the conversion.
const
getGenderValue
=
(
label
)
=>
{
const
selectedGender
=
genders
.
find
(
gender
=>
gender
.
label
.
toUpperCase
()
===
label
.
toUpperCase
());
return
selectedGender
?
selectedGender
.
value
:
null
;
};
const
getSizeValue
=
(
label
)
=>
{
const
selectedSize
=
sizes
.
find
(
size
=>
size
.
label
.
toUpperCase
()
===
label
.
toUpperCase
());
return
selectedSize
?
selectedSize
.
value
:
null
;
};
The getGenderValue
and getSizeValue
are case insensitive, so if the user enters ‘men,’ ‘Men,’ or ‘MEN', the function getGenderValue
will correctly map it to ‘M.’
Remeber to make these functions accessible by other modules by updating module.exports.
module.exports = { getSizes: sizes, getGender: genders, getGenderValue, getSizeValue, retriveProducts }
Now that we have updated our bot engine, we can deploy the changes to Heroku.
Now when a user sends a message like ‘Hi’ to our bot, our bot will respond with a greeting message as defined in our Wit.ai story.
If a user sends a message to search for t-shirts for a given gender and size, our bot will directly show matching t-shirts.
If a user sends a message to search for t-shirts without selecting both gender and size, we can reuse the existing structured message to prompt the user to make a selection.
In this chapter we used Wit.ai for natural language processing. Another popular framework to build conversational apps is API.AI.
API.AI was acquired by Google in 2016 and works similar to wit.ai. A bot or app sends input to the API.AI platform, API.AI matches the input to an intent, converts it to actionable data, and sends back a JSON response. To perform any custom business logic, we can provide an optional webhook to an external web service. While Wit.ai offers speech recognisation and can take both text and voice input. API.AI only accepts text input. However, we can use services like Google Speech API to convert speech to text.
Similar to wit.ai Stories, in API.AI we define agents for our app with intents, entities, actions and reponses. Both API.AI and wit.ai offers built-in entities, however API.AI also has the concept of domains. As defined by the API.AI team, domains are predefined knowledge packages. Think of them as predefined intents with entities, actions and reponses, and everything required to directly use them with our bot without any training data. There are various built-in domains for common topics like news or weather for example. You can allow developers to add the ability like reading news or making phone calls to their conversation apps without writing any code.
Another interesting feature API.AI offers is easy integration with various bot platforms like Facebook Messenger, Amazon Alexa, and Slack. With one-click integration developers can build a fully conversational bot using the platform.
You can read more about API.AI here.
While in development only the administrators of the Facebook page associated with our bot can chat with the bot. We have to make the bot public to make it accessible to everyone. Our bot will have to go through a review process by the Facebook team before it will be made publicly accessible.
In the app dashboard, go to the ‘App Review’ page. This page will show the current status of the app and will allow us to submit our bot for review and make it public. Before the review process, it will show that the app is in development and unavailable to public.
Facebook automatically approves our bot to access information like a user’s public profile, email and list of friends. Some features or intergations need approval before public usage. Click on the ‘Start a Submission’ button, and select and add the permissions required for the bot. For our virtual shopping assistant bot, we need pages_messaging
permission.
Once you have selected the required permissions, click on ‘Edit Notes’ corresponding to each of these permissions and fill out the corresponding forms. The App Review page will also show a list of things we need to complete before we can submit the app for review, like adding an app icon and privacy policy URL. Complete all of the items in the list and click on the ‘Submit for Review’ button.
Once submitted, the Facebook team will review the app. We can check the status of the approval on the App Review page.
You can read more about the review process here: here
Once the bot is approved and made public, you can promote the bot in various ways. Users can search the bot in Facebook Messenger or click on the ‘Send Message’ button on the Facebook page to start a conversation with the bot. Make sure you have the ‘Send Message’ call to action configured for your page.
You can add ‘Send to Messenger’ Or ‘Message Us’ button to your website which will allow the user to launch and start a conversation with your bot. You can also share your bot Messenger link, https://m.me/[username], via email and sms.
If you have a contact number of your users and their constent to contact them, you can also send messages to them via Messenger. For this you will need to add the pages_messaging_phone_number
permission while submitting the app for review.
Lastly, If you are using Facebook ads to advertise your products or service, you can select Messenger as a destination, allowing users to directly open a Messenger chat by clicking on the ad.
You can read more about entry points here: here
After making the bot public and promoting it, you may want to know who is interacting with your bot and how are they using it. For this you can use the Facebook Analytics for Apps tool.
Go to the Facebook Analytics for Apps dashboard and select the bot from the list of apps.
The analytics will give us information about our users, like the number of active users, the number of unique users, the demographics of the users interacting with our bot, their age, gender, country, and more. We can also track events like the number of messages sent and received by our bot, or the number of times the call to action was clicked.
There are various other reports and tools Facebook offers to get insight on who is using your bot and how it is used. See Analytics for Apps for more details.
In this chapter we made our bot conversational by using the natural language processing API Wit.ai. This allows users to talk to our bot as if they are communicating with another human. In the next chapter, we will continue the conversational interface discussion and learn how we can build a voice-based conversation bot.