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 bör du överväga att använda Power Virtual Agents och läsa om hur du väljer rätt chattrobotlösning.

Mer information finns i Framtiden för robotbygge.

Förutsättningar

Om det här exemplet

Exemplet med flersvängsprompter använder en vattenfallsdialogruta, några frågor och en komponentdialogruta för att skapa en linjär interaktion som ställer en rad frågor till användaren. Koden använder en dialogruta för att gå igenom följande steg:

Steg Typ av fråga
Be användaren om deras transportsätt Kommandotolk för val
Be användaren om deras namn Textprompt
Fråga användaren om de vill ange sin ålder Bekräfta uppmaning
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 Fråga om bifogade filer med validering för att tillåta en bifogad fil som saknas
Fråga om den insamlade informationen är "ok" Fråga om att 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.

Dialogrutor\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. I bakgrunden är frågor en dialogruta i två steg. 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; }
}

Dialogrutor\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ämtas användarprofilens accessor och användarprofilen uppdateras. Om du vill hämta användarprofilen anropar GetAsync du och anger sedan värdena för userProfile.Transportegenskaperna , userProfile.NameuserProfile.Age och userProfile.Picture . Sammanfatta slutligen informationen för användaren innan du anropar EndDialogAsync, vilket avslutar dialogrutan. När dialogrutan avslutas visas den från dialogstacken och returnerar ett valfritt resultat till dialogrutans överordnade. Den överordnade är den dialogruta eller metod som startade dialogrutan 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

Robotar\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 svä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 roboten för fråga med flera svar.

Ytterligare information

Om dialog- och robottillstånd

I den här roboten definieras två tillståndsegenskapsåtkomster:

  • En som skapats i konversationstillståndet för egenskapen dialogtillstånd. 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.

Hämta och ange metoder för en tillståndsegenskapsåtkomst hämta och ange värdet för egenskapen i tillståndshanteringsobjektets cacheminne. Cachen fylls i första gången värdet för en tillståndsegenskap begärs i tur och ordning, 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 roboten fungerar som en klientdel 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