-
Notifications
You must be signed in to change notification settings - Fork 157
07. Natural Language Processing Bot (.NET Core)
When using microservices it just happens that you can focus on a very specific thing so a demo and research can be even simpler to understand. For this scenario, we will be focusing on a single ASP.NET Core Web API service which is our Bot service. This Bot service is consuming LUIS and the Azure Bot Service so it can surface to multiple channels, like SKYPE.
In addition, this Bot is surfacing all the AI/ML features we have implemented in eShopOnContainersAI, like the Product Search Image-Based, Products Recommender, etc. But for this scenario, we just need to focus on the Bot itself and how it uses LUIS for building its "Language Understanding" and provide some "intelligent" discussion with the users.
NOTE: The bot feature to login to the Identity microservice is not implemented yet, so the features that depend on it, like ordering or checking basket are not available right now.
This guide describes two levels for testing the bot in this application:
- Using the Bot Emulator with local bot microservice
- Using Skype with local bot microservice
However, whichever way you choose, you will use LUIS to handle the natural language interface with the user, so the first step is completing the LUIS registration.
You need to create a LUIS resource using the Azure Portal. In the search box used for creating new resources, look up for a resource of type Cognitive Services. The Create Cognitive Service blade will look like the following screenshot:
In the previous panel, you need to fill up the Name, Subscription, Location, Pricing Tier (you can select the Free tier for testing purposes or paid tiers) and the Resource group (create new one or used existing one). In the API Type
list, you select Language understanding (LUIS), to create a LUIS resource hitting the Ok button.
After creating the LUIS resource from the Azure portal, you need to access the LUIS portal, which hosts LUIS applications. You can access the LUIS portal accessing the LUIS resource, select Quick Start under the Resource Management section in the left blade, and then on the right panel, click on the link Language Understanding Portal
. The process is described in the following screen flow:
You can also access the same portal visiting the LUIS homepage (which includes more information about the service) and sign in the portal, as shown in the next screenshot:
After signing up / accessing LUIS portal, you will get to the LUIS My Apps screen, as shown below:
LUIS applications can be imported from / exported to json files. You have to use this feature to load a by-default LUIS application file to use it from the eShopOnContainersAI Bot.
This by-default LUIS application file for eShopOnContainersAI is pre-loaded with useful intentions and entities ready to be used in the eShopOnContainersAI chatbot.
To import the application, click on the “Import new app” button, click on “Choose File” and find the location where your Bot.Core.API/Services/LUIS/LuiseShopOnContainersAI.json file is located in your local repository, then type an optional name (like eShopOnContainersAI Bot), and finally click in Done to finish the dialog. The complete import process is illustrated in the following figure.
After importing the application, you need to publish it to the internet. This will expose your model as a web service, and then you will be able to query the service from your chatbot. For publishing, first you need to click on the Train button; this will train a language understanding model based on the intents and entities previously imported from the application file. When the training is done, you can select the Publish menu, and then click the “Publish to Production slot” button. The process is documented in the following screen flow:
Since you'll begin by using the emulator, you just need to configure the LUIS parameters in the .bot
file, as shown below:
{
"name": "eShopOnContainersAI-Bot.Net.Core",
"secretKey": "",
"services": [
{
"type": "endpoint",
"name": "development",
"id": "1",
"endpoint": "http://localhost:6654/api/messages",
"appId": "",
"appPassword": ""
},
{
"type": "endpoint",
"name": "production",
"id": "2",
"endpoint": "http://localhost:5135/api/messages",
"appId": "",
"appPassword": ""
},
{
"type": "luis",
"name": "eShopAI-LUIS",
"id": "3",
"appId": "<your-luis-app-id>",
"authoringKey": "<your-luis-authoring-key>",
"subscriptionKey": "<your-subscription-key>",
"region": "<your-luis-region>",
"version": "2.0"
}
],
"padlock": "",
"version": "2.0"
}
The configuration relies on setting up the current IP address of the machine running the Bot Emulator, before opening the .bot file
, as show in the image below:
After configuring the current IP address of your machine, CLOSE THE EMULATOR and then:
- Open the emulator back again
- Open the
.bot
file with the emulator and - Click on the
production
configuration
You should see the bot responding, as shown below:
The bot application can be tested using the Bot Emulator, either locally or through the web channel, or using the Skype channel (Skype client).
At this point you can only use the Bot Emulator locally, but we'll get into the required setup for using the web channel and Skype.
Also, because of some compatibility issues, the login function is not working right now, so neither are the features that require a session, like the ones related to the basket or orders.
The following screenshots are based on the web channel, but the flow and utterances can be used as well in the skype channel in a similar way. There exists only minor UI differences when displaying bot controls, for example, displaying the sign in card, as you can see in the following figure:
In this section we'll review some commands you can use in conversation with the bot. When you first launch the bot emulator, you'll see the welcome dialog. This is a confirmation that the bot is ready to accept your commands. Type a simple “hello” as shown in the following screenshot and the bot will kindly reply.
Now, you are at the home dialog. From there you can begin a dialog to browse for products, add products to the cart or check past orders. For example, as suggested by the bot, you can type “show me the catalog” and you'll start a dialog to filter the product navigation, as shown next:
The bot is able to suggest some actions to the user; these actions are displayed as buttons at the top of the “Type your message…” textbox. You might click on “All” to display all product brands. Next, you click in the suggested action to filter types of product; click “All” button. Finally, you'll get the products displayed in a carrousel, like this:
NOTE: if you don't see the images, check the ESHOPAI_BOT_IMAGEURL
environment variable in the .env
file.
You can now click Home (to initiate another conversation with the bot), Login (to authenticate and be able to add products to the cart) or Show more (to display more products).
Hit the “Home” action and try another action to the bot like “I want to buy something from .NET”. This will skip the filtering stage of the product navigation and will directly show the products filtered by the .NET brand.
You can also filter by images. Click on the image button at the left of the “Type your message …” textbox. In the file selector dialog, check the image you want to search for, click ok, and then you'll see the products filtered by your image. The process is described in the next screen flow:
NOTE: The Skype channel does not allow access image attachments (as of FEB-2019) to bots, so you won’t be able to search products by image.
Next, we'll explain the authentication process. (Note this is not working now, with Bot Builder v4)
- click on the Login suggested action (in the product navigation, is between the Home and Show more actions.
- in the SignIn card, click on the Login button
- a page will be open in the browser with the Authentication form from IdentityServer
- fill the email and password, and click on login
- if the authentication is correct, back in the bot client, you will get a confirmation prompt
After log in, you will be able to add products to the cart: products in the carousel will have a button for adding to the cart. The order process should go as follows:
- click in “Add to cart” button from the product you want to purchase
- bot will ask you “How many [products] do you want to buy?” and you will type the number of products
- in the next dialog, you will hit “Checkout” button
- in the order dialog, you click “Order now” button
- You get a message from the bot confirming the purchase order
You can review the whole process in the following screen flow:
Last command we will test will be checking our order state. Hit the Home button, and type “what's going on with my order?”. This will bring up a dialog showing the state of your last order, as shown next:
The outline of the required steps to use Skype with the local microservice is the following:
You need to configure several settings in the bot configuration file: src\Bots\Bot.Core.API\Bot.Core.API.bot.
In the Azure Portal, find the Bot Channels Registration resource created in Step 1, and select the Settings option under the Bot Management section; the right panel will look like this screenshot:
From the previous panel, you need to copy the Microsoft App ID value.
Furthermore, you need the Microsoft password associated with the Bot Channel Registration resource; this password can be recovered in the Application Registration Portal. To access this portal, click on the Manage link beside the Microsoft App Id label (check previous figure) and a new web page will be open in browser, as in the following figure:
In the Application Registration portal, you need to click on the bot application link; in the Application Secrets section, click on the “Generate New Password” button. A new password will be generated and shown in the New password generated dialog, and you will use this value as the MicrosoftAppPassword
in the bot configuration file, as show in the following screen flow:
With the information gathered before, you need to populate the endpoint services types:
{
"type": "endpoint",
"name": "development",
"id": "1",
"endpoint": "http://localhost:6654/api/messages",
"appId": "<Microsoft App Id>",
"appPassword": "<Microsoft App Password>"
},
{
"type": "endpoint",
"name": "docker",
"id": "2",
"endpoint": "http://localhost:5135/api/messages",
"appId": "<Microsoft App Id>",
"appPassword": "<Microsoft App Password>"
},
You also need to collect some settings from LUIS application. Back in the LUIS portal, the Authoring Key is available in the settings menu of the chatbot application as shown in the following screen flow
The Application Id is available from the Manage
menu, selecting the Application Information
tab, as described in the following screenshot:
The Subscription Key can be accessed from the Manage
menu, select the Keys and Endpoints
tab, and then scroll down to the Resources and Keys
section; there you can use copy to clipboard button the Key 1 value and annotate the Region value. The following screenshot describes where to find the subscription key:
The former data is needed to configure following settings in the Bot.Core.API/Bot.Core.API.bot file in the Bot.Core.API
project. In this file, look up the luis service type, and fill following settings
{
"id": "3",
"type": "luis",
"name": "eShopAI-LUIS",
"version": "2.0",
"appId": "<application Id>",
"authoringKey": "<authoring Key>",
"subscriptionKey": "<subscription Key>",
"region": "<region>"
}
After the containers are running, you should have the Bot.API exposed in the port 5135, and the api gateway for the catalog, exposed in the port 5202. You need to expose those ports to the internet under a public address.
In this case, we will use ngrok. This application exposes local networked services behinds NATs and firewalls to the public internet over a secure tunnel. ngrok is available for Windows, OS X and Linux, and you can download ngrok from the homepage.
There is a template pre-configured to use Bot.API with ngrok, located at Bot.Core.API / ngrok / eshopai.core.ngrok.yml. If you purchased an ngrok license, we advise to edit this file and include your Authorization token and static domains
The following command will execute ngrok using the configuration file eshopai.core.ngrok.yml
:
src\Bots\Bot.Core.API\ngrok\start-ngrok.cmd
and then you should see a screen like this one:
You need to take note of the public hostnames (in the domain .ngrok.io). If you are using the free version of ngrok, these hostnames are random and change every time you execute the command, so you should change the places where you use them accordingly.
The BotFramework is able to handle multiple authorization providers using the Bot Adapter. Such authorization providers include Facebook, GitHub, Office 365 and more. eShopOnContainersAI has already an OAuth Identity server running for authenticating requests, and we will use ngrok to expose this service to the Internet using a secure tunnel. Check previous ngrok configuration (Step 3) and annotate the domain name of the identity service expose through ngrok (<identity.ngrok>) .
In the Azure portal, open your Bot Channels Registration resource, and select the Settings tab. In this panel, scroll down the bottom of the blade, and in the OAuth Connection Settings section, click the Add Settings button. In the new blade, fill the following settings:
-
Name
: eShopIdentity -
Service Provider
: Generic OAuth 2 -
Client Id
: Bot -
Client secret
: secret -
Authorization URL
: <identity.ngrok>/connect/authorize -
Token URL
: <identity.ngrok>/connect/token -
Refresh URL
: <identity.ngrok>/connect/refresh -
Scopes
: openid profile offline_access orders basket marketing locations
The .env file contains two settings which need to be configured. ESHOPAI_BOT_AUTHENTICATION_CONNECTION_NAME
is the name of the OAuth provider registered in the Bot Registration Channel (created in previous step). ESHOPAI_BOT_IMAGEURL is the URL domain name of the catalog ai service exposed through ngrok (check Step 3)
ESHOPAI_BOT_AUTHENTICATION_CONNECTION_NAME=eShopIdentity
ESHOPAI_BOT_IMAGEURL=https://eshopaicatalog.ngrok.io
We recommend using the new Bot Emulator v.4 for testing the bot application. This emulator can be downloaded from BotFramework-Emulator releases page.
After installing the Bot Emulator, launch the application and select the File
/ Open Bot
menu option. Then you should select the file that is named Bot.Core.API / Bot.Core.API.bot and open it.
This .bot file contains two main endpoints: if the bot application is running inside a docker, you should choose the eShopBot-Docker
endpoint; if the bot application is running in your local IIS Express, you should select the eShopBot-Local
endpoint.
IMPORTANT: When running the Bot.API application inside a Docker container, you should uncheck the option
Bypass ngrok for local addresses
plus you need to have installed NGROK in your machine and set the path to the .EXE in the BOT Emulator, as in the screenshot shown below.
You can configure those parameters in View / Emulator settings menu option:
Finally, select the correct endpoint (Docker or Local). If the Bot.API is running in the background, you should see the user welcome dialog in the emulator, as shown below:
Try asking about products, uploading an image to make a product search based on image recognition, etc.
Access the Azure Portal, select the Bot Channel Registration resource formerly created, and click on the Channels option under the Bot Management. At the right panel, you can examine current channels used by the bot and register new channels. Click in the Skype icon under the “Add a featured channel” label. In the next screen, you don't need to change any setting, so you can proceed and click on Save. You can check the Skype channel registration process in the following screen flow:
Finally, you need to update the message endpoint in the Bot Channel Registration, using the new ngrok bot URI:
In order to use the bot in your Skype application, first you need to add it as a contact in your account. For this action, you need to go back to the Azure Portal and select the Bot Channel Registration created for this project. In this blade, select the Channel option under the Bot Management section, and click on Skype link. This click will launch another tab in your browser, allowing to Add as a contact the bot to your Skype account. The process is explained in the following screen flow:
After adding the bot to your contact list, you are ready to open your Skype client and start chatting with the bot application, as shown in the next figure:
public class eShopBot : IBot
{
private readonly DialogSet dialogs;
private readonly DomainPropertyAccessors eShopBotAccessors;
public eShopBot(DomainPropertyAccessors eShopBotAccessors, IDialogFactory dialogFactory)
{
this.dialogs = new DialogSet(eShopBotAccessors.DialogStateProperty);
this.dialogs.Add(dialogFactory.MainDialog);
this.eShopBotAccessors = eShopBotAccessors;
}
public async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken)
{
var dc = await dialogs.CreateContextAsync(turnContext);
var result = await dc.ContinueDialogAsync();
if (result.Status == DialogTurnStatus.Empty)
{
await dc.BeginDialogAsync(MainDialog.Name);
}
await eShopBotAccessors.SaveStatesAsync(turnContext, cancellationToken);
}
}
The main Bot loop occurs in the eShopBot
class, which inherits the IBot
interface. The OnTurnAsync
method contains the entry access point to the Bot control, which handles Bot requests depending on the activity type. In this case, we forward all requests to the MainDialog
, and then we save the state to persistent storage.
Bot.Core.API / Dialogs / Main / MainDialog.cs
protected override async Task RouteAsync(DialogContext dc, CancellationToken cancellationToken = default(CancellationToken))
{
var dialogCtx = dc.Context;
var result = await luisService.GetResultAsync(dialogCtx);
var topIntent = result != null ? result.TopIntent().intent : eShopLuisResult.Intent.None;
switch (topIntent)
{
case eShopLuisResult.Intent.Catalog:
if (result.Entities.brand != null || result.Entities.type != null)
{
var catalogFilter = await accessors.CatalogFilterProperty.GetAsync(dialogCtx, () => new CatalogFilterData());
ConvertToConvesationInfo(result, ref catalogFilter);
await accessors.CatalogFilterProperty.SetAsync(dialogCtx, catalogFilter);
await dc.BeginDialogAsync(CatalogDialog.Name);
}
else
await dc.BeginDialogAsync(CatalogFilterDialog.Name);
break;
case eShopLuisResult.Intent.Login:
await dc.BeginDialogAsync(LoginDialog.Name);
break;
case eShopLuisResult.Intent.Hello:
await sharedResponses.ReplyWith(dc.Context, Shared.SharedResponses.HowCanIHelp);
break;
case eShopLuisResult.Intent.Basket:
await dc.BeginDialogAsync(BasketDialog.Name);
break;
case eShopLuisResult.Intent.Orders:
await dc.BeginDialogAsync(MyOrdersDialog.Name, /*latestOrder*/ false);
break;
case eShopLuisResult.Intent.Order:
await dc.BeginDialogAsync(MyOrdersDialog.Name, /*latestOrder*/ true);
break;
case eShopLuisResult.Intent.Bye:
await Logout(dialogCtx, cancellationToken);
break;
case eShopLuisResult.Intent.Help:
await dc.BeginDialogAsync(HelpDialog.Name);
break;
default:
string utterance = dialogCtx.Activity.Text.ToLowerInvariant();
await dialogCtx.SendActivityAsync($"Sorry, I did not understand '{utterance}'. Type 'help' if you need assistance.");
break;
}
}
Most of the message activities are processed in the RouteAsync
method, which sends all utterances to the LUIS (for topic inference), and then routes each topic to the related bot dialog which will handle the conversation.
public class LuisService : ILuisService
{
private readonly LuisApplication luisModel;
private readonly ILogger<LuisService> logger;
public LuisService(BotConfiguration botConfiguration, ILogger<LuisService> logger)
{
var luisService = botConfiguration.Services.Find(cs => cs.Type == ServiceTypes.Luis) as Microsoft.Bot.Configuration.LuisService;
this.luisModel = new LuisApplication(luisService.AppId, luisService.SubscriptionKey, luisService.GetEndpoint());
this.logger = logger;
}
public async Task<eShopLuisResult> GetResultAsync(ITurnContext context)
{
CancellationToken ct;
var luisRecognizer = CreateLUISRecognizer();
var result = await luisRecognizer.RecognizeAsync<eShopLuisResult>(context, ct);
return result;
}
}
The LuisService
is a class wrapper, mainly setting up a LUIS model with the correct values and sending requests for utterance classification.
public class CatalogFilterDialog : ComponentDialog
{
public const string Name = nameof(CatalogFilterDialog) + ".MainDriver"; // CatalogFilterDialog.PromptProductBrands
private const string PromptProductTypes = nameof(CatalogFilterDialog) + "." + nameof(PromptProductTypes);
private const string PromptChoices = nameof(CatalogFilterDialog) + "." + nameof(PromptChoices);
private readonly DomainPropertyAccessors eShopBotAccessors;
private readonly IOptions<AppSettings> appSettings;
private readonly ICatalogService catalog;
private readonly ICatalogFilterDialogService catalogFilterDialogService;
public CatalogFilterDialog(DomainPropertyAccessors eShopBotAccessors, IOptions<AppSettings> appSettings, ICatalogService catalogService, ICatalogFilterDialogService catalogFilterDialogService, IDialogFactory dialogFactory) : base(Name)
{
this.eShopBotAccessors = eShopBotAccessors;
this.appSettings = appSettings;
catalog = catalogService;
this.catalogFilterDialogService = catalogFilterDialogService;
AddDialog(new WaterfallDialog(Name, new WaterfallStep []
{
AskBrandStep, ValidateBrandStep
}));
AddDialog(new WaterfallDialog(PromptProductTypes, new WaterfallStep []
{
AskTypeStep, ValidateTypeStep
}));
AddDialog(new ChoicePrompt(PromptChoices));
AddDialog(dialogFactory.CatalogDialog);
InitialDialogId = Name;
}
}
Basically, we handle most bot conversations using a ComponentDialog
. This dialog needs to configure child dialogs in the constructor, and then we change the context to this Dialog using context.BeginDialogAsync()
or context.ReplaceDialogAsync()
. We finish current dialog using context.EndDialogAsync()
. For detailed examples, please check the original source.
-
Recommendation systems
Product recommendation (ML Studio, C#) -
Computer Vision
Image classification-
Cognitive Services - Mobile applications (Xamarin, C#)
-
TensorFlow - Custom Model (ML Workbench, CNTK, TensorFlow, C#)
-
Natural Language Processing
Skype Bot (Microsoft Bot Framework, LUIS, C#) -
Regression
Sales Forecasting (ML.NET, C#)