使对话失效

适用于:SDK v4

机器人有时需要从头开始重新聊天。 例如,如果用户在一段时间后仍未响应。 本文介绍使聊天过期的两种方法:

  • 跟踪上次从用户收到消息的时间,如果该时间大于预配置的时长,则在收到用户的下一条消息时清除状态。 有关详细信息,请参阅用户交互过期部分。
  • 使用存储层功能(例如 Cosmos DB Time To Live (TTL)),在预先配置的一段时间后自动清除状态。 有关详细信息,请参阅存储过期部分。

重要

Bot Framework SDK 和 Bot Framework Emulator 已在 GitHub 上存档。 项目不再更新或维护。 自 2025 年 12 月 31 日起,Bot Framework SDK 的支持票证将不再提供服务。

若要使用所选的 AI 服务、业务流程和知识生成代理,请考虑使用 Microsoft 365 代理 SDK。 代理 SDK 对 C#、JavaScript 或 Python 具有语言支持。 可以在 aka.ms/agents 了解有关代理 SDK 的详细信息。 如果现有的机器人是使用 Bot Framework SDK 生成的,则可以将机器人更新到代理 SDK。 查看 Bot Framework SDK 到代理 SDK 迁移指南的核心更改和更新。

如果要构建设计为在 Microsoft Teams 中工作的协作代理,请考虑使用 Teams SDK。 它为在 Teams 环境中运行的代理提供 Teams 特定的 API、自适应卡支持和内置 AI 协同调度功能。 可以在 Teams SDK(Teams AI 库)中了解详细信息。

如果要查找基于 SaaS 的代理平台,请考虑 Microsoft Copilot Studio

先决条件

关于此示例

本文中的示例代码首先演示多轮次机器人的结构,然后通过添加更多的代码(在后面的部分中会提供)来扩展该机器人的功能。 此扩展代码演示如何在特定的时间段过后清除聊天状态。

用户交互到期

要实现这种类型的聊天过期,可以将上次访问时间属性添加到机器人的聊天状态。 然后可以在处理活动之前,将此属性值与活动处理程序中的当前时间进行比较。

注意

此示例使用 30 秒超时来轻松测试此模式。

appsettings.json

首先将 ExpireAfterSeconds 设置添加到 appsettings.json:

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

Bots\DialogBot.cs

接下来,将 ExpireAfterSecondsLastAccessedTimePropertyDialogStateProperty 字段添加到 bot 类,并在机器人的构造函数中初始化这些字段。 另外,将 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 门户中或在创建容器时(使用特定语言的 Cosmos DB SDK)进行配置。

Bot Framework SDK 不公开 TTL 配置设置。 但可以替代容器初始化,并可以使用 Cosmos DB SDK 在 Bot Framework 存储初始化之前配置 TTL。

先从 多轮提示 示例的一个全新副本开始,并将 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>"
}

请注意两个 ContainerId,一个用于 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...

现在,在处于非活动状态 30 秒后,Cosmos DB 将自动删除聊天状态记录。

有关详细信息,请参阅在 Azure Cosmos DB 中配置生存时间

为了测试机器人程序

  1. 安装 Bot Framework Emulator(如果尚未安装)。

  2. 在计算机本地运行示例。

  3. 启动 Emulator,连接到机器人并向其发送消息。

  4. 出现一条提示后,等待 30 秒后再做出响应。