Step 3: Handling Other Questions

Adding a No Intent

Before you move forward with answering other questions, add the built in AMAZON.NoIntent to your Interaction model. Add a few sample utterances like “no”, “no thanks”, and “nope”. This way, when Alexa asks the user if there is anything else it can help with, the user can either ask another question or say no to end the conversation. Save and build the model and download the interaction model locally. Add a NoIntentHandler to index.js and add it to the list of request handlers.

const NoHandler = {
    canHandle(handlerInput) {
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
            && (Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.NoIntent');
    },
    handle(handlerInput) {
        console.log('------ ENTERING NoHandler -----')

        const { responseBuilder } = handlerInput;

        return responseBuilder
            .speak('If you need anything else, head to our website. Goodbye!')
            .getResponse();

    }
}

//...other code
exports.handler = Alexa.SkillBuilders.custom()
    .addRequestHandlers(
        LaunchRequestHandler,
        FindBranchHandler,
        HelpIntentHandler,
        NoHandler,
        CancelAndStopIntentHandler,
        FallbackIntentHandler,
        SessionEndedRequestHandler,
        IntentReflectorHandler)
    .addErrorHandlers(
        ErrorHandler)
    .withCustomUserAgent('sample/hello-world/v1.2')
    .lambda();

Adding a General Question Intent

To capture general questions that you can then pass as a query in a Vertical Search, you need a way to trigger a new intent while also capturing what the user is saying. In your Interaction model, you’ll add a QuestionIntent that will trigger when the user starts a sentence with an interrogative word . After the word the first word, you’ll place a slot of type AMAZON.SearchQuery that will capture the remainder of the user’s sentence. You can check out the other built-in Alexa slot types here .

{
  "interactionModel": {
    "languageModel": {
      "invocationName": "second national",
      "intents": [
        {
          "name": "AMAZON.CancelIntent",
          "samples": []
        },
        {
          "name": "AMAZON.HelpIntent",
          "samples": []
        },
        {
          "name": "AMAZON.StopIntent",
          "samples": []
        },
        {
          "name": "FindBranchIntent",
          "slots": [],
          "samples": [
            "where is the closest branch",
            "where is the closest location",
            "where is the nearest branch",
            "where is the nearest location",
          ]
        },
        {
          "name": "QuestionIntent",
          "slots": [
            {
              "name": "Query",
              "type": "AMAZON.SearchQuery"
            }
          ],
          "samples": [
            "are {query}",
            "can {query}",
            "do {query}",
            "which {query}",
            "who {query}",
            "where {query}",
            "when {query}",
            "how {query}",
            "which {query}",
            "what {query}"
          ]
        },
        {
          "name": "AMAZON.NavigateHomeIntent",
          "samples": []
        },
        {
          "name": "AMAZON.NoIntent",
          "samples": [
            "no thank you",
            "no thanks",
            "nope",
            "no"
          ]
        }
      ],
      "types": []
    }
  },
  "version": "4"
}

Handling FAQ Intent

You’ll need to add a new method to api.js in order to query answers for FAQs.

// api.js

const retrieveFaqAnswer = async (query) => {
  try {
    searchResults = await core.verticalSearch({ query, verticalKey: 'faqs', limit: 1})

    if(searchResults && searchResults.verticalResults.results.length > 0){
      console.log('FAQ Found');
      const faqData = searchResults.verticalResults.results[0].rawData;
      const question = faqData.question;
      const answer = faqData.answer;
      return { question, answer }
    } else {
      console.log('No FAQ Found');
      return {
        answer: `Sorry, I don't have an answer for that.`
      }
    }
  } catch (err) {
    console.log(`Answers Error: ${err}`);
    return {
      answer: 'Uh Oh! It looks like something went wrong. Please try again.'
    }
  }
}

Add a new QuestionIntentHandler to index.js. The query that will be passed to Search is derived from the Query slot in the QuestionIntent.

//index.js

const QuestionIntentHandler = {
    canHandle(handlerInput) {
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
            && Alexa.getIntentName(handlerInput.requestEnvelope) === 'QuestionIntent';
    },
    async handle(handlerInput) {
        console.log('------ ENTERING QuestionHandler -----')

        const { responseBuilder, requestEnvelope } = handlerInput;

        // query is derived from the 'Query' slot value in the QuestionIntent
        const query = Alexa.getSlotValue(requestEnvelope, 'Query');
        const { question, answer } = await retrieveFaqAnswer(query);

        if(question){
            return responseBuilder
                // REPROMPT_MESSAGE is a string constant containing 'Is there anything else I can help you with today?'
                .speak(answer + REPROMT_MESSAGE)
                .withSimpleCard(question, answer)
                .reprompt(REPROMT_MESSAGE)
                .getResponse();
        } else {
            return responseBuilder
                .speak(answer + REPROMT_MESSAGE)
                .reprompt(REPROMT_MESSAGE)
                .getResponse();
        }
    }
};

When adding the QuestionIntentHandler to the list Request Handlers, add it after FindBranchHander to act as a fallback if the user is not asking for a branch location. You could add more intents and handlers and continue to use your QuestionHandler to look for FAQs if none of your other intents are triggered first.

/**
 * This handler acts as the entry point for your skill, routing all request and response
 * payloads to the handlers above. Make sure any new handlers or interceptors you've
 * defined are included below. The order matters - they're processed top to bottom 
 * */
exports.handler = Alexa.SkillBuilders.custom()
    .addRequestHandlers(
        LaunchRequestHandler,
        FindBranchHandler,
        QuestionIntentHandler,
        HelpIntentHandler,
        CancelAndStopIntentHandler,
        FallbackIntentHandler,
        SessionEndedRequestHandler,
        IntentReflectorHandler)
    .addErrorHandlers(
        ErrorHandler)
    .withCustomUserAgent('sample/hello-world/v1.2')
    .lambda();

Testing the General Question Handler

At this point, you should be able to test that your skill can both find branch locations and answer frequently asked questions.