Guardar usuario y datos de conversación

SE APLICA A: SDK v4

Inherentemente, un bot no tiene estado. Una vez implementado el bot, no se puede ejecutar en el mismo proceso o en el mismo equipo de turno a otro. Sin embargo, el bot debe poder hacer un seguimiento del contexto de una conversación, para que pueda controlar su comportamiento y recordar las respuestas a las preguntas anteriores. Las características de almacenamiento y estado de Bot Framework SDK permiten agregar un estado al bot. Los bots usan objetos de almacenamiento y administración de estado para administrar y conservar el estado. El administrador de estados proporciona una capa de abstracción que permite acceder a las propiedades de estado mediante descriptores de acceso de las propiedades, independientemente del tipo de almacenamiento subyacente.

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 de este ejemplo

Al recibir la entrada del usuario, este ejemplo comprueba el estado de la conversación almacenada para ver si se le ha pedido a este usuario que proporcione su nombre anteriormente. Si no es así, se solicita el nombre del usuario y se almacena esa entrada en el estado del usuario. Si ya se le ha pedido, se usará el nombre almacenado en el estado del usuario para conversar con este y se devolverán sus datos de entrada junto con la hora de recepción y el identificador del canal de entrada, al usuario. Los valores de hora y de identificador del canal se recuperan de los datos de conversación del usuario y se guardan en el estado de conversación. El siguiente diagrama muestra la relación entre el bot, el perfil de usuario y las clases de datos de conversación.

Definir las clases

El primer paso para configurar la administración de estados es definir las clases que contendrán la información que queremos administrar del usuario y el estado de la conversación. En el ejemplo que se usa en este artículo se definen las clases siguientes:

  • En UserProfile.cs, se define una clase UserProfile para la información de usuario que recopilará el bot.
  • En ConversationData.cs, se define una clase ConversationData para controlar el estado de la conversación mientras se recopila información del usuario.

En los ejemplos de código siguientes se muestran las definiciones de las clases UserProfile y ConversationData.

UserProfile.cs

public class UserProfile
{
    public string Name { get; set; }
}

ConversationData.cs

public class ConversationData
{
    // The time-stamp of the most recent incoming message.
    public string Timestamp { get; set; }

    // The ID of the user's channel.
    public string ChannelId { get; set; }

    // Track whether we have already asked the user's name
    public bool PromptedUserForName { get; set; } = false;
}

Creación de objetos de estado de conversación y usuario

A continuación, se registra MemoryStorage que se usa para crear objetos UserState y ConversationState. Los objetos de estado de usuario y conversación se crean en Startup y se inserta la dependencia en el constructor del bot. Otros servicios que se registran para un bot son: un proveedor de credenciales, un adaptador y la implementación del bot.

Startup.cs

// {
//     TypeNameHandling = TypeNameHandling.All,
// var storage = new BlobsStorage("<blob-storage-connection-string>", "bot-state");

// With a custom JSON SERIALIZER, use this instead.
// var storage = new BlobsStorage("<blob-storage-connection-string>", "bot-state", jsonSerializer);

/* END AZURE BLOB STORAGE */

Bots/StateManagementBot.cs

private BotState _conversationState;
private BotState _userState;

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

Incorporación de descriptores de acceso de propiedad de estado

Ahora se crearán descriptores de acceso de propiedad mediante el método CreateProperty que proporciona un control sobre el objeto BotState. Cada descriptor de acceso a una propiedad de estado permite obtener o establecer el valor de la propiedad de estado correspondiente. Antes de usar nuestras propiedades de estado, usamos cada descriptor de acceso para cargar la propiedad desde el almacenamiento y obtenerla de la caché de estado. Para obtener la clave de ámbito correcto asociada con la propiedad de estado, el método se llamará GetAsync.

Bots/StateManagementBot.cs

var conversationStateAccessors = _conversationState.CreateProperty<ConversationData>(nameof(ConversationData));
var userStateAccessors = _userState.CreateProperty<UserProfile>(nameof(UserProfile));

Estado de acceso desde el bot

En las secciones anteriores se describen los pasos en tiempo de inicialización para agregar a nuestro bot los descriptores de acceso de las propiedades de estado. Ahora, podemos usar los descriptores de acceso a las propiedades de estado para leer y escribir la información sobre el estado en tiempo de ejecución. El siguiente ejemplo de código usa este flujo de lógica:

  • Si userProfile.Name está vacío y conversationData.PromptedUserForName es true, recupera el nombre de usuario proporcionado y lo almacena dentro del estado de usuario.
  • Si userProfile.Name está vacío y conversationData.PromptedUserForName es false, se pregunta por el nombre del usuario.
  • Si ya se almacenó userProfile.Name anteriormente, se recuperará la hora del mensaje y el identificador del canal a partir de la entrada del usuario, se enviarán de nuevo todos los datos al usuario y se almacenarán los datos recuperados en el estado de la conversación.

Bots/StateManagementBot.cs

protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
    // Get the state properties from the turn context.

    var conversationStateAccessors = _conversationState.CreateProperty<ConversationData>(nameof(ConversationData));
    var conversationData = await conversationStateAccessors.GetAsync(turnContext, () => new ConversationData());

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

    if (string.IsNullOrEmpty(userProfile.Name))
    {
        // First time around this is set to false, so we will prompt user for name.
        if (conversationData.PromptedUserForName)
        {
            // Set the name to what the user provided.
            userProfile.Name = turnContext.Activity.Text?.Trim();

            // Acknowledge that we got their name.
            await turnContext.SendActivityAsync($"Thanks {userProfile.Name}. To see conversation data, type anything.");

            // Reset the flag to allow the bot to go through the cycle again.
            conversationData.PromptedUserForName = false;
        }
        else
        {
            // Prompt the user for their name.
            await turnContext.SendActivityAsync($"What is your name?");

            // Set the flag to true, so we don't prompt in the next turn.
            conversationData.PromptedUserForName = true;
        }
    }
    else
    {
        // Add message details to the conversation data.
        // Convert saved Timestamp to local DateTimeOffset, then to string for display.
        var messageTimeOffset = (DateTimeOffset)turnContext.Activity.Timestamp;
        var localMessageTime = messageTimeOffset.ToLocalTime();
        conversationData.Timestamp = localMessageTime.ToString();
        conversationData.ChannelId = turnContext.Activity.ChannelId.ToString();

        // Display state data.
        await turnContext.SendActivityAsync($"{userProfile.Name} sent: {turnContext.Activity.Text}");
        await turnContext.SendActivityAsync($"Message received at: {conversationData.Timestamp}");
        await turnContext.SendActivityAsync($"Message received from: {conversationData.ChannelId}");
    }
}

Antes de salir del controlador de turnos, se usará el método SaveChangesAsync() de los objetos de administración de estados para escribir todos los cambios de estados de nuevo en el almacenamiento.

Bots/StateManagementBot.cs

public override async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken))
{
    await base.OnTurnAsync(turnContext, cancellationToken);

    // Save any state changes that might have occurred during the turn.
    await _conversationState.SaveChangesAsync(turnContext, false, cancellationToken);
    await _userState.SaveChangesAsync(turnContext, false, cancellationToken);
}

Pruebe su bot

  1. Descargue e instale la versión más reciente de Bot Framework Emulator.
  2. Ejecute el ejemplo localmente en la máquina. Si necesita instrucciones, consulte el LÉAME para C#, JavaScript, Java, o Python.
  3. Usar el emulador para probar el bot de ejemplo

Información adicional

En este artículo se describe cómo agregar el estado al bot. Consulte la siguiente tabla para obtener más información sobre los temas relacionados.

Tema Notas
Privacidad Si tiene intención de almacenar datos personales del usuario, debe garantizar el cumplimiento del Reglamento general de protección de datos.
Administración de estados Todas las llamadas de administración de estado son asincrónicas y prevalece el último en escribir de forma predeterminada. En la práctica, debe obtener, establecer y guardar el estado lo más próximos en el bot como sea posible. Para obtener una explicación sobre cómo implementar el bloqueo optimista, consulte Implementación del almacenamiento personalizado para el bot.
Datos empresariales críticos: Utilice el estado del bot para almacenar las preferencias, el nombre de usuario o lo último que haya solicitado, pero no lo utilice para almacenar datos empresariales críticos. Para los datos críticos, cree sus propios componentes de almacenamiento o escríbalos directamente en el almacenamiento.
Recognizer-Text: El ejemplo usa las bibliotecas Microsoft/Recognizers-Text para analizar y validar la entrada del usuario. Para obtener más información, consulte la página de información general.

Pasos siguientes

Aprenda a plantear al usuario una serie de preguntas, validar sus respuestas y guardar sus datos.