Zpracování přerušení uživatele

PLATÍ PRO: SDK v4

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.

Poznámka:

Sady SDK služby Bot Framework JavaScript, C# a Python budou nadále podporovány, ale sada Java SDK se vyřazuje s konečnou dlouhodobou podporou končící v listopadu 2023.

Stávající roboti sestavení pomocí sady Java SDK budou i nadále fungovat.

Při vytváření nových robotů zvažte použití aplikace Microsoft Copilot Studio a přečtěte si o výběru správného řešení copilotu.

Další informace najdete v tématu Budoucnost vytváření robotů.

Požadavky

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.

Poznámka:

Služba Language Understanding (LUIS) bude vyřazena 1. října 2025. Od 1. dubna 2023 nebudete moct vytvářet nové prostředky LUIS. Novější verze language understanding je teď dostupná jako součást jazyka Azure AI.

Konverzační jazyk understanding (CLU), funkce jazyka Azure AI, je aktualizovaná verze služby LUIS. Další informace o podpoře porozumění jazyku v sadě SDK služby Bot Framework najdete v tématu Principy přirozeného jazyka.

O této ukázce

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í:

  • Úroveň turn: Vynechání zpracování na úrovni otáčení, ale nechte dialogové okno v zásobníku s informacemi, které byly poskytnuty. V dalším kroku pokračujte tam, kde konverzace skončila.
  • Úroveň dialogového okna: Úplně zrušte zpracování, aby robot mohl začít znovu.

Definování a implementace logiky přerušení

Nejprve definujte a implementujte nápovědu a zrušte 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;
}

Kontrola přerušení jednotlivých turnů

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.

Zpracování neočekávaných chyb

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

Registrace služeb

Startup.cs

Startup.csNakonec 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

Otestování robota

  1. Pokud jste to ještě neudělali, nainstalujte bot Framework Emulator.
  2. Spusťte ukázku místně na svém počítači.
  3. Spusťte emulátor, připojte se k robotovi a odešlete zprávy, jak je znázorněno níže.

Další informace

  • Ukázka 24.bot-authentication-msgraph v jazyce C#, JavaScript, Python nebo Java ukazuje, jak zpracovat žádost o odhlášení. Používá vzor podobný vzoru, který je zde zobrazený pro zpracování přerušení.

  • Měli byste odeslat výchozí odpověď místo toho, abyste nic neudělali, a nechat uživatele zajímat, co se děje. Výchozí odpověď by měla uživateli sdělit, jaké příkazy robot rozumí, aby se uživatel mohl vrátit zpět ke sledování.

  • V každém okamžiku pak vlastnost odpovědi kontextu otáčení označuje, jestli robot odeslal uživateli zprávu o tomto turnu. Před koncem turnu by robot měl uživateli poslat nějakou zprávu, i když se jedná o jednoduché potvrzení vstupu.