Building your own Alexa Skill from scratch
Apoorva Dave
Posted on February 2, 2019
Amazon’s Alexa (named after the ancient library of Alexandria) is Amazon’s voice-control system. Alexa’s responsiveness is something that makes it different from the phone-based voice assistant. We can play music, search the Web, create to-do and shopping lists, shop online, get instant weather reports, and control popular smart-home products using nothing but the sound of your voice.
Alexa skill is like an app. You can enable or disable the skill using the Alexa app. Once a skill is enabled, you can launch the skill and do stuff which the skill is capable of performing.
How does Alexa skill work?
Before we start creating our own Alexa skill, it is important for us to understand how does it work. So there are two main components: skill interface and skill service. The request follows the path:
User -> Echo device -> Skill Interface (Amazon Alexa dev platform) -> Skill service (HTTP server/AWS Lambda)
The request made by the user to echo device is sent to the skill interface. The skill interface encodes request into a JSON format and hits the skill service. The response is generated by the skill service which is again in JSON format. This response is provided to the skill interface then to echo device. Echo device translates it into speech and gives the information to the user.
Skill interface is the Alexa dev platform where we create our interaction model. It receives speech as input and provides JSON encoded request. Skill service is the brain of our skill which provides the response according to the request made by the user.
Motivation
The question that pops into many heads including mine is Why to even build an Alexa skill? There has to be a source of motivation so that we complete what we start (at least for me 😛). There actually is. Amazon has an incentive program where the developers can bring their idea to life using Alexa, publish their skill and at the minimum get a free, limited-edition Alexa developer t-shirt. If more than 150 customers enable and use your skill in the first 30 days of publication, you could also receive an Echo Dot. Motivated? 😄 I am! If you want to get your own Alexa skill published, follow through the rest of the post. I assure you, you can have your skill up and running in like 2 hours. So let’s get started!
Building your Alexa Skill
In this article, we are going to create an Alexa skill which provides bio and facts on the Marvel superheroes (Superheroes Hub: This is my first skill published so far. If you any of you have echo devices, I request you people to please enable it and check. Please let me know in the comments if you face any difficulties or what more feature you would want in it 👼. You can check the skill in the link).
Now this same code can be extended as well as modified to get your skill.
- Build interaction model in Amazon Alexa developer portal.Here you will define the intents (i.e actions performed by your skill), sample utterances (sentences which would invoke your skill and intents) and the endpoints.
- Creating an AWS Lambda function (skill service). You will be writing a function to handle the requests made by the user and generate a JSON response for the same. I will be using Python. Other languages like Node.js is also supported.
- Testing the skill using service simulator.
- Submitting the skill for publishing.
- Creating an icon/logo for your Alexa skill.
Interaction Model
Go to this link and sign up if you don’t have a developer account already. Follow steps: Alexa -> Create Alexa skills -> Start a skill -> Create skill. Choose a skill name and select Custom.
In the next screen, give invocation name of your skill. This is the name which would be said by the user to invoke your skill. So try to be creative and unique. There are by default some intents included in the list. To get bio and facts of superheroes, we would create 2 more intents: marvelBioand marvelFacts
Click on Add Intent and give name marvelBio. It handles requests made by the user to get a bio on the superhero.
After this, define your sample utterances (a sentence that would invoke this intent). Example: tell me about iron man. Now because this “iron man” can be variable, a slot named marvel is created. Give sample utterances as tell me about {marvel}.
This marvelslot needs to have a slot type. Create a slot type by clicking on Add Slot type and give name marvelCharacterNames. The slot type would contain the name of superheroes. You can fill in the name of superheroes you want.
Similarly, add an intent named marvelFacts. Sample utterances will be like: give me a fact on {marvel}. The slot {marvel} for this particular intent is created. However, no new slot type is needed. The same slot type can be used as we want to replace the slot {marvel} by the name of superheroes which we already defined in marvelCharacterNames.
To add built-in intent “AMAZON.NoIntent”, click on Add Intent and use an existing intent from Alexa’s built-in library. Search and select NoIntent. (We are including this intent as you could see by default it is not included in our interaction model). The final list of intents looks like this:
Go to Endpoints section, select AWS lambda function and copy the skill id. It would be used later in lambda function to link skill interface and skill service.
Do Save your interaction model by clicking on Save model. You would not want to do all that again believe me 😟. And check if you can build successfully.
AWS Lambda function
Go to this link. In case you don’t have an account already, sign up and then login. Search Lambda and click on create function and author from scratch. Give the name of the lambda function. Select python 3.6 and role as custom role.
In the IAM manager click on the “Allow” button and you will be redirected back to the “Create function” form.
Click on the Alexa Skills Kit from triggers option on the left side, paste the skill ID you copied from “Endpoints”. Click on Add.
Click on the function name, scroll to the bottom of the page and change the function content. Remove the existing code and paste the below function.
from random import randint
#------------------------------Part1--------------------------------
Marvel_LIST = ["Ant Man","War Machine"]
Marvel_BIOGRAPHY = {"ant man":"Ant-Man is a legacy super-hero name,
primarily associated with the ability to shrink in size. Former thief
Scott Lang once stole an advanced size-altering suit in order to aid
his ailing daughter, only to discover that the stolen tech belonged to the
world-renowned Dr. Hank Pym. Seeing the heroic potential within him,
Dr. Pym allowed Scott to continue using the suit, as well as the identity
Pym once battled evil under. As the Astonishing Ant-Man, Scott now
handles the jobs too small for any other Super Hero.",
"war machine" : "Colonel James Rupert Rhodes, commonly known as Rhodey,
is the officer of the United States Air Force. He is the best friend of
Tony Stark and the liaison between Stark Industries
and the military in the Department of Acquisitions."}
Marvel_FACT = {"ant man":["When Paul Rudd told his nine-year old son
that he was playing the titular character in Ant-Man, he responded with,
Wow, I can’t wait to see how stupid that’ll be.","Rudd got in such good
shape that they had to alter the Ant-Man costume to
account for his new physique.","In the comics, Hank Pym is actually
the one who creates Ultron.Of course, Tony Stark does so in
the movies because, y’know, drama."],
"war machine":["IN AN EARLY DRAFT OF THE FIRST IRON MAN MOVIE,
TONY'S FATHER HOWARD STARK WAS THE MAIN VILLAIN, CALLING HIMSELF
WAR MACHINE","DON CHEADLE TOOK ANTI-ANXIETY MEDICATION TO WEAR THE
WAR MACHINE SUIT","JAMES RHODES ORIGINALLY MET TONY STARK
DURING THE VIETNAM WAR"]
}
#------------------------------Part2--------------------------------
def lambda_handler(event, context):
if event['session']['new']:
on_start()
if event['request']['type'] == "LaunchRequest":
return on_launch(event)
elif event['request']['type'] == "IntentRequest":
return intent_scheme(event)
elif event['request']['type'] == "SessionEndedRequest":
return on_end()
#------------------------------Part3--------------------------------
def on_start():
print("Session Started.")
def on_launch(event):
onlunch_MSG = "Hi, welcome to the Alexa Skill. My favourite marvel
characters are: " + ', '.join(map(str, Marvel_LIST)) + ". "\
"If you would like to hear more about a particular character or a
fact related to it, you could say for example: tell me about Ant Man?
or give me a fact on Ant Man "
reprompt_MSG = "Do you want to hear more about a particular
marvel character?"
card_TEXT = "Pick a marvel."
card_TITLE = "Choose a marvel."
return output_json_builder_with_reprompt_and_card(onlunch_MSG,
card_TEXT, card_TITLE, reprompt_MSG, False)
def on_end():
print("Session Ended.")
#-----------------------------Part3.1-------------------------------
def intent_scheme(event):
intent_name = event['request']['intent']['name']
if intent_name == "marvelBio":
return marvel_bio(event)
elif intent_name == "marvelFacts":
return marvel_fact(event)
elif intent_name in ["AMAZON.NoIntent", "AMAZON.StopIntent", "AMAZON.CancelIntent"]:
return stop_the_skill(event)
elif intent_name == "AMAZON.HelpIntent":
return assistance(event)
elif intent_name == "AMAZON.FallbackIntent":
return fallback_call(event)
#---------------------------Part3.1.1-------------------------------
def marvel_bio(event):
name=event['request']['intent']['slots']['marvel']['value']
marvel_list_lower=[w.lower() for w in Marvel_LIST]
if name.lower() in marvel_list_lower:
reprompt_MSG = "Do you want to hear more about a particular
character?"
card_TEXT = "You've picked " + name.lower()
card_TITLE = "You've picked " + name.lower()
return output_json_builder_with_reprompt_and_card
(Marvel_BIOGRAPHY[name.lower()], card_TEXT, card_TITLE,
reprompt_MSG,False)
else:
wrongname_MSG = "I cannot help you with that. If you have
forgotten which characters you can pick say Help."
reprompt_MSG = "Do you want to hear more about a particular ?"
card_TEXT = "Use the full name."
card_TITLE = "Wrong name."
return output_json_builder_with_reprompt_and_card
(wrongname_MSG, card_TEXT, card_TITLE, reprompt_MSG, False)
def marvel_fact(event):
name=event['request']['intent']['slots']['marvel']['value']
marvel_list_lower=[w.lower() for w in Marvel_LIST]
if name.lower() in marvel_list_lower:
reprompt_MSG = "Do you want to hear a fact on a particular character?"
card_TEXT = "You've picked " + name.lower()
card_TITLE = "You've picked " + name.lower()
return output_json_builder_with_reprompt_and_card
(Marvel_FACT[name.lower()][randint(0, 2)], card_TEXT, card_TITLE,
reprompt_MSG, False)
else:
wrongname_MSG = "I cannot help you with that. If you have
forgotten which characters you can pick say Help."
reprompt_MSG = "Do you want to hear more about a particular ?"
card_TEXT = "Use the full name."
card_TITLE = "Wrong name."
return output_json_builder_with_reprompt_and_card
(wrongname_MSG, card_TEXT, card_TITLE, reprompt_MSG, False)
def stop_the_skill(event):
stop_MSG = "Thank you. Bye!"
reprompt_MSG = ""
card_TEXT = "Bye."
card_TITLE = "Bye Bye."
return output_json_builder_with_reprompt_and_card
(stop_MSG, card_TEXT, card_TITLE, reprompt_MSG, True)
def assistance(event):
assistance_MSG = "You can choose among these marvels: " +
', '.join(map(str, Marvel_LIST)) + ". Be sure to use the full name when asking about the character."
reprompt_MSG = "Do you want to hear more about a particular
marvel character?"
card_TEXT = "You've asked for help."
card_TITLE = "Help"
return output_json_builder_with_reprompt_and_card
(assistance_MSG, card_TEXT, card_TITLE, reprompt_MSG, False)
def fallback_call(event):
fallback_MSG = "I can't help you with that, try rephrasing
the question or ask for help by saying HELP."
reprompt_MSG = "Do you want to hear more about a particular
marvel character?"
card_TEXT = "You've asked a wrong question."
card_TITLE = "Wrong question."
return output_json_builder_with_reprompt_and_card
(fallback_MSG, card_TEXT, card_TITLE, reprompt_MSG, False)
#------------------------------Part4--------------------------------
def plain_text_builder(text_body):
text_dict = {}
text_dict['type'] = 'PlainText'
text_dict['text'] = text_body
return text_dict
def reprompt_builder(repr_text):
reprompt_dict = {}
reprompt_dict['outputSpeech'] = plain_text_builder(repr_text)
return reprompt_dict
def card_builder(c_text, c_title):
card_dict = {}
card_dict['type'] = "Simple"
card_dict['title'] = c_title
card_dict['content'] = c_text
return card_dict
def response_field_builder_with_reprompt_and_card
(outputSpeach_text, card_text, card_title, reprompt_text, value):
speech_dict = {}
speech_dict['outputSpeech'] = plain_text_builder(outputSpeach_text)
speech_dict['card'] = card_builder(card_text, card_title)
speech_dict['reprompt'] = reprompt_builder(reprompt_text)
speech_dict['shouldEndSession'] = value
return speech_dict
def output_json_builder_with_reprompt_and_card
(outputSpeach_text, card_text, card_title, reprompt_text, value):
response_dict = {}
response_dict['version'] = '1.0'
response_dict['response'] = response_field_builder_with_reprompt_and_card
(outputSpeach_text, card_text, card_title, reprompt_text, value)
return response_dict
I will talk briefly about what we have done in the code. Initially, in Part 1 we are defining Marvel_LIST(list of superheroes), a Marvel_BIOGRAPHY dictionary with the key as Marvel superhero name and value as his bio and finally a dictionary Marvel_FACT which is a multivalued dictionary with 3 values (3 facts) associated to 1 character.
In Part 2, a lambda_handler function takes event and context as parameter and handles the requests made (Launch, Intent or Session End Requests). LaunchRequest is sent to invoke the skill, IntentRequest is sent when an intent is invoked to handle the event, SessionEndedRequest is sent when the session ends due to an error or user says exit.
In Part 3, request handler functions on_start(), on_launch() and on_end() are defined. output_json_builder_with_reprompt_and_card() is being called from on_launch() function calls which I will be explaining below. In Part 3.1, the intent_scheme(event) calls respective function according to the intents that are being invoked. Example if someone is requesting a fact on a marvel, marvel_fact(event) is called.
The response from Lambda function is a JSON encoded output as follows:
{
"body": {
"version": "1.0",
"response": {
"outputSpeech": {
"type": "PlainText",
"text": ""
},
"card": {
"type": "Simple",
"title": "",
"content": ""
},
"reprompt": {
"outputSpeech": {
"type": "PlainText",
"text": ""
}
},
"shouldEndSession": value
}
}
}
To build this response, in Part 4, we define plain_text_builder(text_body), reprompt_builder(repr_text), card_builder(c_text, c_title), response_field_builder_with_reprompt_and_card(outputSpeach_text, card_text, card_title, reprompt_text, value). Finally function output_json_builder_with_reprompt_and_card() creates the JSON encoded output with the help of these response builder functions.
If any part of the code is unclear, you can leave a comment as this would be the brain of your skill. The same code can be tweaked in order to get your own skill.
Please save your lambda function and copy the ARN (Amazon Resource Name) displayed in the upper right corner. Paste this ARN in your Endpoints tab in the “Default region” in Amazon developer Alexa skill portal. Save your Endpoints.
Save your interaction model and build it. Make sure it builds successfully without any errors. This completes the development part of our skill. Time to have some fun! 😄
Testing the Skill
To test the skill in the simulator, go to the test tab and select Development option from the drop-down. Write down your requests or use your microphone.
- Launch (your invocation name)
- Tell me about Ant Man
- Give me fact on Ant Man
- Help (The request “help” invokes the assistance() function and provides a list of marvels which can be chosen. )
- Bye (Quits the skill)
In case any of the requests fail and you don’t get the correct response, Alexa developer portal provides an excellent way to debug. Amazon CloudWatch contains the logs with the help of which we can remove the error. Just make sure to put proper log statements in your application as it makes debugging easier.
Publishing your Skill
Before you publish your skill, make sure you have covered all the cases and testing is done. In case it fails the certification process, you can resubmit your skill multiple times, but it is good to at least test the basic cases thoroughly beforehand.
To publish your skill, go to the Distribution tab. Fill in the details like Skill name, description, sample utterances, privacy policy and terms of use. The skill name should be chosen in such a way so that there are no copyright issues (I faced this issue 😝). The description should include everything your skill is capable of doing. Save and continue to move to the next page.
Creating an icon/logo for your skill
Icon creation is one of the fun parts 😛. Usually, people struggle to develop their logo online for free. Alexa portal provides you with one of the most efficient and quick ways for creating your icon. You can use different symbols from the list of symbols available. The size of the symbol can be altered depending upon your need. To make your icon look more beautiful, you can use solid as well as gradient color. Put on your creative hat and have fun 👼. This is the logo which I created for my skill:
Once you have filled in all the details, the portal runs Validation tests after which you can submit the skill for review. The Amazon team is quite quick and awesome while reviewing it. They send you mail if they find any issues and suggest steps as well how can we remove the error! Once the issues are resolved, you receive a mail saying You Skill is now live! 😄
This was my first attempt to write on something other than machine learning. Do leave a comment if you face any issues. If you liked the article, do show some ❤. Do enable my skill "Superheroes Hub". It is available in India and United States. 150 of you guys can help me get an echo device 😛. Its available here.
Special thanks to the following articles and people:
@anamritraj’s video tutorial. This series was one of the motivations to build an Alexa skill :)
Posted on February 2, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.