Att hantera avbrott är en viktig aspekt av en robust robot. Användarna följer inte alltid ditt definierade konversationsflöde steg för steg. De kan försöka ställa en fråga mitt i processen, eller helt enkelt vilja avbryta den i stället för att slutföra den. I den här artikeln beskrivs några vanliga sätt att hantera användaravbrott i din robot.
Huvudrobotexemplet använder Language Understanding (LUIS) för att identifiera användarinsikter. Det är dock inte fokus för den här artikeln att identifiera användar avsikt.
Information om hur du identifierar användarsyften finns i Förstå naturligt språk och Lägg till förståelse för naturligt språk i din robot.
Exemplet som används i den här artikeln modellerar en flygbokningsrobot som använder dialogrutor för att hämta flyginformation från användaren. När som helst under konversationen med roboten kan användaren utfärda hjälp eller avbryta kommandon för att orsaka ett avbrott. Det finns två typer av avbrott som hanteras:
Om du vill använda dialogrutor installerar du NuGet-paketet Microsoft.Bot.Builder.Dialogs .
Dialogrutor\CancelAndHelpDialog.cs
CancelAndHelpDialog
Implementera klassen för att hantera användaravbrott. Dialogrutor som kan avbrytas och DateResolverDialog
härledas BookingDialog
från den här klassen.
public class CancelAndHelpDialog : ComponentDialog
CancelAndHelpDialog
I klassen OnContinueDialogAsync
anropar InterruptAsync
metoden metoden för att kontrollera om användaren har avbrutit det normala flödet. Om flödet avbryts anropas basklassmetoder. annars returneras returvärdet från 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);
}
Om användaren skriver "hjälp" InterruptAsync
skickar metoden ett meddelande och anropar DialogTurnResult (DialogTurnStatus.Waiting)
sedan för att indikera att dialogrutan längst upp väntar på ett svar från användaren. På så sätt avbryts konversationsflödet endast under en tur och nästa tur fortsätter där konversationen slutade.
Om användaren skriver "avbryt" anropas CancelAllDialogsAsync
dess inre dialogkontext, vilket rensar dess dialogstack och gör att den avslutas med en avbruten status och inget resultatvärde. MainDialog
Till (visas senare) visas att bokningsdialogrutan avslutades och returnerade null, ungefär som när användaren väljer att inte bekräfta sin bokning.
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;
}
Om du vill använda dialogrutor installerar du npm-paketet botbuilder-dialogs .
dialogrutor/cancelAndHelpDialog.js
CancelAndHelpDialog
Implementera klassen för att hantera användaravbrott. Dialogrutorna som kan avbrytas BookingDialog
och DateResolverDialog
utöka den här klassen.
class CancelAndHelpDialog extends ComponentDialog {
CancelAndHelpDialog
I klassen onContinueDialog
anropar interrupt
metoden metoden för att kontrollera om användaren har avbrutit det normala flödet. Om flödet avbryts anropas basklassmetoder. annars returneras returvärdet från interrupt
.
async onContinueDialog(innerDc) {
const result = await this.interrupt(innerDc);
if (result) {
return result;
}
return await super.onContinueDialog(innerDc);
}
Om användaren skriver "hjälp" interrupt
skickar metoden ett meddelande och returnerar sedan ett { status: DialogTurnStatus.waiting }
objekt som anger att dialogrutan längst upp väntar på ett svar från användaren. På så sätt avbryts konversationsflödet endast under en tur och nästa tur fortsätter där konversationen slutade.
Om användaren skriver "avbryt" anropas cancelAllDialogs
dess inre dialogkontext, vilket rensar dess dialogstack och gör att den avslutas med en avbruten status och inget resultatvärde. MainDialog
Till (visas senare) visas att bokningsdialogrutan avslutades och returnerade null, ungefär som när användaren väljer att inte bekräfta sin bokning.
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
Implementera klassen för att hantera användaravbrott. Dialogrutor som kan avbrytas och DateResolverDialog
härledas BookingDialog
från den här klassen.
public class CancelAndHelpDialog extends ComponentDialog {
CancelAndHelpDialog
I klassen onContinueDialog
anropar interrupt
metoden metoden för att kontrollera om användaren har avbrutit det normala flödet. Om flödet avbryts anropas basklassmetoder. annars returneras returvärdet från interrupt
.
@Override
protected CompletableFuture<DialogTurnResult> onContinueDialog(DialogContext innerDc) {
return interrupt(innerDc).thenCompose(result -> {
if (result != null) {
return CompletableFuture.completedFuture(result);
}
return super.onContinueDialog(innerDc);
});
}
Om användaren skriver "hjälp" interrupt
skickar metoden ett meddelande och anropar DialogTurnResult(DialogTurnStatus.WAITING)
sedan för att indikera att dialogrutan längst upp väntar på ett svar från användaren. På så sätt avbryts konversationsflödet endast under en tur och nästa tur fortsätter där konversationen slutade.
Om användaren skriver "avbryt" anropas cancelAllDialogs
dess inre dialogkontext, vilket rensar dess dialogstack och gör att den avslutas med en avbruten status och inget resultatvärde. MainDialog
Till (visas senare) visas att bokningsdialogrutan avslutades och returnerade null, ungefär som när användaren väljer att inte bekräfta sin bokning.
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);
}
Om du vill använda dialogrutor installerar botbuilder-dialogs
du paketet och ser till att exempelfilen requirements.txt
innehåller rätt referens, till exempel botbuilder-dialogs>=4.5.0
.
Mer information om hur du installerar paketen finns i readme-filen för exempellagringsplatsen.
Kommentar
Körningen pip install botbuilder-dialogs
kommer också att installera botbuilder-core
, botbuilder-connector
och botbuilder-schema
.
dialogrutor/cancel-and-help-dialog.py
CancelAndHelpDialog
Implementera klassen för att hantera användaravbrott. Dialogrutor som kan avbrytas och DateResolverDialog
härledas BookingDialog
från den här klassen.
class CancelAndHelpDialog(ComponentDialog):
CancelAndHelpDialog
I klassen on_continue_dialog
anropar interrupt
metoden metoden för att kontrollera om användaren har avbrutit det normala flödet. Om flödet avbryts anropas basklassmetoder. annars returneras returvärdet från 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)
Om användaren skriver "hjälp" eller "?", interrupt
skickar metoden ett meddelande och anropar DialogTurnResult(DialogTurnStatus.Waiting)
sedan för att indikera att dialogrutan ovanpå stacken väntar på ett svar från användaren. På så sätt avbryts konversationsflödet endast under en tur och nästa tur fortsätter där konversationen slutade.
Om användaren skriver "avbryt" eller "avsluta" anropas cancel_all_dialogs()
dess inre dialogkontext, vilket rensar dess dialogstack och gör att den avslutas med en avbruten status och inget resultatvärde. Till , MainDialog
som visas senare, visas det att bokningsdialogrutan avslutades och returnerade null, ungefär som när användaren väljer att inte bekräfta sin bokning.
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
När klassen för avbrottshantering har implementerats granskar du vad som händer när den här roboten tar emot ett nytt meddelande från användaren.
Dialogrutor\MainDialog.cs
När den nya meddelandeaktiviteten anländer kör roboten MainDialog
. Användaren MainDialog
uppmanas att ange vad det kan hjälpa till med. Och sedan startar BookingDialog
den i MainDialog.ActStepAsync
-metoden, med ett anrop till BeginDialogAsync
enligt nedan.
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);
}
FinalStepAsync
I klassens MainDialog
metod avslutades sedan bokningsdialogrutan och bokningen anses vara slutförd eller avbokad.
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);
}
Koden i BookingDialog
visas inte här eftersom den inte är direkt relaterad till avbrottshantering. Den används för att uppmana användarna att ange bokningsinformation. Du hittar koden i Dialogs\BookingDialogs.cs.
dialogrutor/mainDialog.js
När den nya meddelandeaktiviteten anländer kör roboten MainDialog
. Användaren MainDialog
uppmanas att ange vad det kan hjälpa till med. Och sedan startar bookingDialog
den i MainDialog.actStep
-metoden, med ett anrop till beginDialog
enligt nedan.
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();
}
finalStep
I klassens MainDialog
metod avslutades sedan bokningsdialogrutan och bokningen anses vara slutförd eller avbokad.
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?' });
}
Koden i BookingDialog
visas inte här eftersom den inte är direkt relaterad till avbrottshantering. Den används för att uppmana användarna att ange bokningsinformation. Du hittar koden i dialogrutor/bookingDialogs.js.
MainDialog.java
När den nya meddelandeaktiviteten anländer kör roboten MainDialog
. Användaren MainDialog
uppmanas att ange vad det kan hjälpa till med. Och sedan startar BookingDialog
den i MainDialog.actStep
-metoden, med ett anrop till beginDialog
enligt nedan.
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));
}
});
}
finalStep
I klassens MainDialog
metod avslutades sedan bokningsdialogrutan och bokningen anses vara slutförd eller avbokad.
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));
}
Koden i BookingDialog
visas inte här eftersom den inte är direkt relaterad till avbrottshantering. Den används för att uppmana användarna att ange bokningsinformation. Du hittar koden i BookingDialogs.java.
dialogrutor/main_dialog.py
När den nya meddelandeaktiviteten anländer kör roboten MainDialog
. Användaren MainDialog
uppmanas att ange vad det kan hjälpa till med. Och sedan startar bookingDialog
den i act_step
-metoden, med ett anrop till begin_dialog
enligt nedan.
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)
final_step
I klassens MainDialog
metod avslutades sedan bokningsdialogrutan och bokningen anses vara slutförd eller avbokad.
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)
Adapterns felhanterare hanterar alla undantag som inte har fångats i roboten.
AdapterWithErrorHandler.cs
I exemplet tar adapterns OnTurnError
hanterare emot eventuella undantag som genereras av robotens turlogik. Om ett undantag utlöses tar hanteraren bort konversationstillståndet för den aktuella konversationen för att förhindra att roboten fastnar i en felloop som orsakas av ett felaktigt tillstånd.
{
// 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
I exemplet tar adapterns onTurnError
hanterare emot eventuella undantag som genereras av robotens turlogik. Om ett undantag utlöses tar hanteraren bort konversationstillståndet för den aktuella konversationen för att förhindra att roboten fastnar i en felloop som orsakas av ett felaktigt tillstånd.
// 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.
Genom att registrera en AdapterWithErrorHandler
med Spring-ramverket i Application.java för BotFrameworkHttpAdapter
i det här exemplet tar adapterns onTurnError
hanterare emot eventuella undantag som genereras av robotens turlogik. Om ett undantag utlöses tar hanteraren bort konversationstillståndet för den aktuella konversationen för att förhindra att roboten fastnar i en felloop som orsakas av ett felaktigt tillstånd. I Java SDK AdapterWithErrorHandler
implementeras den som en del av SDK:t och ingår i integrationspaketet com.microsoft.bot.integration . Mer information om implementeringen av det här kortet finns i Java SDK-källkoden.
adapter_with_error_handler.py
I exemplet tar adapterns on_error
hanterare emot eventuella undantag som genereras av robotens turlogik. Om ett undantag utlöses tar hanteraren bort konversationstillståndet för den aktuella konversationen för att förhindra att roboten fastnar i en felloop som orsakas av ett felaktigt tillstånd.
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
Slutligen skapas roboten i Startup.cs
som en tillfällig, och vid varje tur skapas en ny instans av roboten.
// Register the BookingDialog.
Som referens finns här de klassdefinitioner som används i anropet för att skapa roboten ovan.
public class DialogAndWelcomeBot<T> : DialogBot<T>
public class DialogBot<T> : ActivityHandler
where T : Dialog
public class MainDialog : ComponentDialog
index.js
Slutligen skapas roboten i index.js
.
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');
Som referens finns här de klassdefinitioner som används i anropet för att skapa roboten ovan.
class MainDialog extends ComponentDialog {
class DialogAndWelcomeBot extends DialogBot {
class DialogBot extends ActivityHandler {
Application.java
Slutligen skapas roboten i Application.java
.
@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);
}
Som referens finns här de klassdefinitioner som används i anropet för att skapa roboten ovan.
public class DialogAndWelcomeBot<T extends Dialog> extends DialogBot {
public class DialogBot<T extends Dialog> extends ActivityHandler {
public class MainDialog extends ComponentDialog {
app.py Slutligen skapas roboten i app.py
.
# Create dialogs and Bot
RECOGNIZER = FlightBookingRecognizer(CONFIG)
BOOKING_DIALOG = BookingDialog()
DIALOG = MainDialog(RECOGNIZER, BOOKING_DIALOG)
BOT = DialogAndWelcomeBot(CONVERSATION_STATE, USER_STATE, DIALOG)
Som referens finns här de klassdefinitioner som används i anropet för att skapa roboten.
class MainDialog(ComponentDialog):
class DialogAndWelcomeBot(DialogBot):
class DialogBot(ActivityHandler):