At Alexa Dev Days, my colleagues and I lead a voice design workshop that focuses on voice-first experiences and shows how to capture a set of slot values through a turn-by-turn conversation with a user. I was recently asked, "I'm making a skill that recommends products. Can I change the question that Alexa asks to fill my slot(s) and can I make an optional slot required?"
Because dialog management requires you to predefine a set of prompts for each required slot, it seems like it wouldn’t be possible to dynamically change Alexa’s response. However, using this.emit(':elicitSlot', slotName, prompt, reprompt);
in conjunction with the dialog management state machine can override prompts and determine which slot is going to be prompted for next, including optional slots.
This post will walk through how to accomplish dynamic slot elicitation by overriding predefined prompts and elicited slots with dialog management, using a skill that recommends products as the example. Let's start by taking take a look at how you could use elicitSlot to achieve this.
Before you dive in, be sure to read out recent blog post on taking control of the dialog management state machine, which explains how to “hook into” the state machine.
For our example, we are going to recommend a computer to the customer based on the slots the customer fills conversing with the skill. Let's name our intent, RecommendComputerIntent. Our custom slots will include, purpose, typeOfComputer, typeOfGamer, and budget, where purpose, typeOfComputer and budget are required and typeOfGamer is optional.
Our custom slot values are:
purpose | typeOfComputer | typeOfGamer | budget |
gaming | desktop | casual | low |
homework | laptop | hardcore | medium |
tablet | high | ||
watching video | smartphone |
Our budget slot contains a set of synonyms like cheap (which resolves to low), midrange (which resolves to medium), and premium (which resolves to high).
Our utterances are:
"I want a computer"
"I am a {typeOfGamer} {purpose}."
"I want a {purpose} {typeOfComputer}."
"I want a computer for {purpose}."
"I want a {budget} {purpose} {typeOfComputer}."
"I want a {purpose} computer."
"I want a {budget} {purpose} computer."
"I am a {typeOfGamer}."
Our interaction model covers utterances with no slots, one slot, and multiple slots. Let’s say the customer says, "I want a gaming computer" and our skill has a connection to the store's database and knows that there are only 3 gaming computers in stock: 2 premium, and 1 cheap. It wouldn't be good to ask, "We have cheap, midrange, and premium computers. Which would you like?" since you know there are no midrange computers left. Instead the skill should say, "We have 3 gaming computers, 2 premium computers, and 1 cheap computer available. Which would you like?" Let's take a look at how we could elicit the budget slot and override the predefined prompt.
To elicit a slot, you need to provide three things: slotName, which is the name of the slot your skill wants the user to fill; prompt, which is the question your skill asks the user; and reprompt, which is the question your skill asks the user if they don't reply to the prompt within 8 seconds. Now that we know how it works, let's override the budget slot's prompt.
let prompt = "We have 3 gaming computers on the shelf. ";
prompt += "Two premium computers and one cheap one. Which would you like?";
let reprompt = "In stock, we have premium and cheap computers, which would you like?";
this.emit(':elicitSlot', 'budget', prompt, reprompt);
Now that we have the lines necessary to elicit the slot, let's think about where that code should go. During dialog management, Alexa reports the status of the dialogState, STARTED, IN_PROGRESS, and COMPLETED. If the dialogState is not COMPLETED, we can pass the delegate directive back so Alexa will continue to elicit the remaining required slots, but if we provide the elicitSlot directive, Alexa will prompt for that slot instead.
'RecommendComputerIntent': function() {
let filledSlots = delegateSlotCollection.call(this);
if (!filledSlots) {
return;
}
// If we reach this point we have collected all of our required slots!
// Look up the recommendation and tell the user about it.
},
From the RecommendComputerIntent we are calling delegateSlotCollection to hook into the state machine. From there we are checking the filled slots and using elicitSlot to modify our prompt.
function delegateSlotCollection() {
let updatedIntent = this.event.request.intent.dialogState;
if (this.event.request.intent.dialogState === "STARTED") {
this.emit(':delegate', updatedIntent);
} else if (this.event.request.intent.dialogState !== "COMPLETED") {
if(this.event.request.intent.slots.purpose.value) {
let prompt = "We have 3 gaming computers on the shelf. ";
prompt += "Two premium computers and one cheap one. Which would you like?";
let reprompt = "In stock, we have premium and cheap computers, which would you like?";
this.emit(':elicitSlot', 'budget', prompt, reprompt);
} else {
this.emit(':delegate', updatedIntent);
}
} else {
// we have collected all of our slots!
// time to return them
return updatedIntent
}
return null;
}
In the example above, we are checking to see if this.event.request.intent.slots.purpose.value has been filled and we are overriding the prompt. Without making any assumptions about your database and how you would connect to it, it is good practice to add additional logic to check your inventory. You might also want to move the check into a function and run it from either the STARTED or IN_PROGRESS state.
When you define your voice interaction model, you can define the order in which Alexa will prompt the user to fill the missing slots by using the up and down arrows to the left of the slot name.
There may be times where you want to override what is elicited next. For example, if you want to conditionally collect the zip code or the city and state (A || B & C) and the user gives you the state first, it would be best to prompt for the city instead of the zip code.
While there is no way to programmatically change the original order you set in the developer portal at runtime, you can tell Alexa which slot to elicit next by using elicitSlot, like if the user gives us the state we can prompt for the city.
if (this.event.request.intent.slots.city.value) {
let prompt = 'What city are you going to?';
this.emit(':elicitSlot', 'city', prompt, prompt);
}
One important thing to note about dialog management: You're not limited to required slots. You can use elicitSlot to elicit any slot defined in the intent.
While the customer is interacting with your skill, they may provide an answer to a question that makes a previously optional slot required. To handle this case, you can hook into the dialog management state machine and use elicitSlot to ask the user to provide an answer.
In our previous example that recommends computers, we had four slots: purpose, typeOfComputer, typeOfGamer, and budget. Although typeOfGamer is optional, since it belongs to the RecommendComputerIntent we are able to use elicitSlot to prompt for it during the dialog management.
If the user says they want a gaming computer, we can elicit the typeOfGamer slot to gain some helpful information. If they are a hardcore gamer we should find computers with great graphics cards, and if they are a casual gamer we should find a quality computer with decent specifications.
// if we have a value for gamer but not for typeOfGamer,
// we need to elicit typeOfGamer
if (this.event.request.intent.slots.gamer.value &&
!this.event.request.intent.slots.typeOfGamer.value) {
let prompt = "Would you consider yourself a hardcore or a casual gamer?";
let reprompt = "Are you are hardcore or casual gamer?";
this.emit(':elicitSlot', 'typeOfGamer', prompt, reprompt);
}
As you can see, dialog management is very powerful. You can hand off the entire interaction to Alexa, or override slots here and there throughout the interaction. If you're feeling adventurous, you could take the reins and use dialogState and the provided slots to programmatically determine what to prompt for next.
If you have any questions or comments, let's continue the discussion on Twitter. Follow me @sleepydeveloper, ask questions, and I may end up writing a blog post to share the answer!
Every month, developers can earn money for eligible skills that drive some of the highest customer engagement. Developers can increase their level of skill engagement and potentially earn more by improving their skill, building more skills, and making their skills available in in the US, the UK and Germany. Learn more about our rewards program and start building today.