Partilhar via


Expirar uma conversa

APLICA-SE A: SDK v4

Um bot às vezes precisa reiniciar uma conversa desde o início. Por exemplo, se um usuário não responder após um determinado período de tempo. Este artigo descreve dois métodos para expirar uma conversa:

  • Rastreie a última vez que uma mensagem foi recebida de um usuário e limpe o estado se o tempo for maior do que um comprimento pré-configurado ao receber a próxima mensagem do usuário. Para obter mais informações, consulte a seção de expiração da interação do usuário.
  • Use um recurso de camada de armazenamento, como o Cosmos DB Time To Live (TTL), para limpar automaticamente o estado após um período de tempo pré-configurado. Para obter mais informações, consulte a seção de expiração de armazenamento.

Nota

Para criar agentes com sua escolha de serviços, orquestração e conhecimento de IA, considere usar o SDK de agentes do Microsoft 365. O SDK de agentes tem suporte para C#, JavaScript ou Python. Você pode saber mais sobre o SDK de agentes em aka.ms/agents. Se você estiver procurando por uma plataforma de agente baseada em SaaS, considere o Microsoft Copilot Studio. Se você tiver um bot existente criado com o SDK do Bot Framework, poderá atualizar seu bot para o SDK de agentes. Você pode revisar as principais alterações e atualizações nas diretrizes de migração do SDK do Bot Framework para o SDK de agentes. Os tíquetes de suporte para o SDK do Bot Framework não serão mais atendidos a partir de 31 de dezembro de 2025.

Pré-requisitos

Sobre este exemplo

O código de exemplo neste artigo começa com a estrutura de um bot de várias voltas e estende a funcionalidade desse bot adicionando código adicional (fornecido nas seções a seguir). Esse código estendido demonstra como limpar o estado da conversa após um determinado período de tempo.

Expiração da interação do usuário

Esse tipo de conversa expirante é realizado adicionando uma propriedade de hora do último acesso ao estado de conversa do bot. Esse valor de propriedade é então comparado com a hora atual dentro do manipulador de atividade antes das atividades de processamento.

Nota

Este exemplo usa um tempo limite de 30 segundos para facilitar o teste desse padrão.

appsettings.json

Primeiro, adicione uma ExpireAfterSeconds configuração ao appsettings.json:

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

Bots\DialogBot.cs

Em seguida, adicione ExpireAfterSeconds, LastAccessedTimePropertye DialogStateProperty campos à classe bot e inicialize-os no construtor do bot. Adicione também um IConfiguration parâmetro ao construtor com o qual recuperar o ExpireAfterSeconds valor.

Em vez de criar o acessador da propriedade de estado da caixa de diálogo embutido no método, você está criando e gravando-o no momento da OnMessageActivityAsync inicialização. O bot precisará do acessador da propriedade state não apenas para executar a caixa de diálogo, mas também para limpar o estado da caixa de diálogo.

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

Finalmente, adicione código ao método do OnTurnAsync bot para limpar o estado da caixa de diálogo se a conversa for muito antiga.

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

Expiração do armazenamento

O Cosmos DB fornece um recurso de tempo de vida (TTL) que permite excluir itens automaticamente de um contêiner após um determinado período de tempo. Isso pode ser configurado de dentro do portal do Azure ou durante a criação de contêiner (usando os SDKs do Cosmos DB específicos do idioma).

O SDK do Bot Framework não expõe uma definição de configuração TTL. No entanto, a inicialização do contêiner pode ser substituída e o SDK do Cosmos DB pode ser usado para configurar o TTL antes da inicialização do armazenamento do Bot Framework.

Comece com uma nova cópia do exemplo de prompt de várias voltas e adicione o Microsoft.Bot.Builder.Azure pacote NuGet ao projeto.

appsettings.json

Atualize appsettings.json para incluir opções de armazenamento do 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>"
}

Observe os dois ContainerIds, um para UserState e outro para ConversationState. O TTL padrão é definido no ConversationState contêiner, mas não no UserState.

CosmosDbStorageInitializerHostedService.cs

Em seguida, crie uma CosmosDbStorageInitializerHostedService classe, que criará o contêiner com o Time To Live configurado.

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

Por fim, atualize Startup.cs para usar o inicializador de armazenamento e o Cosmos DB para o estado:

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

O Cosmos DB agora excluirá automaticamente os registros de estado da conversa após 30 segundos de inatividade.

Para obter mais informações, consulte Configurar o tempo de vida no Azure Cosmos DB

Para testar o bot

  1. Se você ainda não fez isso, instale o Bot Framework Emulator.
  2. Execute a amostra localmente na sua máquina.
  3. Inicie o emulador, conecte-se ao seu bot e envie uma mensagem para ele.
  4. Após um dos prompts, aguarde 30 segundos antes de responder.