Vytvoření vlastních výzev ke shromáždění uživatelských vstupů

PLATÍ PRO: SDK v4

Konverzace mezi robotem a uživatelem často zahrnuje dotaz (vyzvání) uživatele k zadání informací, parsování odpovědi uživatele a následnému jednání s těmito informacemi. Robot by měl sledovat kontext konverzace, aby mohl spravovat své chování a pamatovat si odpovědi na předchozí otázky. Stav robota je informace, které sleduje, aby správně reagoval na příchozí zprávy.

Tip

Knihovna dialogových oken poskytuje předdefinované výzvy, které poskytují více funkcí, které můžou uživatelé používat. Příkladytěchtoch

Poznámka:

Sady SDK služby Bot Framework JavaScript, C# a Python budou nadále podporovány, ale sada Java SDK se vyřazuje s konečnou dlouhodobou podporou končící v listopadu 2023.

Stávající roboti sestavení pomocí sady Java SDK budou i nadále fungovat.

Pro nové vytváření robotů zvažte použití Power Virtual Agents a přečtěte si o výběru správného řešení chatovacího robota.

Další informace najdete v tématu Budoucnost vytváření robotů.

Požadavky

Informace o vzorovém kódu

Ukázkový robot se zeptá uživatele na řadu otázek, ověří některé odpovědi a uloží svůj vstup. Následující diagram znázorňuje vztah mezi třídami toku chatbota, profilu uživatele a toku konverzace.

Diagram tříd pro ukázku jazyka C#.

  • UserProfile Třída pro informace o uživateli, které robot bude shromažďovat.
  • Třída ConversationFlow , která má řídit stav naší konverzace při shromažďování informací o uživateli.
  • Vnitřní ConversationFlow.Question výčet pro sledování místa, kde jste v konverzaci.

Stav uživatele bude sledovat jméno uživatele, věk a zvolené datum a stav konverzace bude sledovat, co jste naposledy požádali uživatele. Vzhledem k tomu, že tento robot neplánujete nasadit, nakonfigurujete stav uživatele a konverzace tak, aby používaly úložiště paměti.

Pomocí obslužné rutiny pro otáčení zprávy robota a vlastností stavu konverzace a uživatele můžete spravovat tok konverzace a kolekci vstupů. V robotovi zaznamenáte informace o vlastnosti stavu přijaté během každé iterace obslužné rutiny otáčení zprávy.

Vytváření konverzací a uživatelských objektů

Vytvořte objekty stavu uživatele a konverzace při spuštění a spotřebujte je prostřednictvím injektáže závislostí v konstruktoru robota.

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

Roboti/CustomPromptBot.cs

private readonly BotState _userState;
private readonly BotState _conversationState;

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

Vytvoření přístupových objektů vlastností

Vytvořte přístupové objekty vlastností pro profil uživatele a vlastnosti toku konverzace a potom voláním GetAsync načtěte hodnotu vlastnosti ze stavu.

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

Před koncem turnu zavolejte SaveChangesAsync , aby se změny stavu v úložišti změnily.

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

Obslužná rutina otáčení zprávy

Při zpracování aktivit zpráv obslužná rutina zprávy používá pomocnou metodu ke správě konverzace a vyzvání uživatele. Pomocná metoda je popsaná v následující části.

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

Vyplnění profilu uživatele

Robot vyzve uživatele k zadání informací podle toho, na jaké otázce se robot v předchozím kroku zeptal. Vstup se analyzuje pomocí metody ověřování.

Každá metoda ověřování má podobný návrh:

  • Vrácená hodnota označuje, jestli je vstup platnou odpovědí na tuto otázku.
  • Pokud ověření projde, vytvoří parsovanou a normalizovanou hodnotu, která se uloží.
  • Pokud se ověření nezdaří, vytvoří zprávu, se kterou může robot znovu požádat o informace.

Metody ověřování jsou popsány v následující části.

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

Parsování a ověření vstupu

Robot k ověření vstupu používá následující kritéria.

  • Název musí být neprázdný řetězec. Normalizuje se tím, že oříznou prázdné znaky.
  • Věk musí být mezi 18 a 120. Normalizuje se vrácením celého čísla.
  • Datum musí být libovolné datum nebo čas aspoň hodinu v budoucnu. Normalizuje se vrácením jenom části analyzovaného vstupu s datem.

Poznámka:

Pro vstup stáří a data se v ukázce používají knihovny Microsoft/Recognizers-Text k provedení počáteční analýzy. Toto je jen jeden způsob, jak analyzovat vstup. Další informace o těchto knihovnách najdete v souboru README projektu.

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

Místní testování robota

Stáhněte a nainstalujte bot Framework Emulator a otestujte robota místně.

  1. Spusťte ukázku místně na svém počítači. Pokud potřebujete pokyny, projděte si soubor pro ukázkuREADME jazyka C#, ukázku JS nebo ukázku Pythonu.
  2. Otestujte ho pomocí emulátoru.

Další materiály

Knihovna Dialogs poskytuje třídy, které automatizují mnoho aspektů správy konverzací.

Další krok