Поделиться через


Завершение срока действия диалога

ОБЛАСТЬ ПРИМЕНЕНИЯ: ПАКЕТ SDK версии 4

Иногда боту нужно перезапустить беседу с самого начала. Например, если пользователь не отвечает через определенный период времени. В этой статье описаны два метода истечения срока действия беседы:

  • Отслеживайте время последнего получения сообщения от пользователя и очистите состояние, если время больше предварительно настроенной длины при получении следующего сообщения от пользователя. Дополнительные сведения см. в разделе о истечении срока действия взаимодействия пользователя.
  • Используйте функцию уровня хранилища, например Время жизни Cosmos DB (TTL), чтобы автоматически очистить состояние после предварительно настроенного периода времени. Дополнительные сведения см. в разделе срока действия хранилища.

Примечание.

Пакеты SDK для JavaScript, C# и Python для Bot Framework по-прежнему будут поддерживаться, однако пакет SDK java отменяется с окончательной долгосрочной поддержкой, заканчивающейся в ноябре 2023 года.

Существующие боты, созданные с помощью пакета SDK для Java, будут продолжать функционировать.

Для создания нового бота рекомендуется использовать Microsoft Copilot Studio и ознакомиться с выбором подходящего решения copilot.

Дополнительные сведения см. в статье "Будущее создания бота".

Необходимые компоненты

Об этом примере

Пример кода в этой статье начинается со структуры многоэтапного бота и расширяет функциональные возможности бота, добавив дополнительный код (указанный в следующих разделах). Этот расширенный код демонстрирует, как очистить состояние беседы после определенного периода времени.

Истечение срока действия взаимодействия пользователя

Этот тип диалога истекает путем добавления свойства времени последнего доступа в состояние беседы бота. Это значение свойства затем сравнивается с текущим временем обработчика действий перед обработкой действий.

Примечание.

В этом примере используется 30-секундное время ожидания для упрощения тестирования этого шаблона.

appsettings.json

Сначала добавьте ExpireAfterSeconds параметр в appsettings.json:

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

Bots\DialogBot.cs

Затем добавьте ExpireAfterSecondsи LastAccessedTimePropertyполя в класс бота и DialogStateProperty инициализировать их в конструкторе бота. Также добавьте параметр в конструктор, с помощью которого извлекается IConfiguration ExpireAfterSeconds значение.

Вместо создания встроенного в метод метода объекта доступа к свойству состояния диалогового OnMessageActivityAsync окна вы создаете и записываете его во время инициализации. Боту потребуется средство доступа к свойству состояния не только для запуска диалогового окна, но и для очистки состояния диалога.

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

Наконец, добавьте код в метод бота OnTurnAsync , чтобы очистить состояние диалога, если беседа слишком старая.

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

Срок действия хранилища

Cosmos DB предоставляет функцию времени жизни (TTL), которая позволяет автоматически удалять элементы из контейнера после определенного периода времени. Это можно настроить из портал Azure или во время создания контейнера (с помощью пакетов SDK для Cosmos DB для конкретного языка).

Пакет SDK Bot Framework не предоставляет параметр конфигурации TTL. Однако инициализация контейнера может быть переопределена, а пакет SDK Cosmos DB можно использовать для настройки TTL до инициализации хранилища Bot Framework.

Начните с новой копии примера запроса с несколькими поворотами и добавьте Microsoft.Bot.Builder.Azure пакет NuGet в проект.

appsettings.json

Обновите appsettings.json, чтобы включить параметры хранилища 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>"
}

Обратите внимание на два контейнера, один для UserState одного и одного для ConversationState. TTL по умолчанию устанавливается в контейнере ConversationState , но не включен UserState.

CosmosDbStorageInitializerHostedService.cs

Затем создайте CosmosDbStorageInitializerHostedService класс, который создаст контейнер с настроенным временем жизни.

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

Наконец, обновите Startup.cs для использования инициализатора хранилища и Cosmos DB для состояния:

// 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 теперь автоматически удаляет записи состояния беседы через 30 секунд бездействия.

Дополнительные сведения см. в статье "Настройка времени жизни в Azure Cosmos DB"

Тестирование бота

  1. Если это еще не сделано, установите эмулятор Bot Framework.
  2. Выполните этот пример на локальном компьютере.
  3. Запустите эмулятор, подключитесь к боту и отправьте в него сообщение.
  4. После одного из запросов подождите 30 секунд, прежде чем отвечать.