Onderbrekingen verwerken is een belangrijk aspect van een robuuste bot. Gebruikers volgen niet altijd uw gedefinieerde gespreksstroom, stap voor stap. Ze kunnen proberen een vraag in het midden van het proces te stellen of deze te annuleren in plaats van het te voltooien. In dit artikel worden enkele veelvoorkomende manieren beschreven om onderbrekingen van gebruikers in uw bot af te handelen.
Het kernbotvoorbeeld maakt gebruik van Language Understanding (LUIS) om gebruikersintenties te identificeren; Het identificeren van gebruikersintentie is echter niet de focus van dit artikel.
Zie Voor informatie over het identificeren van gebruikersintenties natuurlijke taal en het toevoegen van natuurlijke taal aan uw bot.
Het voorbeeld dat in dit artikel wordt gebruikt, modelleert een vluchtboekingsbot die dialoogvensters gebruikt om vluchtinformatie van de gebruiker op te halen. Tijdens het gesprek met de bot kan de gebruiker op elk gewenst moment hulp verlenen of opdrachten annuleren om een onderbreking te veroorzaken. Er zijn twee soorten onderbrekingen verwerkt:
Als u dialoogvensters wilt gebruiken, installeert u het NuGet-pakket Microsoft.Bot.Builder.Dialogs .
Dialoogvensters\CancelAndHelpDialog.cs
Implementeer de CancelAndHelpDialog
klasse om onderbrekingen van gebruikers af te handelen. De geannuleerde dialoogvensters BookingDialog
en DateResolverDialog
zijn afgeleid van deze klasse.
public class CancelAndHelpDialog : ComponentDialog
In de CancelAndHelpDialog
klasse roept de OnContinueDialogAsync
methode de InterruptAsync
methode aan om te controleren of de gebruiker de normale stroom heeft onderbroken. Als de stroom wordt onderbroken, worden basisklassemethoden aangeroepen; anders wordt de retourwaarde van de InterruptAsync
retour geretourneerd.
protected override async Task<DialogTurnResult> OnContinueDialogAsync(DialogContext innerDc, CancellationToken cancellationToken = default)
{
var result = await InterruptAsync(innerDc, cancellationToken);
if (result != null)
{
return result;
}
return await base.OnContinueDialogAsync(innerDc, cancellationToken);
}
Als de gebruiker 'help' typt, verzendt de InterruptAsync
methode een bericht en roept DialogTurnResult (DialogTurnStatus.Waiting)
deze vervolgens aan om aan te geven dat het dialoogvenster bovenaan wacht op een antwoord van de gebruiker. Op deze manier wordt de gespreksstroom alleen onderbroken voor een beurt en gaat de volgende beurt verder vanaf waar het gesprek is gebleven.
Als de gebruiker 'annuleren' typt, roept CancelAllDialogsAsync
deze de interne dialoogvenstercontext aan, waardoor de dialoogvensterstack wordt gewist en wordt afgesloten met een geannuleerde status en geen resultaatwaarde. Op de MainDialog
(later weergegeven) ziet u dat het boekingsdialoogvenster is beëindigd en null is geretourneerd, vergelijkbaar met wanneer de gebruiker ervoor kiest om de reservering niet te bevestigen.
private async Task<DialogTurnResult> InterruptAsync(DialogContext innerDc, CancellationToken cancellationToken)
{
if (innerDc.Context.Activity.Type == ActivityTypes.Message)
{
var text = innerDc.Context.Activity.Text.ToLowerInvariant();
switch (text)
{
case "help":
case "?":
var helpMessage = MessageFactory.Text(HelpMsgText, HelpMsgText, InputHints.ExpectingInput);
await innerDc.Context.SendActivityAsync(helpMessage, cancellationToken);
return new DialogTurnResult(DialogTurnStatus.Waiting);
case "cancel":
case "quit":
var cancelMessage = MessageFactory.Text(CancelMsgText, CancelMsgText, InputHints.IgnoringInput);
await innerDc.Context.SendActivityAsync(cancelMessage, cancellationToken);
return await innerDc.CancelAllDialogsAsync(cancellationToken);
}
}
return null;
}
Als u dialoogvensters wilt gebruiken, installeert u het npm-pakket botbuilder-dialogs .
dialoogvensters/cancelAndHelpDialog.js
Implementeer de CancelAndHelpDialog
klasse om onderbrekingen van gebruikers af te handelen. De geannuleerde dialoogvensters BookingDialog
en DateResolverDialog
deze klasse uitbreiden.
class CancelAndHelpDialog extends ComponentDialog {
In de CancelAndHelpDialog
klasse roept de onContinueDialog
methode de interrupt
methode aan om te controleren of de gebruiker de normale stroom heeft onderbroken. Als de stroom wordt onderbroken, worden basisklassemethoden aangeroepen; anders wordt de retourwaarde van de interrupt
retour geretourneerd.
async onContinueDialog(innerDc) {
const result = await this.interrupt(innerDc);
if (result) {
return result;
}
return await super.onContinueDialog(innerDc);
}
Als de gebruiker 'help' typt, verzendt de interrupt
methode een bericht en retourneert vervolgens een { status: DialogTurnStatus.waiting }
object om aan te geven dat het dialoogvenster bovenaan wacht op een antwoord van de gebruiker. Op deze manier wordt de gespreksstroom alleen onderbroken voor een beurt en gaat de volgende beurt verder vanaf waar het gesprek is gebleven.
Als de gebruiker 'annuleren' typt, roept cancelAllDialogs
deze de interne dialoogvenstercontext aan, waardoor de dialoogvensterstack wordt gewist en wordt afgesloten met een geannuleerde status en geen resultaatwaarde. Op de MainDialog
(later weergegeven) ziet u dat het boekingsdialoogvenster is beëindigd en null is geretourneerd, vergelijkbaar met wanneer de gebruiker ervoor kiest om de reservering niet te bevestigen.
async interrupt(innerDc) {
if (innerDc.context.activity.text) {
const text = innerDc.context.activity.text.toLowerCase();
switch (text) {
case 'help':
case '?': {
const helpMessageText = 'Show help here';
await innerDc.context.sendActivity(helpMessageText, helpMessageText, InputHints.ExpectingInput);
return { status: DialogTurnStatus.waiting };
}
case 'cancel':
case 'quit': {
const cancelMessageText = 'Cancelling...';
await innerDc.context.sendActivity(cancelMessageText, cancelMessageText, InputHints.IgnoringInput);
return await innerDc.cancelAllDialogs();
}
}
}
}
CancelAndHelpDialog.java
Implementeer de CancelAndHelpDialog
klasse om onderbrekingen van gebruikers af te handelen. De geannuleerde dialoogvensters BookingDialog
en DateResolverDialog
zijn afgeleid van deze klasse.
public class CancelAndHelpDialog extends ComponentDialog {
In de CancelAndHelpDialog
klasse roept de onContinueDialog
methode de interrupt
methode aan om te controleren of de gebruiker de normale stroom heeft onderbroken. Als de stroom wordt onderbroken, worden basisklassemethoden aangeroepen; anders wordt de retourwaarde van de interrupt
retour geretourneerd.
@Override
protected CompletableFuture<DialogTurnResult> onContinueDialog(DialogContext innerDc) {
return interrupt(innerDc).thenCompose(result -> {
if (result != null) {
return CompletableFuture.completedFuture(result);
}
return super.onContinueDialog(innerDc);
});
}
Als de gebruiker 'help' typt, verzendt de interrupt
methode een bericht en roept DialogTurnResult(DialogTurnStatus.WAITING)
deze vervolgens aan om aan te geven dat het dialoogvenster bovenaan wacht op een antwoord van de gebruiker. Op deze manier wordt de gespreksstroom alleen onderbroken voor een beurt en gaat de volgende beurt verder vanaf waar het gesprek is gebleven.
Als de gebruiker 'annuleren' typt, roept cancelAllDialogs
deze de interne dialoogvenstercontext aan, waardoor de dialoogvensterstack wordt gewist en wordt afgesloten met een geannuleerde status en geen resultaatwaarde. Op de MainDialog
(later weergegeven) ziet u dat het boekingsdialoogvenster is beëindigd en null is geretourneerd, vergelijkbaar met wanneer de gebruiker ervoor kiest om de reservering niet te bevestigen.
private CompletableFuture<DialogTurnResult> interrupt(DialogContext innerDc) {
if (innerDc.getContext().getActivity().isType(ActivityTypes.MESSAGE)) {
String text = innerDc.getContext().getActivity().getText().toLowerCase();
switch (text) {
case "help":
case "?":
Activity helpMessage = MessageFactory
.text(helpMsgText, helpMsgText, InputHints.EXPECTING_INPUT);
return innerDc.getContext().sendActivity(helpMessage)
.thenCompose(sendResult ->
CompletableFuture
.completedFuture(new DialogTurnResult(DialogTurnStatus.WAITING)));
case "cancel":
case "quit":
Activity cancelMessage = MessageFactory
.text(cancelMsgText, cancelMsgText, InputHints.IGNORING_INPUT);
return innerDc.getContext()
.sendActivity(cancelMessage)
.thenCompose(sendResult -> innerDc.cancelAllDialogs());
default:
break;
}
}
return CompletableFuture.completedFuture(null);
}
Als u dialoogvensters wilt gebruiken, installeert u het botbuilder-dialogs
pakket en zorgt u ervoor dat het voorbeeldbestand requirements.txt
de juiste verwijzing bevat, zoals botbuilder-dialogs>=4.5.0
.
Zie het README-bestand van de voorbeeldenopslagplaats voor meer informatie over het installeren van de pakketten.
Notitie
Wordt pip install botbuilder-dialogs
uitgevoerd, wordt ook geïnstalleerd botbuilder-core
, botbuilder-connector
en botbuilder-schema
.
dialoogvensters/cancel-and-help-dialog.py
Implementeer de CancelAndHelpDialog
klasse om onderbrekingen van gebruikers af te handelen. De geannuleerde dialoogvensters BookingDialog
en DateResolverDialog
zijn afgeleid van deze klasse.
class CancelAndHelpDialog(ComponentDialog):
In de CancelAndHelpDialog
klasse roept de on_continue_dialog
methode de interrupt
methode aan om te controleren of de gebruiker de normale stroom heeft onderbroken. Als de stroom wordt onderbroken, worden basisklassemethoden aangeroepen; anders wordt de retourwaarde van de interrupt
retour geretourneerd.
async def on_continue_dialog(self, inner_dc: DialogContext) -> DialogTurnResult:
result = await self.interrupt(inner_dc)
if result is not None:
return result
return await super(CancelAndHelpDialog, self).on_continue_dialog(inner_dc)
Als de gebruiker 'help' of '?' typt, verzendt de interrupt
methode een bericht en roept DialogTurnResult(DialogTurnStatus.Waiting)
deze vervolgens aan om aan te geven dat het dialoogvenster boven aan de stack wacht op een antwoord van de gebruiker. Op deze manier wordt de gespreksstroom alleen onderbroken voor een beurt en gaat de volgende beurt verder vanaf waar het gesprek is gebleven.
Als de gebruiker 'annuleren' of 'afsluiten' typt, roept cancel_all_dialogs()
deze de binnenste dialoogvenstercontext aan, waardoor de dialoogvensterstack wordt gewist en wordt afgesloten met een geannuleerde status en geen resultaatwaarde. Op de MainDialog
, die later wordt weergegeven, wordt het weergegeven dat het boekingsdialoogvenster is beëindigd en null heeft geretourneerd, vergelijkbaar met wanneer de gebruiker ervoor kiest om de reservering niet te bevestigen.
async def interrupt(self, inner_dc: DialogContext) -> DialogTurnResult:
if inner_dc.context.activity.type == ActivityTypes.message:
text = inner_dc.context.activity.text.lower()
help_message_text = "Show Help..."
help_message = MessageFactory.text(
help_message_text, help_message_text, InputHints.expecting_input
)
if text in ("help", "?"):
await inner_dc.context.send_activity(help_message)
return DialogTurnResult(DialogTurnStatus.Waiting)
cancel_message_text = "Cancelling"
cancel_message = MessageFactory.text(
cancel_message_text, cancel_message_text, InputHints.ignoring_input
)
if text in ("cancel", "quit"):
await inner_dc.context.send_activity(cancel_message)
return await inner_dc.cancel_all_dialogs()
return None
Zodra de interruptafhandelingsklasse is geïmplementeerd, controleert u wat er gebeurt wanneer deze bot een nieuw bericht van de gebruiker ontvangt.
Dialoogvensters\MainDialog.cs
Wanneer de nieuwe berichtactiviteit binnenkomt, wordt de MainDialog
bot uitgevoerd. De MainDialog
gebruiker wordt gevraagd om waar deze mee kan helpen. Vervolgens wordt de BookingDialog
MainDialog.ActStepAsync
methode gestart, met een aanroep naar BeginDialogAsync
zoals hieronder wordt weergegeven.
private async Task<DialogTurnResult> ActStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
if (!_luisRecognizer.IsConfigured)
{
// LUIS is not configured, we just run the BookingDialog path with an empty BookingDetailsInstance.
return await stepContext.BeginDialogAsync(nameof(BookingDialog), new BookingDetails(), cancellationToken);
}
// Call LUIS and gather any potential booking details. (Note the TurnContext has the response to the prompt.)
var luisResult = await _luisRecognizer.RecognizeAsync<FlightBooking>(stepContext.Context, cancellationToken);
switch (luisResult.TopIntent().intent)
{
case FlightBooking.Intent.BookFlight:
await ShowWarningForUnsupportedCities(stepContext.Context, luisResult, cancellationToken);
// Initialize BookingDetails with any entities we may have found in the response.
var bookingDetails = new BookingDetails()
{
// Get destination and origin from the composite entities arrays.
Destination = luisResult.ToEntities.Airport,
Origin = luisResult.FromEntities.Airport,
TravelDate = luisResult.TravelDate,
};
// Run the BookingDialog giving it whatever details we have from the LUIS call, it will fill out the remainder.
return await stepContext.BeginDialogAsync(nameof(BookingDialog), bookingDetails, cancellationToken);
case FlightBooking.Intent.GetWeather:
// We haven't implemented the GetWeatherDialog so we just display a TODO message.
var getWeatherMessageText = "TODO: get weather flow here";
var getWeatherMessage = MessageFactory.Text(getWeatherMessageText, getWeatherMessageText, InputHints.IgnoringInput);
await stepContext.Context.SendActivityAsync(getWeatherMessage, cancellationToken);
break;
default:
// Catch all for unhandled intents
var didntUnderstandMessageText = $"Sorry, I didn't get that. Please try asking in a different way (intent was {luisResult.TopIntent().intent})";
var didntUnderstandMessage = MessageFactory.Text(didntUnderstandMessageText, didntUnderstandMessageText, InputHints.IgnoringInput);
await stepContext.Context.SendActivityAsync(didntUnderstandMessage, cancellationToken);
break;
}
return await stepContext.NextAsync(null, cancellationToken);
}
Vervolgens wordt in de FinalStepAsync
methode van de MainDialog
klas het boekingsdialoogvenster beëindigd en wordt de reservering beschouwd als voltooid of geannuleerd.
private async Task<DialogTurnResult> FinalStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
// If the child dialog ("BookingDialog") was cancelled, the user failed to confirm or if the intent wasn't BookFlight
// the Result here will be null.
if (stepContext.Result is BookingDetails result)
{
// Now we have all the booking details call the booking service.
// If the call to the booking service was successful tell the user.
var timeProperty = new TimexProperty(result.TravelDate);
var travelDateMsg = timeProperty.ToNaturalLanguage(DateTime.Now);
var messageText = $"I have you booked to {result.Destination} from {result.Origin} on {travelDateMsg}";
var message = MessageFactory.Text(messageText, messageText, InputHints.IgnoringInput);
await stepContext.Context.SendActivityAsync(message, cancellationToken);
}
// Restart the main dialog with a different message the second time around
var promptMessage = "What else can I do for you?";
return await stepContext.ReplaceDialogAsync(InitialDialogId, promptMessage, cancellationToken);
}
De code wordt BookingDialog
hier niet weergegeven, omdat deze niet rechtstreeks is gerelateerd aan de verwerking van onderbrekingen. Het wordt gebruikt om gebruikers te vragen om reserveringsgegevens. U vindt die code in Dialogs\BookingDialogs.cs.
dialoogvensters/mainDialog.js
Wanneer de nieuwe berichtactiviteit binnenkomt, wordt de MainDialog
bot uitgevoerd. De MainDialog
gebruiker wordt gevraagd om waar deze mee kan helpen. Vervolgens wordt de bookingDialog
MainDialog.actStep
methode gestart, met een aanroep naar beginDialog
zoals hieronder wordt weergegeven.
async actStep(stepContext) {
const bookingDetails = {};
if (!this.luisRecognizer.isConfigured) {
// LUIS is not configured, we just run the BookingDialog path.
return await stepContext.beginDialog('bookingDialog', bookingDetails);
}
// Call LUIS and gather any potential booking details. (Note the TurnContext has the response to the prompt)
const luisResult = await this.luisRecognizer.executeLuisQuery(stepContext.context);
switch (LuisRecognizer.topIntent(luisResult)) {
case 'BookFlight': {
// Extract the values for the composite entities from the LUIS result.
const fromEntities = this.luisRecognizer.getFromEntities(luisResult);
const toEntities = this.luisRecognizer.getToEntities(luisResult);
// Show a warning for Origin and Destination if we can't resolve them.
await this.showWarningForUnsupportedCities(stepContext.context, fromEntities, toEntities);
// Initialize BookingDetails with any entities we may have found in the response.
bookingDetails.destination = toEntities.airport;
bookingDetails.origin = fromEntities.airport;
bookingDetails.travelDate = this.luisRecognizer.getTravelDate(luisResult);
console.log('LUIS extracted these booking details:', JSON.stringify(bookingDetails));
// Run the BookingDialog passing in whatever details we have from the LUIS call, it will fill out the remainder.
return await stepContext.beginDialog('bookingDialog', bookingDetails);
}
case 'GetWeather': {
// We haven't implemented the GetWeatherDialog so we just display a TODO message.
const getWeatherMessageText = 'TODO: get weather flow here';
await stepContext.context.sendActivity(getWeatherMessageText, getWeatherMessageText, InputHints.IgnoringInput);
break;
}
default: {
// Catch all for unhandled intents
const didntUnderstandMessageText = `Sorry, I didn't get that. Please try asking in a different way (intent was ${ LuisRecognizer.topIntent(luisResult) })`;
await stepContext.context.sendActivity(didntUnderstandMessageText, didntUnderstandMessageText, InputHints.IgnoringInput);
}
}
return await stepContext.next();
}
Vervolgens wordt in de finalStep
methode van de MainDialog
klas het boekingsdialoogvenster beëindigd en wordt de reservering beschouwd als voltooid of geannuleerd.
async finalStep(stepContext) {
// If the child dialog ("bookingDialog") was cancelled or the user failed to confirm, the Result here will be null.
if (stepContext.result) {
const result = stepContext.result;
// Now we have all the booking details.
// This is where calls to the booking AOU service or database would go.
// If the call to the booking service was successful tell the user.
const timeProperty = new TimexProperty(result.travelDate);
const travelDateMsg = timeProperty.toNaturalLanguage(new Date(Date.now()));
const msg = `I have you booked to ${ result.destination } from ${ result.origin } on ${ travelDateMsg }.`;
await stepContext.context.sendActivity(msg, msg, InputHints.IgnoringInput);
}
// Restart the main dialog with a different message the second time around
return await stepContext.replaceDialog(this.initialDialogId, { restartMsg: 'What else can I do for you?' });
}
De code wordt BookingDialog
hier niet weergegeven, omdat deze niet rechtstreeks is gerelateerd aan de verwerking van onderbrekingen. Het wordt gebruikt om gebruikers te vragen om reserveringsgegevens. U vindt die code in dialoogvensters/bookingDialogs.js.
MainDialog.java
Wanneer de nieuwe berichtactiviteit binnenkomt, wordt de MainDialog
bot uitgevoerd. De MainDialog
gebruiker wordt gevraagd om waar deze mee kan helpen. Vervolgens wordt de BookingDialog
methode MainDialog.actStep
gestart, met een aanroep naar beginDialog
zoals hieronder wordt weergegeven.
private CompletableFuture<DialogTurnResult> actStep(WaterfallStepContext stepContext) {
if (!luisRecognizer.isConfigured()) {
// LUIS is not configured, we just run the BookingDialog path with an empty BookingDetailsInstance.
return stepContext.beginDialog("BookingDialog", new BookingDetails());
}
// Call LUIS and gather any potential booking details. (Note the TurnContext has the response to the prompt.)
return luisRecognizer.recognize(stepContext.getContext()).thenCompose(luisResult -> {
switch (luisResult.getTopScoringIntent().intent) {
case "BookFlight":
// Extract the values for the composite entities from the LUIS result.
ObjectNode fromEntities = luisRecognizer.getFromEntities(luisResult);
ObjectNode toEntities = luisRecognizer.getToEntities(luisResult);
// Show a warning for Origin and Destination if we can't resolve them.
return showWarningForUnsupportedCities(
stepContext.getContext(), fromEntities, toEntities)
.thenCompose(showResult -> {
// Initialize BookingDetails with any entities we may have found in the response.
BookingDetails bookingDetails = new BookingDetails();
bookingDetails.setDestination(toEntities.get("airport").asText());
bookingDetails.setOrigin(fromEntities.get("airport").asText());
bookingDetails.setTravelDate(luisRecognizer.getTravelDate(luisResult));
// Run the BookingDialog giving it whatever details we have from the LUIS call,
// it will fill out the remainder.
return stepContext.beginDialog("BookingDialog", bookingDetails);
}
);
case "GetWeather":
// We haven't implemented the GetWeatherDialog so we just display a TODO message.
String getWeatherMessageText = "TODO: get weather flow here";
Activity getWeatherMessage = MessageFactory
.text(
getWeatherMessageText, getWeatherMessageText,
InputHints.IGNORING_INPUT
);
return stepContext.getContext().sendActivity(getWeatherMessage)
.thenCompose(resourceResponse -> stepContext.next(null));
default:
// Catch all for unhandled intents
String didntUnderstandMessageText = String.format(
"Sorry, I didn't get that. Please "
+ " try asking in a different way (intent was %s)",
luisResult.getTopScoringIntent().intent
);
Activity didntUnderstandMessage = MessageFactory
.text(
didntUnderstandMessageText, didntUnderstandMessageText,
InputHints.IGNORING_INPUT
);
return stepContext.getContext().sendActivity(didntUnderstandMessage)
.thenCompose(resourceResponse -> stepContext.next(null));
}
});
}
Vervolgens wordt in de finalStep
methode van de MainDialog
klas het boekingsdialoogvenster beëindigd en wordt de reservering beschouwd als voltooid of geannuleerd.
private CompletableFuture<DialogTurnResult> finalStep(WaterfallStepContext stepContext) {
CompletableFuture<Void> stepResult = CompletableFuture.completedFuture(null);
// If the child dialog ("BookingDialog") was cancelled,
// the user failed to confirm or if the intent wasn't BookFlight
// the Result here will be null.
if (stepContext.getResult() instanceof BookingDetails) {
// Now we have all the booking details call the booking service.
// If the call to the booking service was successful tell the user.
BookingDetails result = (BookingDetails) stepContext.getResult();
TimexProperty timeProperty = new TimexProperty(result.getTravelDate());
String travelDateMsg = timeProperty.toNaturalLanguage(LocalDateTime.now());
String messageText = String.format("I have you booked to %s from %s on %s",
result.getDestination(), result.getOrigin(), travelDateMsg
);
Activity message = MessageFactory
.text(messageText, messageText, InputHints.IGNORING_INPUT);
stepResult = stepContext.getContext().sendActivity(message).thenApply(sendResult -> null);
}
// Restart the main dialog with a different message the second time around
String promptMessage = "What else can I do for you?";
return stepResult
.thenCompose(result -> stepContext.replaceDialog(getInitialDialogId(), promptMessage));
}
De code wordt BookingDialog
hier niet weergegeven, omdat deze niet rechtstreeks is gerelateerd aan de verwerking van onderbrekingen. Het wordt gebruikt om gebruikers te vragen om reserveringsgegevens. U vindt die code in BookingDialogs.java.
dialoogvensters/main_dialog.py
Wanneer de nieuwe berichtactiviteit binnenkomt, wordt de MainDialog
bot uitgevoerd. De MainDialog
gebruiker wordt gevraagd om waar deze mee kan helpen. Vervolgens wordt de bookingDialog
act_step
methode gestart, met een aanroep naar begin_dialog
zoals hieronder wordt weergegeven.
async def act_step(self, step_context: WaterfallStepContext) -> DialogTurnResult:
if not self._luis_recognizer.is_configured:
# LUIS is not configured, we just run the BookingDialog path with an empty BookingDetailsInstance.
return await step_context.begin_dialog(
self._booking_dialog_id, BookingDetails()
)
# Call LUIS and gather any potential booking details. (Note the TurnContext has the response to the prompt.)
intent, luis_result = await LuisHelper.execute_luis_query(
self._luis_recognizer, step_context.context
)
if intent == Intent.BOOK_FLIGHT.value and luis_result:
# Show a warning for Origin and Destination if we can't resolve them.
await MainDialog._show_warning_for_unsupported_cities(
step_context.context, luis_result
)
# Run the BookingDialog giving it whatever details we have from the LUIS call.
return await step_context.begin_dialog(self._booking_dialog_id, luis_result)
if intent == Intent.GET_WEATHER.value:
get_weather_text = "TODO: get weather flow here"
get_weather_message = MessageFactory.text(
get_weather_text, get_weather_text, InputHints.ignoring_input
)
await step_context.context.send_activity(get_weather_message)
else:
didnt_understand_text = (
"Sorry, I didn't get that. Please try asking in a different way"
)
didnt_understand_message = MessageFactory.text(
didnt_understand_text, didnt_understand_text, InputHints.ignoring_input
)
await step_context.context.send_activity(didnt_understand_message)
return await step_context.next(None)
Vervolgens wordt in de final_step
methode van de MainDialog
klas het boekingsdialoogvenster beëindigd en wordt de reservering beschouwd als voltooid of geannuleerd.
async def final_step(self, step_context: WaterfallStepContext) -> DialogTurnResult:
# If the child dialog ("BookingDialog") was cancelled or the user failed to confirm,
# the Result here will be null.
if step_context.result is not None:
result = step_context.result
# Now we have all the booking details call the booking service.
# If the call to the booking service was successful tell the user.
# time_property = Timex(result.travel_date)
# travel_date_msg = time_property.to_natural_language(datetime.now())
msg_txt = f"I have you booked to {result.destination} from {result.origin} on {result.travel_date}"
message = MessageFactory.text(msg_txt, msg_txt, InputHints.ignoring_input)
await step_context.context.send_activity(message)
prompt_message = "What else can I do for you?"
return await step_context.replace_dialog(self.id, prompt_message)
De fouthandler van de adapter verwerkt eventuele uitzonderingen die niet in de bot zijn opgeslagen.
AdapterWithErrorHandler.cs
In het voorbeeld ontvangt de handler van OnTurnError
de adapter eventuele uitzonderingen die worden gegenereerd door de beurtlogica van uw bot. Als er een uitzondering optreedt, verwijdert de handler de gespreksstatus voor het huidige gesprek om te voorkomen dat de bot vastloopt in een foutlus die wordt veroorzaakt door een slechte status.
{
// Log any leaked exception from the application.
// NOTE: In production environment, you should consider logging this to
// Azure Application Insights. Visit https://aka.ms/bottelemetry to see how
// to add telemetry capture to your bot.
logger.LogError(exception, $"[OnTurnError] unhandled error : {exception.Message}");
// Send a message to the user
var errorMessageText = "The bot encountered an error or bug.";
var errorMessage = MessageFactory.Text(errorMessageText, errorMessageText, InputHints.IgnoringInput);
await turnContext.SendActivityAsync(errorMessage);
errorMessageText = "To continue to run this bot, please fix the bot source code.";
errorMessage = MessageFactory.Text(errorMessageText, errorMessageText, InputHints.ExpectingInput);
await turnContext.SendActivityAsync(errorMessage);
if (conversationState != null)
{
try
{
// Delete the conversationState for the current conversation to prevent the
// bot from getting stuck in a error-loop caused by being in a bad state.
// ConversationState should be thought of as similar to "cookie-state" in a Web pages.
await conversationState.DeleteAsync(turnContext);
}
catch (Exception e)
{
logger.LogError(e, $"Exception caught on attempting to Delete ConversationState : {e.Message}");
}
}
// Send a trace activity, which will be displayed in the Bot Framework Emulator
await turnContext.TraceActivityAsync("OnTurnError Trace", exception.Message, "https://www.botframework.com/schemas/error", "TurnError");
};
}
index.js
In het voorbeeld ontvangt de handler van onTurnError
de adapter eventuele uitzonderingen die worden gegenereerd door de beurtlogica van uw bot. Als er een uitzondering optreedt, verwijdert de handler de gespreksstatus voor het huidige gesprek om te voorkomen dat de bot vastloopt in een foutlus die wordt veroorzaakt door een slechte status.
// Send a trace activity, which will be displayed in Bot Framework Emulator
await context.sendTraceActivity(
'OnTurnError Trace',
`${ error }`,
'https://www.botframework.com/schemas/error',
'TurnError'
);
// Send a message to the user
let onTurnErrorMessage = 'The bot encountered an error or bug.';
await context.sendActivity(onTurnErrorMessage, onTurnErrorMessage, InputHints.ExpectingInput);
onTurnErrorMessage = 'To continue to run this bot, please fix the bot source code.';
await context.sendActivity(onTurnErrorMessage, onTurnErrorMessage, InputHints.ExpectingInput);
// Clear out state
await conversationState.delete(context);
};
// Set the onTurnError for the singleton CloudAdapter.
adapter.onTurnError = onTurnErrorHandler;
// Define a state store for your bot. See https://aka.ms/about-bot-state to learn more about using MemoryStorage.
// A bot requires a state store to persist the dialog and user state between messages.
// For local development, in-memory storage is used.
Door een AdapterWithErrorHandler
exemplaar te registreren bij het Spring-framework in Application.java voor het BotFrameworkHttpAdapter
in dit voorbeeld, ontvangt de handler van onTurnError
de adapter eventuele uitzonderingen die worden gegenereerd door de beurtlogica van uw bot. Als er een uitzondering optreedt, verwijdert de handler de gespreksstatus voor het huidige gesprek om te voorkomen dat de bot vastloopt in een foutlus die wordt veroorzaakt door een slechte status. In de Java SDK wordt de AdapterWithErrorHandler
sdk geïmplementeerd als onderdeel van de SDK en is deze opgenomen in het com.microsoft.bot.integration-pakket . Zie de Java SDK-broncode voor meer informatie over de implementatie van deze adapter.
adapter_with_error_handler.py
In het voorbeeld ontvangt de handler van on_error
de adapter eventuele uitzonderingen die worden gegenereerd door de beurtlogica van uw bot. Als er een uitzondering optreedt, verwijdert de handler de gespreksstatus voor het huidige gesprek om te voorkomen dat de bot vastloopt in een foutlus die wordt veroorzaakt door een slechte status.
def __init__(
self,
settings: ConfigurationBotFrameworkAuthentication,
conversation_state: ConversationState,
):
super().__init__(settings)
self._conversation_state = conversation_state
# Catch-all for errors.
async def on_error(context: TurnContext, error: Exception):
# This check writes out errors to console log
# NOTE: In production environment, you should consider logging this to Azure
# application insights.
print(f"\n [on_turn_error] unhandled error: {error}", file=sys.stderr)
traceback.print_exc()
# Send a message to the user
await context.send_activity("The bot encountered an error or bug.")
await context.send_activity(
"To continue to run this bot, please fix the bot source code."
)
# Send a trace activity if we're talking to the Bot Framework Emulator
if context.activity.channel_id == "emulator":
# Create a trace activity that contains the error object
trace_activity = Activity(
label="TurnError",
name="on_turn_error Trace",
timestamp=datetime.utcnow(),
type=ActivityTypes.trace,
value=f"{error}",
value_type="https://www.botframework.com/schemas/error",
)
# Send a trace activity, which will be displayed in Bot Framework Emulator
await context.send_activity(trace_activity)
# Clear out state
nonlocal self
await self._conversation_state.delete(context)
self.on_turn_error = on_error
Startup.cs
Ten slotte Startup.cs
wordt de bot gemaakt als een tijdelijke, en bij elke beurt wordt er een nieuw exemplaar van de bot gemaakt.
// Register the BookingDialog.
Ter referentie: hier volgen de klassedefinities die worden gebruikt in de aanroep om de bovenstaande bot te maken.
public class DialogAndWelcomeBot<T> : DialogBot<T>
public class DialogBot<T> : ActivityHandler
where T : Dialog
public class MainDialog : ComponentDialog
index.js
Ten slotte wordt index.js
de bot gemaakt.
const dialog = new MainDialog(luisRecognizer, bookingDialog);
const bot = new DialogAndWelcomeBot(conversationState, userState, dialog);
// Create HTTP server
const server = restify.createServer();
server.use(restify.plugins.bodyParser());
server.listen(process.env.port || process.env.PORT || 3978, function() {
console.log(`\n${ server.name } listening to ${ server.url }`);
console.log('\nGet Bot Framework Emulator: https://aka.ms/botframework-emulator');
Ter referentie: hier volgen de klassedefinities die worden gebruikt in de aanroep om de bovenstaande bot te maken.
class MainDialog extends ComponentDialog {
class DialogAndWelcomeBot extends DialogBot {
class DialogBot extends ActivityHandler {
Application.java
Ten slotte wordt Application.java
de bot gemaakt.
@Bean
public Bot getBot(
Configuration configuration,
UserState userState,
ConversationState conversationState
) {
FlightBookingRecognizer recognizer = new FlightBookingRecognizer(configuration);
MainDialog dialog = new MainDialog(recognizer, new BookingDialog());
return new DialogAndWelcomeBot<>(conversationState, userState, dialog);
}
Ter referentie: hier volgen de klassedefinities die worden gebruikt in de aanroep om de bovenstaande bot te maken.
public class DialogAndWelcomeBot<T extends Dialog> extends DialogBot {
public class DialogBot<T extends Dialog> extends ActivityHandler {
public class MainDialog extends ComponentDialog {
app.py ten slotte app.py
wordt de bot gemaakt.
# Create dialogs and Bot
RECOGNIZER = FlightBookingRecognizer(CONFIG)
BOOKING_DIALOG = BookingDialog()
DIALOG = MainDialog(RECOGNIZER, BOOKING_DIALOG)
BOT = DialogAndWelcomeBot(CONVERSATION_STATE, USER_STATE, DIALOG)
Ter referentie: hier volgen de klassedefinities die worden gebruikt in de aanroep om de bot te maken.
class MainDialog(ComponentDialog):
class DialogAndWelcomeBot(DialogBot):
class DialogBot(ActivityHandler):