Skapa egna uppmaningar för att samla in användarindata

GÄLLER FÖR: SDK v4

En konversation mellan en robot och en användare handlar ofta om att be (fråga) användaren om information, parsa användarens svar och sedan agera på den informationen. Roboten bör spåra kontexten för en konversation så att den kan hantera sitt beteende och komma ihåg svar på tidigare frågor. En robots tillstånd är information som den spårar för att svara korrekt på inkommande meddelanden.

Dricks

Dialogrutebiblioteket innehåller inbyggda frågor som ger fler funktioner som användarna kan använda. Exempel på dessa uppmaningar finns i artikeln Implementera sekventiellt konversationsflöde .

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 exempelkoden

Exempelroboten ställer en rad frågor till användaren, validerar några av deras svar och sparar indata. Följande diagram visar relationen mellan roboten, användarprofilen och konversationsflödesklasserna.

Klassdiagram för C#-exemplet.

  • En UserProfile klass för den användarinformation som roboten samlar in.
  • En ConversationFlow klass som styr vårt konversationstillstånd när du samlar in användarinformation.
  • En inre ConversationFlow.Question uppräkning för att spåra var du befinner dig i konversationen.

Användartillståndet spårar användarens namn, ålder och valda datum, och konversationstillståndet spårar det du senast bad användaren om. Eftersom du inte planerar att distribuera den här roboten konfigurerar du användar- och konversationstillstånd för att använda minneslagring.

Du använder robotens meddelandevändarhanterare plus egenskaper för användar- och konversationstillstånd för att hantera konversationsflödet och insamlingen av indata. I roboten registrerar du informationen om tillståndsegenskapen som tas emot under varje iteration av meddelandevändarhanteraren.

Skapa konversations- och användarobjekt

Skapa användar- och konversationstillståndsobjekt vid start och använd dem via beroendeinmatning i robotkonstruktorn.

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

Robotar/CustomPromptBot.cs

private readonly BotState _userState;
private readonly BotState _conversationState;

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

Skapa egenskapsåtkomster

Skapa egenskapsåtkomster för egenskaper för användarprofil och konversationsflöde och anropa GetAsync sedan för att hämta egenskapsvärdet från tillstånd.

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

Innan svängen slutar anropar du SaveChangesAsync för att skriva tillståndsändringar i lagringen.

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

Meddelandevändarhanterare

När du hanterar meddelandeaktiviteter använder meddelandehanteraren en hjälpmetod för att hantera konversationen och fråga användaren. Hjälpmetoden beskrivs i följande avsnitt.

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

Fylla i användarprofilen

Roboten uppmanar användaren att ange information baserat på vilken fråga, om någon, som roboten ställde i föregående tur. Indata parsas med hjälp av en valideringsmetod.

Varje valideringsmetod följer en liknande design:

  • Returvärdet anger om indata är ett giltigt svar för den här frågan.
  • Om valideringen godkänns genererar den ett parsat och normaliserat värde att spara.
  • Om verifieringen misslyckas skapar den ett meddelande som roboten kan be om informationen med igen.

Valideringsmetoderna beskrivs i följande avsnitt.

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

Parsa och verifiera indata

Roboten använder följande villkor för att verifiera indata.

  • Namnet måste vara en icke-tom sträng. Det normaliseras genom att trimma blanksteg.
  • Åldern måste vara mellan 18 och 120 år. Det normaliseras genom att returnera ett heltal.
  • Datumet måste vara ett datum eller en tidpunkt minst en timme i framtiden. Det normaliseras genom att bara returnera datumdelen av de parsade indata.

Kommentar

För ålders- och datumindata använder exemplet biblioteken Microsoft/Recognizers-Text för att utföra den inledande parsningen. Det här är bara ett sätt att parsa indata. Mer information om dessa bibliotek finns i projektets README.

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

Testa roboten lokalt

Ladda ned och installera Bot Framework-emulatorn för att testa roboten lokalt.

  1. Kör exemplet lokalt på datorn. Om du behöver instruktioner kan du läsa filen för C#-exempel, JS-exempel eller Python-exemplet.README
  2. Testa den med emulatorn.

Ytterligare resurser

Dialogbiblioteket innehåller klasser som automatiserar många aspekter av att hantera konversationer.

Gå vidare