It’s no secret that dialog management is one of my favorite features in the Alexa Skills Kit. I’ve spent countless hours experimenting with it. This has enabled me to develop some useful techniques to improve skill quality, such as context switching and conditional flexibility. Some of this experimentation was inspired by the questions you ask me at Alexa events and online @SleepyDeveloper. The Alexa Skills Kit team is constantly updating dialog management with new features that you can use to improve conversational interactions with your customers. One of those new features is called intent chaining.
Intent chaining allows your skill code to start dialog management from any intent, including the LaunchRequest. You can chain into any of your custom intents as long as your interaction model has a dialog model. How do you know if you have a dialog model? If you have at least one intent with a required slot, or you’ve enabled auto delegation, your skill has a dialog model. While you can chain from any intent, there are some intents that you cannot chain to. For example, you can’t chain to the LaunchRequest or any of the built-in intents, such as, Amazon.HelpIntent.
In the past, your skill code could only return a dialog directive once dialog management was triggered through a user utterance. Intent chaining has me super excited because our skills can now activate dialog management programmatically!
Let’s consider an interaction that uses dialog management without intent chaining. The conversation takes place between a coffee shop skill that our customers will use to order tea or coffee. We won’t go through the entire conversation start to finish. We’ll focus on just the transition from the LaunchRequest to the intent that we want to chain into, OrderIntent. The LaunchRequest greets the customer and the OrderIntent uses dialog management to take their order. As you read think about how this conversation could be simplified.
CUSTOMER
Alexa, open coffee shop.
ALEXA
Welcome to the coffee shop. Say start my order to get started.
CUSTOMER
Start my order.
ALEXA
Which would you like coffee or tea?
CUSTOMER
coffee
Were you able to identify how we could simplify this conversation? How about instead of having the user say, “start my order” to have your skill start the order conversation, why don’t we just start taking their order? In that case our conversation would look something like:
CUSTOMER
Alexa, open coffee shop.
ALEXA
Welcome to the coffee shop. Which would you like tea or coffee?
CUSTOMER
coffee
That’s so much simpler, isn’t it? We can immediately start taking their order. Before I show you how to chain intents, let’s take some time to fully understand the benefits of chaining intents.
It’s entirely possible without intent chaining to update our LaunchRequest to say, “Welcome to the coffee shop. Which would you like tea or coffee?” However, when the customer responds, dialog management isn’t active. The customer needs to say something to trigger the OrderIntent. Upon doing so, dialog management springs into action tracking the conversation for us. One major advantage to using dialog management is improved accuracy. The Alexa service is able to more accurately anticipate what your customer will say next and the intent that should handle it. In this case, our customer is most likely going to respond with their choice of either “tea” or “coffee,” which needs to be handled by our OrderIntent.
You can even use intent chaining to chain from one intent with dialog management to another. You can, for example, create a set of intents designed to collect sets of information and chain between them based on what you have and have not collected. Furthermore, you can even chain from a custom intent that uses auto delegation. Now that we understand the benefits of intent chaining, let’s take a look at how it’s done.
Our example coffee shop skill has an OrderIntent that we want to chain into when the customer says, “Alexa, open coffee shop” from our LaunchRequest. To do that all we have to do is return a dialog delegate and the name of the intent we want to chain to from our LaunchRequest. First, we’ll take a look at how we’d do that with the addDelegateDirective function.
.addDelegateDirective({
name: 'OrderIntent',
confirmationStatus: 'NONE',
slots: {}
})
That’s pretty easy! Here we used addDelegateDirective to add the Dialog.Delegate directive to our response, and we set the name property to OrderIntent which is the intent we want to chain to. The confirmationStatus indicates whether the intent has been CONFIRMED or DENIED. While NONE indicates that our customer hasn’t been asked to confirm. The slots object allows us to programmatically set slot values. This comes in handy when restoring the collected slots when returning from a context switch. For more on that, take a look at Build for Context Switching: Don’t Forget Important Information When Switching Between Intents.
If you’re feeling adventurous, you can use the addDirective() function instead. You’ll have to manually set the directive type to Dialog.Delegate, define updatedIntent and set name to OrderIntent:
.addDirective({
type: 'Dialog.Delegate',
updatedIntent: {
name: 'OrderIntent',
confirmationStatus: 'NONE',
slots: {}
}
})
The choice is yours, but I’d recommend addDelegateDirective because it reduces the amount of code you have to write. Now that you understand how to build the directive, let’s take a look at how we build the whole response with the responseBuilder.
return handlerInput.responseBuilder
.addDelegateDirective({
name: 'OrderIntent',
confirmationStatus: 'NONE',
slots: {}
})
.speak("Welcome to the coffee shop.")
.getResponse();
The addDelegateDirective function belongs to the responseBuilder, which we are using to build the response that our skill sends back to Alexa. The speak function defines what Alexa will say. Take a look at the text we’ve passed to it.
.speak("Welcome to the coffee shop.")
Something seems to be missing? It does not include the question, “Which would you like tea or coffee?” Why did we leave it out? When we defined our OrderIntent, we marked the coffee slot required. Doing so requires us to define prompts in our dialog model, which Alexa will use to ask our customer to fill the slot! Returning the Dialog.Delegate from our LaunchRequest triggers Alexa to immediately start prompting for our required slots using our dialog model.
Dialog.Delegate isn’t the only dialog directive at our disposal. The elicit slot directive allows us to specify what the Alexa voice service will prompt for next. We can also use it to programmatically convert optional slots into required slots.
Intent chaining supports the elicit slot directive. So, we’re able to chain into a specific intent while eliciting a specific slot. Let’s take a look at how we’d do that using the addElicitSlotDirective function.
.addElicitSlotDirective('drink',
{
name: 'OrderIntent',
confirmationStatus: 'NONE',
slots: {}
})
The first parameter we pass to addElicitSlotDirective is the name of the slot that we want to elicit. In this case, we’re going to elicit the drink slot. Just like addDelegateDirective we set name to the intent that we are going to chain to (OrderIntent) and the confirmationStatus to NONE. Let’s take a look at how we’d do the same with the addDirective function.
.addDirective({
type: 'Dialog.ElicitSlot',
slotToElicit: 'drink',
updatedIntent: {
name: 'OrderIntent',
confirmationStatus: 'NONE'
}
})
In this case we are manually defining the directive. We have to set the type to Dialog.ElicitSlot and slotToElicit to drink and then define and wrap the name and confirmationStatus in updatedIntent. It’s great to have the flexibility to build the request manually, but just like Dialog.Delegate, I recommend using the helper function. Let’s take a look at the whole responseBuilder. Keep an eye out to what we are passing to the speak function in this case.
return handlerInput.responseBuilder
.addElicitSlotDirective('drink', {
name: 'OrderIntent',
confirmationStatus: 'NONE',
slots: {}
})
.speak("Welcome to the coffee shop. Which would you like tea or coffee?")
.reprompt("Which would you like tea or coffee?")
.getResponse();
Did you notice when our skill chains with the Dialog.ElicitSlot directive, we include the question, “Which would you like tea or coffee?” Unlike Dialog.Delegate, Alexa won’t use our prompt defined in the dialog model to automatically ask the question. When we use Dialog.ElicitSlot to specify what slot to have Alexa prompt for next, it’s our responsibility to also define the prompt. If we don’t provide the question, Alexa will say, “Welcome to the coffee shop” and leave the microphone temporarily open for our customer to respond without asking a question. After a few seconds of awkward silence, your skill will speak the text that you passed to reprompt. If your reprompt also lacks a question, the awkwardness will repeat. The session will then close after a few seconds. This will confuse your customer, so be sure to include the question when you use Dialog.ElicitSlot to chain intents. On the other hand, if you’re using Dialog.Delegate to chain intents, and you include the question, Alexa will repeat the question, once with your initial response and again with prompt defined in your dialog model.
As you can see, intent chaining allows you to streamline the conversation that your customer will have with your skill. As a bonus, you get increased accuracy since you’re setting the context that allows Alexa to anticipate what the customer is going to say next and which intent should handle it.
I hope this has inspired you to think about how you can put intent chaining to use in your skills. If you do, I’d love to hear about it! Please reach out to me on Twitter @SleepyDeveloper to let me know!