Implementace sekvenčního toku konverzace

PLATÍ PRO: SDK v4

Shromažďování informací zadáním otázek je jedním z hlavních způsobů, jak robot komunikuje s uživateli. Knihovna dialogových oken poskytuje užitečné integrované funkce, jako jsou třídy výzvy , které usnadňují kladení otázek a ověření odpovědi, aby se ujistila, že odpovídá určitému datovému typu nebo splňuje vlastní ověřovací pravidla.

Pomocí knihovny dialogových oken můžete spravovat lineární a složitější toky konverzací. V lineární interakci robot prochází pevným pořadím kroků a konverzace se dokončí. Dialogové okno je užitečné, když robot potřebuje shromáždit informace od uživatele.

Tento článek ukazuje, jak implementovat lineární konverzační tok vytvořením výzev a jejich voláním z vodopádového dialogového okna. Příklady, jak napsat vlastní výzvy bez použití knihovny dialogových oken, najdete v článku Vytvoření vlastních výzev ke shromáždění uživatelského vstupu .

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.

Pro nové vytváření robotů zvažte použití Power Virtual Agents a přečtěte si o výběru správného řešení chatovacího robota.

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

Požadavky

O této ukázce

Ukázka více výzev používá vodopádový dialog, několik výzev a dialogové okno komponent k vytvoření lineární interakce, která uživatele zeptá na řadu otázek. Kód pomocí dialogového okna prochází těmito kroky:

Kroky Typ výzvy
Požádejte uživatele o způsob dopravy. Výzva k volbě
Požádejte uživatele o své jméno. Výzva k zadání textu
Zeptejte se uživatele, jestli chce poskytnout svůj věk. Potvrdit výzvu
Pokud odpověděli ano, požádejte o svůj věk. Výzva k zadání čísla s ověřením, která přijímá pouze věky větší než 0 a menší než 150
Pokud nepoužívají Microsoft Teams, požádejte ho o profilový obrázek. Výzva k příloze s ověřením, aby se povolila chybějící příloha
Zeptejte se, jestli jsou shromážděné informace "ok". Znovu použít výzvu k potvrzení

A konečně, pokud odpověděli ano, zobrazte shromážděné informace; jinak dejte uživateli vědět, že se jejich informace neuchovávají.

Vytvoření hlavního dialogového okna

Pokud chcete použít dialogy, nainstalujte balíček NuGet Microsoft.Bot.Builder.Dialogs .

Robot komunikuje s uživatelem prostřednictvím UserProfileDialog. Při vytváření třídy robota DialogBotUserProfileDialog se nastaví jako hlavní dialogové okno. Robot pak použije pomocnou metodu Run pro přístup k dialogu.

Diagram tříd pro ukázku jazyka C#.

Dialogy\UserProfileDialog.cs

Začněte tím, že vytvoříte UserProfileDialog , který je odvozený z ComponentDialog třídy, a má sedm kroků.

V konstruktoru UserProfileDialog vytvořte vodopádové kroky, zobrazí výzvu a dialogové okno vodopádu a přidáte je do sady dialogových oken. Výzvy musí být ve stejném dialogovém okně, ve kterém se používají.

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

Dále přidejte kroky, které dialogové okno používá k zobrazení výzvy k zadání vstupu. Chcete-li použít výzvu, zavolejte ji z kroku v dialogovém okně a načtěte výsledek výzvy v následujícím kroku pomocí stepContext.Result. Výzvy jsou na pozadí dvoustupňovým dialogem. Nejprve se zobrazí výzva k zadání vstupu. Potom vrátí platnou hodnotu nebo začne od začátku převrácenou hodnotou, dokud neobdrží platný vstup.

Vždy byste měli vrátit nenulovou DialogTurnResult hodnotu z vodopádového kroku. Pokud to neuděláte, dialogové okno nemusí fungovat tak, jak je navrženo. Níže je znázorněna implementace NameStepAsync v vodopádovém dialogovém okně.

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

Do AgeStepAsyncpole Zadejte výzvu k opakování, pokud se vstup uživatele nepodaří ověřit, a to buď proto, že je ve formátu, který výzva nemůže analyzovat, nebo vstup selže s ověřovacími kritérii. Pokud se v takovém případě nezobrazí žádná výzva k opakování, výzva použije počáteční text výzvy k přehrání uživatele pro zadání.

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

Režim dopravy, jména a věku uživatele se uloží v instanci UserProfile třídy.

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

    public string Name { get; set; }

    public int Age { get; set; }

    public Attachment Picture { get; set; }
}

Dialogy\UserProfileDialog.cs

V posledním kroku zkontrolujte stepContext.Result vrácený dialog volaný v předchozím vodopádovém kroku. Pokud je vrácená hodnota pravdivá, získá přístup k profilu uživatele a aktualizuje profil uživatele. Chcete-li získat profil uživatele, zavolejte GetAsync a pak nastavte hodnoty userProfile.Transport, userProfile.NameuserProfile.Age a userProfile.Picture vlastnosti. Nakonec shrňte informace pro uživatele před voláním EndDialogAsync, který ukončí dialogové okno. Ukončení dialogového okna se zobrazí mimo zásobník dialogového okna a vrátí volitelný výsledek nadřazeného dialogu. Nadřazený je dialog nebo metoda, která spustila dialogové okno, které právě skončilo.

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

Spuštění dialogového okna

Roboti\DialogBot.cs

Obslužná rutina OnMessageActivityAsync používá metodu RunAsync ke spuštění nebo pokračování dialogového okna. OnTurnAsync používá objekty správy stavu robota k zachování jakýchkoli změn stavu v úložišti. Metoda ActivityHandler.OnTurnAsync volá různé metody obslužné rutiny aktivity, například OnMessageActivityAsync. Tímto způsobem se stav uloží po dokončení obslužné rutiny zprávy, ale před dokončením samotného odevzdání.

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

Registrace služeb pro robota

Tento robot používá následující služby:

  • Základní služby robota: poskytovatel přihlašovacích údajů, adaptér a implementace robota.
  • Služby pro správu stavu: úložiště, stav uživatele a stav konverzace.
  • Dialogové okno, které robot použije.

Startup.cs

Zaregistrujte služby pro robota v Startup. Tyto služby jsou dostupné pro jiné části kódu prostřednictvím injektáže závislostí.

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

Poznámka:

Úložiště paměti se používá jenom pro účely testování a není určené pro produkční použití. Ujistěte se, že pro produkčního robota používáte trvalý typ úložiště.

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

Příklad přepisu konverzace s robotem s více výzvami

Další informace

Informace o stavu dialogového okna a robota

V tomto robotovi jsou definovány dva přístupové objekty vlastností stavu:

  • Jedna vytvořená ve stavu konverzace pro vlastnost stavu dialogového okna Stav dialogového okna sleduje, kde se uživatel nachází v dialogových oknech sady dialogových oken, a aktualizuje se kontextem dialogového okna, například při vyvolání dialogového okna nebo metody dialogového okna pokračovat.
  • Jeden vytvořený ve stavu uživatele pro vlastnost profilu uživatele. Robot ho používá ke sledování informací o uživateli a v kódu dialogového okna musíte explicitně spravovat tento stav.

Get a set metody přístupového objektu state vlastnost získat a nastavit hodnotu vlastnosti v mezipaměti objektu správy stavu. Mezipaměť se naplní při prvním vyžádání hodnoty vlastnosti stavu, ale musí být explicitně zachována. Aby bylo možné zachovat změny obou těchto vlastností stavu, provede se volání metody save changes odpovídajícího objektu správy stavu.

Tato ukázka aktualizuje stav profilu uživatele z dialogového okna. Tento postup může u některých robotů fungovat, ale nebude fungovat, pokud chcete znovu použít dialog mezi roboty.

Existují různé možnosti, jak udržovat kroky dialogového okna a stav robota oddělené. Když například dialogové okno shromáždí úplné informace, můžete:

  • Pomocí metody koncového dialogového okna zadejte shromážděná data jako vrácenou hodnotu zpět do nadřazeného kontextu. Může to být obslužná rutina robota nebo dřívější aktivní dialogové okno v zásobníku dialogů a způsob návrhu tříd výzvy.
  • Vygenerujte požadavek na příslušnou službu. To může fungovat dobře, pokud váš robot funguje jako front-end pro větší službu.

Definice metody validátoru výzvy

UserProfileDialog.cs

Níže je příklad kódu validátoru pro definici AgePromptValidatorAsync metody. promptContext.Recognized.Value obsahuje analyzovanou hodnotu, což je celé číslo pro výzvu k číslu. promptContext.Recognized.Succeeded určuje, jestli se výzva mohla analyzovat vstup uživatele, nebo ne. Validátor by měl vrátit hodnotu false, aby indikoval, že hodnota nebyla přijata, a dialogové okno výzvy by mělo převést uživatele; v opačném případě vraťte hodnotu true, abyste přijali vstup a vrátili se z dialogového okna výzvy. Hodnotu v validátoru můžete změnit podle vašeho scénáře.

    }

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

Další kroky