Creación de mensajes propios para recopilar datos de entrada del usuario

SE APLICA A: SDK v4

A menudo, una conversación entre un usuario y un bot implica mostrar preguntas (avisos) al usuario para obtener información, analizar la respuesta del usuario y, a continuación, actuar en dicha información. El bot debe hacer un seguimiento del contexto de una conversación, para que pueda controlar su comportamiento y recordar las respuestas a las preguntas anteriores. El estado de un bot es la información que sigue con el fin de responder de forma adecuada a los mensajes entrantes.

Sugerencia

La biblioteca de diálogos proporciona avisos incorporados que proporcionan más funcionalidad que pueden usar los usuarios. Se pueden encontrar ejemplos de estos avisos en el artículo Implementación de un flujo de conversación secuencial.

Nota:

Los SDK de JavaScript, C# y Python de Bot Framework seguirán siendo compatibles, pero el SDK de Java se va a retirar con la finalización del soporte técnico a largo plazo en noviembre de 2023. Solo se realizarán correcciones de errores y seguridad críticos en este repositorio.

Los bots existentes creados con el SDK de Java seguirán funcionando.

Para la nueva compilación de bots, considere la posibilidad de usar Power Virtual Agents y lea sobre cómo elegir la solución de bot de chat adecuada.

Para obtener más información, consulte El futuro de la construcción de bots.

Requisitos previos

Acerca del código de ejemplo

El bot de ejemplo le formula al usuario una serie de preguntas, valida algunas de sus respuestas y guarda sus comentarios. El siguiente diagrama muestra la relación entre el bot, el perfil de usuario y las clases de flujos de conversación.

Class diagram for the C# sample.

  • Una clase UserProfile para la información de usuario que recopilará el bot.
  • Una clase ConversationFlow para controlar el estado de la conversación mientras se recopila información del usuario.
  • Una enumeración ConversationFlow.Question interna para el seguimiento de dónde nos encontramos en la conversación.

El estado del usuario realiza un seguimiento del nombre, edad y fecha elegida del usuario, y el estado de conversación, un seguimiento de lo que se acaba de preguntar al usuario. Dado que no tiene previsto implementar este bot, configurará el estado del usuario y de la conversación para utilizar el almacenamiento en memoria.

Se usa el controlador de turnos de mensaje del bot y las propiedades de estado de usuario y de conversación para administrar el flujo de la conversación y la colección de entradas. En el bot, se registrará la información de propiedad de estado que se reciba durante cada iteración del controlador de turnos de mensajes.

Creación de objetos de conversación y usuario

Cree los objetos de estado del usuario y la conversación en el inicio y, para consumirlos, inserte dependencias en el constructor 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;
}

Creación de los descriptores de acceso de la propiedad

Cree descriptores de acceso de propiedad para las propiedades de perfil de usuario y flujo de conversación y, a continuación, llame a GetAsync para recuperar el valor de propiedad del estado.

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

Antes de que finalice el turno, llame a SaveChangesAsync para escribir los cambios de estado en el almacenamiento.

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

Controlador de turno de mensajes

Al controlar las actividades de mensaje, el controlador de mensajes utiliza un método auxiliar para administrar la conversación y preguntar al usuario. El método auxiliar se describe en la siguiente sección.

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

Rellenado del perfil de usuario

El bot solicita información al usuario, en función de la pregunta que el bot hizo en el turno anterior. La entrada se analiza con un método de validación.

Cada método de validación sigue un diseño similar:

  • El valor devuelto indica si la entrada es una respuesta válida para esta pregunta.
  • Si la validación es correcta, se genera un valor normalizado y analizado para guardarlo.
  • Si se produce un error de validación, se genera un mensaje con el que el bot puede volver a pedir la información.

En las siguientes secciones se describen los métodos de validación.

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

Análisis y validación de la entrada

El bot usa los siguientes criterios para validar la entrada.

  • El nombre debe ser una cadena que no esté vacía. Para normalizarlo, se recortan los espacios en blanco.
  • La edad debe estar entre 18 y 120. Para normalizarlo, devuelve un entero.
  • La fecha debe ser cualquier fecha u hora al menos una hora en el futuro. Para normalizarlo, se devuelve solo la parte de fecha de la entrada analizada.

Nota:

Para la entrada de edad y fecha, el ejemplo usa bibliotecas Microsoft/Recognizers-Text para realizar el análisis inicial. Esta es solo una manera de analizar la entrada. Para más información acerca de estas bibliotecas, consulte el archivo Léame del proyecto.

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

Prueba local del bot

Descargue e instale Bot Framework Emulator para probar el bot localmente.

  1. Ejecute el ejemplo localmente en la máquina. Si necesita instrucciones, consulte el archivo README en el ejemplo de C#, el ejemplo de JS o el ejemplo de Python.
  2. Pruébelo con el emulador.

Recursos adicionales

La biblioteca de diálogos proporciona clases que automatizan muchos aspectos de la administración de las conversaciones.

Paso siguiente