AWS Machine Learning Blog

Create a questionnaire bot with Amazon Lex and Amazon Alexa

In the Create a Question and Answer Bot with Amazon Lex and Amazon Alexa blog post, we showed you how you could create a QnABot (pronounced “Q and A Bot”) for a situation in which your users have questions and you have answers. Now, what if this situation were reversed? What if you could ask users questions using quizzes, polls, surveys, and tests? These are valuable ways to drive user engagement and collect actionable feedback. Our most recent QnABot update includes the new Questionnaire Bot feature, which allows content designers to rapidly create quizzes for users and integrate them with existing QnABot content.

In this blog post, we  show you how to set up QnABot and use its Questionnaire Bot feature to create simple quizzes for your users. We’ll also go into depth on the new QnABot AWS Lambda hook feature and the ways you can use it to take control of QnABot at a deep level.

Getting Started 

Note: This post uses QnABot which was originally created by Bob Strahan and John Calhoun. We recommend following the previous link to learn how QnABot works after its latest update and to find additional information about deploying QnABot with its multiple client interfaces.

In this section, we’ll go over deploying the bot’s infrastructure and creating a simple quiz. First, deploy QnABot in your AWS account using this AWS CloudFormation button in the AWS CloudFormation console:

Note: At the time of writing, only the us-east-1 Region is supported.

If you prefer the build-it-yourself approach, the source code is available in our GitHub repository with detailed instructions in the readme file.

The QnABot resources will be created in your account within about 30 minutes. See the original post’s “Deploying QnABot” section for a summary of those resources. After the CloudFormation stacks are complete, you will receive an email with a temporary password:

The email you receive will have a password and link unique to your own QnABot deployment. Follow the link to reach your login page where you’ll enter the username “Admin” and the temporary password. You will then be prompted for a password reset. Your new password must have a length of at least 8 characters, and contain upper-case and lower-case characters, plus numbers and special characters.

After you have logged in, you see the Designer UI. This is how you create content for QnABot. The main console will contain a list of QnABot documents, after we create some.

You need to add documents to create each quiz as follows:

  1. Entry Document. This document is a normal question and answer (qna) document that references the Questionnaire Bot Lambda hook and the document ID of the first quiz document in the sequence. First, you need to update the following fields:
    • Item ID: this is a unique identifier for this particular document.
    • Questions: these are questions your users might ask when they would like to start the quiz. Try to think of a few different phrasings and put them here (e.g., “begin quiz” or “start quiz”).
    • Answer: This is how Questionnaire Bot will greet your users when they start your quiz.

      Now, choose the arrow to display the advanced fields:

      There are a number of fields here, but for Questionnaire Bot’s purposes you only need to use the final two:

      • Lambda Hook: Enter the name of the Questionnaire Bot Lambda function as it appears in your AWS account. You can also use QNA:ExampleJSLambdaQuiz, which is an alias for that Lambda function.
      • Lambda Hook Arguments: Enter the Document ID of the first quiz document. We are about to create the quiz documents, so make up an ID and remember it!

      Choose CREATE to create your entry document. You will see it appear in the list of documents in the Designer UI after you choose the REFRESH button. Now you are ready to create the quiz documents!

  1. Quiz Documents. Select the quiz option for document type to create and edit quiz documents. To create a simple quiz, we will need to add values for the following fields:
    • Quiz Question ID: The identifier for the quiz document. Make sure it’s unique. If this is the first question of a quiz, make sure that this ID matches the one you used as the Lambda hook argument in the previous section.
    • Question: This is the question that the bot will ask your users.
    • Correct Answers and Incorrect Answers: Questionnaire Bot will assemble each entry in these fields into a bank of answer options in randomized order. The bot will do this for each question every time it presents the question’s answer options to a user.
    • Next Question: The Quiz Question ID of the next question in the quiz. It is found under the Advanced tab. Leave this blank if you want the current question to be the last.

Add the Quiz Question ID, the question, and at least one correct answer.

Then add a few incorrect answers. Note that you can have as many or as few of either as you like.

Finally, expand the Advanced tab and add the next quiz document’s ID. We use a simple numbering convention to help keep track of the documents for this example, but you can name them whatever you like as long as you chain them together properly using their Next Question fields.

Choose CREATE to create your quiz document. You will see it appear in the list of documents in the Designer UI after you choose the REFRESH button. Repeat this process to create a couple more questions, then do a LEX REBUILD using the menu at the top right of the Designer UI. The rebuild adds the documents’ questions and answers to the list of utterances for the bot’s Lex intent. This will retrain the bot to better match your users’ input to the documents you have just created.

 

Now choose the top left tools menu ( ) and select QnABot Client to bring up the chatbot web interface and test your quiz.

 

Type in one of the questions you added to the entry document to trigger the quiz.

The answer options appear in randomized order. Select one of them by typing its letter and the bot will grade your response.

Continue answering questions until you complete the quiz.

The bot provides feedback after each question and an overall score when the quiz ends.

The question-specific answer feedback and the final thank-you-and-goodbye statement are customizable by using Advanced options in the document Designer UI, so get creative and surprise your users!

Finally, there is a short example quiz available for import by default. Choose the Import button in the top-left tools menu ( ):

You can import the items directly from a file. Choose Import from the top left tools menu ( ), then enter the URL that follows, and choose IMPORT: https://s3.amazonaws.com/aws-ml-blog/artifacts/questionnaire-bot/example-questionnaire-bot-quiz.json

Choose LOAD and documents for a sample quiz on the White House will be added to the bot. Now you can edit them in the console just like the earlier documents.

Questionnaire deep dive

This section will take us under the hood and through the design of Questionnaire Bot.

Questionnaire Bot is a QnABot Lambda hook. A Lambda hook is an AWS Lambda function that a QnABot document references by name. Before responding to a user’s question with the contents of that document, QnABot first calls the Lambda hook, transferring control to the hook and allowing it to dynamically modify the bot’s response. Lambda hooks can even maintain control across multiple user interactions and documents, allowing developers to create arbitrary chat bot behaviors without needing to rewrite core parts of QnABot.

In this context, a Lambda hook is a QnABot feature distinct from Amazon Lex code hooks. QnABot processes users’ questions after they have already been processed by Amazon Lex, so QnABot Lambda hooks execute much later in the architecture’s response flow. This allows developers to implement behaviors that not only react to the raw user questions, but that also depend on the contextual information other parts of QnABot associate with those questions.

Note that Lambda hooks execute like any other AWS Lambda function and therefore have a wide variety of potential uses. They do not have to perform any end-user-visible actions. For example, you could write a Lambda hook that updates an Amazon CloudWatch administrator dashboard each time your users ask a certain question. Also, you are not limited to a single Lambda execution per hook, because your Lambda hook could be a handler function that invokes a number of other Lambda functions you have created. Just be sure to keep execution times in mind while you’re coding. Your users probably won’t appreciate a bot that takes thirty seconds to respond.

As you saw earlier, Questionnaire Bot makes use of a Lambda hook argument. Lambda hook arguments are a new QnABot feature that allows content designers to pass information beyond the standard documents’ schema to Lambda hooks. They are designed to increase the pace of development by helping coders iterate on Lambda hooks without needing to update the Designer UI as frequently. Questionnaire Bot uses one Lambda hook argument to keep track of the first quiz document’s ID. This allows Questionnaire Bot to coexist peacefully with other QnABot features. Your functions can use more than one argument if you need them to. Just choose the ADD LAMBDA HOOK ARGUMENT button in the Designer UI to add more.

Arguments are passed to the Lambda hook as an array in an event generated by QnABot. You can access them in your hook’s code at event[“res”][“result”][“args”].  The relevant section of the event is shown below:

Also, QnABot automatically handles session attributes. You just need to add data to event[“res”][“session”] before returning the event, and QnABot will ensure that the user’s browser retains that information and passes it back with their next response. Questionnaire Bot uses session attributes to track data, such as question count and number of answers correct. Lambda hook best practices dictate that all of a hook’s session data should be confined to a single dictionary in the session to avoid collisions and simplify cleanup. You can see in the following code that Questionnaire Bot uses the “quizBot” dictionary.

Note the “queryLambda” value. It is the name of the Questionnaire Bot Lambda function in the AWS account where this bot is deployed. If the “queryLambda” field is not empty, QnABot will always call the named Lambda function after receiving a user’s input. This is how Lambda hooks can maintain control across multiple user responses.

If the “quizBot” value looks random to you, excellent! To grade questions Questionnaire Bot must use session attributes to recall the correct answers across two responses. We decided to encrypt its session data to prevent tech-savvy users from cheating on quizzes. Without the encryption, Questionnaire Bot’s session data would look like this:

As you can see, a correct answer to that particular question would have been B or C because those letters correspond to indices 1 and 2. To expand on why Questionnaire Bot handles grading this way, let’s go into the structure of a QnABot exchange.

Under normal QnABot operations, the user asks a question and the bot responds with an answer. This question-answer interaction neatly follows the request-response format and the entirety of an interaction can be represented within a single document.

With Questionnaire Bot, the user is answering the bot’s questions, meaning that the user is reacting to the bot’s previous response. We still want to adhere to the existing user-request/bot-response format to take advantage of our serverless processing architecture, so we reverse the roles and create an A-and-Q bot, depicted here with a two-question quiz:

But now there’s a problem! The user’s answers are being processed during exchanges that are independent of the contexts in which the bot asked the corresponding questions. The answers don’t match the questions in the request-response exchanges, so the bot can’t grade them without knowledge of the previous exchange:

A single interaction is now spread across two separate documents and two separate request-response pairings. (This is the same reason behind adding the entry document to start the quiz.) Solving the grading problem requires a tiny amount of memory, just a few digits to represent the correct answers’ positions and track the number of questions answered. A client-side session attribute is perfectly sufficient. Each time the user answers a question, they also transmit QnABot’s session attributes back to the bot, where they are modified or cleaned up as necessary.

Encrypting Questionnaire Bot’s session data prevents users from reading out the correct answers to every question. It is the last piece of the puzzle for enabling persistent interactions without creating additional storage infrastructure or altering the request-response model.

If you want to see Questionnaire Bot’s source code for yourself, you can find it in the GitHub repository as templates/examples/examples/js/Quiz.js.

Conclusion

This post has described how to create new content for your users with Questionnaire Bot and how to experiment with deeper changes to QnABot’s functionality using Lambda hooks. We would love to hear your feedback and ideas in the following comments section. If you want to keep up with QnABot’s development, check out our open-source GitHub repository  Thanks for reading!


About the Authors

Adam Imeson is an Associate Cloud Consultant for AWS Professional Services. Bob Strahan is a Principal Consultant for AWS Professional Services. John Calhoun is an Associate Solutions Architect for the AWS Public Sector Partners team. They work with our customers and partners to provide leadership on a variety of projects, helping them shorten their time to value when using AWS.