Delen via


Sequentiële gespreksstroom implementeren

VAN TOEPASSING OP: SDK v4

Het verzamelen van informatie door vragen te stellen is een van de belangrijkste manieren waarop een bot met gebruikers communiceert. De dialoogvensterbibliotheek biedt handige ingebouwde functies zoals promptklassen waarmee u eenvoudig vragen kunt stellen en het antwoord kunt valideren om ervoor te zorgen dat het overeenkomt met een specifiek gegevenstype of voldoet aan aangepaste validatieregels.

U kunt lineaire en complexere gespreksstromen beheren met behulp van de dialoogvensterbibliotheek. In een lineaire interactie doorloopt de bot een vaste reeks stappen en wordt het gesprek voltooid. Een dialoogvenster is handig wanneer de bot informatie van de gebruiker moet verzamelen.

In dit artikel wordt beschreven hoe u een lineaire gespreksstroom implementeert door prompts te maken en aan te roepen vanuit een watervaldialoogvenster. Zie het artikel Maak uw eigen prompts voor het verzamelen van gebruikersinvoer voor voorbeelden van hoe u uw eigen prompts kunt schrijven zonder de dialogenbibliotheek te gebruiken.

Notitie

De Sdk's voor Bot Framework JavaScript, C# en Python blijven ondersteund, maar de Java SDK wordt buiten gebruik gesteld met definitieve langetermijnondersteuning die eindigt op november 2023.

Bestaande bots die zijn gebouwd met de Java SDK blijven functioneren.

Voor het bouwen van nieuwe bots kunt u Microsoft Copilot Studio gebruiken en lezen over het kiezen van de juiste copilot-oplossing.

Zie De toekomst van botbouw voor meer informatie.

Vereisten

Over dit voorbeeld

In het voorbeeld met meerdere prompts wordt een watervaldialoog, een paar prompts en een componentdialoog gebruikt om een lineaire interactie te maken waarin de gebruiker een reeks vragen wordt gesteld. In de code wordt een dialoogvenster gebruikt om deze stappen te doorlopen:

Stappen Prompttype
Vraag de gebruiker om hun transportmodus Keuzeprompt
Vraag de gebruiker om zijn of haar naam Tekstprompt
Vraag de gebruiker of ze hun leeftijd willen opgeven Prompt bevestigen
Als ze ja hebben beantwoord, vraagt u om hun leeftijd Nummerprompt, met validatie om alleen leeftijden van meer dan 0 en minder dan 150 te accepteren
Als ze Microsoft Teams niet gebruiken, vraagt u hen om een profielfoto Bijlageprompt, met validatie om een ontbrekende bijlage toe te staan
Vraag of de verzamelde informatie 'ok' is Hergebruik bevestigingsprompt

Ten slotte, als ze ja hebben beantwoord, geeft u de verzamelde informatie weer; laat de gebruiker anders weten dat de gegevens niet worden bewaard.

Het hoofddialoogvenster maken

Als u dialoogvensters wilt gebruiken, installeert u het NuGet-pakket Microsoft.Bot.Builder.Dialogs .

De bot communiceert met de gebruiker via UserProfileDialog. Wanneer de DialogBot klasse van de bot wordt gemaakt, wordt de UserProfileDialog ingesteld als zijn hoofddialoog. De bot gebruikt vervolgens een Run helpermethode voor toegang tot het dialoogvenster.

Klassediagram voor het C#-voorbeeld.

Dialogen\UserProfileDialog.cs

Begin met het maken van de UserProfileDialog die is afgeleid van de ComponentDialog klasse en heeft zeven stappen.

Maak in de UserProfileDialog constructor de watervalstappen, prompts en het watervaldialoogvenster en voeg deze toe aan de dialoogvensterset. De prompts moeten zich in dezelfde dialoogvensterset bevinden waarin ze worden gebruikt.

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

Voeg vervolgens de stappen toe die in het dialoogvenster worden gebruikt om om invoer te vragen. Als u een prompt wilt gebruiken, roept u deze aan vanuit een stap in het dialoogvenster en haalt u het promptresultaat op in de volgende stap met behulp van stepContext.Result. Achter de schermen zijn prompts een dialoogvenster in twee stappen. Eerst vraagt de prompt om invoer. Vervolgens wordt de geldige waarde geretourneerd, of begint opnieuw vanaf het begin met een reprompt totdat er een geldige invoer wordt ontvangen.

U moet altijd een niet-null-waarde DialogTurnResult retourneren uit een watervalstap. Als u dat niet doet, werkt het dialoogvenster mogelijk niet zoals ontworpen. Hieronder ziet u de implementatie voor NameStepAsync in het watervaldialoogvenster.

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

Geef in AgeStepAsync een herhaalprompt op voor wanneer de invoer van de gebruiker niet kan worden gevalideerd, omdat deze een indeling heeft die de prompt niet kan parseren, of omdat de invoer faalt bij een validatiecriterium. Als er in dit geval geen herhaalde invoerprompt is opgegeven, wordt de eerste prompttekst gebruikt om de gebruiker opnieuw om invoer te vragen.

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

De manier van transport, naam en leeftijd van de gebruiker worden opgeslagen in een instantie van de UserProfile klasse.

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

Controleer in de laatste stap het stepContext.Result geretourneerde dialoogvenster dat is aangeroepen in de vorige watervalstap. Als de retourwaarde waar is, wordt het gebruikersprofiel opgehaald en bijgewerkt door de gebruikersprofieltoegangsfunctie. Als u het gebruikersprofiel wilt ophalen, roept u GetAsync aan en stelt u de waarden van de userProfile.Transport, userProfile.Name, userProfile.Age en userProfile.Picture eigenschappen in. Ten slotte kunt u de informatie voor de gebruiker samenvatten voordat u aanroept EndDialogAsync, waardoor het dialoogvenster wordt beëindigd. Wanneer u het dialoogvenster beëindigt, wordt het uit de dialoogvensterstack verwijderd en wordt een optioneel resultaat geretourneerd aan de ouder van het dialoogvenster. De ouder is het dialoogvenster of de methode die het zojuist beëindigde dialoogvenster heeft gestart.

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

Het dialoogvenster uitvoeren

Bots\DialogBot.cs

De OnMessageActivityAsync handler gebruikt de RunAsync methode om het dialoogvenster te starten of door te gaan. OnTurnAsync gebruikt de statusbeheerobjecten van de bot om statuswijzigingen in de opslag te behouden. Met ActivityHandler.OnTurnAsync de methode worden de verschillende methoden voor activiteitshandlers aangeroepen, zoals OnMessageActivityAsync. Op deze manier wordt de status opgeslagen nadat de berichthandler is afgerond, maar voordat de beurt zelf is afgerond.

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

Services registreren voor de bot

Deze bot maakt gebruik van de volgende services:

  • Basisservices voor een bot: een referentieprovider, een adapter en de bot-implementatie.
  • Services voor het beheren van de status: opslag, gebruikersstatus en gespreksstatus.
  • Het dialoogvenster dat de bot gaat gebruiken.

Startup.cs

Registreer diensten voor de bot in Startup. Deze services zijn beschikbaar voor andere onderdelen van de code via afhankelijkheidsinjectie.

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

Notitie

Geheugenopslag wordt alleen gebruikt voor testdoeleinden en is niet bedoeld voor productiegebruik. Zorg ervoor dat u een duurzaam type opslag gebruikt voor een productiebot.

Uw bot testen

  1. Als u dit nog niet hebt gedaan, installeert u de Bot Framework Emulator.
  2. Voer het voorbeeld lokaal uit op uw computer.
  3. Start de emulator, maak verbinding met uw bot en verzend berichten zoals hieronder wordt weergegeven.

Een voorbeeldtranscriptie van een gesprek met de bot met meerdere paden.

Aanvullende informatie

Dialoogvenster en botstatus

In deze bot worden twee toegangen tot statuseigenschappen gedefinieerd.

  • Eén aangemaakt binnen de gespreksstatus voor de eigenschap dialoogstatus. De status van het dialoogvenster houdt bij waar de gebruiker zich in de dialoogvensters van een dialoogvensterset bevindt en wordt bijgewerkt door de dialoogvenstercontext, bijvoorbeeld wanneer het dialoogvenster wordt gestart of dialoogvenstermethoden doorgaan worden aangeroepen.
  • Eén gemaakt binnen de gebruikersstatus voor de eigenschap van het gebruikersprofiel. De bot gebruikt deze om informatie over de gebruiker bij te houden en u moet deze status expliciet beheren in de dialoogvenstercode.

De get - en set-methoden van een toegangsobject voor statuseigenschappen ophalen en instellen de waarde van de eigenschap in de cache van het statusbeheerobject. De cache wordt gevuld de eerste keer dat de waarde van een toestandseigenschap in een beurt wordt opgevraagd, maar deze moet expliciet persistent worden opgeslagen. Als u wijzigingen in beide statuseigenschappen wilt behouden, wordt een aanroep naar de methode voor opslaan van wijzigingen , van het bijbehorende statusbeheerobject, uitgevoerd.

In dit voorbeeld wordt de status van het gebruikersprofiel bijgewerkt vanuit het dialoogvenster. Deze procedure kan voor sommige bots werken, maar dit werkt niet als u een dialoogvenster tussen bots opnieuw wilt gebruiken.

Er zijn verschillende opties om dialoogstappen en de status van de bot te scheiden. Zodra uw dialoogvenster bijvoorbeeld volledige informatie verzamelt, kunt u het volgende doen:

  • Gebruik de einddialoogvenstermethode om de verzamelde gegevens op te geven als retourwaarde terug naar de bovenliggende context. Dit kan de beurtverwerker van de bot of een eerder actieve dialoog in de dialogenstack zijn en dat is hoe de promptklassen zijn ontworpen.
  • Genereer een aanvraag voor een geschikte service. Dit werkt mogelijk goed als uw bot fungeert als een front-end voor een grotere service.

Definitie van een promptvalidatormethode

UserProfileDialog.cs

Hieronder ziet u een voorbeeld van een validatiecode voor de AgePromptValidatorAsync methodedefinitie. promptContext.Recognized.Value bevat de geparseerde waarde. Dit is een geheel getal voor de getalprompt. promptContext.Recognized.Succeeded geeft aan of de prompt de invoer van de gebruiker kan parseren of niet. De validator moet false retourneren om aan te geven dat de waarde niet is geaccepteerd en dat het promptdialoogvenster de gebruiker opnieuw om invoer moet vragen; anders retourneert u true om de invoer te accepteren en het promptdialoogvenster af te sluiten. U kunt de waarde in de validator per scenario wijzigen.

    }

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

Volgende stappen