Obsługa przerw w działaniu użytkowników

DOTYCZY: ZESTAW SDK w wersji 4

Obsługa przerw w działaniu jest ważnym aspektem niezawodnego bota. Użytkownicy nie zawsze będą postępować zgodnie ze zdefiniowanym przepływem konwersacji krok po kroku. Mogą spróbować zadać pytanie w trakcie procesu lub po prostu chcieć go anulować zamiast go ukończyć. W tym artykule opisano kilka typowych sposobów obsługi przerw w działaniu bota.

Uwaga

Zestawy SDK języka JavaScript, C# i Python platformy Bot Framework będą nadal obsługiwane, jednak zestaw SDK języka Java jest wycofywany z ostatecznym długoterminowym wsparciem kończącym się w listopadzie 2023 r. Zostaną podjęte tylko krytyczne poprawki zabezpieczeń i usterek w tym repozytorium.

Istniejące boty utworzone za pomocą zestawu JAVA SDK będą nadal działać.

W przypadku tworzenia nowego bota rozważ użycie agentów usługi Power Virtual Agents i przeczytaj o wyborze odpowiedniego rozwiązania czatbota.

Aby uzyskać więcej informacji, zobacz Przyszłość tworzenia botów.

Wymagania wstępne

Podstawowy przykład bota używa usługi Language Understanding (LUIS) do identyfikowania intencji użytkowników; jednak identyfikowanie intencji użytkownika nie jest celem tego artykułu. Aby uzyskać informacje na temat identyfikowania intencji użytkowników, zobacz Opis języka naturalnego i Dodawanie interpretacji języka naturalnego do bota.

Uwaga

Usługa Language Understanding (LUIS) zostanie wycofana 1 października 2025 r. Od 1 kwietnia 2023 r. nie będzie można tworzyć nowych zasobów usługi LUIS. Nowsza wersja interpretacji języka jest teraz dostępna w ramach języka sztucznej inteligencji platformy Azure.

Język konwersacyjny (CLU), funkcja języka AI platformy Azure, to zaktualizowana wersja usługi LUIS. Aby uzyskać więcej informacji na temat obsługi języka w zestawie SDK platformy Bot Framework, zobacz Opis języka naturalnego.

Informacje o tym przykładzie

Przykład używany w tym artykule modeluje bota rezerwacji lotów, który używa okien dialogowych do uzyskiwania informacji o locie od użytkownika. W dowolnym momencie podczas rozmowy z botem użytkownik może wydać pomoc lub anulować polecenia, aby spowodować przerwę. Istnieją dwa typy przerw w działaniu:

  • Poziom obrotu: Pomiń przetwarzanie na poziomie kolei, ale pozostaw okno dialogowe na stosie z podanymi informacjami. W następnej kolejności kontynuuj od miejsca, w którym konwersacja została przerwana.
  • Poziom okna dialogowego: Całkowicie anuluj przetwarzanie, aby bot mógł ponownie rozpocząć pracę.

Definiowanie i implementowanie logiki przerwania

Najpierw zdefiniuj i zaimplementuj pomoc i anuluj przerwy.

Aby użyć okien dialogowych, zainstaluj pakiet NuGet Microsoft.Bot.Builder.Dialogs .

Dialogs\CancelAndHelpDialog.cs

Zaimplementuj klasę w CancelAndHelpDialog celu obsługi przerw w działaniu użytkowników. Okna dialogowe BookingDialog z możliwością anulowania i DateResolverDialog pochodzą z tej klasy.

public class CancelAndHelpDialog : ComponentDialog

CancelAndHelpDialog W klasie metoda wywołuje metodę InterruptAsync , aby sprawdzić, OnContinueDialogAsync czy użytkownik przerwał normalny przepływ. Jeśli przepływ zostanie przerwany, metody klasy bazowej są wywoływane; w przeciwnym razie zwracana jest zwracana wartość InterruptAsync zwracana.

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

Jeśli użytkownik wpisze "help", metoda wysyła komunikat, InterruptAsync a następnie wywołuje DialogTurnResult (DialogTurnStatus.Waiting) polecenie, aby wskazać, że okno dialogowe u góry oczekuje na odpowiedź od użytkownika. W ten sposób przepływ konwersacji zostanie przerwany tylko na kolei, a następny obrót będzie kontynuowany od miejsca, w którym konwersacja została przerwana.

Jeśli użytkownik wpisze "cancel", wywołuje CancelAllDialogsAsync wewnętrzny kontekst okna dialogowego, który czyści stos okna dialogowego i powoduje zakończenie z anulowanym stanem i bez wartości wyniku. Do (pokazano MainDialog później), pojawi się, że okno dialogowe rezerwacji zakończyło się i zwróciło wartość null, podobnie jak wtedy, gdy użytkownik zdecyduje się nie potwierdzić swojej rezerwacji.

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

Sprawdzanie przerw w każdej kolejce

Po zaimplementowaniu klasy obsługi przerwań sprawdź, co się stanie, gdy ten bot otrzyma nowy komunikat od użytkownika.

Dialogs\MainDialog.cs

Po nadejściu nowego działania komunikatu bot uruchamia element MainDialog. Monituje MainDialog użytkownika o pomoc. Następnie uruchamia metodę BookingDialog w metodzie MainDialog.ActStepAsync z wywołaniem metody , jak BeginDialogAsync pokazano poniżej.

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

Następnie w FinalStepAsync metodzie MainDialog klasy okno dialogowe rezerwacji zostało zakończone, a rezerwacja jest uważana za ukończoną lub anulowaną.

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

Kod w BookingDialog pliku nie jest tutaj wyświetlany, ponieważ nie jest bezpośrednio związany z obsługą przerw. Służy do monitowania użytkowników o szczegóły rezerwacji. Ten kod można znaleźć w pliku Dialogs\BookingDialogs.cs.

Obsługa nieoczekiwanych błędów

Program obsługi błędów karty obsługuje wszelkie wyjątki, które nie zostały przechwycone w botze.

AdapterWithErrorHandler.cs

W przykładzie program obsługi karty OnTurnError odbiera wszelkie wyjątki zgłaszane przez logikę kolei bota. Jeśli wystąpi wyjątek, program obsługi usunie stan konwersacji dla bieżącej konwersacji, aby zapobiec zablokowaniu bota w pętli błędów spowodowanej nieprawidłowym stanem.

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

Rejestrowanie usług

Startup.cs

Na koniec w Startup.cssystemie bot jest tworzony jako przejściowy, a na każdym kroku tworzone jest nowe wystąpienie bota.


// Register the BookingDialog.

Poniżej przedstawiono definicje klas, które są używane w wywołaniu do utworzenia bota powyżej.

public class DialogAndWelcomeBot<T> : DialogBot<T>
public class DialogBot<T> : ActivityHandler
    where T : Dialog
public class MainDialog : ComponentDialog

Testowanie bota

  1. Jeśli jeszcze tego nie zrobiono, zainstaluj program Bot Framework Emulator.
  2. Uruchom przykład lokalnie na maszynie.
  3. Uruchom emulator, połącz się z botem i wyślij komunikaty, jak pokazano poniżej.

Dodatkowe informacje

  • Przykład 24.bot-authentication-msgraph w języku C#, JavaScript, Python lub Java pokazuje, jak obsługiwać żądanie wylogowania. Używa wzorca podobnego do przedstawionego tutaj w celu obsługi przerw w działaniu.

  • Zamiast nic nie robić, należy wysłać domyślną odpowiedź i pozostawić użytkownika zastanawiającego się, co się dzieje. Domyślna odpowiedź powinna poinformować użytkownika, jakie polecenia rozumie bot, aby użytkownik mógł wrócić do śledzenia.

  • W dowolnym momencie z kolei właściwość kontekst turn odpowiada wskazuje, czy bot wysłał wiadomość do użytkownika. Przed zakończeniem kolei bot powinien wysłać do użytkownika komunikat, nawet jeśli jest to proste potwierdzenie danych wejściowych.