Udostępnij za pośrednictwem


Implementowanie sekwencyjnego przepływu konwersacji

DOTYCZY: SDK w wersji 4

Zbieranie informacji przez zadawanie pytań jest jednym z głównych sposobów interakcji bota z użytkownikami. Biblioteka okien dialogowych udostępnia przydatne wbudowane funkcje, takie jak klasy monitów , które ułatwiają zadawanie pytań i weryfikowanie odpowiedzi w celu upewnienia się, że jest ona zgodna z określonym typem danych lub spełnia niestandardowe reguły walidacji.

Przepływami konwersacji liniowych i bardziej złożonych można zarządzać, używając biblioteki dialogów. W interakcji liniowej bot przechodzi przez stałą sekwencję kroków i kończy konwersację. Okno dialogowe jest przydatne, gdy bot musi zebrać informacje od użytkownika.

W tym artykule pokazano, jak zaimplementować liniowy przepływ konwersacji poprzez tworzenie monitów i wywoływanie ich z dialogu kaskadowego. Przykłady sposobu pisania własnych monitów bez korzystania z biblioteki okien dialogowych można znaleźć w artykule Tworzenie własnych monitów o zebranie danych wejściowych użytkownika.

Uwaga

Zestawy SDK języka JavaScript, C# i Python platformy Bot Framework będą nadal obsługiwane, jednak SDK dla języka Java jest wycofywane, a jego ostateczne długoterminowe wsparcie zakończy się w listopadzie 2023 r.

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

W przypadku tworzenia nowych botów rozważ użycie programu Microsoft Copilot Studio i przeczytaj o wyborze odpowiedniego rozwiązania kopilota.

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

Wymagania wstępne

Informacje o tym przykładzie

Przykład z wieloma etapami pytań używa dialogu typu waterfall, kilku monitów oraz dialogu komponentowego, aby stworzyć liniową interakcję, która zadaje użytkownikowi serię pytań. Kod używa okna dialogowego, aby wykonać następujące kroki:

Kroki Typ monitu
Poproś użytkownika o sposób transportu Monit wyboru
Poproś użytkownika o podanie swojej nazwy Polecenie tekstowe
Zapytaj użytkownika, czy chce podać swój wiek Potwierdź monit
Jeśli odpowiedzieli tak, poproś o swój wiek Monit o liczbę z walidacją umożliwiający zaakceptowanie wieku większego niż 0 i mniej niż 150
Jeśli nie korzystają z usługi Microsoft Teams, poproś go o zdjęcie profilowe Komunikat o dołączeniu załącznika z walidacją pozwalającą na brak załącznika.
Zapytaj, czy zebrane informacje są "ok" Ponowne użycie monitu potwierdzania

Na koniec, jeśli odpowiedzieli tak, wyświetl zebrane informacje; w przeciwnym razie poinformuj użytkownika, że ich informacje nie będą przechowywane.

Tworzenie głównego okna dialogowego

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

Bot wchodzi w interakcję z użytkownikiem za pośrednictwem polecenia UserProfileDialog. Podczas tworzenia klasy DialogBot bota, UserProfileDialog jest ustawiany jako jego główny dialog. Następnie bot używa metody pomocniczej Run , aby uzyskać dostęp do okna dialogowego.

Diagram klas dla przykładu języka C#.

Dialogs\UserProfileDialog.cs

Zacznij od utworzenia elementu UserProfileDialog , który pochodzi z ComponentDialog klasy i ma siedem kroków.

W konstruktorze UserProfileDialog utwórz kroki kaskadowe, wezwania i dialog kaskadowy, a następnie dodaj te elementy do zestawu dialogów. Komunikaty muszą znajdować się w tym samym zestawie dialogowym, w którym są używane.

public UserProfileDialog(UserState userState)
    : base(nameof(UserProfileDialog))
{
    _userProfileAccessor = userState.CreateProperty<UserProfile>("UserProfile");

    // This array defines how the Waterfall will execute.
    var waterfallSteps = new WaterfallStep[]
    {
        TransportStepAsync,
        NameStepAsync,
        NameConfirmStepAsync,
        AgeStepAsync,
        PictureStepAsync,
        SummaryStepAsync,
        ConfirmStepAsync,
    };

    // Add named dialogs to the DialogSet. These names are saved in the dialog state.
    AddDialog(new WaterfallDialog(nameof(WaterfallDialog), waterfallSteps));
    AddDialog(new TextPrompt(nameof(TextPrompt)));
    AddDialog(new NumberPrompt<int>(nameof(NumberPrompt<int>), AgePromptValidatorAsync));
    AddDialog(new ChoicePrompt(nameof(ChoicePrompt)));
    AddDialog(new ConfirmPrompt(nameof(ConfirmPrompt)));
    AddDialog(new AttachmentPrompt(nameof(AttachmentPrompt), PicturePromptValidatorAsync));

    // The initial child Dialog to run.
    InitialDialogId = nameof(WaterfallDialog);
}

Następnie dodaj kroki używane przez okno dialogowe w celu wyświetlenia monitu o podanie danych wejściowych. Aby użyć wezwania, wywołaj je z kroku w dialogu i pobierz wynik wezwania w następnym kroku przy użyciu stepContext.Result. Za kulisami, podpowiedzi są dwuetapowym dialogiem. Najpierw zostanie wyświetlony monit o podanie danych wejściowych. Następnie zwraca prawidłową wartość lub zaczyna ponownie od początku, ponawiając prośbę, dopóki nie otrzyma prawidłowych danych wejściowych.

Zawsze należy zwrócić wartość inną niż null DialogTurnResult z etapu kaskadowego. Jeśli tego nie zrobisz, okno dialogowe może nie działać zgodnie z założeniami. Poniżej przedstawiono implementację dla elementu NameStepAsync w kaskadowym oknie dialogowym.

private static async Task<DialogTurnResult> NameStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
    stepContext.Values["transport"] = ((FoundChoice)stepContext.Result).Value;

    return await stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions { Prompt = MessageFactory.Text("Please enter your name.") }, cancellationToken);
}

W AgeStepAsync określ monit dla ponowienia próby, gdy weryfikacja danych wejściowych użytkownika kończy się niepowodzeniem, albo z powodu formatu, którego monit nie może zrozumieć, albo gdy dane wejściowe nie spełniają kryteriów weryfikacji. W takim przypadku, jeśli nie podano monitu o ponowienie próby, użyje się początkowego tekstu monitu, aby ponownie poprosić użytkownika o wprowadzenie danych wejściowych.

private async Task<DialogTurnResult> AgeStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
    if ((bool)stepContext.Result)
    {
        // User said "yes" so we will be prompting for the age.
        // WaterfallStep always finishes with the end of the Waterfall or with another dialog; here it is a Prompt Dialog.
        var promptOptions = new PromptOptions
        {
            Prompt = MessageFactory.Text("Please enter your age."),
            RetryPrompt = MessageFactory.Text("The value entered must be greater than 0 and less than 150."),
        };

        return await stepContext.PromptAsync(nameof(NumberPrompt<int>), promptOptions, cancellationToken);
    }
    else
    {
        // User said "no" so we will skip the next step. Give -1 as the age.
        return await stepContext.NextAsync(-1, cancellationToken);
    }
}

UserProfile.cs

Tryb transportu oraz nazwa i wiek użytkownika są zapisywane w wystąpieniu klasy UserProfile.

public class UserProfile
{
    public string Transport { get; set; }

    public string Name { get; set; }

    public int Age { get; set; }

    public Attachment Picture { get; set; }
}

Dialogs\UserProfileDialog.cs

W ostatnim kroku sprawdź stepContext.Result zwrócone przez wywołane w poprzednim kroku dialogowym okno dialogowe. Jeśli zwracana wartość ma wartość true, dostęp do profilu użytkownika pobiera i aktualizuje profil użytkownika. Aby uzyskać profil użytkownika, wywołaj GetAsync, a następnie ustaw wartości właściwości userProfile.Transport, userProfile.Name, userProfile.Age i userProfile.Picture. Na koniec podsumuj informacje dla użytkownika przed wywołaniem EndDialogAsync, co kończy okno dialogowe. Zakończenie okna dialogowego powoduje usunięcie go ze stosu i zwraca opcjonalny wynik do nadrzędnego okna dialogowego. Element nadrzędny to okno dialogowe lub metoda, za pomocą którego rozpoczęto okno dialogowe, które właśnie zostało zakończone.

    else
    {
        msg += $" Your profile will not be kept.";
    }

    await stepContext.Context.SendActivityAsync(MessageFactory.Text(msg), cancellationToken);

    // WaterfallStep always finishes with the end of the Waterfall or with another dialog; here it is the end.
    return await stepContext.EndDialogAsync(cancellationToken: cancellationToken);
}

private async Task<DialogTurnResult> SummaryStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
    stepContext.Values["picture"] = ((IList<Attachment>)stepContext.Result)?.FirstOrDefault();

    // Get the current profile object from user state.
    var userProfile = await _userProfileAccessor.GetAsync(stepContext.Context, () => new UserProfile(), cancellationToken);

    userProfile.Transport = (string)stepContext.Values["transport"];
    userProfile.Name = (string)stepContext.Values["name"];
    userProfile.Age = (int)stepContext.Values["age"];
    userProfile.Picture = (Attachment)stepContext.Values["picture"];

    var msg = $"I have your mode of transport as {userProfile.Transport} and your name as {userProfile.Name}";

    if (userProfile.Age != -1)
    {
        msg += $" and your age as {userProfile.Age}";
    }

    msg += ".";

    await stepContext.Context.SendActivityAsync(MessageFactory.Text(msg), cancellationToken);

    if (userProfile.Picture != null)
    {
        try
        {
            await stepContext.Context.SendActivityAsync(MessageFactory.Attachment(userProfile.Picture, "This is your profile picture."), cancellationToken);
        }
        catch
        {
            await stepContext.Context.SendActivityAsync(MessageFactory.Text("A profile picture was saved but could not be displayed here."), cancellationToken);

Uruchamianie okna dialogowego

Bots\DialogBot.cs

Obsługiwany przez OnMessageActivityAsync handler używa RunAsync metody, aby uruchomić lub kontynuować dialog. OnTurnAsync używa obiektów zarządzania stanem bota do zachowania wszelkich zmian stanu w pamięci. Metoda ActivityHandler.OnTurnAsync wywołuje różne metody obsługi działań, takie jak OnMessageActivityAsync. W ten sposób stan jest zapisywany po zakończeniu obsługi komunikatów, ale przed ukończeniem samego cyklu.

public override async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default)
{
    await base.OnTurnAsync(turnContext, cancellationToken);

    // Save any state changes that might have occurred during the turn.
    await ConversationState.SaveChangesAsync(turnContext, false, cancellationToken);
    await UserState.SaveChangesAsync(turnContext, false, cancellationToken);
}

protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
    Logger.LogInformation("Running dialog with Message Activity.");

    // Run the Dialog with the new message Activity.
    await Dialog.RunAsync(turnContext, ConversationState.CreateProperty<DialogState>(nameof(DialogState)), cancellationToken);
}

Rejestrowanie usług bota

Ten bot korzysta z następujących usług:

  • Podstawowe usługi bota: dostawca danych uwierzytelniających, adapter i implementacja bota.
  • Usługi zarządzania stanem: magazyn, stan użytkownika i stan konwersacji.
  • Dialog, który będzie używał bot.

Startup.cs

Zarejestruj usługi dla bota w Startup. Te usługi są dostępne dla innych części kodu poprzez wstrzyknięcie zależności.

{
    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddHttpClient().AddControllers().AddNewtonsoftJson(options =>
        {
            options.SerializerSettings.MaxDepth = HttpHelper.BotMessageSerializerSettings.MaxDepth;
        });

        // Create the Bot Framework Authentication to be used with the Bot Adapter.
        services.AddSingleton<BotFrameworkAuthentication, ConfigurationBotFrameworkAuthentication>();

        // Create the Bot Adapter with error handling enabled.
        services.AddSingleton<IBotFrameworkHttpAdapter, AdapterWithErrorHandler>();

        // Create the storage we'll be using for User and Conversation state. (Memory is great for testing purposes.)
        services.AddSingleton<IStorage, MemoryStorage>();

        // Create the User state. (Used in this bot's Dialog implementation.)
        services.AddSingleton<UserState>();

        // Create the Conversation state. (Used by the Dialog system itself.)
        services.AddSingleton<ConversationState>();

Uwaga

Pamięć masowa jest używana wyłącznie do celów testowych i nie jest przeznaczona do użytku produkcyjnego. Pamiętaj, aby użyć persistentnego typu przechowywania dla bota przeznaczonego do produkcji.

Przetestuj swojego bota

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

Przykładowa transkrypcja konwersacji z botem konwersacyjnym obsługującym wiele etapów.

Dodatkowe informacje

Informacje o oknie dialogowym i stanie bota

W tym botze zdefiniowano dwa metody dostępu właściwości stanu:

  • Jeden element utworzony w stanie rozmowy dla właściwości stanu dialogu. Stan dialogu śledzi, gdzie użytkownik znajduje się w zestawie dialogów, i jest aktualizowany przez kontekst dialogu, na przykład podczas wywołania metod dialogowych rozpocznij dialog lub kontynuuj dialog.
  • Jeden utworzony w stanie użytkownika dla właściwości profilu użytkownika. Bot używa tego do śledzenia informacji o użytkowniku i musisz jawnie zarządzać tym stanem w kodzie okna dialogowego.

Metody pobierania i ustawiania wartości właściwości w pamięci podręcznej obiektu zarządzania stanem. Pamięć podręczna jest wypełniana przy pierwszym żądaniu wartości właściwości stanu w danym kroku, ale musi zostać jawnie utrwalona. Aby utrwalić zmiany obu tych właściwości stanu, wykonywane jest wywołanie metody save changes odpowiedniego obiektu zarządzania stanem.

Ten przykład aktualizuje stan profilu użytkownika z poziomu okna dialogowego. Ta praktyka może działać w przypadku niektórych botów, ale nie będzie działać, jeśli chcesz ponownie użyć okna dialogowego w botach.

Istnieją różne opcje oddzielania kroków dialogu od stanu bota. Na przykład po zebraniu pełnych informacji w oknie dialogowym można wykonać następujące czynności:

  • Użyj metody end dialog, aby przekazać zebrane dane jako wartość zwracaną do kontekstu nadrzędnego. Może to być obsługa tury bota lub wcześniejszy aktywny dialog w stosie dialogów i tak zostały zaprojektowane klasy monitów.
  • Wygeneruj żądanie do odpowiedniej usługi. Może to działać dobrze, jeśli bot działa jako fronton do większej usługi.

Definicja metody modułu sprawdzania poprawności monitu

UserProfileDialog.cs

Poniżej znajduje się przykład kodu walidatora dla definicji metody AgePromptValidatorAsync. promptContext.Recognized.Value zawiera przeanalizowaną wartość, która jest liczbą całkowitą tutaj dla wezwania o liczbę. promptContext.Recognized.Succeeded wskazuje, czy monit był w stanie przeanalizować dane wejściowe użytkownika, czy nie. Moduł sprawdzania poprawności powinien zwrócić wartość false, aby wskazać, że wartość nie została zaakceptowana, a okno dialogowe monitu powinno powtórzyć pytanie użytkownikowi; w przeciwnym razie zwróć wartość true, aby zaakceptować dane wejściowe i zakończyć działanie okna dialogowego monitu. Wartość modułu sprawdzania poprawności można zmienić w danym scenariuszu.

    }

    // WaterfallStep always finishes with the end of the Waterfall or with another dialog; here it is a Prompt Dialog.
    return await stepContext.PromptAsync(nameof(ConfirmPrompt), new PromptOptions { Prompt = MessageFactory.Text("Is this ok?") }, cancellationToken);
}

Następne kroki