Alexa skills and intents

Written by lizrice | Published 2017/01/03
Tech Story Tags: aws-lambda | aws | alexa | alexa-skills | alexa-skills-and-intents

TLDRvia the TL;DR App

This is the third article about my journey creating a custom Alexa skill. I’ve set up a very, very basic skill and given it some DynamoDB storage. Now I want to make it a little more interactive by adding some more Intents.

At the moment, if the database has the names Anne, Bob and Charlie, conversation with my skill goes like this:

“Alexa, who’s in the house?”

“Anne, Bob and Charlie are in the house”

And that’s it. The only way to change the list of names is to edit them in the database. I want to be able to tell Alexa when people arrive and leave, so she can keep the database updated herself.

Alexa Intents

Alexa skills use Intents to tell the Lambda function what it should do. In practice I actually wrote the Python code first and then added the Intents, but it’s easier to explain the other way around.

Here is the Intent Schema I set up for Who is in the House. You define these in the developer portal, under the Interaction Model for your Alexa skill.

{"intents": [{"intent": "WhoIsInTheHouse"},{"intent": "ArrivedInTheHouse","slots": [{"name": "Name","type": "AMAZON.GB_FIRST_NAME"}]},{"intent": "LeftTheHouse","slots": [{"name": "Name","type": "AMAZON.GB_FIRST_NAME"}]},{"intent": "AMAZON.HelpIntent"},{"intent": "AMAZON.CancelIntent"},{"intent": "AMAZON.StopIntent"}]}

I want to be able to add or remove a person’s name, so there is an intent for each of those actions. Values (in my case the name to be added or removed) are passed to the intent in slots, so I have a slot for the name in each of those intents. When we come to defining sentences or phrases that will invoke these intents, we’ll indicate where in the sentence these slots appear.

I’ve used the AMAZON.GB_FIRST_NAME built-in slot type for names as I hope this will help Alexa recognise that I’m expecting people to talk about names rather than, say, objects. I have a feeling I’ll be coming back to this later as I can see it has shortcomings, for example, my users can only use a first name, and no qualifiers like a surname.

Alexa sessions

When you start talking to an Alexa skill, you start a session. Up until now my lambda function has ended the session with its first response, by setting the should_end_session flag to True (and this in turn sets shouldEndSession in the JSON response that the Lambda function returns when it exits).

Now I’d like to my skill to open a session, optionally perform an add or remove action, and then end the session. So I’ve modified get_welcome_message() to set should_end_session to False.

Shut up, Alexa!

When you’re setting up the intents for your Alexa skill, you need to include any built-in ones that you’d like to us — including the intent to stop a session.

At one point my Lambda function defaulted to the welcome message in the event of an unrecognised intent — and I had changed the welcome message code to not end the session. Until I explicitly added the AMAZON.StopIntent to my Alexa skill, it meant that once I started a session, whatever I said to her Alexa would just read out the list of names without ever ending the session. There were quite a lot of names in my database, and it got a bit wearing.

Lambda implementation of the intents

Zeros and ones

Before I get started on handling the intents, there is another improvement to make. Mario Manfre kindly responded to my original post to suggest handling the sentence correctly if there is only one person in the house. And I suppose we might theoretically have no-one here. It could be my Google Home asking Alexa who’s here. So I’ve slightly updated my code to handle both.

I modified my get_names() function to return the number of names in the list as well as the names themselves. Then I added a function to turn this into a nice sentence for use wherever we require it.

def get_name_list():names, number = get_names()if number == 0:return “There is no-one here in the house”elif number == 1:return get_names() + “is the only person here in the house.”else:return get_names() + “are here in the house.”

Adding and deleting from DynamoDB

I need functions for adding and deleting names from the database. They’ll return an error message if something goes wrong. (I’ve also pulled the definition of variables dynamodb and table out of get_names() so I don’t have to keep repeating myself.)

dynamodb = boto3.resource('dynamodb', region_name='eu-west-1')table = dynamodb.Table('WhoIsInTheHouse')

def add_name(name):try:response = table.put_item(Item={'NameId': name})except ClientError as e:return e.response['Error']['Code']

return None

def delete_name(name):try:response = table.delete_item(Key={'NameId': name})except ClientError as e:return e.response['Error']['Code']

return None

A very quick test

As we’ve discussed before, you can test Lambda functions with a trigger event. But to start with I simply hardcoded an add and delete call just to confirm that they work.

def get_welcome_response():

add\_name("Andy")  
delete\_name("Suzie")  
  
speech\_output = get\_name\_list()

session\_attributes = {}  
card\_title = "Welcome"  
should\_end\_session = True

return build\_response(session\_attributes, build\_speechlet\_response(card\_title, speech\_output, reprompt\_text, should\_end\_session))

I can ask Alexa “Who is in the house?” and get the response with Andy added and Suzie taken away. Quick, remove those hard-coded add and delete calls! Instead, I want to add and remove names when I get an Intent telling me the name to add or remove.

Adding a name from an intent

Here’s my function for adding a name, along with some rudimentary error handling. (There’s something very similar for removing names too).

def add_name_in_session(intent, session):speech_output = ""reprompt_text = ""

if 'Name' in intent\['slots'\]:  
    name = intent\['slots'\]\['Name'\]\['value'\]  
    should\_end\_session = True  
    error = add\_name(name)  
    if error is not None:  
        speech\_output = "That didn't work. " + error  
    else:  
        speech\_output = "Now that " + name + " is here, " + get\_name\_list()  
else:  
    reprompt\_text = "Sorry, I didn't understand. Please try again."  
    should\_end\_session = False

card\_title = intent\['name'\]  
return build\_response({}, build\_speechlet\_response(  
    card\_title, speech\_output, reprompt\_text, should\_end\_session))

The card_title is shown in the Alexa app when an intent is invoked. I’m going to skip over this for now, suffice to say that my cards need some work.

Handling intents

We need to call the appropriate function inside the Lambda call depending on which intent we receive from the Alexa skill.

def on_intent(intent_request, session):intent = intent_request['intent']intent_name = intent_request['intent']['name']

if intent\_name == "WhoIsInTheHouse":  
    return get\_welcome\_response()  
elif intent\_name == "ArrivedInTheHouse":  
    return add\_name\_in\_session(intent, session)  
elif intent\_name == "LeftTheHouse":  
    return remove\_name\_in\_session(intent, session)  
elif intent\_name == "AMAZON.CancelIntent" or intent\_name == "AMAZON.StopIntent":  
    return handle\_session\_end\_request()  
elif intent\_name == "AMAZON.HelpIntent":  
    return get\_help\_response()  
else:  
     raise ValueError("Invalid intent")

Testing the intents

I’d like to check that my Lambda function behaves as expected for the different intents. You this by configuring test events, and there are some useful Alexa event templates provided.

When you edit the sample event template, your changes will be saved, but note that if you pick another sample event template, all your previous changes are discarded. I suppose that’s what they mean by “changes to the event will only be saved locally”. Once I’ve gone to the trouble of setting up a test event, I take a copy and save it somewhere in my workspace on my local machine so I can go back to it later — which requires copy-pasting it back in. It’s a shame there isn’t a neater way of saving all your test events (or is there? Let me know if you’ve found one!).

When you issue the test event you can see what output would be returned if it triggered that event. This includes the speech output.

I built a bunch of test events to check that all my intents return the speech output I expected. I kept copies of these so eventually I’ll publish them all on Git — let me know if this would be helpful.

Converting speech to intents

Back in the Alexa developer portal, I’ve created a few sample utterances to indicate how to convert speech to intents. I don’t think this is the full set yet, but it’s a few to get started.

The first word in each of these definitions has to be the name of the intent. {Name} in some of these utterances indicates the slot called Name.

Putting it all together

  • The Sample Utterances tell the skill what sort of sentence should invoke each Intent, including where the name in the sentence.
  • The Intent is passed to the Lambda function in JSON form, including the name of the intent, and the name and value of any slots.
  • The Lambda function looks at that JSON, does its thing, and passed back some JSON which tells Alexa what to say in response.

Does it work?

I can interact with my skill to have short conversations like this:

Alexa, who is in the house?

Liz and Phil are here in the house.

Phil has left the house.

Now that Phil has gone, Liz is the only person here in the house.

There are a few things I don’t like yet.

  • Alexa quite often mis-hears the names. Perhaps I need to make this more conversational so that I can confirm whether the the name is correct before she adds it to the list.
  • At the moment the session ends as soon as you’ve added or removed one person. I’m not sure that’s the best behaviour. What if more than one person arrives or leaves at the same time?

Those are a few things for me to work on to improve the conversational experience with my skill.

What’s next?

I’ll make some improvements to those conversational elements, and I want to make some much nicer cards for what shows up for my skill in the Alexa app. I’ve been asked if I’m going to publish my skill — and if I’m going to do that, then everyone who uses it needs their own list of name, which requires some changes to the database as well as recognising each user and connecting them to their own list.

And even more excitingly, Phil Pearl was playing around today to see whether we could use device connections to our home router to automatically add and remove people according to whether their phone is on our wifi network. How awesome would that be?!

If you think this article might help other people write their own Alexa skills, please hit the green heart to recommend it— that helps other people find it.

Did you REALLY like this article? I’m considering writing a book about developing for Alexa. You can encourage me to do that by signing up here. Thank you! ❤︎


Published by HackerNoon on 2017/01/03