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 thehouse?”
“Anne, Bob and Charlie are in thehouse”
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 IntentsAlexa 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 sessionsWhen 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 theintents Zeros andonesBefore 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 fromDynamoDBI 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 quicktestAs 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 anintentHere’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 = "T