Dela via


Implementera sekventiellt konversationsflöde

GÄLLER FÖR: SDK v4

Att samla in information genom att ställa frågor är ett av de viktigaste sätten som en robot interagerar med användare på. Dialogrutebiblioteket innehåller användbara inbyggda funktioner, till exempel promptklasser som gör det enkelt att ställa frågor och validera svaret för att se till att det matchar en viss datatyp eller uppfyller anpassade valideringsregler.

Du kan hantera linjära och mer komplexa konversationsflöden med hjälp av dialogrutebiblioteket. I en linjär interaktion går roboten igenom en fast sekvens med steg och konversationen avslutas. En dialogruta är användbar när roboten behöver samla in information från användaren.

Den här artikeln visar hur du implementerar linjärt konversationsflöde genom att skapa prompter och anropa dem från en vattenfallsdialogruta. Exempel på hur du skriver egna frågor utan att använda dialogrutebiblioteket finns i artikeln Skapa egna uppmaningar för att samla in användarindata .

Kommentar

Bot Framework JavaScript-, C#- och Python-SDK:erna fortsätter att stödjas, men Java SDK dras tillbaka med slutligt långsiktigt stöd som slutar i november 2023.

Befintliga robotar som skapats med Java SDK fortsätter att fungera.

Om du vill skapa en ny robot kan du använda Microsoft Copilot Studio och läsa om hur du väljer rätt copilot-lösning.

Mer information finns i Framtiden för robotbygge.

Förutsättningar

Om det här exemplet

Exemplet med flerstegsuppmaningar använder en vattenfallsdialog, några uppmaningar och en komponentdialog för att skapa en linjär interaktion som ställer en serie frågor till användaren. Koden använder en dialogruta för att gå igenom följande steg:

Steg Uppmaningstyp
Be användaren om deras transportsätt Kommandotolk för val
Be användaren om deras namn Textuppmaning
Fråga användaren om de vill ange sin ålder Bekräfta meddelande
Om de svarade ja, be om deras ålder Talprompt, med validering för att endast acceptera åldrar som är större än 0 och mindre än 150
Om de inte använder Microsoft Teams ber du dem om en profilbild Bifogningsuppmaning med validering för att tillåta en saknad bifogad fil
Fråga om den insamlade informationen är "ok" Bekräfta återanvändning

Slutligen, om de svarade ja, visa den insamlade informationen; Annars kan du tala om för användaren att deras information inte sparas.

Skapa huvuddialogrutan

Om du vill använda dialogrutor installerar du NuGet-paketet Microsoft.Bot.Builder.Dialogs .

Roboten interagerar med användaren via UserProfileDialog. När du skapar robotens DialogBot klass anges den UserProfileDialog som huvuddialogruta. Roboten använder sedan en Run hjälpmetod för att komma åt dialogrutan.

Klassdiagram för C#-exemplet.

Dialoger\UserProfileDialog.cs

Börja med att skapa UserProfileDialog som härleds från ComponentDialog klassen och har sju steg.

UserProfileDialog I konstruktorn skapar du vattenfallsstegen, anvisningarna och vattenfallsdialogrutan och lägger till dem i dialogrutan. Prompterna måste finnas i samma dialogruta som de används i.

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

Lägg sedan till de steg som används i dialogrutan för att fråga efter indata. Om du vill använda en prompt anropar du den från ett steg i dialogrutan och hämtar promptresultatet i följande steg med hjälp av stepContext.Result. Bakom kulisserna är uppmaningar en tvåstegsdialog. Först frågar uppmaningen efter indata. Sedan returneras det giltiga värdet, eller börjar om från början med en reprompt tills det tar emot en giltig indata.

Du bör alltid returnera en icke-null DialogTurnResult från ett vattenfallssteg. Om du inte gör det kanske dialogrutan inte fungerar som den är utformad. Nedan visas implementeringen för NameStepAsync i vattenfallsdialogrutan.

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

I AgeStepAsyncanger du en återförsöksprompt för när användarens indata inte kan verifieras, antingen för att det är i ett format som prompten inte kan parsa, eller så misslyckas indata med ett valideringsvillkor. I det här fallet, om ingen återförsöksprompt angavs, använder prompten den första prompttexten för att skicka om användaren för indata.

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

Användarens transportsätt, namn och ålder sparas i en instans av UserProfile klassen.

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

I det sista steget kontrollerar du den stepContext.Result som returnerades av dialogrutan som anropades i föregående vattenfallssteg. Om returvärdet är sant, hämtar och uppdaterar användarprofilens accessor användarprofilen. Om du vill hämta användarprofilen anropar du GetAsync och anger sedan värdena för egenskaperna userProfile.Transport, userProfile.Name, userProfile.Age och userProfile.Picture. Sammanfatta slutligen informationen för användaren innan du anropar EndDialogAsync, vilket avslutar dialogrutan. När dialogrutan avslutas tas den bort från dialogstacken och returnerar ett valfritt resultat till föräldern. Den överordnade är den dialog eller metod som startade den dialog som precis avslutades.

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

Kör dialogrutan

Bots\DialogBot.cs

OnMessageActivityAsync Hanteraren använder RunAsync metoden för att starta eller fortsätta dialogrutan. OnTurnAsync använder robotens tillståndshanteringsobjekt för att spara eventuella tillståndsändringar i lagringen. Metoden ActivityHandler.OnTurnAsync anropar de olika aktivitetshanterarmetoderna, till exempel OnMessageActivityAsync. På så sätt sparas tillståndet när meddelandehanteraren har slutförts, men innan själva omgången slutförs.

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

Registrera tjänster för roboten

Den här roboten använder följande tjänster:

  • Grundläggande tjänster för en robot: en leverantör av autentiseringsuppgifter, ett kort och robotimplementeringen.
  • Tjänster för att hantera tillstånd: lagring, användartillstånd och konversationstillstånd.
  • Dialogrutan som roboten ska använda.

Startup.cs

Registrera tjänster för roboten i Startup. Dessa tjänster är tillgängliga för andra delar av koden via beroendeinmatning.

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

Kommentar

Minneslagring används endast i testsyfte och är inte avsett för produktionsanvändning. Se till att använda en beständig typ av lagring för en produktionsrobot.

Testa din robot

  1. Om du inte redan har gjort det installerar du Bot Framework-emulatorn.
  2. Kör exemplet lokalt på datorn.
  3. Starta emulatorn, anslut till roboten och skicka meddelanden enligt nedan.

Ett exempel på en avskrift av en konversation med den flerstegs frågeboten.

Ytterligare information

Om dialog- och robottillstånd

I den här boten definieras två tillståndsdataåtkomster:

  • Ett objekt skapat inom konversationstillståndet för dialogtillståndets egenskap. Dialogtillståndet spårar var användaren befinner sig i dialogrutorna i en dialogruta och uppdateras av dialogkontexten, till exempel när dialogrutorna begin eller continue anropas.
  • En som skapats i användartillståndet för användarprofilegenskapen. Roboten använder detta för att spåra information om användaren, och du måste uttryckligen hantera det här tillståndet i dialogkoden.

get och set metoderna för en tillståndsegenskapsaccessor hämtar och ställer in värdet för egenskapen i tillståndshanteringsobjektets cache. Cachen upprättas första gången värdet för en stategenskap begärs under en omgång, men den måste sparas explicit. För att spara ändringar i båda dessa tillståndsegenskaper utförs ett anrop till metoden spara ändringar , för motsvarande tillståndshanteringsobjekt.

Det här exemplet uppdaterar användarprofilens tillstånd inifrån dialogrutan. Den här metoden kan fungera för vissa robotar, men det fungerar inte om du vill återanvända en dialogruta mellan robotar.

Det finns olika alternativ för att hålla dialogsteg och robottillstånd åtskilda. När dialogrutan till exempel har samlat in fullständig information kan du:

  • Använd slutdialogrutan för att ange insamlade data som returvärde tillbaka till den överordnade kontexten. Detta kan vara robotens turhanterare eller en tidigare aktiv dialogruta i dialogstacken och det är så promptklasserna utformas.
  • Generera en begäran till en lämplig tjänst. Detta kan fungera bra om din bot fungerar som ett gränssnitt till en större tjänst.

Definition av en prompt-validatormetod

UserProfileDialog.cs

Nedan visas ett valideringskodexempel för metoddefinitionen AgePromptValidatorAsync . promptContext.Recognized.Value innehåller det parsade värdet, som är ett heltal här för talprompten. promptContext.Recognized.Succeeded anger om uppmaningen kunde parsa användarens indata eller inte. Validatorn ska returnera false för att indikera att värdet inte accepterades och att dialogrutan för fråga ska skicka användaren igen. Annars returnerar du true för att acceptera indata och returnera från dialogrutan för fråga. Du kan ändra värdet i validatorn enligt ditt scenario.

    }

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

Nästa steg