Developing Your First Skill

The Setting Up the ASK SDK guide showed how to set up and install the ASK SDK for Python into a specific directory or into a virtual environment using virtualenv. This guide walks you through developing your first skill with the ASK SDK for Python.

Prerequisites

In addition to an installed version of the ASK SDK for Python you need:

Creating Hello World

You'll write your Hello World in a single python file named hello_world.py . In the skill folder that you have created earlier, use your favorite text editor or IDE to create a file named hello_world.py .

Implementing Hello World

Request handlers

A custom skill needs to respond to events sent by the Alexa service. For instance, when you ask your Alexa device (e.g. Echo, Echo Dot, Echo Show, etc.) to "open hello world," your skill needs to respond to the LaunchRequest that is sent to your Hello World skill. With the ASK SDK for Python, you simply need to write a request handler, which is code to handle incoming requests and return a response. Your code is responsible for making sure that the right request handler is used to process incoming requests and for providing a response. The ASK SDK for Python provides two ways to create request handlers:

  1. Implement the AbstractRequestHandler class under ask_sdk_core.dispatch_components package. The class should contain implementations for can_handle and handle methods. This is described under Implementation using handler classes section.
  2. Use the request_handler decorator in instantiated skill builder object to tag functions that act as handlers for different incoming requests. This is described under Implementation using decorators section.

The implementation of the Hello World skill explores using handler classes first and then shows how to write the same skill using decorators. The functionality of these is identical and you can use either.

Exception handlers

Sometimes things go wrong, and your skill code needs a way to handle the problem gracefully. The ASK SDK for Python supports exception handling in a similar way to handling requests. You have a choice of using classes or decorators. The following implementation sections explore how to implement exception handling.

Tip: You may use either Implementation using classes or Implementation using decorators options to write a skill.

Warning: We recommend that you choose one of the options and use it consistently throughout your skill, for better code structure.

Option 1: Implementation using handler classes

Start by creating a skill builder object. The skill builder object helps in adding components responsible for handling input requests and generating custom responses for your skill.

Type or paste the following code into your hello_world.py file.

from ask_sdk_core.skill_builder import SkillBuilder sb = SkillBuilder() 

To use handler classes, each request handler is written as a class that implements two methods of the AbstractRequestHandler class; can_handle and handle .

The can_handle method returns a Boolean value indicating if the request handler can create an appropriate response for the request. The can_handle method has access to the request type and additional attributes that the skill may have set in previous requests or even saved from a previous interaction. The Hello World skill only needs to reference the request information to decide if each handler can respond to an incoming request.

LaunchRequest handler

The following code example shows how to configure a handler to be invoked when the skill receives a LaunchRequest. The LaunchRequest event occurs when the skill is invoked without a specific intent.

Type or paste the following code into your hello_world.py file, after the previous code.

from ask_sdk_core.dispatch_components import AbstractRequestHandler from ask_sdk_core.utils import is_request_type, is_intent_name from ask_sdk_core.handler_input import HandlerInput from ask_sdk_model import Response from ask_sdk_model.ui import SimpleCard class LaunchRequestHandler(AbstractRequestHandler): def can_handle(self, handler_input): # type: (HandlerInput) -> bool return is_request_type("LaunchRequest")(handler_input) def handle(self, handler_input): # type: (HandlerInput) -> Response speech_text = "Welcome to the Alexa Skills Kit, you can say hello!" handler_input.response_builder.speak(speech_text).set_card( SimpleCard("Hello World", speech_text)).set_should_end_session( False) return handler_input.response_builder.response 

The can_handle function returns True if the incoming request is a LaunchRequest. The handle function generates and returns a basic greeting response.

HelloWorldIntent handler

The following code example shows how to configure a handler to be invoked when the skill receives an intent request with the name HelloWorldIntent. Type or paste the following code into your hello_world.py file, after the previous handler.

class HelloWorldIntentHandler(AbstractRequestHandler): def can_handle(self, handler_input): # type: (HandlerInput) -> bool return is_intent_name("HelloWorldIntent")(handler_input) def handle(self, handler_input): # type: (HandlerInput) -> Response speech_text = "Hello World" handler_input.response_builder.speak(speech_text).set_card( SimpleCard("Hello World", speech_text)).set_should_end_session( True) return handler_input.response_builder.response 

The can_handle function detects if the incoming request is an IntentRequest, and returns True if the intent name is HelloWorldIntent. The handle function generates and returns a basic "Hello World" response.

HelpIntent handler

The following code example shows how to configure a handler to be invoked when the skill receives the built-in intent AMAZON.HelpIntent. Type or paste the following code into your hello_world.py file , after the previous handler.

class HelpIntentHandler(AbstractRequestHandler): def can_handle(self, handler_input): # type: (HandlerInput) -> bool return is_intent_name("AMAZON.HelpIntent")(handler_input) def handle(self, handler_input): # type: (HandlerInput) -> Response speech_text = "You can say hello to me!" handler_input.response_builder.speak(speech_text).ask(speech_text).set_card( SimpleCard("Hello World", speech_text)) return handler_input.response_builder.response 

Similar to the previous handler, this handler matches an IntentRequest with the expected intent name. Basic help instructions are returned, and .ask(speech_text) causes the user's microphone to open up for the user to respond.

CancelAndStopIntent handler

The CancelAndStopIntentHandler is similar to the HelpIntent handler, as it is also triggered by the built-In AMAZON.CancelIntent or AMAZON.StopIntent Intents. The following example uses a single handler to respond to both intents. Type or paste the following code into your hello_world.py file, after the previous handler.

class CancelAndStopIntentHandler(AbstractRequestHandler): def can_handle(self, handler_input): # type: (HandlerInput) -> bool return is_intent_name("AMAZON.CancelIntent")(handler_input) or is_intent_name("AMAZON.StopIntent")(handler_input) def handle(self, handler_input): # type: (HandlerInput) -> Response speech_text = "Goodbye!" handler_input.response_builder.speak(speech_text).set_card( SimpleCard("Hello World", speech_text)).set_should_end_session(True) return handler_input.response_builder.response 

The response to both intents is the same, so having a single handler reduces repetitive code.

SessionEndedRequest handler

Although you cannot return a response with any speech, card or directives after receiving a SessionEndedRequest, the SessionEndedRequestHandler is a good place to put your cleanup logic. Type or paste the following code into your hello_world.py file, after the previous handler.

class SessionEndedRequestHandler(AbstractRequestHandler): def can_handle(self, handler_input): # type: (HandlerInput) -> bool return is_request_type("SessionEndedRequest")(handler_input) def handle(self, handler_input): # type: (HandlerInput) -> Response # any cleanup logic goes here return handler_input.response_builder.response 

Implementing exception handlers

The following sample adds a catch all exception handler to your skill, to ensure the skill returns a meaningful message for all exceptions. Type or paste the following code into your hello_world.py file, after the previous handler.

from ask_sdk_core.dispatch_components import AbstractExceptionHandler class AllExceptionHandler(AbstractExceptionHandler): def can_handle(self, handler_input, exception): # type: (HandlerInput, Exception) -> bool return True def handle(self, handler_input, exception): # type: (HandlerInput, Exception) -> Response # Log the exception in CloudWatch Logs print(exception) speech = "Sorry, I didn't get it. Can you please say it again!!" handler_input.response_builder.speak(speech).ask(speech) return handler_input.response_builder.response 

Creating the Lambda handler

Note: For a custom skill, you can host your service in AWS Lambda or as a web service hosted on your own endpoint.

Generally, hosting the skill code on AWS Lambda is the easiest way. The below sections provide information on how to achieve this.

However, if you wish to host it with any other cloud hosting provider, the SDK provides some support packages ( ask-sdk-webservice-support , flask-ask-sdk , django-ask-sdk ). You can find more information on this configuration here.

The Lambda handler is the entry point for your AWS Lambda function. The following code example creates a Lambda handler function to route all inbound requests to your skill. The Lambda handler function creates an SDK skill instance configured with the request handlers that you just created. Type or paste the following code into your hello_world.py file, after the previous handler.

sb.add_request_handler(LaunchRequestHandler()) sb.add_request_handler(HelloWorldIntentHandler()) sb.add_request_handler(HelpIntentHandler()) sb.add_request_handler(CancelAndStopIntentHandler()) sb.add_request_handler(SessionEndedRequestHandler()) sb.add_exception_handler(AllExceptionHandler()) handler = sb.lambda_handler() 

Option 2: Implementation using decorators

The following code implements the same functionality as above but uses function decorators. You can think of the decorators as a replacement for the can_handle method implemented in each handler class above.

Start by creating a skill builder object. The skill builder object helps in adding components responsible for handling input requests and generating custom responses for your skill.

Type or paste the following code into your hello_world.py file.

from ask_sdk_core.skill_builder import SkillBuilder sb = SkillBuilder() 

LaunchRequest handler

The following code example shows how to configure a handler to be invoked when the skill receives a LaunchRequest. The LaunchRequest event occurs when the skill is invoked without a specific intent.

Type or paste the following code into your hello_world.py file, after the previous code.

from ask_sdk_core.utils import is_request_type, is_intent_name from ask_sdk_core.handler_input import HandlerInput from ask_sdk_model import Response from ask_sdk_model.ui import SimpleCard @sb.request_handler(can_handle_func=is_request_type("LaunchRequest")) def launch_request_handler(handler_input): # type: (HandlerInput) -> Response speech_text = "Welcome to the Alexa Skills Kit, you can say hello!" handler_input.response_builder.speak(speech_text).set_card( SimpleCard("Hello World", speech_text)).set_should_end_session( False) return handler_input.response_builder.response 

Similar to the can_handle function for the LaunchRequestHandler in the Class pattern, the decorator returns True if the incoming request is a LaunchRequest. The handle function generates and returns a basic greeting response in the same way the handle function works for the Class pattern.

HelloWorldIntent handler

The following code example shows how to configure a handler to be invoked when the skill receives an intent request with the name HelloWorldIntent. Type or paste the following code into your hello_world.py file, after the previous handler.

@sb.request_handler(can_handle_func=is_intent_name("HelloWorldIntent")) def hello_world_intent_handler(handler_input): # type: (HandlerInput) -> Response speech_text = "Hello World!" handler_input.response_builder.speak(speech_text).set_card( SimpleCard("Hello World", speech_text)).set_should_end_session( True) return handler_input.response_builder.response 

HelpIntent handler

The following code example shows how to configure a handler to be invoked when the skill receives the built-in intent AMAZON.HelpIntent. Type or paste the following code into your hello_world.py file , after the previous handler.

@sb.request_handler(can_handle_func=is_intent_name("AMAZON.HelpIntent")) def help_intent_handler(handler_input): # type: (HandlerInput) -> Response speech_text = "You can say hello to me!" handler_input.response_builder.speak(speech_text).ask(speech_text).set_card( SimpleCard("Hello World", speech_text)) return handler_input.response_builder.response 

Similar to the previous handler, this handler matches an IntentRequest with the expected intent name. Basic help instructions are returned, and .ask(speech_text) causes the user's microphone to open up for the user to respond.

CancelAndStopIntent handler

The CancelAndStopIntentHandler is similar to the HelpIntent handler, as it is also triggered by the built-in AMAZON.CancelIntent or AMAZON.StopIntent intents. The following example uses a single handler to respond to both Intents. Type or paste the following code into your hello_world.py file, after the previous handler.

@sb.request_handler( can_handle_func=lambda handler_input : is_intent_name("AMAZON.CancelIntent")(handler_input) or is_intent_name("AMAZON.StopIntent")(handler_input)) def cancel_and_stop_intent_handler(handler_input): # type: (HandlerInput) -> Response speech_text = "Goodbye!" handler_input.response_builder.speak(speech_text).set_card( SimpleCard("Hello World", speech_text)).set_should_end_session( True) return handler_input.response_builder.response 

In the above example, can_handle needs a function to be passed. is_intent_name returns a function, but we need to check if the request is either AMAZON.CancelIntent or AMAZON.StopIntent. We achieve this by creating an anonymous function on the fly using Python's in-built lambda function.

The response to both intents is the same, so having a single handler reduces repetitive code.

SessionEndedRequest handler

Although you cannot return a response with any speech, card or directives after receiving a SessionEndedRequest, the SessionEndedRequestHandler is a good place to put your cleanup logic. Type or paste the following code into your hello_world.py file, after the previous handler.

@sb.request_handler(can_handle_func=is_request_type("SessionEndedRequest")) def session_ended_request_handler(handler_input): # type: (HandlerInput) -> Response # any cleanup logic goes here return handler_input.response_builder.response 

Implementing exception handlers

The following sample adds a catch all exception handler to your skill, to ensure the skill returns a meaningful message in case of all exceptions. Type or paste the following code into your hello_world.py file, after the previous handler.

@sb.exception_handler(can_handle_func=lambda i, e: True) def all_exception_handler(handler_input, exception): # type: (HandlerInput, Exception) -> Response # Log the exception in CloudWatch Logs print(exception) speech = "Sorry, I didn't get it. Can you please say it again!!" handler_input.response_builder.speak(speech).ask(speech) return handler_input.response_builder.response 

Creating the Lambda handler

Note: For a custom skill, you can host your service in AWS Lambda or as a web service hosted on your own endpoint.

Generally, hosting the skill code on AWS Lambda is the easiest way. The below sections provide information on how to achieve this.

However, if you wish to host it with any other cloud hosting provider, the SDK provides some support packages ( ask-sdk-webservice-support , flask-ask-sdk , django-ask-sdk ). You can find more information on this configuration here.

The Lambda handler is the entry point for your AWS Lambda function. The following code example creates a Lambda handler function to route all inbound requests to your skill. The Lambda Handler function creates an SDK skill instance configured with the request handlers that you just created.

Type or paste the following code into your hello_world.py file, after the previous handler.

handler = sb.lambda_handler() 

When using decorators, your request handlers and exception handlers are automatically recognized by the Skill Builder object instantiated at the top of the code.

Full source code

The full source code for hello_world.py can be found here.

Preparing your code for AWS Lambda

Your code is now complete and you need to create .zip files that contain the files ready to upload to Lambda.

When you upload your code to AWS Lambda, you must include your skill code and its dependencies inside a zip file as a flat file structure, so you'll place your code in the same folder as the ASK SDK for Python, before zipping it.

If you set up the SDK using a virtual environment, the dependencies are installed in the site-packages folder in your virtual environment. So, navigate to the site-packages folder in skill_env .

Note: On Windows the site-packages folder is located inside the skill_env\Lib folder.

Note: For macOS/Linux the site-packages folder location depends on the version of Python you are using. For instance Python 3.8 users will find site-packages inside the skill_env/lib/Python3.8 folder.

Copy the hello_world.py file into the site-packages folder and create a .zip file of the contents of the folder (not the folder itself). Name the file skill.zip . You can check the AWS Lambda docs to get more information on creating a deployment package.

If you set up the SDK in a specific folder, the dependencies are installed in that specific folder. That would be skill_env folder if you followed the steps mentioned in the getting started guide.

Copy the hello_world.py file into the skill_env folder and create a .zip file of the contents of the folder (not the folder itself). Name the file skill.zip . You can check the AWS Lambda docs to get more information on creating a deployment package.

Before uploading the code to AWS Lambda, you need to create an AWS Lambda function and create the skill on the Alexa Developer Portal.

Creating an AWS Lambda function

Refer to Hosting a Custom Skill as an AWS Lambda Function for a walkthrough on creating an AWS Lambda function with the correct role for your skill. When creating the function, select the Author from scratch option and select Python 3.8 or later runtime.

Once you've created your AWS Lambda function, it's time to give the Alexa service the ability to invoke it. To do this, navigate to the Triggers tabs in your Lambda's configuration, and add Alexa Skills Kit as the trigger type. Once this is done, upload the skill.zip file produced in the previous step and fill in the handler information with module_name.handler which is hello_world.handler for this example.

Configuring and testing your skill

Now that the skill code has been uploaded to AWS Lambda, you can configure the skill with Alexa.

 say hello say hello world hello say hi say hi world hi how are you 
  "interactionModel":  "languageModel":  "invocationName": "greeter", "intents": [  "name": "AMAZON.CancelIntent", "samples": [] >,  "name": "AMAZON.HelpIntent", "samples": [] >,  "name": "AMAZON.StopIntent", "samples": [] >,  "name": "HelloWorldIntent", "slots": [], "samples": [ "how are you", "hi", "say hi world", "say hi", "hello", "say hello world", "say hello" ] > ], "types": [] > > > 
  1. Under your skill, click the Endpoint tab, select AWS Lambda ARN, and copy the Skill ID of the skill you just created.
  2. Open the AWS Developer Console in a new tab.
  3. Navigate to the AWS Lambda function created in the previous step.
  4. From the Designer menu, add the Alexa Skills Kit trigger menu, and scroll down to paste the skill ID into the Skill ID Verification configuration. Click Add and save once completed to update the AWS Lambda function.
  5. Copy the AWS Lambda function ARN from the top right corner of the page. An ARN is a unique resource number that helps Alexa service identify the AWS Lambda function it needs to call during skill invocation.
  6. Navigate to the Alexa Skills Kit Developer Console, and click on your HelloWorld skill.
  7. Under your skill, click Endpoint tab, select AWS Lambda ARN and paste in the ARN under Default Region field.
  8. The rest of the settings can be left at their default values. Click Save Endpoints.
  9. Click Invocation tab, save and build the model.

Source code for Hello World sample skill

The following code shows the full source code for Hello World skill that uses classes and decorators.

# -*- coding: utf-8 -*- # This is a simple Hello World Alexa Skill, built using # the implementation of handler classes approach in skill builder. import logging from ask_sdk_core.skill_builder import SkillBuilder from ask_sdk_core.dispatch_components import AbstractRequestHandler from ask_sdk_core.dispatch_components import AbstractExceptionHandler from ask_sdk_core.utils import is_request_type, is_intent_name from ask_sdk_core.handler_input import HandlerInput from ask_sdk_model.ui import SimpleCard from ask_sdk_model import Response sb = SkillBuilder() logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) class LaunchRequestHandler(AbstractRequestHandler): """Handler for Skill Launch.""" def can_handle(self, handler_input): # type: (HandlerInput) -> bool return is_request_type("LaunchRequest")(handler_input) def handle(self, handler_input): # type: (HandlerInput) -> Response speech_text = "Welcome to the Alexa Skills Kit, you can say hello!" handler_input.response_builder.speak(speech_text).set_card( SimpleCard("Hello World", speech_text)).set_should_end_session( False) return handler_input.response_builder.response class HelloWorldIntentHandler(AbstractRequestHandler): """Handler for Hello World Intent.""" def can_handle(self, handler_input): # type: (HandlerInput) -> bool return is_intent_name("HelloWorldIntent")(handler_input) def handle(self, handler_input): # type: (HandlerInput) -> Response speech_text = "Hello Python World from Classes!" handler_input.response_builder.speak(speech_text).set_card( SimpleCard("Hello World", speech_text)).set_should_end_session( True) return handler_input.response_builder.response class HelpIntentHandler(AbstractRequestHandler): """Handler for Help Intent.""" def can_handle(self, handler_input): # type: (HandlerInput) -> bool return is_intent_name("AMAZON.HelpIntent")(handler_input) def handle(self, handler_input): # type: (HandlerInput) -> Response speech_text = "You can say hello to me!" handler_input.response_builder.speak(speech_text).ask( speech_text).set_card(SimpleCard( "Hello World", speech_text)) return handler_input.response_builder.response class CancelOrStopIntentHandler(AbstractRequestHandler): """Single handler for Cancel and Stop Intent.""" def can_handle(self, handler_input): # type: (HandlerInput) -> bool return (is_intent_name("AMAZON.CancelIntent")(handler_input) or is_intent_name("AMAZON.StopIntent")(handler_input)) def handle(self, handler_input): # type: (HandlerInput) -> Response speech_text = "Goodbye!" handler_input.response_builder.speak(speech_text).set_card( SimpleCard("Hello World", speech_text)) return handler_input.response_builder.response class FallbackIntentHandler(AbstractRequestHandler): """ This handler will not be triggered except in supported locales, so it is safe to deploy on any locale. """ def can_handle(self, handler_input): # type: (HandlerInput) -> bool return is_intent_name("AMAZON.FallbackIntent")(handler_input) def handle(self, handler_input): # type: (HandlerInput) -> Response speech_text = ( "The Hello World skill can't help you with that. " "You can say hello!!") reprompt = "You can say hello!!" handler_input.response_builder.speak(speech_text).ask(reprompt) return handler_input.response_builder.response class SessionEndedRequestHandler(AbstractRequestHandler): """Handler for Session End.""" def can_handle(self, handler_input): # type: (HandlerInput) -> bool return is_request_type("SessionEndedRequest")(handler_input) def handle(self, handler_input): # type: (HandlerInput) -> Response return handler_input.response_builder.response class CatchAllExceptionHandler(AbstractExceptionHandler): """Catch all exception handler, log exception and respond with custom message. """ def can_handle(self, handler_input, exception): # type: (HandlerInput, Exception) -> bool return True def handle(self, handler_input, exception): # type: (HandlerInput, Exception) -> Response logger.error(exception, exc_info=True) speech = "Sorry, there was some problem. Please try again!!" handler_input.response_builder.speak(speech).ask(speech) return handler_input.response_builder.response sb.add_request_handler(LaunchRequestHandler()) sb.add_request_handler(HelloWorldIntentHandler()) sb.add_request_handler(HelpIntentHandler()) sb.add_request_handler(CancelOrStopIntentHandler()) sb.add_request_handler(FallbackIntentHandler()) sb.add_request_handler(SessionEndedRequestHandler()) sb.add_exception_handler(CatchAllExceptionHandler()) handler = sb.lambda_handler() 
# -*- coding: utf-8 -*- # This is a simple Hello World Alexa Skill, built using # the decorators approach in skill builder. import logging from ask_sdk_core.skill_builder import SkillBuilder from ask_sdk_core.utils import is_request_type, is_intent_name from ask_sdk_core.handler_input import HandlerInput from ask_sdk_model.ui import SimpleCard from ask_sdk_model import Response sb = SkillBuilder() logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) @sb.request_handler(can_handle_func=is_request_type("LaunchRequest")) def launch_request_handler(handler_input): """Handler for Skill Launch.""" # type: (HandlerInput) -> Response speech_text = "Welcome to the Alexa Skills Kit, you can say hello!" return handler_input.response_builder.speak(speech_text).set_card( SimpleCard("Hello World", speech_text)).set_should_end_session( False).response @sb.request_handler(can_handle_func=is_intent_name("HelloWorldIntent")) def hello_world_intent_handler(handler_input): """Handler for Hello World Intent.""" # type: (HandlerInput) -> Response speech_text = "Hello Python World from Decorators!" return handler_input.response_builder.speak(speech_text).set_card( SimpleCard("Hello World", speech_text)).set_should_end_session( True).response @sb.request_handler(can_handle_func=is_intent_name("AMAZON.HelpIntent")) def help_intent_handler(handler_input): """Handler for Help Intent.""" # type: (HandlerInput) -> Response speech_text = "You can say hello to me!" return handler_input.response_builder.speak(speech_text).ask( speech_text).set_card(SimpleCard( "Hello World", speech_text)).response @sb.request_handler( can_handle_func=lambda handler_input: is_intent_name("AMAZON.CancelIntent")(handler_input) or is_intent_name("AMAZON.StopIntent")(handler_input)) def cancel_and_stop_intent_handler(handler_input): """Single handler for Cancel and Stop Intent.""" # type: (HandlerInput) -> Response speech_text = "Goodbye!" return handler_input.response_builder.speak(speech_text).set_card( SimpleCard("Hello World", speech_text)).response @sb.request_handler(can_handle_func=is_intent_name("AMAZON.FallbackIntent")) def fallback_handler(handler_input): """ This handler will not be triggered except in supported locales, so it is safe to deploy on any locale. """ # type: (HandlerInput) -> Response speech = ( "The Hello World skill can't help you with that. " "You can say hello!!") reprompt = "You can say hello!!" handler_input.response_builder.speak(speech).ask(reprompt) return handler_input.response_builder.response @sb.request_handler(can_handle_func=is_request_type("SessionEndedRequest")) def session_ended_request_handler(handler_input): """Handler for Session End.""" # type: (HandlerInput) -> Response return handler_input.response_builder.response @sb.exception_handler(can_handle_func=lambda i, e: True) def all_exception_handler(handler_input, exception): """Catch all exception handler, log exception and respond with custom message. """ # type: (HandlerInput, Exception) -> Response logger.error(exception, exc_info=True) speech = "Sorry, there was some problem. Please try again!!" handler_input.response_builder.speak(speech).ask(speech) return handler_input.response_builder.response handler = sb.lambda_handler()