Overview
This chapter teaches you how to design a chatbot using Amazon Lex. You will start by learning the basics of Conversational Artificial Intelligence and some of the best practices that go behind using that technology to design custom chatbots. Then, you will use Amazon Lex to create a custom chatbot that gets the latest stock market quotes by recognizing the intent in text. By the end of this chapter, you would be well-versed with the basics of chatbots, and the process that goes into designing them. Using this knowledge, you will be able to create your own chatbots to solve a variety of business challenges.
Like the other chapters in this book, this chapter spans the conceptual aspects as well as pragmatic hands-on building – this time, the domain is Conversational AI. From many reports, it's stated that the conversational AI market will grow more than 30% per year and that the majority of customers, as well as employees, will be interacting with digital assistants.
The challenge in creating responsive, intelligent, and interactive bots is that, for machines, conversation is very hard to achieve. Let's look at the top three reasons why this is the case:
This brings us nicely to the start of this chapter: Amazon Lex uses the same technology as Alexa and the Amazon Contact Center uses Amazon Connect, which means that we get to deploy their best practices. In fact, Amazon Connect was developed in order to meet the strict requirements of their customer service departments. So, we are in luck – we can leverage the state-of-the art interactions that our customers are used to in their daily lives.
When we talk about conversational AI, there are two main types – task-oriented and open-ended conversation. As the name implies, task-oriented conversations function to fulfill a task (for example, query balances in an account, order an item, check the weather, check the price of a stock, and find out how many vacation days are remaining). Open-ended general conversations are broader as they cover various topics – this could be the weather, movies, financial well-being, investing, and so on. In this chapter, we will focus on task-oriented conversations.
Another point to keep in mind is the omnichannel aspect of conversations – conversations can be had via a chatbot with a text interface or via a voice UI (VUI); they can jump from one to the other, and with the new concept of Visual IVR, they might even be concurrent. That is why we will cover chatbots and then voice in this chapter. Multimodality is an important part of conversational AI.
In short, in this chapter, you will learn how to build a chatbot using Amazon Lex. We will also cover the design of conversational AIs. We will then dive into Amazon Connect and explore adding voice to our bots. First, we'll talk about how to design a chatbot. Then, we will dive into exploring the Amazon Lex service by creating a sample chatbot.
A chatbot is a specific instance of a task-oriented conversational AI – the goal is to be able to hold a conversation with the user to the extent required in order to resolve customer queries, perform a task that the customer requests, or suggest a way to move forward from them.
As in normal conversation, the means by which we interact with the bot can be written text or speech. Often, the chatbots are integrated with messaging platforms, such as Slack, Facebook, Kik, and WeChat. This can also be integrated with a custom web or mobile interface.
It is easier, of course, to integrate within an existing messaging platform, since the user is likely to be familiar with the interface. Moreover, these platforms provide support to the chatbot developers with infrastructure and development tools.
Some examples of chatbots include systems for ordering products, reporting, internal communication, and scheduling.
NLP is the general term for a set of technologies that deal with natural language. Natural Language Understanding (NLU) is a focused subset of NLP that deals with actual conversational input.
NLU can handle unstructured inputs and convert them into a structured, machine-understandable form. Words that the user enters are transformed into intents and entities, or slots. The NLU chatbot is further able to infer intents and slots from user input, which may be similar to – but not the same as – the examples it has been trained with.
Before we can get started with building chatbots, you will need to understand some concepts first. Let's take a look at the technical meaning of the term chatbot and the names of the pieces that make up a chatbot and work together to deliver a conversational experience to the user.
A chatbot, also known as a bot or artificial conversation entity, is a piece of software that can converse using natural language with the user. The goal is for the user to believe that they can interact freely and naturally with the bot, almost as if speaking with another person.
Things that the user says to the bot are called utterances. The bot regards the utterances from the user as input and is able to parse them into machine-recognizable formats. Some examples of utterances are as follows:
An intent is something that a user wants to do, based on the content of their utterances. An intent can be a single step (for example, get a balance) or a multi-step process (for example, booking a trip that includes booking tickets, booking a hotel, booking transportation, and so on). The bot infers the intent from the user's utterances as well as the context and supports them based on its internal set of business rules or application flow, with the result of either a change in its internal state or an action being performed. These also typically result in a response being provided to the user as feedback or information.
So, from the preceding utterance examples, a bot may infer intents such as the following:
Inferring intent is a large part of what NLU platforms such as Lex do behind the scenes. A number of training examples, in the form of sentences that the user might provide, are fed to the platform, and a probabilistic model is built from these examples. This means that, in practice, the platform should be able to infer the correct intent from input, which is similar to, but not necessarily a part of, the examples that the system was trained on.
When the bot requires more information from the user or is unclear about an intent, it can ask the user follow-up questions, in order to collect more data. These are called prompts. Prompts typically fill in slot values that are required, although your application logic may attempt to fill in values that are optional as well if you desire.
A slot is a piece of information, or parameter, that is associated with an intent. Information can be provided within the initial user request, and Lex will be able to parse out the information and correctly assign it to the corresponding slot correctly. If this information is not provided as a part of the request, then the bot should be able to prompt the user for the information separately. Slots may be optional or required.
The type of information represented by a slot is known as the slot type. There are a number of built-in slot types within Lex that represent common types of information, such as a city or state. The following are a few examples of common slot types that are incorporated into Lex:
Of course, this is just a very limited subset of examples. There are many more built-in types, as well as different types for different languages.
Note
You can refer to the following link to get a full list of built-in intents and slots: https://docs.aws.amazon.com/lex/latest/dg/howitworks-builtins.html.
Most of the built-in intents and slots are documented as part of the Alexa Skills Kit documentation, with some differences for Lex, which are documented at the preceding link. Make sure to keep the link bookmarked and refer to the page regularly, since Amazon keeps updating the service and things may change.
If the type of information that you would like your bot to handle is not represented by one of these built-in types, you can define your own, along with the actual values that the slot is allowed to take.
Note that the bot will not be able to proceed to the next step until it fills in all of the required slot values. Naturally, this does not apply to slot values that are optional.
When all of the required slots for an intent have been filled, the slot is then ready for fulfillment. At this stage, the bot is ready to execute the business logic that's required to fulfill the intent. Business logic may be any of the following actions:
The fulfillment action can be performed with or without some feedback to the user, but as a matter of best practice, it is always better to err on the side of more feedback to the user, rather than less.
Before we dive into some of the best practices, we will go over two quick points. We are only covering the most important ones and there are excellent materials and books available for a more in-depth study. Second, you will encounter situations where these tips might need to be ignored – this may include delivery pressures and resource constraints, but they may just be wrong due to advances in technologies. However, we need to be aware of the potential technical debt that can be created and document them somewhere for future reference. A few best practices are as follows:
At the current maturity of technologies, we are at level 3 – Dialogues via Contextual Bots, which is at its very initial stages. Understanding these levels and positioning your bot service in the continuum is very important for estimating what is possible as well as the effort required, especially when it comes to the expectations of users.
In this section, we will create a custom chatbot to get stock market quotes using Amazon Lex. The bot will listen to our utterances for a valid intent: GetQuote. This signals to the bot that, for example, we had to get a stock market quote for a given stock ticker symbol, which will reside in a slot named ticker. The bot will then look up the quote for that ticker symbol from a freely available financial API named IEX, and will return the information to the user via a conversational response:
Note
A stock ticker symbol is the standard way in which stocks that are traded on an exchange, such as the New York Stock Exchange or NASDAQ, are represented. A sequence of alphabetical letters represents the company's stock that is being traded.
We can create a flowchart for this process, as shown in the following diagram. Let's go over it in further detail:
Recognizing the Intent and Filling the Slot Value
As a first step, the bot waits for the user's input in order to recognize a valid intent. When it extracts the GetQuote intent as the intent from an utterance posted by the user, it will then try to fill the required slots. In our case, we only have one slot of the StockTicker type (which is a custom slot type). The bot will issue a prompt, asking the user to provide the value of the slot and parse the utterance in response, in order to fill the slot value.
Valid slots are those that the system recognizes. If the slot value is not part of the list of permitted values, or if the system does not recognize what is entered for the slot value, it is said to be invalid, or not valid.
If the slot value is not valid, it will go back to trying to fill the slot (at least up to the number of times we have specified it should try before giving up and going back to the beginning). Once the bot has a slot filled with a valid value, it then proceeds to fulfill the intent.
Fulfilling the Intent with a Lambda Function
While the default fulfillment action is to return the intent and slot value to the user so that they can proceed to work with it within their own application, we will instead choose to set up a Lambda function on AWS that can handle the intent and run the business logic required to fulfill it.
At this point, the bot process running within Lex proceeds to call the Lambda function, which we have written and specified for fulfillment:
Lambda_function.Lambda_handler
When Lex calls out to the function for fulfillment, it sends a JSON payload containing various pieces of information about the sender, as well as the intent and slot value. The Lambda_handler() method parses the intent and slot parameter value from the JSON payload, and then dispatches another function call to the method, which gets the market quote value that we're looking for from the external API.
Finally, the Lambda function also packages the response as another JSON string and returns it to Lex. Lex parses the JSON response behind the scenes and presents the response message to the user.
We will go through all of these elements in a lot more depth in the next two exercises. In the first exercise, we will set up the new chatbot, and in the second one, we will implement our Lambda handler function so that it returns back to the user the actual value of the market price of the ticker symbol that the user asks the bot for.
In the next exercise, you will create a custom chatbot that recognizes the intent, named GetQuote, in order to get a market price quote for a given ticker symbol. The bot will prompt the user for the value of the ticker symbol that the user is interested in, until the slot is filled. You will also learn how to state the intent and fill the slot in the same utterance. This chatbot can be tested via a conversational interface.
In this exercise, we will create and test an Amazon Lex-based bot with a custom intent and slot. The steps that have to be performed to create a bot with a custom intent and slot are as follows:
The session timeout can be set to the default of 5 min. The IAM role field displays the name of the IAM role, which is automatically created by Lex for use by bot applications. Let's set Sentiment Analysis to Yes.
Note
A law was passed in 1998 to protect the privacy of children under 13. It states that online sites may not collect personal information from users younger than 13 years of age without parental consent, among other provisions. You can learn more about the COPPA act at https://www.ftc.gov/enforcement/rules/rulemaking-regulatory-reform-proceedings/childrens-online-privacy-protection-rule.
You can test your new intent within the bot in the Test bot pane, in the upper right-hand corner of the screen.
Note
If the Test bot pane is not visible, you may have to click on an arrow button in order to expand it and make it visible.
At this point, your bot doesn't do anything much apart from try to recognize the GetQuote intent and flag that it is ready for fulfillment. This is because we have not added any slots to the intent.
ticker:GOOG
As you can see from the testing shown in the preceding screenshot, not only does Lex recognize the correct intent from an utterance it has not been trained on, but it also recognizes a new symbol that it has not seen before (ADP) correctly as the value for the ticker slot.
Clearly, quite a bit of flexibility is possible between training and realworld examples of conversational input with an NLU engine.
NLU demonstrates the advantage of using an NLU engine that has been trained on a huge set of conversational sentences and has formed a large inference model.
It is able to connect sentences that are not the same as the ones it has specifically been trained on. In fact, they can be significantly different, but the model is large enough to infer that the semantic meanings are similar.
There is one more trick that you can use to make it easier for the user to interact with your bot. You can fill the slot value with the same utterance as the one that establishes intent. This can be accomplished by simply including the slot placeholder token ({ticker}, in this case) in your sample utterances. Perform the following steps to do so:
You can see that the intent is ready for fulfillment and that the slot value is filled appropriately, in a single step.
We have now gone through the process of defining a custom chatbot, complete with a custom intent, slot type, and slot, within Amazon Lex. Furthermore, we have trained and tested the bot to verify that it is able to classify the correct intent and correctly infer the slot values from conversational input to a high degree of accuracy.
Finally, we added a shortcut method to fill in the slot value directly in the initial utterance by inserting the placeholder token for the slot value in the sample utterance to train the NLU engine behind Lex.
You can create AWS Lambda functions that can be triggered from your Amazon Lex bot. As we discussed in Chapter 2, Analyzing Documents and Text with Natural Language Processing, serverless computing and Lambda functions are a good match for implementing the fulfillment and validation functions in your Lex bot. The Lambda functions integrate better and faster and scale better than returning the intent to a backend application for every step, such as validation. Once the intent has been validated and you are satisfied with the parameters, you can call a backend API to fulfill the request. You can implement simple fulfillment requests as Lambda functions, thereby making your bot responsive and scalable.
In the next exercise, you will learn how to implement the business logic behind the bot as a Lambda function in AWS and call a real-world REST API to get information that you can return to the user from an external service.
In this exercise, we will handle chatbot fulfillment business logic with a Lambda function that is created and deployed on AWS. In the previous exercise, we created a chatbot with a GetQuote intent and ticker slot. Perform the following steps to implement business logic:
In this exercise, you learned how to handle chatbot fulfillment business logic with a Lambda function created and deployed on AWS.
Here, you will use the Lambda Function editor entirely in-line, which means that you can enter and modify the code directly without having to upload any files to AWS. The code that you enter will be executed when the Lambda function is invoked:
First, let's look at the structure of the Lambda function.
When you created the marketNannyHandler function, AWS created a folder with the same name, with a Python file named Lambda_function.py within the folder. This file contains a stub for the Lambda_handler function, which is the entry point of our Lambda function. The entry point takes two parameters as arguments:
The return value of the function can be of any type that is serializable by JSON. This value is returned to the calling application after serializing.
Now, let's take a closer look at the structure of the event argument that gets passed to the Lambda_handler function. If we are asking for a market quote with a ticker value of GOOG, the JSON value of the intent section within the parameter will appear as follows:
{
…
"currentIntent":
{
"name": "GetQuote",
"slots":
{
"ticker": "GOOG"
},
…
}
}
The relevant values that we are interested in for processing are name and the single ticker value within the slots section under currentIntent.
Since our JSON input gets converted to a Python dictionary, we can obtain these values within the Lambda function as follows:
event['currentIntent']['name']
event['currentIntent']['slots']['ticker']
Note
The lambda_function.py file contains the full source code. It is available on GitHub at https://packt.live/2O8TUwA. You can refer to it as you type in the code in the Lambda editor. We have included debugging tips at the end of this example. It might be a good idea to read through the example as well as the tips first before you start implementing.
The first step in implementing our handler is to identify the intent name and call the corresponding function that implements it. The pseudocode for this looks as follows:
import json
def get_quote(request):
return "Quote handling logic goes here."
def lambda_handler(event, context):
# TODO implement
print(event)
intent = event['currentIntent']['name']
if intent == 'GetQuote':
return get_quote(event)
return {
'statusCode': 200,
'body':
json.dumps("Sorry, I'm not sure what you have in mind. "
"Please try again.")
}
This is sufficiently complete to actually be tested against your chatbot at this point, if you so desire, but let's press on with the implementation.
To test, you should add a test event, as shown in the instructions that follow:
Go to Configure test events:
Edit it as shown here. The Lambda function requires the JSON structure as shown:
The next step will be to implement the get_quote function, which does the work of actually getting the market quote information and returning it to the calling handler function:
def get_quote(request):
Slots = request['currentIntent']['slots']
ticker = Slots['ticker']
price = call_quote_api(ticker)
Note that we have named the parameter request, so the object event to which we send the function is referred to as a request within this function. It contains the same value and structure, just renamed. Therefore, we can get the value of the ticker Slot, as mentioned previously, by getting the value of the item with the ticker key under it by using the following code:
request['currentIntent']['Slots']
Then, we call the call_quote_api() function to retrieve the value of the market quote for the value of the ticker item. We haven't implemented call_quote_api() yet, so let's do this next.
We will implement the call_quote_api function as follows:
def call_quote_api(ticker):
response =
urlopen('https://www.alphavantage.co/query?'
'function=GLOBAL_QUOTE&symbol={}'
'&apikey=3WIN88G0AVG7RZPX'.format(ticker))
response = json.load(response)
''' Sample Response:
{'Global Quote': {'01. symbol': 'AAPL',
'02. open': '316.2700',
'03. high': '318.7400',
'04. low': '315.0000',
'05. price': '318.7300',
'06. volume': '33454635',
'07. latest trading day': '2020-01-17',
'08. previous close': '315.2400',
'09. change': '3.4900',
'10. change percent': '1.1071%'}} '''
return response['Global Quote']["05. price"]
Here, ticker is the value of the ticker parameter (in this specific example, it would be GOOG). We use Alpha Vantage, which provides a static endpoint on the internet at https://www.alphavantage.co/, to retrieve a quote. We have also captured a sample response as an example. You should get your own API key.
Since it is implemented as a simple GET request, with the ticker parameter embedded within the URL, with the API key, we can simply use the built-in urlopen method in the urllib.request module (which we will have to remember to import) to receive a response from the URL with the ticker embedded within it.
Since the response is also in JSON format, we need to import json module and load the response using the json.load function. The only field we are interested in within the response is 05. price, so we return that as the return value from our function.
Now that we have the market quote value, we can return it to our calling application, which is the chatbot that we implemented. We have to do a couple of small things, however, to return this value. First, we need to format it as a conversational response, as shown in the following string:
message = 'The last price (delayed) of ticker {} was {}'
.format(ticker, price)
This should let the chatbot display the following message:
The last price (delayed) of ticker GOOG was 1107.32
There is one final step, which is to construct an Amazon Lex JSON return format that contains our message and a couple of other items of information. We will use the close helper function to do this:
return close(message)
Our close function takes a single parameter, which is the string that we wish to return to the chatbot (in this case, this is the value of the message variable). It generates a JSON wrapper around the content, which conforms to the structure that our Lex-based bot is expecting and from which it can extract the content and deliver it to the user. The structure of the wrapper is not important at this stage, but if you are curious, you can look at the implementation of the close function. As we mentioned earlier, the lambda-function.py file contains the full source code for the lambda function. It is available in GitHub at https://packt.live/2O8TUwA.
The code window should look as follows:
At this point, the only task remaining is to connect the Lambda function to the chatbot and test it. Perform the following steps to do so:
Here are some debugging tips that will help you:
We started this chapter with an introduction to Conversational Artificial Intelligence and learned how different aspects of this technology help us build a good, useful chatbot. We also learned some core concepts, such as utterances, intents, plots, and so on, that serve as the foundation for building a chatbot. Later, right before we built our first chatbot, we discussed some best practices that come in handy while designing Conversational AI. Equipped with this knowledge, we performed an exercise to create a bot to recognize intent and fill a slot to retrieve stock prices. We then created a Lambda function in AWS to help us implement the business logic behind the bot. In the next chapter, we will learn how to use speech with chatbots.
3.147.85.194