Adding Slots to an Intent

The Star Port 75 Travel skill needs to be able to book trips for spacefaring adventurers based on three parameters: the destination, the departure date, and the return date. To enable this, we’ll add a new intent to the skill that accepts those parameters in slots. But first, let’s start by writing a BST test specification that captures and asserts what we want the intent to do:

 ---
 configuration:
  locales: ​en-US
 
 ---
 - test: ​Schedule a trip
 - ​"​​Schedule​ ​a​ ​trip​ ​to​ ​Mercury​ ​leaving​ ​Friday​ ​and​ ​returning​ ​next​ ​Thursday"​:
  - prompt: ​I've got you down for a trip to Mercury,
 leaving on Friday and returning next Thursday.
 
 ---
 - test: ​Plan a trip
 - ​"​​Plan​ ​a​ ​trip​ ​between​ ​June​ ​9th​ ​and​ ​June​ ​17th​ ​to​ ​visit​ ​Mars"​:
  - prompt: ​I've got you down for a trip to Mars,
 leaving on June 9th and returning June 17th.

These tests cover two possible ways of wording a request to schedule a trip. But even more interesting, they each cover different values for the three parameters and assert that those values are reflected in the intent’s response.

The first step toward making these tests pass is to define the new intent in the interaction model. As with the HelloWorldIntent, we’ll add a new entry to skill-package/interactionModels/custom/en-US.json within the intents property. And, just like HelloWorldIntent, we’ll give it a name and a list of sample utterances. But as you can see from this interaction model excerpt, ScheduleTripIntent has a few new tricks:

 "intents": [ ... { "name": "ScheduleTripIntent", "samples": [ "schedule a trip to {destination} leaving {departureDate} and returning {returnDate}", "plan a trip between {departureDate} and {returnDate} to visit {destination}" ], "slots": [ { "name": "destination", "type": "AMAZON.City" }, { "name": "departureDate", "type": "AMAZON.DATE" }, { "name": "returnDate", "type": "AMAZON.DATE" } ] } ]

The first thing you’ll notice is that the sample utterances don’t have any explicitly stated destination or date values. Instead, they have placeholders in the form of variable names wrapped in curly-braces. These represent the places in the utterance where the slots will be provided.

The slots themselves are defined in the slots property. Each has a name and a type. The name must match exactly with the placeholder in the utterances. As for type, Amazon provides several built-in types,[15] including types for dates, numbers, and phone numbers. Amazon also provides nearly 100 slot types that identify a list of potential values such movie names, sports, book titles, and cities.

The slots defined in ScheduleTripIntent take advantage of two of Amazon’s built-in types: AMAZON.City and AMAZON.DATE. It makes sense that the “departureDate” and “returnDate” slots are typed as AMAZON.DATE. But you might be wondering why “destination” is defined as AMAZON.City. Put simply, it’s because Amazon doesn’t define a built-in type for “planets” or any other astronomical locations. We’ll create a custom type for planets in the next section. But until we get around to that, AMAZON.City will be a fine temporary stand-in.

Slot types are used as hints to help Alexa’s natural language processing match what a user says to an intent. For example, suppose that the user asks to plan a trip to Seattle, but pronounces the city as “see cattle.” The natural language processor may hear “see cattle,” but since that sounds a lot like “Seattle,” it can infer that the user meant “Seattle” based on the slot type AMAZON.City.

On the other hand, suppose that the user asks to plan a trip to “Jupiter,” which is not a city included in the AMAZON.City type. Alexa’s natural language processor will hear “Jupiter” and since no entry in the AMAZON.City type sounds anything like that, it will give the user benefit of the doubt and give “Jupiter” as the slot’s value.

Now that we’ve defined the intent, sample utterances, and slots in the interaction model, we need to write the intent handler. Rather than pile it on along with the other intent handlers in index.js, let’s split it out into its own JavaScript module. This will help keep the skill’s project code more organized and prevent index.js from growing unwieldy. The intent handler’s code, defined in lambda/ScheduleTripIntentHandler.js, looks like this:

 const​ Alexa = require(​'ask-sdk-core'​);
 
 const​ ScheduleTripIntentHandler = {
  canHandle(handlerInput) {
 return​ Alexa.getRequestType(
  handlerInput.requestEnvelope) === ​'IntentRequest'
  && Alexa.getIntentName(
  handlerInput.requestEnvelope) === ​'ScheduleTripIntent'​;
  },
  handle(handlerInput) {
 const​ destination =
  Alexa.getSlotValue(handlerInput.requestEnvelope, ​'destination'​);
 const​ departureDate =
  Alexa.getSlotValue(handlerInput.requestEnvelope, ​'departureDate'​);
 const​ returnDate =
  Alexa.getSlotValue(handlerInput.requestEnvelope, ​'returnDate'​);
 
 const​ speakOutput = handlerInput.t(​'SCHEDULED_MSG'​,
  {
  destination: destination,
  departureDate: departureDate,
  returnDate: returnDate
  });
 
 return​ handlerInput.responseBuilder
  .speak(speakOutput)
  .withShouldEndSession(​true​)
  .getResponse();
  },
 };
 
 module.exports=ScheduleTripIntentHandler;

You’ll want to be sure to register this new intent handler with the skill builder, just like we did with HelloWorldIntentHandler. The intent handler can be imported into index.js using require() and then added to the list of handlers like this:

 const​ HelloWorldIntentHandler = require(​'./HelloWorldIntentHandler'​);
»const​ ScheduleTripIntentHandler = require(​'./ScheduleTripIntentHandler'​);
 const​ StandardHandlers = require(​'./StandardHandlers'​);
 
  ...
 
 exports.handler = Alexa.SkillBuilders.custom()
  .addRequestHandlers(
  HelloWorldIntentHandler,
» ScheduleTripIntentHandler,
  StandardHandlers.LaunchRequestHandler,
  StandardHandlers.HelpIntentHandler,
  StandardHandlers.CancelAndStopIntentHandler,
  StandardHandlers.FallbackIntentHandler,
  StandardHandlers.SessionEndedRequestHandler,
  StandardHandlers.IntentReflectorHandler)
  .addErrorHandlers(
  StandardHandlers.ErrorHandler)
  .addRequestInterceptors(
  LocalisationRequestInterceptor)
  .lambda();

As with the HelloWorldIntentHandler, ScheduleTripIntentHandler is defined by two functions: canHandle() to determine if the intent handler is capable of handling the request’s intent, and handle() to handle the request if so.

The most significant and relevant difference in ScheduleTripIntentHandler, however, is in the first few lines of the handle() function. They use the Alexa.getSlotValue() function to extract the values of the “destination”, “departureDate”, and “returnDate” slots and assign them to constants of their own. Those constants are referenced in an object passed to the t() function when looking up the “SCHEDULED_MSG” message assigned to speakOutput.

For that to work, we’ll need to define “SCHEDULED_MSG” to languageStrings.js.

 module.exports = {
  en: {
  translation: {
  ...
  SCHEDULED_MSG: ​"I've got you down for a trip to {{destination}}, "​ +
 "leaving on {{departureDate}} and returning {{returnDate}}"​,
  ...
  }
  }
 }

As you can see, embedded within the “SCHEDULED_MSG” string are placeholders, denoted by double-curly-braces, that will be replaced with the properties from the object we passed to the t() function.

With the new intent defined in the interaction model and its corresponding intent handler and language string written, we’ve just added basic trip scheduling support to the skill. Let’s run the tests and see if it works:

 % bst test --jest.collectCoverage=false schedule-trip.test.yml
 
 BST: v2.6.0 Node: v17.6.0
 Did you know? You can use the same YAML syntax for both your end-to-end
 and unit tests. Find out more at https://read.bespoken.io.
 
  PASS test/unit/schedule-trip.test.yml
  en-US
  Schedule a trip
  ✓ Schedule a trip to Mercury leaving Friday and returning next Thursday
  Plan a trip
  ✓ Plan a trip between June 9th and June 17th to visit Mars
 
 Test Suites: 1 passed, 1 total
 Tests: 2 passed, 2 total
 Snapshots: 0 total
 Time: 1.128s, estimated 2s
 Ran all test suites.

As you can see, both tests passed! We’ve successfully used slots to handle variable input from the user when booking a trip! For brevity’s sake, we can run bst test with --jest.collectCoverage=false so that the test coverage report is not in the output. And, the test specification’s name is passed as a parameter in order to focus on the tests relevant to our new intent.

Although it works, there’s still room for improvement. Ultimately our skill is for planning interplanetary travel. Therefore, AMAZON.City isn’t really the best slot type for our needs. But before we swap it out for a custom planets slot type, let’s take a quick look at how some entities may offer more information than just the entity’s name.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset