Skip to content

Commit d9a1877

Browse files
committed
edits
1 parent 547e3ac commit d9a1877

File tree

4 files changed

+195
-99
lines changed

4 files changed

+195
-99
lines changed

articles/cognitive-services/LUIS/luis-csharp-tutorial-bf-v4.md

Lines changed: 142 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,6 @@ ms.date: 10/14/2019
99

1010
Use C# to build a chat bot integrated with language understanding (LUIS). The bot is built with the Azure [Web app bot](https://docs.microsoft.com/azure/bot-service/) resource and [Bot Framework version](https://github.com/Microsoft/botbuilder-dotnet) V4.
1111

12-
[!INCLUDE [Waiting for Bot refresh](./includes/wait-bot-upgrade.md)]
13-
14-
1512
**In this tutorial, you learn how to:**
1613

1714
> [!div class="checklist"]
@@ -58,7 +55,8 @@ Use C# to build a chat bot integrated with language understanding (LUIS). The bo
5855

5956
1. Select **Create**. This creates and deploys the bot service to Azure. Part of this process creates a LUIS app named `luis-csharp-bot-XXXX`. This name is based on the /Azure Bot Service app name.
6057

61-
[![Create web app bot](./media/bfv4-csharp/create-web-app-service.png)](./media/bfv4-csharp/create-web-app-service.png#lightbox)
58+
> [!div class="mx-imgBorder"]
59+
> [![Create web app bot](./media/bfv4-csharp/create-web-app-service.png)](./media/bfv4-csharp/create-web-app-service.png#lightbox)
6260
6361
Wait until the bot service is created before continuing.
6462

@@ -144,66 +142,157 @@ In order to develop the web app bot code, download the code and use on your loca
144142
1. Open **Dialogs -> MainDialog.cs** captures the utterance and sends it to the executeLuisQuery in the actStep method.
145143

146144
```csharp
147-
public class MainDialog : ComponentDialog
148-
{
149-
private readonly FlightBookingRecognizer _luisRecognizer;
145+
// Copyright (c) Microsoft Corporation. All rights reserved.
146+
// Licensed under the MIT License.
150147
151-
...
148+
using System;
149+
using System.Collections.Generic;
150+
using System.Linq;
151+
using System.Threading;
152+
using System.Threading.Tasks;
153+
using Microsoft.Bot.Builder;
154+
using Microsoft.Bot.Builder.Dialogs;
155+
using Microsoft.Bot.Schema;
156+
using Microsoft.Extensions.Logging;
157+
using Microsoft.Recognizers.Text.DataTypes.TimexExpression;
152158

153-
public MainDialog(FlightBookingRecognizer luisRecognizer, BookingDialog bookingDialog, ILogger<MainDialog> logger)
154-
: base(nameof(MainDialog))
159+
namespace Microsoft.BotBuilderSamples.Dialogs
160+
{
161+
public class MainDialog : ComponentDialog
155162
{
156-
_luisRecognizer = luisRecognizer;
157-
...
158-
}
163+
private readonly FlightBookingRecognizer _luisRecognizer;
164+
protected readonly ILogger Logger;
159165

160-
private async Task<DialogTurnResult> ActStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
161-
{
162-
if (!_luisRecognizer.IsConfigured)
166+
// Dependency injection uses this constructor to instantiate MainDialog
167+
public MainDialog(FlightBookingRecognizer luisRecognizer, BookingDialog bookingDialog, ILogger<MainDialog> logger)
168+
: base(nameof(MainDialog))
163169
{
164-
// LUIS is not configured, we just run the BookingDialog path with an empty BookingDetailsInstance.
165-
return await stepContext.BeginDialogAsync(nameof(BookingDialog), new BookingDetails(), cancellationToken);
170+
_luisRecognizer = luisRecognizer;
171+
Logger = logger;
172+
173+
AddDialog(new TextPrompt(nameof(TextPrompt)));
174+
AddDialog(bookingDialog);
175+
AddDialog(new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[]
176+
{
177+
IntroStepAsync,
178+
ActStepAsync,
179+
FinalStepAsync,
180+
}));
181+
182+
// The initial child Dialog to run.
183+
InitialDialogId = nameof(WaterfallDialog);
166184
}
167185

168-
// Call LUIS and gather any potential booking details. (Note the TurnContext has the response to the prompt.)
169-
var luisResult = await _luisRecognizer.RecognizeAsync<FlightBooking>(stepContext.Context, cancellationToken);
170-
switch (luisResult.TopIntent().intent)
186+
private async Task<DialogTurnResult> IntroStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
171187
{
172-
case FlightBooking.Intent.BookFlight:
173-
await ShowWarningForUnsupportedCities(stepContext.Context, luisResult, cancellationToken);
174-
175-
// Initialize BookingDetails with any entities we may have found in the response.
176-
var bookingDetails = new BookingDetails()
177-
{
178-
// Get destination and origin from the composite entities arrays.
179-
Destination = luisResult.ToEntities.Airport,
180-
Origin = luisResult.FromEntities.Airport,
181-
TravelDate = luisResult.TravelDate,
182-
};
183-
184-
// Run the BookingDialog giving it whatever details we have from the LUIS call, it will fill out the remainder.
185-
return await stepContext.BeginDialogAsync(nameof(BookingDialog), bookingDetails, cancellationToken);
186-
187-
case FlightBooking.Intent.GetWeather:
188-
// We haven't implemented the GetWeatherDialog so we just display a TODO message.
189-
var getWeatherMessageText = "TODO: get weather flow here";
190-
var getWeatherMessage = MessageFactory.Text(getWeatherMessageText, getWeatherMessageText, InputHints.IgnoringInput);
191-
await stepContext.Context.SendActivityAsync(getWeatherMessage, cancellationToken);
192-
break;
193-
194-
default:
195-
// Catch all for unhandled intents
196-
var didntUnderstandMessageText = $"Sorry, I didn't get that. Please try asking in a different way (intent was {luisResult.TopIntent().intent})";
197-
var didntUnderstandMessage = MessageFactory.Text(didntUnderstandMessageText, didntUnderstandMessageText, InputHints.IgnoringInput);
198-
await stepContext.Context.SendActivityAsync(didntUnderstandMessage, cancellationToken);
199-
break;
188+
if (!_luisRecognizer.IsConfigured)
189+
{
190+
await stepContext.Context.SendActivityAsync(
191+
MessageFactory.Text("NOTE: LUIS is not configured. To enable all capabilities, add 'LuisAppId', 'LuisAPIKey' and 'LuisAPIHostName' to the appsettings.json file.", inputHint: InputHints.IgnoringInput), cancellationToken);
192+
193+
return await stepContext.NextAsync(null, cancellationToken);
194+
}
195+
196+
// Use the text provided in FinalStepAsync or the default if it is the first time.
197+
var messageText = stepContext.Options?.ToString() ?? "What can I help you with today?\nSay something like \"Book a flight from Paris to Berlin on March 22, 2020\"";
198+
var promptMessage = MessageFactory.Text(messageText, messageText, InputHints.ExpectingInput);
199+
return await stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions { Prompt = promptMessage }, cancellationToken);
200200
}
201201

202-
return await stepContext.NextAsync(null, cancellationToken);
203-
}
202+
private async Task<DialogTurnResult> ActStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
203+
{
204+
if (!_luisRecognizer.IsConfigured)
205+
{
206+
// LUIS is not configured, we just run the BookingDialog path with an empty BookingDetailsInstance.
207+
return await stepContext.BeginDialogAsync(nameof(BookingDialog), new BookingDetails(), cancellationToken);
208+
}
209+
210+
// Call LUIS and gather any potential booking details. (Note the TurnContext has the response to the prompt.)
211+
var luisResult = await _luisRecognizer.RecognizeAsync<FlightBooking>(stepContext.Context, cancellationToken);
212+
switch (luisResult.TopIntent().intent)
213+
{
214+
case FlightBooking.Intent.BookFlight:
215+
await ShowWarningForUnsupportedCities(stepContext.Context, luisResult, cancellationToken);
216+
217+
// Initialize BookingDetails with any entities we may have found in the response.
218+
var bookingDetails = new BookingDetails()
219+
{
220+
// Get destination and origin from the composite entities arrays.
221+
Destination = luisResult.ToEntities.Airport,
222+
Origin = luisResult.FromEntities.Airport,
223+
TravelDate = luisResult.TravelDate,
224+
};
225+
226+
// Run the BookingDialog giving it whatever details we have from the LUIS call, it will fill out the remainder.
227+
return await stepContext.BeginDialogAsync(nameof(BookingDialog), bookingDetails, cancellationToken);
228+
229+
case FlightBooking.Intent.GetWeather:
230+
// We haven't implemented the GetWeatherDialog so we just display a TODO message.
231+
var getWeatherMessageText = "TODO: get weather flow here";
232+
var getWeatherMessage = MessageFactory.Text(getWeatherMessageText, getWeatherMessageText, InputHints.IgnoringInput);
233+
await stepContext.Context.SendActivityAsync(getWeatherMessage, cancellationToken);
234+
break;
235+
236+
default:
237+
// Catch all for unhandled intents
238+
var didntUnderstandMessageText = $"Sorry, I didn't get that. Please try asking in a different way (intent was {luisResult.TopIntent().intent})";
239+
var didntUnderstandMessage = MessageFactory.Text(didntUnderstandMessageText, didntUnderstandMessageText, InputHints.IgnoringInput);
240+
await stepContext.Context.SendActivityAsync(didntUnderstandMessage, cancellationToken);
241+
break;
242+
}
243+
244+
return await stepContext.NextAsync(null, cancellationToken);
245+
}
246+
247+
// Shows a warning if the requested From or To cities are recognized as entities but they are not in the Airport entity list.
248+
// In some cases LUIS will recognize the From and To composite entities as a valid cities but the From and To Airport values
249+
// will be empty if those entity values can't be mapped to a canonical item in the Airport.
250+
private static async Task ShowWarningForUnsupportedCities(ITurnContext context, FlightBooking luisResult, CancellationToken cancellationToken)
251+
{
252+
var unsupportedCities = new List<string>();
253+
254+
var fromEntities = luisResult.FromEntities;
255+
if (!string.IsNullOrEmpty(fromEntities.From) && string.IsNullOrEmpty(fromEntities.Airport))
256+
{
257+
unsupportedCities.Add(fromEntities.From);
258+
}
259+
260+
var toEntities = luisResult.ToEntities;
261+
if (!string.IsNullOrEmpty(toEntities.To) && string.IsNullOrEmpty(toEntities.Airport))
262+
{
263+
unsupportedCities.Add(toEntities.To);
264+
}
204265

205-
...
266+
if (unsupportedCities.Any())
267+
{
268+
var messageText = $"Sorry but the following airports are not supported: {string.Join(',', unsupportedCities)}";
269+
var message = MessageFactory.Text(messageText, messageText, InputHints.IgnoringInput);
270+
await context.SendActivityAsync(message, cancellationToken);
271+
}
272+
}
273+
274+
private async Task<DialogTurnResult> FinalStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
275+
{
276+
// If the child dialog ("BookingDialog") was cancelled, the user failed to confirm or if the intent wasn't BookFlight
277+
// the Result here will be null.
278+
if (stepContext.Result is BookingDetails result)
279+
{
280+
// Now we have all the booking details call the booking service.
206281
282+
// If the call to the booking service was successful tell the user.
283+
284+
var timeProperty = new TimexProperty(result.TravelDate);
285+
var travelDateMsg = timeProperty.ToNaturalLanguage(DateTime.Now);
286+
var messageText = $"I have you booked to {result.Destination} from {result.Origin} on {travelDateMsg}";
287+
var message = MessageFactory.Text(messageText, messageText, InputHints.IgnoringInput);
288+
await stepContext.Context.SendActivityAsync(message, cancellationToken);
289+
}
290+
291+
// Restart the main dialog with a different message the second time around
292+
var promptMessage = "What else can I do for you?";
293+
return await stepContext.ReplaceDialogAsync(InitialDialogId, promptMessage, cancellationToken);
294+
}
295+
}
207296
}
208297
```
209298

@@ -220,7 +309,7 @@ In Visual Studio 2019, start the bot. A browser window opens with the web app bo
220309
1. Enter the **Microsoft App ID** and **Microsoft App password**, found in the **appsettings.json** file in the root of the bot code you downloaded.
221310

222311

223-
1. In the bot emulator, enter `Book a flight from Seattle to Berlin tomorrow` and get the same response for the basic bot as you received in the **Test in Web Chat**.
312+
1. In the bot emulator, enter `Book a flight from Seattle to Berlin tomorrow` and get the same response for the basic bot as you received in the **Test in Web Chat** in a previous section.
224313

225314
[![Basic bot response in emulator](./media/bfv4-nodejs/ask-bot-emulator-a-question-and-get-response.png)](./media/bfv4-nodejs/ask-bot-emulator-a-question-and-get-response.png#lightbox)
226315

0 commit comments

Comments
 (0)