Share via


Faire expirer une conversation

S'APPLIQUE À : SDK v4

Un bot doit parfois redémarrer une conversation à partir du début. Par exemple, si un utilisateur ne répond pas après un certain temps. Cet article décrit deux méthodes pour l'expiration d'une conversation :

  • Suivez la dernière fois qu'un message a été reçu d'un utilisateur et effacez l'état si l'heure est supérieure à une longueur préconfigurée lors de la réception du message suivant de l'utilisateur. Pour plus d'informations, consultez la section relative à l'expiration de l'interaction avec l'utilisateur.
  • Utilisez une fonctionnalité de couche de stockage, telle que Cosmos DB durée de vie (TTL), pour effacer automatiquement l'état après une durée de temps préconfigurée. Pour plus d'informations, consultez la section relative à l'expiration du stockage.

Remarque

Les kits SDK JavaScript, C# et Python Bot Framework continueront d’être pris en charge. Toutefois, le kit de développement logiciel (SDK) Java est mis hors service avec une prise en charge finale à long terme se terminant en novembre 2023.

Les bots existants créés avec le kit de développement logiciel (SDK) Java continueront de fonctionner.

Pour la nouvelle génération de bots, envisagez d'utiliser Power Virtual Agents et découvrez comment choisir la solution de chatbot appropriée.

Pour plus d’informations, consultez Les futures versions de bot.

Prérequis

À propos de cet exemple

L'exemple de code de cet article commence par la structure d'un bot multi-tour de base, puis étend les fonctionnalités de ce bot par ajout de code (fourni dans les sections suivantes). Ce code étendu montre comment effacer l'état de conversation après une certaine période.

Expiration de l'interaction utilisateur

Ce type de conversation arrivant à expiration est effectué en ajoutant une propriété de dernière heure d'accès à l'état de conversation du bot. Cette valeur de propriété est ensuite comparée à l'heure actuelle dans le gestionnaire d'activités avant le traitement des activités.

Remarque

Cet exemple utilise un délai d'expiration de 30 secondes pour faciliter le test de ce modèle.

appsettings.json

Tout d'abord, ajoutez un paramètre ExpireAfterSeconds à appsettings.json :

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

Bots\DialogBot.cs

Ensuite, ajoutez les champs ExpireAfterSeconds, LastAccessedTimeProperty et DialogStateProperty à la classe de bot et initialisez-les dans le constructeur du bot. Ajoutez également un paramètre IConfiguration au constructeur avec lequel récupérer la valeur ExpireAfterSeconds.

Au lieu de créer l'accesseur de propriété d'état de boîte de dialogue incluss dans la méthode OnMessageActivityAsync, vous créez et enregistrez-le au moment de l'initialisation. Le bot a besoin de l'accesseur de propriété d'état non seulement pour exécuter la boîte de dialogue, mais également pour effacer l'état du dialogue.

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;

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

Enfin, ajoutez du code à la méthode OnTurnAsync du bot pour effacer l'état du dialogue si la conversation est trop ancienne.

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

Expiration de stockage

Cosmos DB propose une fonctionnalité TTL (durée de vie) qui permet de supprimer automatiquement des éléments d'un conteneur après une certaine période de la vie. Cela peut être configuré à partir de l'Portail Azure ou lors de la création du conteneur (à l'aide des kits de développement logiciel (SDK) Cosmos DB spécifiques au langage).

Le kit de développement logiciel (SDK) Bot Framework n'expose pas de paramètre de configuration de durée de vie. Toutefois, l'initialisation de conteneur peut être remplacée et le kit de développement logiciel (SDK) Cosmos DB peut être utilisé pour configurer la durée de vie avant l'initialisation du stockage Bot Framework.

Commencez par une nouvelle copie de l'échantillon d'invite à plusieurs tours et ajoutez le package NuGet Microsoft.Bot.Builder.Azure au projet.

appsettings.json

Mettez à jour appsettings.json pour inclure les options de stockage Cosmos DB :

{
  "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>"
}

Notez les deux ContainerIds, un pour UserState et un autre pour ConversationState. La TTL par défaut est définie sur le conteneur ConversationState, mais pas sur UserState.

CosmosDbStorageInitializerHostedService.cs

Ensuite, créez une classe CosmosDbStorageInitializerHostedService, qui créera le conteneur avec la durée de vie configurée.

// 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.cs

Enfin, mettez à jour Startup.cs pour utiliser l'initialiseur de stockage et Cosmos DB pour l'état :

// Existing code omitted...

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

// Add the Initializer as a HostedService (so it's 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 supprime désormais automatiquement les enregistrements d'état de conversation après 30 secondes d'inactivité.

Pour plus d'informations, consultez Configurer la durée de vie dans Azure Cosmos DB

Pour tester le bot

  1. Si vous ne l'avez pas encore fait, installez Bot Framework Emulator.
  2. Exécutez l’exemple en local sur votre machine.
  3. Démarrez l'émulateur, connectez-vous à votre bot et envoyez un message.
  4. Après l'une des invites, attendez 30 secondes avant de répondre.