Zpracování přerušení je důležitým aspektem robustního robota. Uživatelé nebudou vždy sledovat definovaný tok konverzace krok za krokem. Můžou se pokusit položit otázku uprostřed procesu nebo ji jednoduše zrušit místo dokončení. Tento článek popisuje některé běžné způsoby, jak řešit přerušení uživatelů robota.
Ukázka základního robota používá službu Language Understanding (LUIS) k identifikaci záměrů uživatelů; ale identifikace záměru uživatele není fokusem tohoto článku.
Informace o identifikaci záměrů uživatelů najdete v tématu Principy přirozeného jazyka a Přidání porozumění přirozenému jazyku do robota.
Ukázka použitá v tomto článku modeluje robota rezervace letů, který používá dialogy k získání informací o letu od uživatele. Kdykoli během konverzace s robotem může uživatel vydat příkazy nápovědy nebo zrušit , aby způsobil přerušení. Zpracovávají se dva typy přerušení:
Pokud chcete použít dialogy, nainstalujte balíček NuGet Microsoft.Bot.Builder.Dialogs .
Dialogy\CancelAndHelpDialog.cs
CancelAndHelpDialog
Implementujte třídu pro zpracování přerušení uživatele. Dialogové okno s BookingDialog
možností zrušení a DateResolverDialog
odvození z této třídy.
public class CancelAndHelpDialog : ComponentDialog
CancelAndHelpDialog
Metoda ve třídě volá metoduInterruptAsync
, aby zkontrolovala, OnContinueDialogAsync
jestli uživatel přerušil normální tok. Pokud je tok přerušen, volají se metody základní třídy; v opačném případě se vrátí vrácená návratová hodnota.InterruptAsync
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);
}
Pokud uživatel zadá "nápovědu", InterruptAsync
metoda odešle zprávu a potom zavolá DialogTurnResult (DialogTurnStatus.Waiting)
, že dialogové okno nahoře čeká na odpověď od uživatele. Tímto způsobem se tok konverzace přeruší jenom pro otáčení a další turn pokračuje tam, kde konverzace skončila.
Pokud uživatel zadá "zrušit", zavolá CancelAllDialogsAsync
vnitřní kontext dialogového okna, který vymaže jeho zásobník dialogového okna a způsobí, že se ukončí se zrušeným stavem a žádná výsledná hodnota. MainDialog
V případě (zobrazeného později) se zobrazí, že dialogové okno rezervace skončilo a vrátilo hodnotu null, podobně jako když se uživatel rozhodne rezervaci nepotvrdit.
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;
}
Pokud chcete použít dialogy, nainstalujte balíček npm botbuilder-dialogs .
dialogy/cancelAndHelpDialog.js
CancelAndHelpDialog
Implementujte třídu pro zpracování přerušení uživatele. Zrušitelné dialogy BookingDialog
a DateResolverDialog
rozšířit tuto třídu.
class CancelAndHelpDialog extends ComponentDialog {
CancelAndHelpDialog
Metoda ve třídě volá metoduinterrupt
, aby zkontrolovala, onContinueDialog
jestli uživatel přerušil normální tok. Pokud je tok přerušen, volají se metody základní třídy; v opačném případě se vrátí vrácená návratová hodnota.interrupt
async onContinueDialog(innerDc) {
const result = await this.interrupt(innerDc);
if (result) {
return result;
}
return await super.onContinueDialog(innerDc);
}
Pokud uživatel zadá "help", interrupt
metoda odešle zprávu a pak vrátí { status: DialogTurnStatus.waiting }
objekt, který indikuje, že dialogové okno nahoře čeká na odpověď od uživatele. Tímto způsobem se tok konverzace přeruší jenom pro otáčení a další turn pokračuje tam, kde konverzace skončila.
Pokud uživatel zadá "zrušit", zavolá cancelAllDialogs
vnitřní kontext dialogového okna, který vymaže jeho zásobník dialogového okna a způsobí, že se ukončí se zrušeným stavem a žádná výsledná hodnota. MainDialog
V případě (zobrazeného později) se zobrazí, že dialogové okno rezervace skončilo a vrátilo hodnotu null, podobně jako když se uživatel rozhodne rezervaci nepotvrdit.
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
CancelAndHelpDialog
Implementujte třídu pro zpracování přerušení uživatele. Dialogové okno s BookingDialog
možností zrušení a DateResolverDialog
odvození z této třídy.
public class CancelAndHelpDialog extends ComponentDialog {
CancelAndHelpDialog
Metoda ve třídě volá metoduinterrupt
, aby zkontrolovala, onContinueDialog
jestli uživatel přerušil normální tok. Pokud je tok přerušen, volají se metody základní třídy; v opačném případě se vrátí vrácená návratová hodnota.interrupt
@Override
protected CompletableFuture<DialogTurnResult> onContinueDialog(DialogContext innerDc) {
return interrupt(innerDc).thenCompose(result -> {
if (result != null) {
return CompletableFuture.completedFuture(result);
}
return super.onContinueDialog(innerDc);
});
}
Pokud uživatel zadá "nápovědu", interrupt
metoda odešle zprávu a potom zavolá DialogTurnResult(DialogTurnStatus.WAITING)
, že dialogové okno nahoře čeká na odpověď od uživatele. Tímto způsobem se tok konverzace přeruší jenom pro otáčení a další turn pokračuje tam, kde konverzace skončila.
Pokud uživatel zadá "zrušit", zavolá cancelAllDialogs
vnitřní kontext dialogového okna, který vymaže jeho zásobník dialogového okna a způsobí, že se ukončí se zrušeným stavem a žádná výsledná hodnota. MainDialog
V případě (zobrazeného později) se zobrazí, že dialogové okno rezervace skončilo a vrátilo hodnotu null, podobně jako když se uživatel rozhodne rezervaci nepotvrdit.
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);
}
Chcete-li použít dialogy, nainstalujte botbuilder-dialogs
balíček a ujistěte se, že ukázkový requirements.txt
soubor obsahuje správný odkaz, například botbuilder-dialogs>=4.5.0
.
Další informace o instalaci balíčků najdete v souboru README úložiště ukázek.
Poznámka:
Spuštěno pip install botbuilder-dialogs
se také nainstaluje botbuilder-core
, botbuilder-connector
a botbuilder-schema
.
dialogy/cancel-and-help-dialog.py
CancelAndHelpDialog
Implementujte třídu pro zpracování přerušení uživatele. Dialogové okno s BookingDialog
možností zrušení a DateResolverDialog
odvození z této třídy.
class CancelAndHelpDialog(ComponentDialog):
CancelAndHelpDialog
Metoda ve třídě volá metoduinterrupt
, aby zkontrolovala, on_continue_dialog
jestli uživatel přerušil normální tok. Pokud je tok přerušen, volají se metody základní třídy; v opačném případě se vrátí vrácená návratová hodnota.interrupt
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)
Pokud uživatel zadá "help" nebo "?", interrupt
metoda odešle zprávu a pak zavolá DialogTurnResult(DialogTurnStatus.Waiting)
, že dialogové okno v horní části zásobníku čeká na odpověď od uživatele. Tímto způsobem se tok konverzace přeruší jenom pro otáčení a další turn pokračuje tam, kde konverzace skončila.
Pokud uživatel zadá "zrušit" nebo "quit", zavolá cancel_all_dialogs()
vnitřní kontext dialogového okna, který vymaže jeho zásobník dialogového okna a způsobí, že se ukončí se zrušeným stavem a žádná výsledná hodnota. Na zobrazeném obrázku MainDialog
se zobrazí, že dialogové okno rezervace skončilo a vrátilo hodnotu null, podobně jako když se uživatel rozhodne rezervaci nepotvrzovat.
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
Po implementaci třídy zpracování přerušení zkontrolujte, co se stane, když tento robot obdrží novou zprávu od uživatele.
Dialogy\MainDialog.cs
Při příchodu nové aktivity zprávy robot spustí MainDialog
. Zobrazí MainDialog
uživateli výzvu k tomu, s čím může pomoct. A pak spustí metodu BookingDialog
MainDialog.ActStepAsync
voláním BeginDialogAsync
, jak je znázorněno níže.
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);
}
V FinalStepAsync
metodě MainDialog
třídy se pak dialogové okno rezervace ukončilo a rezervace se považuje za dokončenou nebo zrušenou.
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);
}
Kód se BookingDialog
tady nezobrazuje, protože nesouvisí přímo se zpracováním přerušení. Používá se k zobrazení výzvy uživatelům k zadání podrobností rezervace. Tento kód najdete v dialogech\BookingDialogs.cs.
dialogy/mainDialog.js
Při příchodu nové aktivity zprávy robot spustí MainDialog
. Zobrazí MainDialog
uživateli výzvu k tomu, s čím může pomoct. A pak spustí metodu bookingDialog
MainDialog.actStep
voláním beginDialog
, jak je znázorněno níže.
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();
}
V finalStep
metodě MainDialog
třídy se pak dialogové okno rezervace ukončilo a rezervace se považuje za dokončenou nebo zrušenou.
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?' });
}
Kód se BookingDialog
tady nezobrazuje, protože nesouvisí přímo se zpracováním přerušení. Používá se k zobrazení výzvy uživatelům k zadání podrobností rezervace. Tento kód najdete v dialogových oknech nebo bookingDialogs.js.
MainDialog.java
Při příchodu nové aktivity zprávy robot spustí MainDialog
. Zobrazí MainDialog
uživateli výzvu k tomu, s čím může pomoct. A pak spustí metodu BookingDialog
MainDialog.actStep
voláním beginDialog
, jak je znázorněno níže.
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));
}
});
}
V finalStep
metodě MainDialog
třídy se pak dialogové okno rezervace ukončilo a rezervace se považuje za dokončenou nebo zrušenou.
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));
}
Kód se BookingDialog
tady nezobrazuje, protože nesouvisí přímo se zpracováním přerušení. Používá se k zobrazení výzvy uživatelům k zadání podrobností rezervace. Tento kód najdete v BookingDialogs.java.
dialogy/main_dialog.py
Při příchodu nové aktivity zprávy robot spustí MainDialog
. Zobrazí MainDialog
uživateli výzvu k tomu, s čím může pomoct. A pak spustí metodu bookingDialog
act_step
voláním begin_dialog
, jak je znázorněno níže.
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)
V final_step
metodě MainDialog
třídy se pak dialogové okno rezervace ukončilo a rezervace se považuje za dokončenou nebo zrušenou.
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)
Obslužná rutina chyby adaptéru zpracovává všechny výjimky, které nebyly zachyceny v robotovi.
AdapterWithErrorHandler.cs
V ukázce obslužná rutina adaptéru OnTurnError
obdrží všechny výjimky vyvolané logikou otáčení robota. Pokud dojde k vyvolání výjimky, obslužná rutina odstraní stav konverzace pro aktuální konverzaci, aby zabránila zablokování robota ve smyčce chyb způsobené chybovým stavem.
{
// 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
V ukázce obslužná rutina adaptéru onTurnError
obdrží všechny výjimky vyvolané logikou otáčení robota. Pokud dojde k vyvolání výjimky, obslužná rutina odstraní stav konverzace pro aktuální konverzaci, aby zabránila zablokování robota ve smyčce chyb způsobené chybovým stavem.
// 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.
Když zaregistrujete AdapterWithErrorHandler
rozhraní Spring v Application.java pro BotFrameworkHttpAdapter
tuto ukázku, obslužná rutina onTurnError
adaptéru obdrží všechny výjimky vyvolané logikou otáčení robota. Pokud dojde k vyvolání výjimky, obslužná rutina odstraní stav konverzace pro aktuální konverzaci, aby zabránila zablokování robota ve smyčce chyb způsobené chybovým stavem. V sadě Java SDK AdapterWithErrorHandler
se implementuje jako součást sady SDK a je součástí balíčku com.microsoft.bot.integration . Podrobnosti o implementaci tohoto adaptéru najdete ve zdrojovém kódu sady Java SDK.
adapter_with_error_handler.py
V ukázce obslužná rutina adaptéru on_error
obdrží všechny výjimky vyvolané logikou otáčení robota. Pokud dojde k vyvolání výjimky, obslužná rutina odstraní stav konverzace pro aktuální konverzaci, aby zabránila zablokování robota ve smyčce chyb způsobené chybovým stavem.
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
Startup.cs
Nakonec se robot vytvoří jako přechodný a při každém zapnutí se vytvoří nová instance robota.
// Register the BookingDialog.
Tady jsou definice tříd, které se používají při volání k vytvoření robota výše.
public class DialogAndWelcomeBot<T> : DialogBot<T>
public class DialogBot<T> : ActivityHandler
where T : Dialog
public class MainDialog : ComponentDialog
index.js
index.js
Nakonec se v robotovi vytvoří.
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');
Tady jsou definice tříd, které se používají při volání k vytvoření robota výše.
class MainDialog extends ComponentDialog {
class DialogAndWelcomeBot extends DialogBot {
class DialogBot extends ActivityHandler {
Application.java
Application.java
Nakonec se v robotovi vytvoří.
@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);
}
Tady jsou definice tříd, které se používají při volání k vytvoření robota výše.
public class DialogAndWelcomeBot<T extends Dialog> extends DialogBot {
public class DialogBot<T extends Dialog> extends ActivityHandler {
public class MainDialog extends ComponentDialog {
app.py Nakonec app.py
se vytvoří robot.
# Create dialogs and Bot
RECOGNIZER = FlightBookingRecognizer(CONFIG)
BOOKING_DIALOG = BookingDialog()
DIALOG = MainDialog(RECOGNIZER, BOOKING_DIALOG)
BOT = DialogAndWelcomeBot(CONVERSATION_STATE, USER_STATE, DIALOG)
Tady jsou definice tříd, které se používají při volání k vytvoření robota.
class MainDialog(ComponentDialog):
class DialogAndWelcomeBot(DialogBot):
class DialogBot(ActivityHandler):