Share 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 Uw eigen invoer voor gebruikersinvoer maken voor voorbeelden van het schrijven van uw eigen prompts zonder de dialoogvensterbibliotheek 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 Power Virtual Agents gebruiken en lezen over het kiezen van de juiste chatbotoplossing.

Zie De toekomst van botbouw voor meer informatie.

Vereisten

Over dit voorbeeld

In het voorbeeld met meerdere prompts wordt een watervaldialoogvenster, een paar prompts en een onderdeeldialoogvenster 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 Type prompt
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 Prompt bevestigen opnieuw gebruiken

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 u de klasse van DialogBot de bot maakt, wordt de UserProfileDialog klasse ingesteld als het hoofddialoogvenster. De bot gebruikt vervolgens een Run helpermethode voor toegang tot het dialoogvenster.

Klassediagram voor het C#-voorbeeld.

Dialoogvensters\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 AgeStepAsyncin, geef een prompt voor opnieuw proberen op wanneer de invoer van de gebruiker niet kan worden gevalideerd, omdat deze een indeling heeft die de prompt niet kan parseren of de invoer mislukt een validatiecriteria. Als er in dit geval geen prompt voor opnieuw proberen is opgegeven, wordt de eerste prompttekst gebruikt om de gebruiker opnieuw in te stellen voor invoer.

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

Dialoogvensters\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 GetAsync u de waarden van de userProfile.Transport, userProfile.NameuserProfile.Age en userProfile.Picture eigenschappen aan en stelt u deze in. Ten slotte kunt u de informatie voor de gebruiker samenvatten voordat u aanroept EndDialogAsync, waardoor het dialoogvenster wordt beëindigd. Als u het dialoogvenster beëindigt, wordt het uit de dialoogvensterstack weergegeven en wordt een optioneel resultaat geretourneerd naar het bovenliggende dialoogvenster. Het bovenliggende item is het dialoogvenster of de methode waarmee het dialoogvenster is gestart dat net is beëindigd.

    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 voltooid, maar voordat de beurt zelf is voltooid.

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 services 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 permanent 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 statuseigenschapstoegangsors gedefinieerd:

  • Eén gemaakt binnen de gespreksstatus voor de eigenschap dialoogvensterstatus. 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 wanneer de waarde van een statuseigenschap op zijn beurt wordt aangevraagd, maar deze moet expliciet worden bewaard. 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 voor het scheiden van dialoogvensterstappen en botstatus. 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 draaihandler van de bot of een eerder actief dialoogvenster in de dialoogvensterstack zijn en 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 onwaar retourneren om aan te geven dat de waarde niet is geaccepteerd en dat het promptdialoogvenster de gebruiker opnieuw moet instellen; anders retourneert u waar om de invoer te accepteren en terug te keren vanuit het promptdialoogvenster. 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