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:
- creates a Dialogflow object;
- synthesized voice welcomes the customer and offers to make an order;
- recognition starts, the customer places an order;
- client words are transmitted to Dialogflow, slot filling occurs;
- synthesized voice voices the order and the client's address;
- bot hangs up, the session closes.
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:
- go to http://demos05.voximplant.com/pizza/ ;
- enter the phone number (for Russia it will be 7900 ...) and click "NEXT";
- from the specified number, dial +74999384952;
- enjoy the miracle bot :)