Creare richieste personalizzate per raccogliere input utente

SI APPLICA A: SDK v4

Una conversazione tra un bot e un utente spesso include la richiesta all'utente di informazioni, l'analisi della risposta e quindi un'azione in base a tali informazioni. Il bot deve tenere traccia del contesto di una conversazione, in modo che possa gestirne il comportamento e ricordare le risposte alle domande precedenti. Lo stato di un bot è un'informazione di cui il bot tiene traccia per rispondere in modo appropriato ai messaggi in arrivo.

Suggerimento

La libreria dialogs fornisce richieste predefinite che forniscono più funzionalità che gli utenti possono usare. Esempi di tali richieste sono disponibili nell'articolo Implementare un flusso di conversazione sequenziale.

Nota

Gli SDK JavaScript, C# e Python di Bot Framework continueranno a essere supportati, ma Java SDK verrà ritirato con il supporto finale a lungo termine che termina a novembre 2023.

I bot esistenti creati con Java SDK continueranno a funzionare.

Per la creazione di nuovi bot, è consigliabile usare Microsoft Copilot Studio e leggere le informazioni sulla scelta della soluzione copilota appropriata.

Per altre informazioni, vedere Il futuro della compilazione di bot.

Prerequisiti

Informazioni sul codice di esempio

Il bot di esempio pone all'utente una serie di domande, convalida alcune risposte e salva l'input. Il diagramma seguente illustra la relazione tra il bot, il profilo utente e le classi del flusso di conversazione.

Diagramma classi per l'esempio C#.

  • Una classe UserProfile per le informazioni dell'utente che verranno raccolte dal bot.
  • Una classe ConversationFlow per controllare lo stato della conversazione durante la raccolta delle informazioni dell'utente.
  • Enumerazione interna ConversationFlow.Question per tenere traccia della posizione in cui ci si trova nella conversazione.

Lo stato utente tiene traccia del nome, dell'età e della data scelta dell'utente e lo stato della conversazione tengono traccia dell'ultima richiesta all'utente. Poiché non si prevede di distribuire questo bot, si configurerà lo stato utente e della conversazione per l'uso dell'archiviazione di memoria.

Per gestire il flusso della conversazione e la raccolta di input, usare il gestore dei turni dei messaggi del bot e le proprietà dello stato utente e della conversazione. Nel bot verranno registrate le informazioni sulla proprietà di stato ricevute durante ogni iterazione del gestore dei turni del messaggio.

Creare gli oggetti della conversazione e dell'utente

Creare gli oggetti di stato dell'utente e della conversazione all'avvio e utilizzarli tramite l'inserimento della dipendenza nel costruttore del bot.

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

Creare le funzioni di accesso alle proprietà

Creare le funzioni di accesso alle proprietà per le proprietà del flusso di conversazione e del profilo utente e quindi chiamare GetAsync per recuperare il valore della proprietà dallo stato.

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

Prima della fine del turno, chiamare SaveChangesAsync per scrivere eventuali modifiche di stato nell'archivio.

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

Gestore dei turni di messaggi

Quando si gestiscono le attività dei messaggi, il gestore di messaggi usa un metodo helper per gestire la conversazione e richiedere input all'utente. Il metodo helper è descritto nella sezione seguente.

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

Compilazione del profilo utente

Il bot richiede informazioni all'utente sulla base della eventuale domanda posta dal bot durante il turno precedente. L'input viene analizzato con un metodo di convalida.

Ogni metodo di convalida è strutturato in modo simile:

  • Il valore restituito indica se l'input è una risposta valida per questa domanda.
  • Se la convalida ha esito positivo, produce un valore analizzato e normalizzato da salvare.
  • Se la convalida non riesce, produce un messaggio con il quale il bot può chiedere nuovamente l'informazione.

I metodi di convalida sono descritti nella sezione seguente.

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

Analizzare e convalidare l'input

Per convalidare l'input, il bot usa i criteri seguenti.

  • Il nome deve essere una stringa non vuota. Questo elemento verrà normalizzato tagliando lo spazio vuoto.
  • L'età deve essere compresa tra 18 e 120. Questo elemento verrà normalizzato restituendo un numero intero.
  • La data deve essere qualsiasi data o ora che sia di almeno un'ora nel futuro. Questo elemento verrà normalizzato restituendo solo la parte dell'input analizzato relativa alla data.

Nota

Per l'input di età e data, l'esempio usa le librerie Microsoft/Recognizers-Text per eseguire l'analisi iniziale. Questo è solo un modo per analizzare l'input. Per altre informazioni su queste librerie, vedere README del progetto.

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

Testare il bot nell'ambiente locale

Scaricare e installare Bot Framework Emulator per testare il bot in locale.

  1. Eseguire l'esempio in locale nel computer. Se sono necessarie istruzioni, vedere il file per l'esempioREADME C#, l'esempio JS o l'esempio Python.
  2. Testare il bot con Emulator.

Risorse aggiuntive

La libreria Dialogs fornisce classi che automatizzano molti aspetti della gestione delle conversazioni.

Passaggio successivo