Our first ChatBot
Bots are not a new solution. They have been on the market for many years. Currently, their importance and use in communication with customers is significantly increasing, among others due to greater and greater popularity of Internet communicators. Thanks to ChatBot connection with communication channels like Facebook, Messenger, which are used every day, we can offer our services via interface which is already known to We move tousers, and which does not differ from the way of „interpersonal” communication. These advantages of “conversational interfaces” cause that more and more companies start offering products through virtual assistants. Such trend can be clearly seen for example in banking sector.
What technologies are being used to build bots at the moment? What capabilites do they have, what is the level of entry to start using this kind of tools, simplicity of use or productivity? We have decided to take a closer look.
In order to do this, we have started an experiment which is about building a ChatBot serving as an additional communication channel for our simplified system for selling insurance. Its task will be to conduct a conversation with the user which leads to selling an insurance. During conversation bot should obtain from the client all the information needed to prepare an offer and conclude an agreement, and then transfer them to sale system in order to get a quote and create policy.
Dialogue line, which we would like to implement, looks as follows:
ChatBot logic
We have decided to use Bot Builder SDK (v3) library. It allows to create bot logic in two languages: C# (.NET) and JavaScript (NodeJS). For a start we have chosen the second option.
Before starting bot development we have to define libraries which we are going to use:
- botbuilder – framework to build bots
- restify – for creating REST interface for our bot
- request – for communicating with insurance system z API
- dotenv-extended – supporting library which allows to define environment variables in text file (.env). We will be using it only at local machine. Eventually, these variables will be injected into Azure runtime environment.
package.json:
...
"dependencies": {
"botbuilder": "^3.15.0",
"dotenv-extended": "^2.0.0",
"request": "^2.88.0",
"restify": "^7.2.1"
},
...
We will start our work with creation of few supporting functions to communicate with sale system’s API – in order to fetch parameters of insurance product, price the insurance offer, purchase the policy and downloading confirmation document:
backend.js:
[...]
function getPrice(params, auth) {
return new Promise(function (resolve, reject) {
request({
method: 'POST',
uri: `http://${backendHost}:8081/api/offers`,
headers: {
'content-type': 'application/json',
'Authorization': 'Bearer ' + auth.accessToken
},
body:
JSON.stringify(params)
},
function (error, response, body) {
if (error) {
reject(error);
} else {
console.log(body);
resolve(JSON.parse(body));
}
}
);
});
}
[...]
To initiate bot we have to define a connector and specify the place for storing conversation state. To make it easier, we are going to use in-memory storage.
bot.js
[...]
const connector = new builder.ChatConnector({
appId: process.env.MicrosoftAppId,
appPassword: process.env.MicrosoftAppPassword
});
const inMemoryStorage = new builder.MemoryBotStorage();
const bot = module.exports = new builder.UniversalBot(connector, getStartingSteps()).set('storage', inMemoryStorage);
[...]
We may now get straight to the point which is developing bot’s dialogue capabilities. Starting point is getStartingSteps function indicated during initialization. It returns an array including two functions. The first one includes definition of welcome message and question directed to user by a bot. The second one specifies the reaction to response received.
bot.js:
[...]
function getStartingSteps() {
return [
function (session) {
session.send('Hello in ASC LAB Insurance Agent Bot!');
builder.Prompts.choice(
session,
'What do you want? (currently you can only buy insurance)',
[IntentType.BUY_POLICY],
{
maxRetries: 3,
retryPrompt: 'Not a valid option'
});
},
function (session, result) {
[...]
const selection = result.response.entity;
switch (selection) {
case IntentType.BUY_POLICY:
return session.beginDialog('policy-sell');
}
}
];
}
[...]
The key element of second function is a line including a command:
return session.beginDialog('policy-sell');
It causes starting a new ‘policy-sell’ dialog defined with the following command:
bot.dialog('policy-sell', getPolicySellSteps());
and getCreatePolicySteps function returning the array of functions defining next steps of dialog – just like in previous example.
Responses sent by the user can be also saved in the session state:
function getCreatePolicySteps() {
return [
function (session) {
session.send('OK, lets sign papers!');
builder.Prompts.text(session, 'What is your first name?');
},
function (session, results, next) {
session.userData.firstName = results.response;
next();
},
[...]
Thanks to that, we can refer to them in further part of the conversation to create and price an offer on their basis, for example:
function _addCalculatePriceSteps(product, steps) {
steps.push(function (session) {
session.send("Calculating price. Please wait...");
const params = {
"productCode": product.code,
"policyFrom": session.dialogData.policyStart,
"policyTo": session.dialogData.policyEnd,
"selectedCovers": product.covers.map(c => c.code),
"answers": session.dialogData.answers
};
console.log(params);
backend.calculatePrice(params).then(function (offer) {
session.send("Your insurance will cost %s EUR. Offer ID: %s", offer.totalPrice, offer.offerNumber);
session.conversationData.offer = offer;
builder.Prompts.choice(
session,
'Are you interested?',
['Yes', 'No']
);
});
SDK also has a functionality which allows to send files to the user. Thanks to it, at the end of the conversation, bot may send a certificate confirming creating policy in PDF file to the client:
backend.createPolicy(params).then(function (policy) {
console.log(policy);
session.send('Your policy has been created: %s', policy.policyNumber);
waitForDocumentCreation()
.then(function () {
return backend.getPolicyAttachments(policy.policyNumber)
})
.then(function (response) {
console.log("processing attachments ");
if (response != null && response.documents != null) {
console.log(response.documents);
response.documents.forEach(function (doc) {
sendInline(session, {
content: doc.content,
contentType: 'application/pdf',
name: 'policy.pdf'
}
);
});
}
session.endDialog();
});
});
[...]
function sendInline(session, document) {
const msg = new builder.Message(session)
.addAttachment({
contentUrl: util.format('data:%s;base64,%s', document.contentType, document.content),
contentType: document.contentType,
name: document.name
});
session.send(msg);
}
Finally, we also have to create http interface, which will allow us to communicate with our bot:
app.js:
const restify = require('restify');
const bot = require('./bot.js');
const server = restify.createServer();
server.post('/api/messages', bot.connector('*').listen());
server.listen(process.env.PORT || 3978, () => {
console.log(`${server.name} listening to ${server.url}`);
});
The full bot code can be found in repository.
To launch bot locally you have to create .env file and define BACKEND_HOST variable (containing IP address of a host on which API Gateway of our simplified insurance selling system has been deployed) inside it and then execute the following command:
node app.js
We may test behaviour of our bot locally using Emulator created by Microsoft.
Installation on Azure
To launch our Bot on Azure all you need is a few simple steps.
In first step we create bot instance:
In order to do so, we have to specify a few parameters. The most important ones are:
- bot name – in our case: asc-insurance-bot
- place – for example, West Europe (due to geographical proximity)
- price layer – F0 option includes free messages’ bundle
- bot template: we choose NodeJS Basic because we would like to create bot’s code in JavaScript language
After a few minutes (about 2-3) bot will be created:
In the second step we store the API Gateway address of insurance system in the environment variable. In order to do so, we open the screen of web service configuration (third from the top on the screen of resource list):
We navigate to “Application settings” tab and add BACKEND_HOST variable containing IP address of the host, on which API Gateway of insurance system has been launched, and then we click the “Save” button:
The last step is to configure the continuous deployment. We start with opening the bot service configuration screen (second from the top on the screen of resource list):
We move to “Build” tab and select an option “Publish updates automatically to Azure with Continuous deployment”:
We click button “Setup” -> “Configure required settings” and provide code repository access data:
Once settings have been confirmed by clicking “OK”, an installation process, which should end within several dozen seconds, will be started automatically:
Finally, we can check correctness of the deployment. We close the window of continuous deployment configuration and open “Test in Web Chat” tab. From here we might conduct a sample conversation with our bot:
We have also tried to install our bot on Azure from the command line. Unfortunately, the current CLI version (azure-cli: 2.0.45, botservice extension: 0.4.0) does not allow to perform the operations described above.
Integration with external communication channels
WebChat
The simplest external communication channel to be configured is a chat. In order to launch it, we open channels tab:
and then in position “Web Chat” we choose “Edit” action:
We can put displayed embedding code (together with a secret key) on any website. For demonstration purposes, we will use an online HTML editor:
Slack
You can find detailed description of integration with Slack in Microsoft documentation:
After performing a few steps described in the instruction above, we may talk to our bot on slack:
Facebook Messenger
In a similar way, our bot can be integrated with Facebook Messenger.
Summary
Here are the observations, which we have made during the abovementioned experiment:
- Bot Builder SDK (v3) library that we have used, has intuitive API, which allowed us to start development quickly and easily, and let us make it working in short time.
- Azure portal allowed us to quickly run our bot in the cloud. Unfortunately, we didn’t manage to obtain the same result using the current version of CLI.
- Integrating bot with Slack is rather simple, however, it seems to be not quite stable. There were some issues like message delivery failures or breaking off the conversation.
We are aware that framework tested by us is rather new technology and we are expecting that shortcomings, which we have encountered, will soon be removed.
The next step in development of our bot will be an attempt to extend its dialog functions with the capability to understand natural language. But we will discuss it in the next article!
Robert Kuśmierek
Lead Software Engineer & Team Leader, ASC LAB