Have you ever had a conversation with someone where you opened up and told them something very important to you? Instead of listening intently, they either didn't pay attention or completely forgot what you said. It's frustrating, isn't it? One often overlooked component of conversation is memory. As conversational partners converse, the information they exchange is committed to their individual memories that they can call upon during the present and future conversations. One way to make your Alexa skills more conversational, personal, and relatable is to add a little bit of memory and personalization to your voice experience.
Remembering important things about your customers will go a long way in making them feel appreciated. As a bonus, future interactions with your skill will be more streamlined because your customers won't have to repeat themselves over and over again.
To demonstrate this we created an example skill called The Foodie which recommends meals to hungry indecisive people. One of the ways that The Foodie example skill uses memory to make future interactions with the skill more personable and streamlined is remembering allergy and dietary restrictions. This information is most likely not going to change often so our skill should not unnecessarily pester your customer over and over again. However, you should provide a way for your customer to change their preferences.
It's quite simple to back your skill up with a “virtual brain” to track information gained throughout the conversation with your customers.
With the Alexa Skills Kit Software Development Kit (SDK) you can easily save information from your skill into Amazon DynamoDB. Your skill can then recall and update that information. DynamoDB is a serverless key-value pair and document database. You don't need to bother provisioning a computer, keeping it updated and running. Once you've configured it and set up a table, your skill through the SDK can easily write and read information to and from your DynamoDB table.
To connect to the DynamoDB table from your skill, specify the table name that you want to connect to using the the withTableName function on your SkillBuilder. For The Foodie we named it theFoodieTable. Assuming that you updated your code without creating a DynamoDB table instance in the AWS Console, when your skill tries to access the theFoodieTable, it will be attempting to access a table that doesn't exist. That's no good! Luckily the SDK is here to help. We can make the SDK create the table for us if it doesn't exist by calling .withAutoCreateTable(true).
The whole thing would like something like:
const skillBuilder = Alexa.SkillBuilders.standard();
exports.handler = skillBuilder
.addRequestHandlers(
// ... handlers
)
.addRequestInterceptors(
// ... request interceptors
)
.addResponseInterceptors(
// ... response interceptors
)
.addErrorHandlers(ErrorHandler)
.withTableName("theFoodie")
.withAutoCreateTable(true)
.lambda();
Now that we've created and can connect to our brain, we need a way to save and recall the information we wish to save to it.
We can leverage the AttributesManager to access our brain through the SDK. Let's say our customer has a peanut allergy. In that case, we want to be sure that we do not recommend anything with peanuts in it. First, we need to tell the AttributesManager what we need to save. To do that we create an object to store the allergies, which we called persistentAttributes. Then we can set and save the attributes using setPersistentAttributes and savePersistentAttributes. The code would look something like:
let persistentAttributes = {
profile: {
allergies: 'nuts'
}
}
handlerInput.attributesManager.setPersistentAttributes(persistentAttributes);
handlerInput.attributesManager.savePersistentAttributes();
Now that we've saved the information, we need to be able to recall it otherwise our skill will continue to needless pester our customer by asking the same question about allergies at each interaction.
Once again, to recall our information we can lean on the AttributesManager. In addition to setting and saving our attributes, we can get them too! To get them we can use the getPersistentAttributes function.
async handle(handlerInput) {
let persistentAttributes = await handlerInput.attributesManager
.getPersistentAttributes();
}
Now that our skill is able to save and recall information about our customers into its brains in the cloud, DynamoDB, we need to do one last thing to avoid asking about allergies again. We need to tell dialog management that we already know their allergies so don’t ask the customer for it. In case you missed it, I covered using dialog management to build a conversational skill in Build for Dialog: Solving the Order Problem When Designing Conversational Alexa Skills.
Dialog management is a great tool for creating conversational skills. You can mark which slots are required for your skill and in your back-end skill code return a Dialog.Delegate directive. Doing so will have the Alexa service follow up with questions using the prompts that you've defined in your interaction model until all of the required slots have been filled. In code to return a Dialog.Delegate directive, you can use the addDelegateDirective() function on the responseBuilder.
In addition to returning the Dialog.Delegate directive, you can return an updated intent that includes the current slots collected, which you can modify. That's how we will sync the alleriges that we remembered by storing them into our DynamoDB table. To modify the slots, we would first use the attributesManager to recall the information that we saved using the getPersistentAttributes() function. Then update the intent's allergies slot with the information we recalled from our database.
let persistentAttributes = await handlerInput.attributesManager.getPersistentAttributes();
let updatedIntent = handlerInput.requestEnvelope.request.intent;
updatedIntent.slots.allergies.value = persistentAttributes.profile.allergies;
Now that we've updated the allergies slot, we can sync our changes by including our updatedIntent with the Dialog.Delegate directive.
return handlerInput.responseBuilder
.addDelegateDirective(updatedIntent);
Putting it all together, our code would look like:
async handle(handlerInput) {
let persistentAttributes = await handlerInput.attributesManager.getPersistentAttributes();
let updatedIntent = handlerInput.requestEnvelope.request.intent;
updatedIntent.slots.allergies.value = persistentAttributes.profile.allergies;
return handlerInput.responseBuilder
.addDelegateDirective(updatedIntent);
}
We will use the async and await keywords to make our code pause until we get the information back from DynamoDB. You can achieve the same affect by using promises or callbacks, but the async and await keywords are far more easier to use and reduce the amount of code you have to write.
Once we've synced our changes to the allergies slot with dialog management, Alexa will skip asking the customer to fill the allergies slot. However, the allergies utterances are still active. If the customer were to say, "I have a shellfish allergy," the allergies slot would then be overwritten with shellfish instead of peanuts.
As you can see, memory (although often overlooked) is a key component of conversation. You'll want to make good use of it in your skills to make future interactions more streamlined and thus more rewarding for your customers. Keep in mind how frustrating it would be if your daily conversational partners never remembered any of your previous interactions and try to identify what you should remember about your customers. With the Alexa Skills Kit, our SDK, and just a few simple lines of code, you can add a brain to remember critical data about your customers.
Now that you've read through this post, try to think about how you can put these techniques and features to use in your own skills. Let's continue the discussion online! You can find me on Twitter @SleepyDeveloper.