AI sells pizza or how Voximplant is friends with Dialogflow

Recently, we presented a business solution called Dialogflow Connector: a bundle of Google Dialogflow and our Voximplant cloud. The connector can work instead of the call center operator and take complex orders. This became possible because Dialogflow allows you to get away from the usual IVR script, where the client is forced to call words from a given set to which the interactive menu can respond. The Google platform can consistently receive input from a customer and collect it together - for example, to place an order in an online store. Probably, you have already heard the buzzword “ slot filling ”, so here it is. Under the cut you are waiting for the details, as we did a bot to order a pizza - by analogy you can cut down something similar and rejoice that the times of the elevator and "eleven" are long gone ...


Phone number


To begin with, you should rent a virtual number to which the bot will “respond”. To do this, in the admin panel of Voximplant, in the “Numbers” menu, click on “Buy a phone number”.


On the new screen, click to put the switch in the “Test numbers” position - this will allow you to rent a virtual number.


Virtual numbers are very cheap and allow you to fully test any solution on our platform. Rent a room and more ...

... create a script


Now we need a cloud script, the heart of our solution. He will receive the call, recognize the speech and transfer it to Dialogflow. On the Scripts tab, click Create Script, give it a name — for example, dialogflow — and click Create. Time to write code!

Let's connect the AI module - there are methods and events for Dialogflow in it - as well as a speech recognition module and a player . Let us declare variables and the tricky function wordend , which accepts a quantity (pizzas) and an array with words ('margarita', 'margarita', 'margarite') and returns a word suitable for a case. It is necessary that the bot did not say "4 margarita", but correctly issued "4 margaritas".

require(Modules.AI); require(Modules.ASR); require(Modules.Player); let mycall = null, voice = Language.Premium.RU_RUSSIAN_YA_FEMALE, account_name = "", dialed_number = "", caller_id = "", flow, lastText = '', player function wordend(num, words) { return words[ ((num=Math.abs(num%100)) > 10 && num < 15 || (num%=10) > 4 || num === 0) + (num !== 1) ]; } 

Now we add a handler for the incoming call and the auxiliary function startASR :

 VoxEngine.addEventListener(AppEvents.CallAlerting, (e) => { mycall = e.call; mycall.addEventListener(CallEvents.Connected, handleCallConnected); account_name = e.toURI.substring(e.toURI.indexOf('.') + 1); account_name = account_name.substring(0, account_name.indexOf('.')); dialed_number = e.destination; caller_id = e.callerid; mycall.answer(); }); function startASR() { mycall.removeEventListener(CallEvents.PlaybackFinished, startASR); mycall.sendMediaTo(flow); } 

When the Connected event occurs, the handleCallConnected function is called . Actually, the whole stuffing happens in it:


Function listing:

handleCallConnected
function handleCallConnected(e) {
flow = AI.createDialogflow({
lang: "ru"
});
if (AI.Events.DialogflowResponse !== undefined)
flow.addEventListener(AI.Events.DialogflowResponse, (event) => {
if (event.response.queryResult !== undefined) {
let result = event.response.queryResult
if (result.queryText === undefined) {
if (result.languageCode !== undefined) startASR();
return
}
if (result.fulfillmentText !== undefined) {
try {
player = VoxEngine.createTTSPlayer(result.fulfillmentText, voice)
player.addMarker(-500)
player.addEventListener(PlayerEvents.PlaybackMarkerReached, startASR)
player.sendMediaTo(mycall)
} catch (err) {}
if (result.allRequiredParamsPresent == true &&
result.outputContexts !== undefined &&
result.parameters.PizzaType != undefined) {
let msg = " ";
for (let k = 0; k < result.parameters.PizzaType.length; k++) {
let count = result.parameters.count[k] !== undefined ? result.parameters.count[k] : 1
switch (count) {
case 1:
msg += " ";
break;
case 2:
msg += " ";
break;
default:
msg += count + " ";
break;
}
if (result.parameters.PizzaSize[k] !== undefined) {
switch (result.parameters.PizzaSize[k]) {
case "small":
msg += wordend(count, ['', '', '']);
break;
case "medium":
msg += wordend(count, ['', '', '']);
break;
case "large":
msg += wordend(count, ['', '', '']);
break;
}
}
msg += " "
switch (result.parameters.PizzaType[k]) {
case "margarita":
msg += wordend(count, ['', '', '']);
break;
case "fourcheese":
msg += wordend(count, ['', '', '']) + " ";
break;
case "vegetarian":
msg += wordend(count, [' ', ' ', ' ']);
break;
case "pepperoni":
msg += wordend(count, ['', '', '']) + " ";
break;
case "cheese":
msg += wordend(count, [' ', ' ', ' ']);
break;
case "chicken":
msg += wordend(count, ['', '', '']) + " ";
break;
}
if (k == result.parameters.PizzaType.length - 1) msg += " , "
else msg += " ";
}
msg = msg.slice(0, msg.length - 2);
if (result.parameters.time !== undefined && result.parameters.time != "") {
let time = new Date(result.parameters.time),
local = time.toTimeString()
local = local.split(":")
time = local[0] + ":" + local[1]
msg += ". " + time
}
if (result.parameters.deliveryaddress !== undefined) msg += ". " + result.parameters.deliveryaddress
msg += ". ?";
player.stop()
player = VoxEngine.createTTSPlayer(msg, voice)
player.addMarker(-500)
player.addEventListener(PlayerEvents.PlaybackMarkerReached, startASR)
player.sendMediaTo(e.call)
} else {
player.stop()
lastText = result.fulfillmentText
player = VoxEngine.createTTSPlayer(result.fulfillmentText, voice)
if (result.action == "pizzaorder.pizzaorder-yes") {
player.addEventListener(PlayerEvents.PlaybackFinished, () => mycall.hangup())
player.sendMediaTo(e.call)
} else {
player.addMarker(-500)
player.addEventListener(PlayerEvents.PlaybackMarkerReached, startASR)
player.sendMediaTo(e.call)
}
}
}
}
})
player = VoxEngine.createTTSPlayer("! , !", voice);
player.addMarker(-500);
player.addEventListener(PlayerEvents.PlaybackMarkerReached, startASR);
player.sendMediaTo(e.call);
mycall.record();
mycall.addEventListener(CallEvents.Disconnected, (event) => {
VoxEngine.terminate();
})
}


Application + Number + Rule


In our telephony there is the concept of "application". The application associates JS scripts with rented numbers. And even if the script is only one (our case), you still need to create an application. To do this, on the Applications tab, click Create Application, enter a name for the new application — the dialogflow-app — and click Create.


Now go to the “Rules” tab -> “Add rule”. A modal form window will appear. The name can be given by analogy - dialogflow-rule . Leave the mask as default and most importantly drag the dialogflow script from left to right. Click "Add", the modal window closes.


Click “Save”. Now the script knows what it needs to respond to any dialed number you rented. Given that we have one number, this is what we need.

To associate a number with an application, in the “Numbers” menu, click on “My phone numbers”.


Assign the application a dialogflow-app and a dialogflow-rule , then click on the check mark. Great, now our number, JS-script and rule are connected and work together.

Dialogflow agent


In terms of Dialogflow, the agent is the bot. We have already described in detail how to create your agent and how to communicate with him, but this time we suggest using our template.

You need to register at dialogflow.com and create a new agent. Do not forget to indicate the Russian language:


By default, the agent does not know any statements (intents) and is not able to do anything at all. Download the assets of our agent and load them into yours: for this, in the agent settings, go to the tab “Export and Import” -> “Import from ZIP”.


Now you have a set of sayings to order pizza! You can study them in the section "Intents"; note that the agent has helper statements like pizza.sizehelp and pizza.typehelp . Thanks to them, after greeting the pizzeria, the client may ask “What kind of pizza do you have?” And the bot will tell you what is on the menu.

Check that the second version of the API is selected in the settings on the General tab.


Finally, download the JSON of your google service account - this is done in a couple of clicks .

Snap an agent to the Voximplant application


In the admin panel of Voximplant, in the “Settings” menu, select “Dialogflow connector”.


Choose “Add Service Accounts”, download JSON and click “Add”. You will see that the JSON file appears in the Service Account list. Click “Snap”, select the application dialogflow-app and save.



Done! A bunch of Dialoglow + Voximplant is configured, all links in the chain are assembled together.

In case you were too lazy to do all these steps, you can touch our pizzeria demo:

Source: https://habr.com/ru/post/412663/


All Articles