Lab 3: Create a Skill by using ACDL in Alexa Conversations

Welcome to lab 3 of our introductory course on building an engaging Alexa Conversations skill by using ACDL. In this lab, we'll learn how to create a skill called "Flight Search" with step-by-step instructions.

Time required: 60 minutes

What you'll learn

  • ACDL artifacts
  • Changing invocation name
  • How to add audio response templates
  • How to add types
  • How to add APIs
  • How to add utterance sets
  • How to add dialogs
  • How to create your skill backend code

ACDL artifacts

There are four key ACDL artifacts: Types, Actions, Events and Dialogs. The first three (Types, Actions, Events) are dependencies for dialogs.

  • Types are equivalent to data type representations in any programming language. They serve a variety of functions for skill building and define the data structures for request and response payloads, slot values, etc.

  • Events are equivalent to utterance groups in the developer console. Each event is linked to a type which defines the slot inputs a user may provide within those utterances.

  • Actions are always triggered by events. Actions define which back-end APIs are invoked on a given turn and define how to respond events within the context of an ongoing dialog.

  • Dialogs pull together all the other ACDL assets to create templatized sample interactions that form the basis of your experience.

Step-by-Step: Build Flight Search Skill

Step 1: Changing invocation name

Let's change the skill invocation name. Skill invocation name is found under skill-package/interactionModels/custom/en-US.json. You will see the current invocation name is "change me", so let's change the skill invocation name to "flight search". This invocation name will be used when customers open your skill. They can say "Alexa, open flight search".

Step 2: How to add audio response templates

Audio responses are under "response/prompts" folder. By default, we will have AlexaConversationsBye, AlexaConversationsOutOfDomain, Alexa ConversationsRequestMore, AlexaConversationsWelcome and AlexaConversationsProvideHelp response prompts. We will start by updating the welcome prompt. Let's open the document.json under AlexaConversationsWelcome folder and replace the "content" with our "flight search" welcome message.

"content": "Welcome to the flight search. I can help you find cheap flights in the main cabin. What do you want to do?"

This is the welcome message that Alexa will prompt when the skill is invoked. Your AlexaConversationsWelcome prompt will look as indicated below:

Now, let's add a prompt for departure city when Alexa asks the user to provide the city name.

  1. First, create a new folder under "response/prompts" folder, and name it "RequestDepartureCityPrompt"
  2. Create a new "document.json" file under that "RequestDepartureCityPrompt"
  3. We will create the content of the "document.json" as follows:

Okay, it's easy, right? This prompt might look a bit different from the previous one. The reason is that we have two prompts (see under "items") from which Alexa will randomly pick.

Let's create the remaining response templates for arrival, date, confirmation and final response prompts by following the same steps.

  • "RequestArrivalCityPrompt":

  • "RequestDatePrompt"

  • "FlightSearchConfirmPrompt"

  • "FlightSearchResponsePrompt"

Your response folder should look as follows:

response/prompts folder

Once we created the response templates, now we are ready to create "types" in ACDL.

Step 3: How to add types

Types define data structures which are declared with the keyword “type”. In the Flight Search skill, we will need to collect departure city, arrival city, and the date from customers. Therefore, we will create a type to collect these three built-in slots. As a response, we will return date, time, airline, and the cost for the given cities. We will create a custom slot type for the airlines. "FlightDetails" type, which we will create later, will contain arrivalCity, departureCity, date, time, airline, and cost. We will also create a "FlightDetailsPayload" type for the payload which will have a child referral to "FlightDetails".

  1. Let's open the en-US.json file under skill-package/interactionModels/custom folder.
  2. We will add a new custom type called "Airline" in "types" object as follows:

Copied to clipboard.

{
    "values": [
    {
        "name": {
            "value": "Lightning Airways"
        }
    },
    {
        "name": {
            "value": "Hippogriff Air Lines"
        }
    },
    {
        "name": {
            "value": "Harpy Intercontinental"
        }
    },
    {
        "name": {
            "value": "Griffin Air"
        }
    }
    ],
    "name": "Airline"
}

Your json file should look as noted below:

  1. Now we are going to change the name of the ACDL file (skill-package/conversations/empty.acdl) to "flightSearch.acdl".

  2. Let's open the "flightSearch.acdl" and change the namespace to "com.flightsearch".

  3. We will import conversations, custom slot type and built-in types to the ACDL file:

Copied to clipboard.

import com.amazon.alexa.ask.conversations.*
import com.amazon.ask.types.builtins.AMAZON.US_CITY
import com.amazon.ask.types.builtins.AMAZON.DATE
import com.amazon.ask.types.builtins.AMAZON.NUMBER
import com.amazon.ask.types.builtins.AMAZON.TIME
import slotTypes.Airline
  1. Create an input type for the skill to collect departureCity, arrivalCity and date

Copied to clipboard.

type FlightSearchDetails {
  optional DATE date
  optional US_CITY arrivalCity
  optional US_CITY departureCity
}

We create a type "FlightSearchDetails" with three optional slots, so customers can provide any permutations of these three slots or they might provide none of them.

  1. Create an output type for the skill to return the flight details

Copied to clipboard.

type FlightDetails {
  DATE date
  NUMBER cost
  US_CITY arrivalCity
  US_CITY departureCity
  TIME time
  Airline airline
}
  1. Create a payload for the output

Copied to clipboard.

type FlightDetailsPayload {
  FlightDetails flightResponse
}

Here is the ACDL file that we created so far:

Step 4: How to add APIs

We are still working on our "flightsearch.acdl" file, and now, we will create an action which will invoke a back-end API and return flight details to us. Our backend API will be named "FlightFinder". The below code is our API FlightFinder which requires date, arrival city and departure city. It will return the FlightDetails type object we created in step 3. Copy the below code under the FlightDetailsPayload type.

Copied to clipboard.

action FlightDetails FlightFinder(DATE date, US_CITY arrivalCity, US_CITY departureCity)

This is the declaration of our action which we will use in the dialog.

action ReturnType <action_name>(Type arg1, Type arg2, ...)

ReturnType → FlightDetails

action_name → FlightFinder

Type arg1 → Date date

Type arg2 → US_CITY arrivalCity

Type arg3 → US_CITY departureCity

"flightsearch.acdl" file should look as noted below:

Step 5: How to add utterance sets

The last piece you need before you can start working on your dialogs are utterance sets. They are a collection of unique utterance variations your customer might say that deviate from your happy path. Although Alexa Conversations automatically predicts how your customer may deviate from your happy path, utterance sets allow you to directly influence what Alexa Conversations adds to your model.

For example, one utterance set will represent a set of utterances the Customer might say to request a flight search that includes all three slots you need to call the API. Utterance sets that belong to a group must include the same slots. Since we created an input with 3 optional slots, we can create one utterance set for the following cases:

  • Customer requests a flight search
  • Customer requests a flight search and provides departure city, arrival city and date
  • Customer requests a flight search and provides departure city, arrival city
  • Customer requests a flight search and provides departure city, date
  • Customer requests a flight search and provides arrival city, date
  • Customer requests a flight search and provides departure city
  • Customer requests a flight search and provides arrival city
  • Customer requests a flight search and provides date

In ACDL, the given utterances represent variations of the utterances that trigger the event and returns an utterance event.

action UtteranceEvent<T> utterances<T>(List<Utterance<T>> samples)

Let's create an utterance set in our "flightsearch.acdl" file which covers the cases we defined above, you will start adding the utterances to the under the action we created in previous step:

Let's also create "Affirm" utterance set for the customer:

Copied to clipboard.

@locale(
    Locale.en_US
  )
  AffirmUtterances = utterances<Nothing>(
    [
      "correct",
      "OK",
      "Yeap",
      "Yep",
      "Yes"
    ]
  )

We use the type "Nothing" for affirm utterance group, so we need to import it to the ACDL. Add the following code to import Nothing. You should add this code to the section where you previously created imports (top of your acdl code):

Copied to clipboard.

import com.amazon.alexa.schema.Nothing

You are doing great, almost done! Here you can find a snapshot of your ACDL code to compare what you created so far:

Step 6: How to add dialogs

So far, we learned how to create responses, utterence sets, types, and APIs. We have all skill assets required for starting to write our dialog samples.

We will use response templates that we created earlier, but first we need to import them. We will add this import statement above the first type definition in our "flightsearch.acdl" file.

Copied to clipboard.

import prompts.*

Let's change the dialog name to "FlightSearch" and add locale annotation.

Copied to clipboard.

@locale(
    Locale.en_US
  )
  dialog Nothing FlightSearch {
    sample {

    }
}

Now, we can create our dialog sample. Our dialog will expect the invocation from customers which can contain any permutations of the three slots, or none of them.

findCheapFlights = expect (Invoke, FlightSearchUtterances)

We created a dialog path for the slot collection through invocation from customers. However, what if customers don't provide all three slots in the invocation? We require all three slots for our API call to find the cheapest flight. Therefore, we will use "ensure" action here, which will make sure that all three slots are collected from customers before moving forward in the dialog flow. "Ensure" action helps to create dialog variations by using request args for the missing slots.

ensure(
    RequestArguments {arguments = [FlightFinder.arguments.departureCity], response = RequestDepartureCityPrompt},
    RequestArguments {arguments = [FlightFinder.arguments.arrivalCity], response = RequestArrivalCityPrompt},
    RequestArguments {arguments = [FlightFinder.arguments.date], response = RequestDatePrompt}
)

Once customers provide all three slots, we would like to confirm that all slots are collected correctly. To do that, we will use "ConfirmAction".

response(
    response = MultiModalResponse {
      apla = FlightSearchConfirmPrompt
    },
    act = ConfirmAction {
      actionName = FlightFinder
    },
    payload = FlightSearchDetails {
      date = findCheapFlights.date,
      arrivalCity = findCheapFlights.arrivalCity,
      departureCity = findCheapFlights.departureCity
    }
  )

In our dialog, we expect customers to either "affirm" or "deny" the collected slots. As a developer, you can define the deny paths along with the affirm paths in your dialog samples, however, Alexa Conversations will also create the deny paths for the simulation out-of-the-box if you don't create the deny paths. In this example, we will just create the affirm path and let Alexa Conversation handle the deny path.

expect (Affirm, AffirmUtterances)

Once the customer has confirmed all slots, we can call the "FlightFinder" API and assign results to the variable (flightResult) we just created.

flightResult = FlightFinder(
    date = findCheapFlights.date,
    arrivalCity = findCheapFlights.arrivalCity,
    departureCity = findCheapFlights.departureCity
  )

Now, we can create the response prompt in Alexa based on the API response.

response(
    response = MultiModalResponse {
      apla = FlightSearchResponsePrompt
    },
    act = Notify {
      success = true,
      actionName = FlightFinder
    },
    payload = FlightDetailsPayload {
      flightResponse = flightResult
    },
  )
}

Here is the dialog we put together so far:

We finished making the changes in our ACDL file, so let's compile our changes. You can compare your flightsearch.acdl file with the following snapshot:

To compile the skill artifacts, you need to open the terminal and navigate to your flight search skill main directory (we created a folder called "FlightSearch"). Then, enter the following command:

Copied to clipboard.

askx compile

Step 7: How to create your skill backend code

We completed our skill artifacts and compiled our skill. Now let's work on our backend logic. We will create a lambda handler to handle our API call and we will create a simple database which contains the flight information for the search. Let's start!

  1. Open the "lambda" folder in your flightsearch directory. We will start by creating the "package.json" file. Copy the below code to the created "package.json".

Copied to clipboard.

{
  "name": "flightsearch",
  "version": "1.2.0",
  "description": "alexa utility for quickly building skills",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Amazon Alexa",
  "license": "Apache License",
  "dependencies": {
    "ask-sdk-core": "^2.7.0",
    "ask-sdk-model": "^1.19.0",
    "aws-sdk": "^2.326.0"
  }
}
  1. We will create a simple database in the lambda directory which has flight information for some cities. Create a file called "flight-data.json" and copy the below code. To keep it simple, we will only search for departure and arrival city without actual dates.

  1. Let's create a flightSearch.js file in lambda folder for a simple search function to query our flight-data.json database.

Copied to clipboard.

const FLIGHT_DATA = require('./flight-data.json');

// query to the flight database
module.exports.getFlightData = (cityFrom, cityTo) => {
    if (cityFrom && FLIGHT_DATA[cityFrom] && cityTo && FLIGHT_DATA[cityFrom][cityTo] ) {
        return FLIGHT_DATA[cityFrom][cityTo]
    }
    return {
        "cost": "",
        "time": "",
        "airline": ""
    };
};
  1. Now, let's work on utility functions that we will use in our beckend; create a "util.js" file in lambda folder to keep common functions that we will use in the backend. Copy the below code to the "util.js":

  1. We are ready to create our lambda handler now. In our lambda handler, we will have our API handler for flight search, session ended request handler and error handler. Let's create a file called "index.js" and copy the below code.

  1. You are ready to deploy your skill now! You can run the following command in your skill directory to start the deployment process. It will take around 20-30 mins.

Copied to clipboard.

askx deploy

Step 8: Test your skill

Once the deployment is completed, you are ready to test your skill! You have two options to test:

  1. You can use your echo devices that are linked to your account and invoke your skill by saying "Alexa, open flight search"
  2. You can test your skill in developer console. If you prefer this option:
Developer Console step 1
Click on Developer Console
  • Click on Alexa Skills kit
Developer Console step 2
Click to Alexa Skills kit
  • It will open the page where you can see all of your skills. Here, click on the Flight Search skill
  • Next, click on the Test tab
Developer Console step 3
Test Tab
  • If it states that the test is disabled for this skill, click on the dropdown list and choose "Development"
Developer Console step 4
Enabling Test
  • Now, you can use the simulator to test your skill
Developer Console step 5
Using Simulator

Wrap Up

Congratulations! You have built your Alexa Conversations skill by using ACDL.

There is still a lot to learn! In the next lab, you will expand the skill by adding a multimodal support to the skill response.


Was this page helpful?