Hantera användaravbrott

GÄLLER FÖR: SDK v4

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.

Kommentar

Bot Framework JavaScript-, C#- och Python-SDK:erna fortsätter att stödjas, men Java SDK dras tillbaka med slutligt långsiktigt stöd som slutar i november 2023.

Befintliga robotar som skapats med Java SDK fortsätter att fungera.

Om du vill skapa en ny robot bör du överväga att använda Power Virtual Agents och läsa om hur du väljer rätt chattrobotlösning.

Mer information finns i Framtiden för robotbygge.

Förutsättningar

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.

Kommentar

Language Understanding (LUIS) dras tillbaka den 1 oktober 2025. Från och med den 1 april 2023 kan du inte skapa nya LUIS-resurser. En nyare version av språktolkning är nu tillgänglig som en del av Azure AI Language.

Conversational Language Understanding (CLU), en funktion i Azure AI Language, är den uppdaterade versionen av LUIS. Mer information om stöd för språktolkning i Bot Framework SDK finns i Förstå naturligt språk.

Om det här exemplet

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:

  • Turnivå: Kringgå bearbetning på turnivå men lämna dialogrutan på stacken med den information som angavs. I nästa tur fortsätter du där konversationen slutade.
  • Dialognivå: Avbryt bearbetningen helt, så att roboten kan börja om igen.

Definiera och implementera avbrottslogik

Först definierar och implementerar du hjälpen och avbryter avbrott.

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;
}

Kontrollera om det finns avbrott i varje tur

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.

Hantera oväntade fel

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");
    };
}

Registrera tjänster

Startup.cs

Slutligen skapas roboten i Startup.cssom 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

Testa roboten

  1. Om du inte redan har gjort det installerar du Bot Framework-emulatorn.
  2. Kör exemplet lokalt på datorn.
  3. Starta emulatorn, anslut till roboten och skicka meddelanden enligt nedan.

Ytterligare information

  • Exemplet 24.bot-authentication-msgraph i C#, JavaScript, Python eller Java visar hur du hanterar en utloggningsbegäran. Det använder ett mönster som liknar det som visas här för att hantera avbrott.

  • Du bör skicka ett standardsvar i stället för att inte göra något och låta användaren undra vad som händer. Standardsvaret bör tala om för användaren vilka kommandon roboten förstår så att användaren kan komma igång igen.

  • När som helst i turordningen anger turn-kontextens svarsegenskap om roboten har skickat ett meddelande till användaren den här svängen. Innan svängen slutar bör roboten skicka ett meddelande till användaren, även om det är en enkel bekräftelse av deras indata.