AWS AI Blog

Building Better Bots Using Amazon Lex (Part 2)

by Niranjan Hira and Harshal Pimpalkhute | on | Permalink | Comments |  Share

In Part 1 we reviewed some elementary bot design considerations, built the Amazon Lex CoffeeBot chatbot, and used the Amazon Lex Test console to confirm that CoffeeBot reacted to text input as expected.  In this post, we make some more design decisions to take CoffeeBot to the next level, including voice interaction.

Note: The code for for Part 1 and Part 2 is located in our Github repo.

Ambiguity

Let’s review where we left off in our CoffeeBot development.  The user started by asking for a small mocha.  Amazon Lex determined that the two required slots had been filled and presented the confirmation prompt with its first response.  Then the user changed her mind and Amazon Lex politely accepted the change.

May_I_have

Now consider that confirmation prompt, “You’d like me to order a large mocha.  Is that right?” The familiar web check-out model might tempt us to try a prompt that suggests the user “continue shopping.” Something like, “We’re ready to order your large mocha.  Would you like something else?”.

But what would happen if the user replied, “yes”?  Should her response be interpreted to mean “place the order,” or “I want something else instead,” or “place the order and I want to get something else as well”?  Especially for voice interactions, it’s best to avoid ambiguity and keep the conversation focused.

Validating input

Did the user really say “ball coffee” or does she have a cold and means “small coffee?  Was the “kitty size hot chocolate” really for a child?  Lex allows developers to use AWS Lambda functions to validate input.

Let’s create a Lambda function and set up CoffeeBot to use it as a “validation code hook.” (For complete instructions, see documentation).

o_bots_part2_1

Here’s an example of the event that the validation code hook would receive.  The invocationSource tells us that one of the slots has been filled.

{
  "messageVersion": "1.0",
  "invocationSource": "DialogCodeHook",
  "userId": "test-1",
  "sessionAttributes": {},
  "bot": {
    "name": "CoffeeBot",
    "alias": null,
    "version": "$LATEST"
  },
  "outputDialogMode": "Text",
  "currentIntent": {
    "name": "cafeOrderBeverageIntent",
    "slots": {
      "BeverageType": "mocha",
      "BeverageStrength": null,
      "Creamer": null,
      "BeverageSize": null,
      "BeverageTemp": null,
      "BeverageExtras": null
    },
    "confirmationStatus": "None"
  }
}

From this code, you can override the default error messages, specify the order in which Amazon Lex prompts for slots, and even determine whether Amazon Lex should prompt for a slot (conditional elicitation).

Consider this validation hook fragment to see how we do that:

if (source === 'DialogCodeHook') {

	// perform validation on the slot values we have
	const slots = intentRequest.currentIntent.slots;

	const beverageType = (slots.BeverageType ? slots.BeverageType : null);
	const beverageSize = (slots.BeverageSize ? slots.BeverageSize : null);
	const beverageTemp = (slots.BeverageTemp ? slots.BeverageTemp : null);

	if (! (beverageType && (beverageType === 'mocha'))) {

		callback(elicitSlot(outputSessionAttributes, intentRequest.currentIntent.name,
			slots, 'BeverageType', buildMessage('Sorry, but we can only make a mocha today.  What kind of beverage would you like?')));
	}

	// let's assume we only accept short, tall, grande, venti, small, medium, and large for now
	if (! (beverageSize && beverageSize.match(/short|tall|grande|venti|small|medium|large/))) {
		callback(elicitSlot(outputSessionAttributes, intentRequest.currentIntent.name,
			slots, 'BeverageSize'));
	}

	// let's say we need to know temperature for mochas
	if (! (beverageTemp && beverageTemp.match(/kids|hot|iced/))) {
		callback(elicitSlot(outputSessionAttributes, intentRequest.currentIntent.name,
			slots, 'BeverageTemp'));
	}

	// if we've come this far, then we simply defer to Amazon Lex
	callback(delegate(outputSessionAttributes, slots));
		return;
}

Let’s try it out.

I_want_a_mocha2

As instructed by the code, Amazon Lex repeated the prompt for beverage size.  If you had set up multiple prompts for this slot, Amazon Lex might have chosen a different prompt.  As expected, the default prompt for BeverageTemp was used to elicit a value for that slot.

Validation code hooks allow developers to exercise a great degree of control over dialog management, but also provide the ability to defer to Amazon Lex when that control interferes with the natural flow of the conversation.

Fulfillment

Let’s imagine that CoffeeBot has to fulfill this request by placing an order using some back-end API.  You would use a fulfillment code hook to do that.  For our example, you could use the same Lambda function.

o_bots_part2_3

You can either end the conversation for this order with a goodbye message, or if you added an intent to support ordering snacks, for example, you could use a follow-up message to suggest that the user continue with that intent.

For this event, a different invocation source is received.

{
    "messageVersion": "1.0",
    "invocationSource": "FulfillmentCodeHook",
    "userId": "test-1",
    "sessionAttributes": {},
    "bot": {
        "name": "CoffeeBot",
        "alias": null,
        "version": "$LATEST"
    },
    "outputDialogMode": "Text",
    "currentIntent": {
        "name": "cafeOrderBeverageIntent",
        "slots": {
            "BeverageType": "mocha",
            "BeverageStrength": null,
            "Creamer": null,
            "BeverageSize": "tall",
            "BeverageTemp": "hot",
            "BeverageExtras": null
        },
        "confirmationStatus": "Confirmed"
    }
}

Here’s the code fragment for this hook:

// place order
// myOrderAPI.placeOrder(…);
callback(close(outputSessionAttributes, 'Fulfilled', {
	contentType: 'PlainText',
	content: `Great!  Your mocha will be available for pickup soon.  Thanks for using CoffeeBot!`
}));

Let’s try it out.

May_I_Have_Venti

You now have a bot that can actually fulfill an intent (well, it can pretend to do that, anyway).  Let’s talk to it.  Follow along with Akshad’s MobileHub integration post to incorporate CoffeeBot into a mobile app.

TextToDemo
Finally, you can try out voice interaction!  To provide voice input on the test console, you use the Mic icon. It makes sense to follow the script at first, but after the first few tries, it’s much more fun to explore.  For example, you could say “Repeat” in response to a prompt.

Please take the code for a spin in our Github repo and tell us what you think!

Conclusion

Although Amazon Lex makes it easy to incorporate natural language interactions into apps, voice interactions, like their traditional counterparts, require considerate design. In the true spirit of agile product development, we can we learn what works only by studying real interactions.

We do know this:  To create frictionless experiences, developers must carefully address ambiguity and embrace the spontaneous order in which natural conversations occur.

If you have questions or suggestions, please leave a comment below.


 

About the Authors

niranjanAs a Solutions Architect, Niranjan Hira is often found near a white board helping our customers assemble the right building blocks to address their business challenges.  In his spare time, he breaks things to see if he can put them back together.

 

 

harshal_pimpalkhute_100As a Product Manager on the Amazon Lex team, Harshal Pimpalkhute spends his time trying to get machines to engage (nicely) with humans.