Implementowanie sekwencyjnego przepływu konwersacji

DOTYCZY: ZESTAW 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ływy konwersacji liniowych i bardziej złożonych można zarządzać przy użyciu biblioteki okien dialogowych. 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, tworząc monity i wywołując je z poziomu okna dialogowego 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 zestaw SDK języka Java jest wycofywany z ostatecznym długoterminowym wsparciem kończącym się w listopadzie 2023 r.

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

Informacje o tym przykładzie

Przykład z wieloma monitami używa okna dialogowego kaskadowego, kilku monitów i okna dialogowego składnika, aby utworzyć interakcję liniową, 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 o wybór
Poproś użytkownika o podanie swojej nazwy Monit tekstowy
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 Monit o załącznik z walidacją, aby umożliwić brakujący załącznik
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 UserProfileDialog bota DialogBot element jest ustawiany jako główne okno dialogowe. 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, monity i okno dialogowe kaskadowe, a następnie dodaj je do zestawu okien dialogowych. Monity 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ć monitu, wywołaj go z kroku w oknie dialogowym i pobierz wynik monitu w poniższym kroku przy użyciu polecenia stepContext.Result. W tle monity są dwuetapowym dialogiem. Najpierw zostanie wyświetlony monit o podanie danych wejściowych. Następnie zwraca prawidłową wartość lub zaczyna się od początku z repromptem, dopóki nie otrzyma prawidłowych danych wejściowych.

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

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 AgeStepAsyncpliku określ monit o ponowienie próby w przypadku niepowodzenia weryfikacji danych wejściowych użytkownika, ponieważ jest w formacie, którego monit nie może przeanalizować, lub dane wejściowe kończą się niepowodzeniem z kryteriami weryfikacji. W takim przypadku, jeśli nie podano monitu o ponowienie próby, monit użyje początkowego tekstu monitu, aby przeprowizować użytkownika w celu wprowadzenia 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, nazwy i wieku użytkownika jest zapisywany w wystąpieniu UserProfile klasy.

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ź zwrócone stepContext.Result przez okno dialogowe o nazwie w poprzednim kroku kaskadowym. 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 metodę GetAsync , a następnie ustaw wartości userProfile.Transportwłaściwości , userProfile.AgeuserProfile.Namei userProfile.Picture . Na koniec podsumuj informacje dla użytkownika przed wywołaniem EndDialogAsyncmetody , co kończy okno dialogowe. Zakończenie okna dialogowego powoduje wyłączenie stosu okna dialogowego i zwraca opcjonalny wynik do elementu nadrzędnego okna dialogowego. Element nadrzędny to okno dialogowe lub metoda, w ramach którego uruchomiono 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

Procedura OnMessageActivityAsync obsługi używa RunAsync metody , aby uruchomić lub kontynuować okno dialogowe. OnTurnAsync używa obiektów zarządzania stanem bota do utrwalania wszelkich zmian stanu w magazynie. 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 kolei.

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 poświadczeń, karta i implementacja bota.
  • Usługi zarządzania stanem: magazyn, stan użytkownika i stan konwersacji.
  • W oknie dialogowym bot będzie używany.

Startup.cs

Rejestrowanie usług bota w programie 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

Magazyn pamięci jest używany tylko do celów testowych i nie jest przeznaczony do użytku produkcyjnego. Pamiętaj, aby użyć trwałego typu magazynu dla bota produkcyjnego.

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.

Przykładowa transkrypcja konwersacji z botem monitu o wiele kolei.

Dodatkowe informacje

Informacje o oknie dialogowym i stanie bota

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

  • Jeden utworzony w stanie konwersacji dla właściwości stanu okna dialogowego. Stan okna dialogowego śledzi, gdzie użytkownik znajduje się w oknach dialogowych zestawu okien dialogowych i jest aktualizowany przez kontekst okna dialogowego, na przykład po wywołaniu okna dialogowego rozpoczęcia lub kontynuowania metod dialogowych .
  • 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 metody dostępu właściwości stanu get i ustawiają wartość 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 z kolei, ale musi zostać jawnie utrwalone. 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 przechowywania kroków okna dialogowego i oddzielnego stanu bota. Na przykład po zebraniu pełnych informacji w oknie dialogowym można wykonać następujące czynności:

  • Użyj metody końcowego okna dialogowego, aby podać zebrane dane jako wartość zwracaną z powrotem do kontekstu nadrzędnego. Może to być program obsługi kolei bota lub wcześniejsze aktywne okno dialogowe w stosie okna dialogowego i jest to sposób projektowania klas 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 modułu sprawdzania AgePromptValidatorAsync poprawności dla definicji metody. promptContext.Recognized.Value zawiera przeanalizowaną wartość, która jest liczbą całkowitą w tym miejscu dla monitu 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 przeprośnić użytkownika; W przeciwnym razie zwróć wartość true, aby zaakceptować dane wejściowe i powrócić z 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