Delen via


Gebruikersonderbrekingen afhandelen

VAN TOEPASSING OP: SDK v4

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.

Notitie

De Sdk's voor Bot Framework JavaScript, C# en Python blijven ondersteund, maar de Java SDK wordt buiten gebruik gesteld met definitieve langetermijnondersteuning die eindigt op november 2023.

Bestaande bots die zijn gebouwd met de Java SDK blijven functioneren.

Voor het bouwen van nieuwe bots kunt u Microsoft Copilot Studio gebruiken en lezen over het kiezen van de juiste copilot-oplossing.

Zie De toekomst van botbouw voor meer informatie.

Vereisten

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.

Notitie

Language Understanding (LUIS) wordt op 1 oktober 2025 buiten gebruik gesteld. Vanaf 1 april 2023 kunt u geen nieuwe LUIS-resources maken. Er is nu een nieuwere versie van taalkennis beschikbaar als onderdeel van Azure AI Language.

Conversational Language Understanding (CLU), een functie van Azure AI Language, is de bijgewerkte versie van LUIS. Zie Natuurlijke taalkennis voor meer informatie over ondersteuning voor taalkennis in de Bot Framework SDK.

Over dit voorbeeld

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:

  • Turn level: Bypass processing at the turn level but leave the dialog on the stack with the information that was provided. Ga in de volgende beurt verder vanaf waar het gesprek was gebleven.
  • Dialoogvensterniveau: De verwerking volledig annuleren, zodat de bot opnieuw kan worden gestart.

De onderbrekingslogica definiëren en implementeren

Definieer en implementeer eerst de help en annuleer onderbrekingen.

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

Controleren op onderbrekingen per beurt

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 MainDialogbot 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.

Onverwachte fouten afhandelen

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

Services registreren

Startup.cs

Ten slotte Startup.cswordt 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

De bot testen

  1. Als u dit nog niet hebt gedaan, installeert u de Bot Framework Emulator.
  2. Voer het voorbeeld lokaal uit op uw computer.
  3. Start de emulator, maak verbinding met uw bot en verzend berichten zoals hieronder wordt weergegeven.

Aanvullende informatie

  • Het voorbeeld 24.bot-authentication-msgraph in C#, JavaScript, Python of Java laat zien hoe u een afmeldingsaanvraag kunt verwerken. Er wordt een patroon gebruikt dat vergelijkbaar is met het patroon dat hier wordt weergegeven voor het afhandelen van onderbrekingen.

  • U moet een standaardantwoord verzenden in plaats van niets te doen en de gebruiker te laten weten wat er aan de hand is. Het standaardantwoord moet de gebruiker vertellen welke opdrachten de bot begrijpt, zodat de gebruiker weer op schema kan komen.

  • Op elk moment op de beurt geeft de eigenschap reactie van de beurtcontext aan of de bot een bericht heeft verzonden naar de gebruiker. Voordat de beurt eindigt, moet uw bot een bericht naar de gebruiker verzenden, zelfs als dit een eenvoudige bevestiging van hun invoer is.