Expiración de una conversaciónExpire a conversation

se aplica a: SDK V4APPLIES TO: SDK v4

A veces, un bot debe reiniciar una conversación desde el principio.A bot sometimes needs to restart a conversation from the beginning. Por ejemplo, si un usuario no responde después de un período de tiempo determinado.For instance, if a user does not respond after a certain period of time. En este artículo se describen dos métodos para expirar una conversación:This article describes two methods for expiring a conversation:

  • Realice un seguimiento de la última vez que se recibió un mensaje de un usuario y borre el estado si la hora es mayor que una longitud preconfigurada al recibir el siguiente mensaje del usuario.Track the last time a message was received from a user, and clear state if the time is greater than a preconfigured length upon receiving the next message from the user. Para más información, consulte la sección expiración de la interacción del usuario.For more information, see the user interaction expiration section.
  • Use una característica de capa de almacenamiento, como Cosmos DB período de vida (TTL), para borrar automáticamente el estado después de un período de tiempo preconfigurado.Use a storage layer feature, such as Cosmos DB Time To Live (TTL), to automatically clear state after a preconfigured length of time. Para más información, consulte la sección expiración del almacenamiento.For more information, see the storage expiration section.

Requisitos previosPrerequisites

Acerca de este ejemploAbout this sample

El código de ejemplo de este artículo comienza con la estructura de un bot de varios turnos y amplía la funcionalidad del bot mediante la adición de código adicional (que se proporciona en las secciones siguientes).The sample code in this article begins with the structure of a multi-turn bot, and extends that bot's functionality by adding additional code (provided in the following sections). Este código extendido muestra cómo borrar el estado de la conversación una vez transcurrido un período de tiempo determinado.This extended code demonstrates how to clear conversation state after a certain time period has passed.

Expiración de la interacción del usuarioUser Interaction Expiration

Este tipo de conversación que expira se logra agregando una propiedad de hora a la que se ha accedido por última vez al estado de conversación del bot.This type of expiring conversation is accomplished by adding a last accessed time property to the bot's conversation state. A continuación, este valor de propiedad se compara con la hora actual dentro del controlador de actividad antes de procesar las actividades.This property value is then compared to the current time within the activity handler before processing activities.

Nota

En este ejemplo se usa un tiempo de espera de 30 segundos para facilitar la prueba de este patrón.This example uses a 30 second timeout for ease of testing this pattern.

appsettings.jsonappsettings.json

En primer lugar, ExpireAfterSeconds agregue una configuración appsettings.jsen:First, add an ExpireAfterSeconds setting to appsettings.json:

{
  "MicrosoftAppId": "",
  "MicrosoftAppPassword": "",
  "ExpireAfterSeconds": 30
}

Bots\DialogBot.csBots\DialogBot.cs

A continuación, ExpireAfterSeconds agregue los campos , y a la clase de bot e inicialíclos en el constructor del LastAccessedTimeProperty DialogStateProperty bot.Next, add ExpireAfterSeconds, LastAccessedTimeProperty, and DialogStateProperty fields to the bot class and initialize them in the bot's constructor. Agregue también un IConfiguration parámetro al constructor con el que recuperar el ExpireAfterSeconds valor.Also add an IConfiguration parameter to the constructor with which to retrieve the ExpireAfterSeconds value.

Tenga en cuenta que, en lugar de crear el accessor de propiedad de estado de cuadro de diálogo en línea en el método , lo está creando y OnMessageActivityAsync grabando en el momento de la inicialización.Note that instead of creating the dialog state property accessor inline in the OnMessageActivityAsync method, you are creating and recording it at initialization time. El bot necesitará el accessor de la propiedad state no solo para ejecutar el diálogo, sino también para borrar el estado del diálogo.The bot will need the state property accessor not only to run the dialog, but also to clear the dialog state.

protected readonly int ExpireAfterSeconds;
protected readonly IStatePropertyAccessor<DateTime> LastAccessedTimeProperty;
protected readonly IStatePropertyAccessor<DialogState> DialogStateProperty;

// Existing fields omitted...

public DialogBot(IConfiguration configuration, ConversationState conversationState, UserState userState, T dialog, ILogger<DialogBot<T>> logger)
{
    ConversationState = conversationState;
    UserState = userState;
    Dialog = dialog;
    Logger = logger;

    TimeoutSeconds = configuration.GetValue<int>("ExpireAfterSeconds");
    DialogStateProperty = ConversationState.CreateProperty<DialogState>(nameof(DialogState));
    LastAccessedTimeProperty = ConversationState.CreateProperty<DateTime>(nameof(LastAccessedTimeProperty));
}

Por último, agregue código al método del bot para borrar el estado del diálogo OnTurnAsync si la conversación es demasiado antigua.Finally, add code to the bot's OnTurnAsync method to clear the dialog state if the conversation is too old.

public override async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default)
{
    // Retrieve the property value, and compare it to the current time.
    var lastAccess = await LastAccessedTimeProperty.GetAsync(turnContext, () => DateTime.UtcNow, cancellationToken).ConfigureAwait(false);
    if ((DateTime.UtcNow - lastAccess) >= TimeSpan.FromSeconds(ExpireAfterSeconds))
    {
        // Notify the user that the conversation is being restarted.
        await turnContext.SendActivityAsync("Welcome back!  Let's start over from the beginning.").ConfigureAwait(false);

        // Clear state.
        await ConversationState.ClearStateAsync(turnContext, cancellationToken).ConfigureAwait(false);
    }

    await base.OnTurnAsync(turnContext, cancellationToken).ConfigureAwait(false);

    // Set LastAccessedTime to the current time.
    await LastAccessedTimeProperty.SetAsync(turnContext, DateTime.UtcNow, cancellationToken).ConfigureAwait(false);

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

Expiración del almacenamientoStorage Expiration

Cosmos DB proporciona una característica de período de vida (TTL) que permite eliminar elementos automáticamente de un contenedor después de un período de tiempo determinado.Cosmos DB provides a Time To Live (TTL) feature which allows you to delete items automatically from a container after a certain time period. Esto se puede configurar desde dentro de la Azure Portal o durante la creación del contenedor (mediante los SDK de Cosmos DB específicos del lenguaje).This can be configured from within the Azure portal or during container creation (using the language-specific Cosmos DB SDKs).

El SDK Bot Framework no expone una configuración de TTL.The Bot Framework SDK does not expose a TTL configuration setting. Sin embargo, se puede invalidar la inicialización del contenedor y el SDK de Cosmos DB se puede usar para configurar TTL antes de Bot Framework de almacenamiento.However, container initialization can be overridden and the Cosmos DB SDK can be used to configure TTL prior to Bot Framework storage initialization.

Comience con una copia nueva del ejemplo de aviso de varios turnos y agregue el paquete Microsoft.Bot.Builder.Azure NuGet al proyecto.Start with a fresh copy of the multi-turn prompt sample, and add the Microsoft.Bot.Builder.Azure NuGet package to the project.

appsettings.jsonappsettings.json

Actualice appsettings.jspara incluir opciones Cosmos DB almacenamiento:Update appsettings.json to include Cosmos DB storage options:

{
  "MicrosoftAppId": "",
  "MicrosoftAppPassword": "",

  "CosmosDbTimeToLive": 30,
  "CosmosDbEndpoint": "<endpoint-for-your-cosmosdb-instance>",
  "CosmosDbAuthKey": "<your-cosmosdb-auth-key>",
  "CosmosDbDatabaseId": "<your-database-id>",
  "CosmosDbUserStateContainerId": "<no-ttl-container-id>",
  "CosmosDbConversationStateContainerId": "<ttl-container-id>"
}

Observe los dos ContainerIds, uno para UserState y otro para ConversationState .Notice the two ContainerIds, one for UserState and one for ConversationState. Esto se debe a que estamos estableciendo un período de vida predeterminado en el ConversationState contenedor, pero no en UserState .This is because we are setting a default Time To Live on the ConversationState container, but not on UserState.

CosmosDbStorageInitializerHostedService.csCosmosDbStorageInitializerHostedService.cs

A continuación, CosmosDbStorageInitializerHostedService cree una clase , que creará el contenedor con el período de vida configurado.Next, create a CosmosDbStorageInitializerHostedService class, which will create the container with the configured Time To Live.

// Add required using statements...

public class CosmosDbStorageInitializerHostedService : IHostedService
{
    readonly CosmosDbPartitionedStorageOptions _storageOptions;
    readonly int _cosmosDbTimeToLive;

    public CosmosDbStorageInitializerHostedService(IConfiguration config)
    {
        _storageOptions = new CosmosDbPartitionedStorageOptions()
        {
            CosmosDbEndpoint = config["CosmosDbEndpoint"],
            AuthKey = config["CosmosDbAuthKey"],
            DatabaseId = config["CosmosDbDatabaseId"],
            ContainerId = config["CosmosDbConversationStateContainerId"]
        };

        _cosmosDbTimeToLive = config.GetValue<int>("CosmosDbTimeToLive");
    }

    public async Task StartAsync(CancellationToken cancellationToken)
    {
        using (var client = new CosmosClient(
            _storageOptions.CosmosDbEndpoint,
            _storageOptions.AuthKey,
            _storageOptions.CosmosClientOptions ?? new CosmosClientOptions()))
        {
            // Create the contaier with the provided TTL
            var containerResponse = await client
                .GetDatabase(_storageOptions.DatabaseId)
                .DefineContainer(_storageOptions.ContainerId, "/id")
                .WithDefaultTimeToLive(_cosmosDbTimeToLive)
                .WithIndexingPolicy().WithAutomaticIndexing(false).Attach()
                .CreateIfNotExistsAsync(_storageOptions.ContainerThroughput)
                .ConfigureAwait(false);
        }
    }

    public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}

Startup.csStartup.cs

Por último, actualice Startup.cs para usar el inicializador de almacenamiento y Cosmos Db para el estado:Lastly, update Startup.cs to use the storage initializer, and Cosmos Db for state:

// Existing code omitted...

// commented out MemoryStorage, since we are using CosmosDbPartitionedStorage instead
// services.AddSingleton<IStorage, MemoryStorage>();

// Add the Initializer as a HostedService (so it is called during the app service startup)
services.AddHostedService<CosmosDbStorageInitializerHostedService>();

// Create the storage options for User state
var userStorageOptions = new CosmosDbPartitionedStorageOptions()
{
    CosmosDbEndpoint = Configuration["CosmosDbEndpoint"],
    AuthKey = Configuration["CosmosDbAuthKey"],
    DatabaseId = Configuration["CosmosDbDatabaseId"],
    ContainerId = Configuration["CosmosDbUserStateContainerId"]
};

// Create the User state. (Used in this bot's Dialog implementation.)
services.AddSingleton(new UserState(new CosmosDbPartitionedStorage(userStorageOptions)));

// Create the storage options for Conversation state
var conversationStorageOptions = new CosmosDbPartitionedStorageOptions()
{
    CosmosDbEndpoint = Configuration["CosmosDbEndpoint"],
    AuthKey = Configuration["CosmosDbAuthKey"],
    DatabaseId = Configuration["CosmosDbDatabaseId"],
    ContainerId = Configuration["CosmosDbConversationStateContainerId"]
};

// Create the Conversation state. (Used by the Dialog system itself.)
services.AddSingleton(new ConversationState(new CosmosDbPartitionedStorage(conversationStorageOptions)));

// Existing code omitted...

Cosmos DB eliminará automáticamente los registros de estado de conversación después de 30 segundos de inactividad.Cosmos DB will now automatically delete conversation state records after 30 seconds of inactivity.

Para obtener más información, consulte Configuración del período de vida en Azure Cosmos DBFor more information, see Configure time to live in Azure Cosmos DB

Prueba del botTo test the bot

  1. Si aún no lo ha hecho, instale Bot Framework Emulator.If you have not done so already, install the Bot Framework Emulator.
  2. Ejecute el ejemplo localmente en la máquina.Run the sample locally on your machine.
  3. Inicie el emulador, conéctese al bot y envíele un mensaje.Start the Emulator, connect to your bot, and send a message to it.
  4. Después de uno de los avisos, espere 30 segundos antes de responder.After one of the prompts, wait 30 seconds before responding.