다음을 통해 공유


대화 만료

적용 대상: SDK v4

봇은 때때로 처음부터 대화를 다시 시작해야 합니다. 예를 들어 사용자가 일정 기간 후에 응답하지 않는 경우입니다. 이 문서에서는 대화를 만료하는 두 가지 방법을 설명합니다.

  • 사용자로부터 메시지를 마지막으로 받은 시간을 추적하고, 사용자로부터 다음 메시지를 받을 때 미리 구성된 길이보다 시간이 긴지 상태를 지웁다. 자세한 내용은 사용자 상호 작용 만료 섹션을 참조하세요.
  • Cosmos DB TTL(Time To Live)과 같은 스토리지 계층 기능을 사용하여 미리 구성된 시간 후에 상태를 자동으로 지웁니다. 자세한 내용은 스토리지 만료 섹션을 참조하세요.

참고 항목

Bot Framework JavaScript, C#및 Python SDK는 계속 지원되지만 Java SDK는 2023년 11월에 종료되는 최종 장기 지원으로 사용 중지됩니다.

Java SDK를 사용하여 빌드된 기존 봇은 계속 작동합니다.

새 봇 빌드의 경우 Microsoft Copilot Studio를 사용하고 올바른 부조종사 솔루션 선택에 대해 알아봅니다.

자세한 내용은 봇 빌드의 미래를 참조 하세요.

필수 조건

이 샘플 정보

이 문서의 샘플 코드는 다중 턴 봇의 구조로 시작하고 코드를 추가하여 해당 봇의 기능을 확장합니다(다음 섹션에서 제공). 이 확장 코드는 특정 기간이 지난 후 대화 상태를 지우는 방법을 보여 줍니다.

사용자 상호 작용 만료

이 유형의 만료 대화는 마지막으로 액세스한 시간 속성을 봇의 대화 상태에 추가하여 수행됩니다. 그런 다음 이 속성 값은 활동을 처리하기 전에 활동 처리기 내의 현재 시간과 비교됩니다.

참고 항목

이 예제에서는 이 패턴을 쉽게 테스트하기 위해 30초 제한 시간을 사용합니다.

appsettings.json

먼저 appsettings.json 설정을 추가 ExpireAfterSeconds 합니다.

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

Bots\DialogBot.cs

다음으로, LastAccessedTimePropertyDialogStateProperty 봇 클래스에 및 필드를 추가하고 ExpireAfterSeconds봇의 생성자에서 초기화합니다. 또한 값을 검색할 생성자에 매개 변수를 추가 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(Time To Live) 기능을 제공합니다. 이는 Azure Portal 내에서 또는 컨테이너를 만드는 동안(언어별 Cosmos DB SDK 사용) 구성할 수 있습니다.

Bot Framework SDK는 TTL 구성 설정을 노출하지 않습니다. 그러나 컨테이너 초기화를 재정의할 수 있으며 Cosmos DB SDK를 사용하여 Bot Framework 스토리지 초기화 전에 TTL을 구성할 수 있습니다.

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

appsettings.json

Cosmos DB 스토리지 옵션을 포함하도록 appsettings.json 업데이트합니다.

{
  "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(하나는 for 및 1 for UserState )를 확인합니다 ConversationState. 기본 TTL은 컨테이너에 ConversationState 설정되어 있지만 설정 UserState되지는 않습니다.

CosmosDbStorageInitializerHostedService.cs

다음으로 구성된 Time To Live를 사용하여 컨테이너를 만드는 클래스를 만듭니 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에서 TL(Time to Live) 구성을 참조 하세요.

봇을 테스트하려면

  1. 아직 설치하지 않은 경우 Bot Framework Emulator설치합니다.
  2. 머신에서 로컬로 샘플을 실행합니다.
  3. 에뮬레이터를 시작하고, 봇에 연결하고, 메시지를 보냅니다.
  4. 프롬프트 중 하나가 표시되면 응답하기 전에 30초 동안 기다립니다.