대화 만료Expire a conversation

적용 대상: SDK v4APPLIES TO: SDK v4

봇이 처음부터 대화를 다시 시작해야 하는 경우도 있습니다.A bot sometimes needs to restart a conversation from the beginning. 예를 들어 사용자가 일정 기간 후에 응답하지 않는 경우입니다.For instance, if a user does not respond after a certain period of time. 이 문서에서는 대화를 만료하는 두 가지 방법을 설명합니다.This article describes two methods for expiring a conversation:

  • 사용자로부터 메시지를 마지막으로 받은 시간을 추적하고, 사용자가 다음 메시지를 받을 때 시간이 미리 구성된 길이보다 큰 경우 상태를 지웁히게 합니다.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. 자세한 내용은 사용자 상호 작용 만료 섹션을 참조하세요.For more information, see the user interaction expiration section.
  • 미리 구성된 시간 후 상태를 자동으로 지우려면 Cosmos DB TTL(Time To Live)과 같은 스토리지 계층 기능을 사용합니다.Use a storage layer feature, such as Cosmos DB Time To Live (TTL), to automatically clear state after a preconfigured length of time. 자세한 내용은 스토리지 만료 섹션을 참조하세요.For more information, see the storage expiration section.

필수 구성 요소Prerequisites

이 샘플 정보About this sample

이 문서의 샘플 코드는 다중 턴 봇의 구조로 시작하며, 다음 섹션에서 제공하는 추가 코드를 추가하여 해당 봇의 기능을 확장합니다.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). 이 확장 코드는 특정 기간이 지난 후 대화 상태를 지우는 방법을 보여줍니다.This extended code demonstrates how to clear conversation state after a certain time period has passed.

사용자 상호 작용 만료User Interaction Expiration

이 유형의 만료 대화는 봇의 대화 상태에 마지막으로 액세스한 시간 속성을 추가하여 수행됩니다.This type of expiring conversation is accomplished by adding a last accessed time property to the bot's conversation state. 그런 다음 이 속성 값은 활동을 처리하기 전에 작업 처리기 내의 현재 시간과 비교됩니다.This property value is then compared to the current time within the activity handler before processing activities.

참고

이 예제에서는 이 패턴을 쉽게 테스트하기 위해 30초 시간 초과를 사용합니다.This example uses a 30 second timeout for ease of testing this pattern.

appsettings.jsonappsettings.json

먼저 appsettings.jsExpireAfterSeconds 설정을 추가합니다.First, add an ExpireAfterSeconds setting to appsettings.json:

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

Bots\DialogBot.csBots\DialogBot.cs

다음으로, ExpireAfterSeconds LastAccessedTimeProperty , 및 필드를 DialogStateProperty 봇 클래스에 추가하고 봇의 생성자에서 초기화합니다.Next, add ExpireAfterSeconds, LastAccessedTimeProperty, and DialogStateProperty fields to the bot class and initialize them in the bot's constructor. 또한 IConfiguration 값을 검색할 생성자에 매개 변수를 ExpireAfterSeconds 추가합니다.Also add an IConfiguration parameter to the constructor with which to retrieve the ExpireAfterSeconds value.

메서드에서 대화 상태 속성 접근자를 인라인으로 만드는 대신 OnMessageActivityAsync 초기화 시 만들고 기록합니다.Note that instead of creating the dialog state property accessor inline in the OnMessageActivityAsync method, you are creating and recording it at initialization time. 봇은 대화 상자를 실행할 뿐만 아니라 대화 상태를 지우기 위해 상태 속성 접근자도 필요합니다.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));
}

마지막으로, OnTurnAsync 대화가 너무 오래되면 대화 상태를 지우는 코드를 봇의 메서드에 추가합니다.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);
}

스토리지 만료Storage Expiration

Cosmos DB 특정 기간 후에 컨테이너에서 항목을 자동으로 삭제할 수 있는 TTL(Time To Live) 기능을 제공합니다.Cosmos DB provides a Time To Live (TTL) feature which allows you to delete items automatically from a container after a certain time period. 이 구성은 Azure Portal 내에서 또는 컨테이너를 만드는 동안(언어별 Cosmos DB SDK 사용) 구성할 수 있습니다.This can be configured from within the Azure portal or during container creation (using the language-specific Cosmos DB SDKs).

Bot Framework SDK는 TTL 구성 설정을 노출하지 않습니다.The Bot Framework SDK does not expose a TTL configuration setting. 그러나 컨테이너 초기화를 재정의할 수 있으며 Cosmos DB SDK를 사용하여 스토리지 초기화를 Bot Framework 전에 TTL을 구성할 수 있습니다.However, container initialization can be overridden and the Cosmos DB SDK can be used to configure TTL prior to Bot Framework storage initialization.

다중 턴 프롬프트 샘플의 새 복사본으로 시작하고 Microsoft.Bot.Builder.Azure NuGet 패키지를 프로젝트에 추가합니다.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

Cosmos DB 스토리지 옵션을 포함하도록 appsettings.js업데이트합니다.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>"
}

두 개의 ContainerId를 확인할 수 있습니다. 하나는 UserState 용이고 다른 하나는 에 대한 ConversationState 것입니다.Notice the two ContainerIds, one for UserState and one for ConversationState. 이는 컨테이너에서 기본 Time To Live를 설정하지만 에서는 ConversationState 설정하지 않기 UserState 때문입니다.This is because we are setting a default Time To Live on the ConversationState container, but not on UserState.

CosmosDbStorageInitializerHostedService.csCosmosDbStorageInitializerHostedService.cs

다음으로, CosmosDbStorageInitializerHostedService 구성된 Time To Live를 사용하여 컨테이너를 만드는 클래스를 만듭니다.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

마지막으로 스토리지 Startup.cs 이니셜라이저를 사용하도록 업데이트하고 상태에 Cosmos Db를 사용하도록 업데이트합니다.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은 30 초 동안 활동이 없을 경우 대화 상태 레코드를 자동으로 삭제 합니다.Cosmos DB will now automatically delete conversation state records after 30 seconds of inactivity.

자세한 내용은 Azure Cosmos DB에서 라이브 기간 구성 을 참조 하세요.For more information, see Configure time to live in Azure Cosmos DB

봇 테스트To test the bot

  1. 아직 설치하지 않은 경우 Bot Framework Emulator를 설치합니다.If you have not done so already, install the Bot Framework Emulator.
  2. 샘플을 머신에서 로컬로 실행합니다.Run the sample locally on your machine.
  3. 에뮬레이터를 시작 하 고, 봇에 연결 하 고, 메시지를 보냅니다.Start the Emulator, connect to your bot, and send a message to it.
  4. 메시지 중 하나가 표시 된 후 30 초 정도 기다린 후 응답 합니다.After one of the prompts, wait 30 seconds before responding.