Uw eigen prompts maken om gebruikersinvoer te verzamelen

VAN TOEPASSING OP: SDK v4

Een gesprek tussen een bot en een gebruiker bestaat vaak uit het vragen (vragen) van de gebruiker om informatie, het parseren van het antwoord van de gebruiker en het reageren op die informatie. Uw bot moet de context van een gesprek bijhouden, zodat deze het gedrag ervan kan beheren en antwoorden op eerdere vragen kan onthouden. De status van een bot is informatie die wordt bijgehouden om op de juiste wijze te reageren op binnenkomende berichten.

Tip

De dialoogvensterbibliotheek bevat ingebouwde prompts die meer functionaliteit bieden die gebruikers kunnen gebruiken. Voorbeelden van deze prompts vindt u in het artikel Sequentieel gesprek implementeren.

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 de voorbeeldcode

De voorbeeldbot stelt de gebruiker een reeks vragen, valideert enkele van hun antwoorden en slaat de invoer op. In het volgende diagram ziet u de relatie tussen de bot, het gebruikersprofiel en de gespreksstroomklassen.

Klassediagram voor het C#-voorbeeld.

  • Een UserProfile klasse voor de gebruikersgegevens die door de bot worden verzameld.
  • Een ConversationFlow klasse om de gespreksstatus te beheren tijdens het verzamelen van gebruikersgegevens.
  • Een interne ConversationFlow.Question opsomming voor het bijhouden van waar u zich in het gesprek bevindt.

De gebruikersstatus houdt de naam, leeftijd en gekozen datum van de gebruiker bij en de gespreksstatus houdt bij wat u de gebruiker het laatst hebt gevraagd. Omdat u niet van plan bent deze bot te implementeren, configureert u de gebruikers- en gespreksstatus om geheugenopslag te gebruiken.

U gebruikt de berichtwisselingshandler van de bot plus eigenschappen van de gebruikers- en gespreksstatus om de stroom van het gesprek en de verzameling invoer te beheren. In uw bot registreert u de statuseigenschapsgegevens die tijdens elke iteratie van de berichtdraaihandler worden ontvangen.

Gespreks- en gebruikersobjecten maken

Maak de gebruikers- en gespreksstatusobjecten bij het opstarten en verbruik deze via afhankelijkheidsinjectie in de botconstructor.

Startup.cs

// 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.
services.AddSingleton<UserState>();

// Create the Conversation state.
services.AddSingleton<ConversationState>();

Bots/CustomPromptBot.cs

private readonly BotState _userState;
private readonly BotState _conversationState;

public CustomPromptBot(ConversationState conversationState, UserState userState)
{
    _conversationState = conversationState;
    _userState = userState;
}

Eigenschapstoegangsors maken

Maak eigenschapstoegangsors voor de eigenschappen van het gebruikersprofiel en de gespreksstroom en roep GetAsync vervolgens aan om de eigenschapswaarde op te halen uit de status.

Bots/CustomPromptBot.cs

protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
    var conversationStateAccessors = _conversationState.CreateProperty<ConversationFlow>(nameof(ConversationFlow));
    var flow = await conversationStateAccessors.GetAsync(turnContext, () => new ConversationFlow(), cancellationToken);

    var userStateAccessors = _userState.CreateProperty<UserProfile>(nameof(UserProfile));
    var profile = await userStateAccessors.GetAsync(turnContext, () => new UserProfile(), cancellationToken);

Voordat de beurt eindigt, roept u SaveChangesAsync aan om statuswijzigingen naar de opslag te schrijven.

    await _conversationState.SaveChangesAsync(turnContext, false, cancellationToken);
    await _userState.SaveChangesAsync(turnContext, false, cancellationToken);
}

Berichtdraaihandler

Bij het verwerken van berichtactiviteiten gebruikt de berichtenhandler een helpermethode om het gesprek te beheren en de gebruiker te vragen. De helpermethode wordt beschreven in de volgende sectie.

Bots/CustomPromptBot.cs

protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
    var conversationStateAccessors = _conversationState.CreateProperty<ConversationFlow>(nameof(ConversationFlow));
    var flow = await conversationStateAccessors.GetAsync(turnContext, () => new ConversationFlow(), cancellationToken);

    var userStateAccessors = _userState.CreateProperty<UserProfile>(nameof(UserProfile));
    var profile = await userStateAccessors.GetAsync(turnContext, () => new UserProfile(), cancellationToken);

    await FillOutUserProfileAsync(flow, profile, turnContext, cancellationToken);

    // Save changes.
    await _conversationState.SaveChangesAsync(turnContext, false, cancellationToken);
    await _userState.SaveChangesAsync(turnContext, false, cancellationToken);
}

Het gebruikersprofiel invullen

De bot vraagt de gebruiker om informatie op basis van welke vraag, indien aanwezig, die de bot heeft gesteld op de vorige beurt. Invoer wordt geparseerd met behulp van een validatiemethode.

Elke validatiemethode volgt een vergelijkbaar ontwerp:

  • De retourwaarde geeft aan of de invoer een geldig antwoord is voor deze vraag.
  • Als de validatie is geslaagd, wordt er een geparseerde en genormaliseerde waarde geproduceerd om op te slaan.
  • Als de validatie mislukt, wordt er een bericht gegenereerd waarmee de bot opnieuw om de informatie kan vragen.

De validatiemethoden worden beschreven in de volgende sectie.

Bots/CustomPromptBot.cs

{
    var input = turnContext.Activity.Text?.Trim();
    string message;

    switch (flow.LastQuestionAsked)
    {
        case ConversationFlow.Question.None:
            await turnContext.SendActivityAsync("Let's get started. What is your name?", null, null, cancellationToken);
            flow.LastQuestionAsked = ConversationFlow.Question.Name;
            break;
        case ConversationFlow.Question.Name:
            if (ValidateName(input, out var name, out message))
            {
                profile.Name = name;
                await turnContext.SendActivityAsync($"Hi {profile.Name}.", null, null, cancellationToken);
                await turnContext.SendActivityAsync("How old are you?", null, null, cancellationToken);
                flow.LastQuestionAsked = ConversationFlow.Question.Age;
                break;
            }
            else
            {
                await turnContext.SendActivityAsync(message ?? "I'm sorry, I didn't understand that.", null, null, cancellationToken);
                break;
            }

        case ConversationFlow.Question.Age:
            if (ValidateAge(input, out var age, out message))
            {
                profile.Age = age;
                await turnContext.SendActivityAsync($"I have your age as {profile.Age}.", null, null, cancellationToken);
                await turnContext.SendActivityAsync("When is your flight?", null, null, cancellationToken);
                flow.LastQuestionAsked = ConversationFlow.Question.Date;
                break;
            }
            else
            {
                await turnContext.SendActivityAsync(message ?? "I'm sorry, I didn't understand that.", null, null, cancellationToken);
                break;
            }

        case ConversationFlow.Question.Date:
            if (ValidateDate(input, out var date, out message))
            {
                profile.Date = date;
                await turnContext.SendActivityAsync($"Your cab ride to the airport is scheduled for {profile.Date}.");
                await turnContext.SendActivityAsync($"Thanks for completing the booking {profile.Name}.");
                await turnContext.SendActivityAsync($"Type anything to run the bot again.");
                flow.LastQuestionAsked = ConversationFlow.Question.None;
                profile = new UserProfile();
                break;
            }
            else
            {
                await turnContext.SendActivityAsync(message ?? "I'm sorry, I didn't understand that.", null, null, cancellationToken);
                break;
            }
    }
}

Invoer parseren en valideren

De bot gebruikt de volgende criteria om invoer te valideren.

  • De naam moet een niet-lege tekenreeks zijn. Het wordt genormaliseerd door witruimte te knippen.
  • De leeftijd moet tussen 18 en 120 zijn. Het wordt genormaliseerd door een geheel getal te retourneren.
  • De datum moet een datum of tijd zijn die ten minste een uur in de toekomst is. Het wordt genormaliseerd door alleen het datumgedeelte van de geparseerde invoer te retourneren.

Notitie

Voor de invoer van leeftijd en datum gebruikt het voorbeeld de Microsoft/Recognizers-Text-bibliotheken om de eerste parsering uit te voeren. Dit is slechts één manier om de invoer te parseren. Zie het LEESMIJ-bestand van het project voor meer informatie over deze bibliotheken.

Bots/CustomPromptBot.cs

private static bool ValidateName(string input, out string name, out string message)
{
    name = null;
    message = null;

    if (string.IsNullOrWhiteSpace(input))
    {
        message = "Please enter a name that contains at least one character.";
    }
    else
    {
        name = input.Trim();
    }

    return message is null;
}

private static bool ValidateAge(string input, out int age, out string message)
{
    age = 0;
    message = null;

    // Try to recognize the input as a number. This works for responses such as "twelve" as well as "12".
    try
    {
        // Attempt to convert the Recognizer result to an integer. This works for "a dozen", "twelve", "12", and so on.
        // The recognizer returns a list of potential recognition results, if any.

        var results = NumberRecognizer.RecognizeNumber(input, Culture.English);

        foreach (var result in results)
        {
            // The result resolution is a dictionary, where the "value" entry contains the processed string.
            if (result.Resolution.TryGetValue("value", out var value))
            {
                age = Convert.ToInt32(value);
                if (age >= 18 && age <= 120)
                {
                    return true;
                }
            }
        }

        message = "Please enter an age between 18 and 120.";
    }
    catch
    {
        message = "I'm sorry, I could not interpret that as an age. Please enter an age between 18 and 120.";
    }

    return message is null;
}

private static bool ValidateDate(string input, out string date, out string message)
{
    date = null;
    message = null;

    // Try to recognize the input as a date-time. This works for responses such as "11/14/2018", "9pm", "tomorrow", "Sunday at 5pm", and so on.
    // The recognizer returns a list of potential recognition results, if any.
    try
    {
        var results = DateTimeRecognizer.RecognizeDateTime(input, Culture.English);

        // Check whether any of the recognized date-times are appropriate,
        // and if so, return the first appropriate date-time. We're checking for a value at least an hour in the future.
        var earliest = DateTime.Now.AddHours(1.0);

        foreach (var result in results)
        {
            // The result resolution is a dictionary, where the "values" entry contains the processed input.
            var resolutions = result.Resolution["values"] as List<Dictionary<string, string>>;

            foreach (var resolution in resolutions)
            {
                // The processed input contains a "value" entry if it is a date-time value, or "start" and
                // "end" entries if it is a date-time range.
                if (resolution.TryGetValue("value", out var dateString)
                    || resolution.TryGetValue("start", out dateString))
                {
                    if (DateTime.TryParse(dateString, out var candidate)
                        && earliest < candidate)
                    {
                        date = candidate.ToShortDateString();
                        return true;
                    }
                }
            }
        }

        message = "I'm sorry, please enter a date at least an hour out.";
    }
    catch
    {
        message = "I'm sorry, I could not interpret that as an appropriate date. Please enter a date at least an hour out.";
    }

    return false;
}

De bot lokaal testen

Download en installeer de Bot Framework Emulator om de bot lokaal te testen.

  1. Voer het voorbeeld lokaal uit op uw computer. Als u instructies nodig hebt, raadpleegt u het bestand voor het README C#-voorbeeld, het JS-voorbeeld of het Python-voorbeeld.
  2. Test deze met behulp van de emulator.

Aanvullende bronnen

De dialoogbibliotheek biedt klassen waarmee veel aspecten van het beheren van gesprekken worden geautomatiseerd.

Volgende stap