Wygasanie konwersacji

DOTYCZY: Zestaw SDK w wersji 4

Bot czasami musi ponownie uruchomić konwersację od początku. Jeśli na przykład użytkownik nie odpowie po pewnym czasie. W tym artykule opisano dwie metody wygasania konwersacji:

  • Śledź czas ostatniego odebrania komunikatu od użytkownika i wyczyść stan, jeśli czas jest większy niż wstępnie skonfigurowana długość po otrzymaniu następnego komunikatu od użytkownika. Aby uzyskać więcej informacji, zobacz sekcję wygasania interakcji użytkownika .
  • Użyj funkcji warstwy magazynu, takiej jak Czas wygaśnięcia usługi Cosmos DB (TTL), aby automatycznie wyczyścić stan po wstępnie skonfigurowanym czasie. Aby uzyskać więcej informacji, zobacz sekcję Wygasanie magazynu .

Uwaga

Zestawy SDK platformy Bot Framework i Java są wycofane z ostatecznej długoterminowej pomocy technicznej kończącej się w listopadzie 2023 r. Zostaną podjęte tylko krytyczne poprawki zabezpieczeń i usterek w tym repozytorium. Istniejące boty utworzone za pomocą tych zestawów SDK będą nadal działać.

W przypadku tworzenia nowych botów rozważ użycie agentów usługi Power Virtual Agents. Aby uzyskać więcej informacji, zobacz Przyszłość tworzenia botów.

Wymagania wstępne

Informacje o tym przykładzie

Przykładowy kod w tym artykule rozpoczyna się od struktury bota wielowrotnego i rozszerza funkcjonalność tego bota, dodając dodatkowy kod (podany w poniższych sekcjach). Ten rozszerzony kod pokazuje, jak wyczyścić stan konwersacji po upływie określonego czasu.

Wygaśnięcie interakcji użytkownika

Ten typ wygasającej konwersacji jest osiągany przez dodanie właściwości czasu ostatniego dostępu do stanu konwersacji bota. Ta wartość właściwości jest następnie porównywana z bieżącym czasem w programie obsługi działań przed przetworzeniem działań.

Uwaga

W tym przykładzie użyto limitu czasu 30 sekund w celu ułatwienia testowania tego wzorca.

appsettings.json

Najpierw dodaj ExpireAfterSeconds ustawienie do pliku appsettings.json:

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

Bots\DialogBot.cs

Następnie dodaj ExpireAfterSecondspola , LastAccessedTimePropertyi DialogStateProperty do klasy bota i zainicjuj je w konstruktorze bota. IConfiguration Dodaj również parametr do konstruktora, za pomocą którego ma zostać pobrana ExpireAfterSeconds wartość.

Zamiast tworzyć metodę dostępu właściwości stanu okna dialogowego w metodzie OnMessageActivityAsync , tworzysz ją i rejestrujesz w czasie inicjowania. Bot będzie potrzebował dostępu do właściwości stanu nie tylko do uruchomienia okna dialogowego, ale także w celu wyczyszczenia stanu okna dialogowego.

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

Na koniec dodaj kod do metody bota OnTurnAsync , aby wyczyścić stan okna dialogowego, jeśli konwersacja jest za stara.

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

Wygaśnięcie magazynu

Usługa Cosmos DB udostępnia funkcję czasu wygaśnięcia (TTL), która umożliwia automatyczne usuwanie elementów z kontenera po upływie określonego czasu. Można to skonfigurować z poziomu Azure Portal lub podczas tworzenia kontenera (przy użyciu zestawów SDK usługi Cosmos DB specyficznych dla języka).

Zestaw SDK platformy Bot Framework nie uwidacznia ustawienia konfiguracji czasu wygaśnięcia. Jednak inicjowanie kontenera może zostać zastąpione, a zestaw SDK usługi Cosmos DB może służyć do konfigurowania czasu wygaśnięcia przed zainicjowaniem magazynu bot Framework.

Zacznij od nowej kopii przykładowego monitu z wieloma zwrotami i dodaj Microsoft.Bot.Builder.Azure pakiet NuGet do projektu.

appsettings.json

Zaktualizuj plik appsettings.json, aby uwzględnić opcje magazynu usługi 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>"
}

Zwróć uwagę na dwa identyfikatory ContainerId, jeden dla UserState i jeden dla ConversationState. Domyślny czas wygaśnięcia jest ustawiany w kontenerze ConversationState , ale nie na UserState.

CosmosDbStorageInitializerHostedService.cs

Następnie utwórz klasę CosmosDbStorageInitializerHostedService , która utworzy kontener ze skonfigurowanym czasem wygaśnięcia.

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

Na koniec zaktualizuj element Startup.cs , aby użyć inicjatora magazynu i usługi Cosmos DB pod kątem stanu:

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

Usługa Cosmos DB automatycznie usunie rekordy stanu konwersacji po 30 sekundach braku aktywności.

Aby uzyskać więcej informacji, zobacz Konfigurowanie czasu wygaśnięcia w usłudze Azure Cosmos DB

Aby przetestować bota

  1. Jeśli jeszcze tego nie zrobiono, zainstaluj Bot Framework Emulator.
  2. Uruchom przykład lokalnie na maszynie.
  3. Uruchom emulator, połącz się z botem i wyślij do niego komunikat.
  4. Po wyświetleniu jednego z monitów poczekaj 30 sekund przed odpowiedzią.