article

Pan@Amazon avatar image
Pan@Amazon posted

Build Your First Alexa Skill as an Alexa-Hosted Skill for Free

By Pan Wangperawong

45 minutes to complete

In this tutorial, we will walk you through building an Alexa skill for looking up acronyms called Acronym Decoder, using the new Alexa-Hosted Skills offering and ASK SDK v2. With Alexa-Hosted Skills, getting started with Alexa skill development is easier than ever. It will automatically provision a Node.js AWS Lambda that will be setup with the skill, letting you skip configuration and focus more on skill development. With this option, you could take advantage of AWS free tier for Lambda and CloudWatch Logs without an AWS account.

Overview

Before we get started, be sure to sign up for a free Alexa Developer account.

By following this guide, you will complete the following:

  • Design an interaction model
  • Setup a new custom skill using the Alexa-Hosted option
  • Create a Voice User Interface (VUI) that a user can interact with
  • Setup a Node.js AWS Lambda backend to receive and respond to intents from the VUI

Before Starting: Being Voice-First

When building a skill, it is helpful to think about the traditional web and mobile interfaces that we interact with regularly and see how those experiences relate to voice-first interfaces. With websites or mobile apps, people interact with them by visually consuming information and clicking/tapping elements on a screen. A Voice User Interface is similar, where users are provided with information through audio and interact through speech. Conceptually, the audio output is the screen and your voice is the click.

Step 1 - Design an Interaction Model

Starting with a good interaction model is important, as it is the foundation for skill development. Below is an interaction model for the acronym lookup skill. The subsequent steps will go over the implementation based on this interaction model.

User: Alexa, ask acronym dictionary to lookup A. S. K.

Alexa: A. S. K. can stand for Alexa Skills Kit

For more information about creating an Interaction Model, see Create the Interaction Model for Your Skill.

Step 2 - Create a New Skill

As we mentioned earlier, the quickest way to set up a custom skill is as an Alexa-Hosted skill. It will create a skill that is linked to an AWS Lambda with the ASK SDK v2 bundled and starter code. No AWS account or credit card is necessary. Below are the steps to create a new custom skill:

a) Click Create Skill

b) Name your skill. For this tutorial, we used the skill name Acronym Decoder

c) Select the following option: - Custom Skill - Alexa-Hosted

d) Click Create

Step 3 - Create an Intent

During this step you will use the Developer Console to create an intent and provide sample utterances that map to it. This is similar to adding a button element to the UI on a web page that triggers a some event. In our case, we are adding a voice commands that could be expressed in multiple ways to trigger a corresponding intent request from our VUI. The intent will then be handled by the Lambda function.

a) Click the + icon by the Intent section

b) Name it LookupMeaningIntent (intent names should describe its purpose)

c) Click on the LookupMeaningIntent that was newly added and provide some sample utterances. Utterances are different ways users could express their intentions to the skill. This is similar to a user clicking a button on a web page, except with speech there are multiple ways to express the same intention. Add the following sample utterances below:


 lookup A. S. K.
- tell me about A. S. K.
- what is A. S. K.
- what does A. S. K. mean

It is also highly recommended that you observe how users might use your skill to figure out what other sample utterances to add.

d) Highlight A. S. K. for each of the sample utterances that were added. A modal will appear where you could create a new slot type. Name it term.

e) Click on + for Slot Type to and add a new custom slot called Term.

f) Add the following Slot Values to Term

a. s. k.
a. v. s.
a. w. s.

Notice that a. s. k. has a period and space after each character. This is important to get Alexa to recognize acronyms. Read more about it here.

This not an exhaustive list and we would want to add more in a production skill.

g) Click term slot value under LookupMeaningIntent and for the Slot Type select Term from the dropdown menu.

h) Click Build Model

Step 4 - Add an Intent Handler

Now that we have sample utterances that users could say to trigger LookupMeaningIntent we need a backend to handle these requests. Since we are using the Alexa-Hosted option, our skill comes preconfigured with a JavaScript/Node.js AWS Lambda backend that we could use for handle intents.

a) Click on the Code tab and open index.js.

b) Add the LookupMeaningIntentHandler object right after the LaunchIntentHandler object.

const LookupMeaningIntentHandler = {  
  canHandle(handlerInput) {  
    // Checks if the type and name of request matches
    return handlerInput.requestEnvelope.request.type === 'IntentRequest'
        && handlerInput.requestEnvelope.request.intent.name === 'LookupMeaningIntent'
  },
  handle(handlerInput) {
    // Read the term slot value from the VUI
    const slotValue = handlerInput.requestEnvelope.request.intent.slots.term.value

    // Standardize the slot value to make sure the format is a letter followed by a period and space for lookup otherwise the format might be wrong and we wouldn't be able to lookup the acronym.
    const acronym = slot.toLowerCase().replace(/\s+/g, '').split('.').join('. ')

    // Add some acronyms for the example. For a published skill there should be a lot more.
    const acronyms = {
      "a. s. k.": "Alexa Skills Kit",
      "a. v. s.": "Alexa Voice Services",
      "a. w. s.": "Amazon Web Services"
    }

    // Speech text for Alexa to speak
    const speechText = `${acronym} can mean ${acronyms[acronym]}`

    return handlerInput.responseBuilder
        .speak(speechText) // have Alexa speak the meaning of the acronym
        .getResponse()
  }
}

The LookupMeaningIntentHandler and other handlers could be thought of as a router for utterance intents to intent handlers. Notice that there is a canHandle function that checks for request type being IntentRequest and intent name being LookupMeaningIntent.

c) Register the LookupMeaningIntentHandler by also passing it in the addRequestHandlers function after LaunchRequestHandler. Your updated code should look like this:

exports.handler = Alexa.SkillBuilders.custom()
    .addRequestHandlers(
        LaunchRequestHandler,
        LookupMeaningIntentHandler, // Register it as a request handler.
        HelloWorldIntentHandler,
        HelpIntentHandler,
        CancelAndStopIntentHandler,
        SessionEndedRequestHandler,
        IntentReflectorHandler)
    .addErrorHandlers(ErrorHandler)
    .lambda()

Step 5 - Test the Skill

At this point, we could test the skill on a device or simulator. We will use the simulator in this tutorial.

a) On the Developer Console click on Test.

b) When testing the skill for the first time, in the dropdown next to “Skill testing is enabled in:” select Development from the dropdown menu. This will allow you to test the development version of the skill in the simulator

If you would like to test with a device you will have to link the Echo device with your developer account. To enable devices on your account, follow the setup instructions at alexa.amazon.com.

c) In the text box on the left type the following and hit enter/return:

Alexa, ask Acronym Decoder to lookup A. S. K.

The response we should receive back would be "A. S. K can mean Alexa Skills Kit" from the array in LookupMeaningIntentHandler.

Step 6 - Clean Up

The core functionality of our skill has been implemented, but you'll notice that when the skill is launched it is speaking the default Hello World message, so let's remove/update the following

a) Remove the HelloWorldIntent from the Alexa Developer Console by doing the following: - Select Acronym Decoder skill - Click the trash can icon for HelloWorldIntent - Click Build Model

b) In Lambda, update LaunchRequestHandler's speechText message to the following:

const LaunchRequestHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'LaunchRequest'
  },
  handle(handlerInput) {
    const speechText = 'Welcome to Acronym Decoder! Ask me to lookup an acronym.' // Update here
    return handlerInput.responseBuilder
      .speak(speechText)
      .getResponse()
  }
}

c) Remove the HelloWorldIntentHandler object from addRequestHandlers. Our final updated Lambda code should look like this:

const Alexa = require('ask-sdk-core')

const LaunchRequestHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'LaunchRequest'
  },
  handle(handlerInput) {
    const speechText = 'Welcome to Acronym Decoder! Ask me to lookup an acronym.'
    return handlerInput.responseBuilder
      .speak(speechText)
      .getResponse()
  }
}

const LookupMeaningIntentHandler = {  
  canHandle(handlerInput) {  
    // Checks if the type and name of request matches
    return handlerInput.requestEnvelope.request.type === 'IntentRequest'
        && handlerInput.requestEnvelope.request.intent.name === 'LookupMeaningIntent'
  },
  handle(handlerInput) {
    // Read the term slot value from the VUI
    const slotValue = handlerInput.requestEnvelope.request.intent.slots.term.value

    // Standardize the slot value to make sure the format is a letter followed by a period and space for lookup otherwise the format might be wrong and we wouldn't be able to lookup the acronym.
    const acronym = slot.toLowerCase().replace(/\s+/g, '').split('.').join('. ')

    // Add some acronyms for the example. For a published skill there should be a lot more.
    const acronyms = {
      "a. s. k.": "Alexa Skills Kit",
      "a. v. s.": "Alexa Voice Services",
      "a. w. s.": "Amazon Web Services"
    }

    // Speech text for Alexa to speak
    const speechText = `${acronym} can mean ${acronyms[acronym]}`

    return handlerInput.responseBuilder
        .speak(speechText) // have Alexa speak the meaning of the acronym
        .getResponse()
  }
}

const HelpIntentHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'IntentRequest'
        && handlerInput.requestEnvelope.request.intent.name === 'AMAZON.HelpIntent'
  },
  handle(handlerInput) {
    const speechText = 'You can say hello to me! How can I help?'

    return handlerInput.responseBuilder
      .speak(speechText)
      .reprompt(speechText)
      .getResponse()
  }
}

const CancelAndStopIntentHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'IntentRequest'
        && (handlerInput.requestEnvelope.request.intent.name === 'AMAZON.CancelIntent'
            || handlerInput.requestEnvelope.request.intent.name === 'AMAZON.StopIntent')
  },
  handle(handlerInput) {
    const speechText = 'Goodbye!'
    return handlerInput.responseBuilder
        .speak(speechText)
        .getResponse()
  }
}

const SessionEndedRequestHandler = {
  canHandle(handlerInput) {
      return handlerInput.requestEnvelope.request.type === 'SessionEndedRequest'
  },
  handle(handlerInput) {
    // Any cleanup logic goes here.
    return handlerInput.responseBuilder.getResponse()
  }
}

const IntentReflectorHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'IntentRequest'
  },
  handle(handlerInput) {
    const intentName = handlerInput.requestEnvelope.request.intent.name
    const speechText = `You just triggered ${intentName}`

    return handlerInput.responseBuilder
        .speak(speechText)
        //.reprompt('add a reprompt if you want to keep the session open for the user to respond')
        .getResponse()
  }
}

const ErrorHandler = {
  canHandle() {
      return true
  },
  handle(handlerInput, error) {
      console.log(`~~~~ Error handled: ${error.message}`)
      const speechText = `Sorry, I couldn't understand what you said. Please try again.`

      return handlerInput.responseBuilder
          .speak(speechText)
          .reprompt(speechText)
          .getResponse()
  }
}

exports.handler = Alexa.SkillBuilders.custom()
  .addRequestHandlers(
    LaunchRequestHandler,
    LookupMeaningIntentHandler,
    HelpIntentHandler,
    CancelAndStopIntentHandler,
    SessionEndedRequestHandler,
    IntentReflectorHandler
  )
  .addErrorHandlers(ErrorHandler)
  .lambda()

Summary

In this tutorial we learned how to build an Alexa skill called Acronym Decoder using the Alexa-Hosted option with Node.js. To achieve this we did the following:

  • Designed a way for users to naturally interact with Alexa for jokes through our interaction model design
  • Created a VUI for Alexa to receive utterances or commands to perform an intent or task.
  • Linked the VUI to the Node.js AWS Lambda backend to provided acronym meanings which Alexa speaks

Congratulations, on completing the tutorial for building an Alexa skill. Take what you learn today and build something awesome!

For more tips on skill building follow me on Twitter @itspanw.

Resources

alexa skills kitalexatutorialsdk v2hosted skill
10 |3000 characters needed characters left characters exceeded

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

Article

Contributors

petertong contributed to this article