你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

向用户发送主动通知

适用于:SDK v4

通常,机器人会直接向用户发送消息,以响应从用户接收的消息。 有时,机器人可能需要发送主动消息,即,为了响应不是来自用户的激发性指令而发送的消息。

主动消息在各种场景中都可以发挥作用。 例如,如果用户之前已经请求机器人监控产品的价格,则机器人可以在产品价格下降了 20% 时提醒用户。 或者,如果机器人需要一些时间来编译对用户问题的响应,则可以通知用户延迟并允许会话在此期间继续。 当机器人编译完对问题的响应时,将与用户共享该信息。

本文将介绍有关机器人主动消息的一般信息。 有关 Microsoft Teams 中的主动消息的信息,请参阅

注意

Bot Framework JavaScript、C# 和 Python SDK 将继续受支持,但 Java SDK 即将停用,最终长期支持将于 2023 年 11 月结束。

使用 Java SDK 构建的现有机器人将继续正常运行。

要生成新的机器人,请考虑使用 Microsoft Copilot Studio 并阅读选择正确的助理解决方案

有关详细信息,请参阅机器人构建的未来

先决条件

关于主动示例

一般而言,作为应用程序的机器人有几个层:

  • 可以接受 HTTP 请求并专门支持消息传递终结点的 Web 应用程序。
  • 用于处理与通道的连接的适配器。
  • 轮次处理程序,通常封装在处理机器人应用的聊天推理的 bot 类中。

为了响应来自用户的传入消息,应用将调用适配器的 process activity 方法,该方法创建轮次和轮次上下文,调用其中间件管道,然后调用机器人的轮次处理程序。

若要发起主动消息,机器人应用程序需要能够接收其他输入。 用于发起主动消息的应用程序逻辑不在 SDK 的范围内。 对于此示例,除了使用标准的消息终结点外,还使用了一个通知终结点来触发主动轮次。

为了响应此通知终结点上的 GET 请求,应用将调用适配器的 continue conversation 方法,该方法的行为类似于 process activity 方法。 continue conversation 方法:

  • 将用户的相应聊天引用和回调方法用于主动轮次。
  • 为主动轮次创建事件活动和轮次上下文。
  • 调用适配器的中间件管道。
  • 调用提供的回调方法。
  • 轮次上下文使用聊天引用向用户发送任何消息。

该示例包含一个机器人、一个消息终结点和一个用于向用户发送主动消息的额外终结点,如下图所示。

显示机器人如何获取对话引用并将其用于发送主动消息的交互图。

检索和存储对话引用

当 Bot Framework Emulator 连接到机器人时,机器人会收到两个对话更新活动。 在机器人的聊天更新活动处理程序中,会检索聊天引用并将其存储在字典中,如下所示。

Bots\ProactiveBot.cs

private void AddConversationReference(Activity activity)
{
    var conversationReference = activity.GetConversationReference();
    _conversationReferences.AddOrUpdate(conversationReference.User.Id, conversationReference, (key, newValue) => conversationReference);
}

protected override Task OnConversationUpdateActivityAsync(ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
{
    AddConversationReference(turnContext.Activity as Activity);

    return base.OnConversationUpdateActivityAsync(turnContext, cancellationToken);
}

聊天引用包含一个 conversation 属性,用于描述其中存在活动的聊天。 聊天包含一个 user 属性,用于列出参与聊天的用户;它还包含一个 service URL 属性,用于指示对当前活动的回复可发送到的位置。 若要向用户发送主动消息,需要有效的聊天引用。 (对于 Teams 通道,服务 URL 将映射到区域化服务器。)

注意

在现实场景中,你会将聊天引用持久保存在数据库中,而不是使用内存中的对象。

发送主动消息

第二个控制器为通知控制器,负责向用户发送主动消息。 它使用以下步骤生成主动消息。

  1. 检索要向其发送主动消息的聊天的引用。
  2. 调用适配器的 continue conversation 方法,提供要使用的聊天引用和轮次处理程序委托。 (continue conversation 方法会为引用的聊天生成一个轮次上下文,然后调用指定的轮次处理程序委托。)
  3. 在委托中,使用轮次上下文来发送主动消息。 在此处,委托是在通知控制器上定义的,它将主动消息发送给用户。

注意

虽然每个通道都应使用稳定的服务 URL,但该 URL 随时可能会更改。 有关服务 URL 的详细信息,请参阅“Bot Framework 活动架构”的基本活动结构服务 URL 部分。

如果服务 URL 发生更改,以前的聊天引用将不再有效,并且调用 continue conversation 会生成错误或异常。 在这种情况下,机器人需要获取用户的新聊天引用,然后才能再次发送主动消息。

Controllers\NotifyController .cs

每次请求机器人的 notify 页时,notify 控制器都会从字典中检索聊天引用。 该控制器然后使用 ContinueConversationAsyncBotCallback 方法来发送主动消息。

[Route("api/notify")]
[ApiController]
public class NotifyController : ControllerBase
{
    private readonly IBotFrameworkHttpAdapter _adapter;
    private readonly string _appId;
    private readonly ConcurrentDictionary<string, ConversationReference> _conversationReferences;

    public NotifyController(IBotFrameworkHttpAdapter adapter, IConfiguration configuration, ConcurrentDictionary<string, ConversationReference> conversationReferences)
    {
        _adapter = adapter;
        _conversationReferences = conversationReferences;
        _appId = configuration["MicrosoftAppId"] ?? string.Empty;
    }

    public async Task<IActionResult> Get()
    {
        foreach (var conversationReference in _conversationReferences.Values)
        {
            await ((BotAdapter)_adapter).ContinueConversationAsync(_appId, conversationReference, BotCallback, default(CancellationToken));
        }
        
        // Let the caller know proactive messages have been sent
        return new ContentResult()
        {
            Content = "<html><body><h1>Proactive messages have been sent.</h1></body></html>",
            ContentType = "text/html",
            StatusCode = (int)HttpStatusCode.OK,
        };
    }

    private async Task BotCallback(ITurnContext turnContext, CancellationToken cancellationToken)
    {
        await turnContext.SendActivityAsync("proactive hello");
    }
}

适配器要求提供机器人的应用 ID 才能发送主动消息。 在生产环境中,可以使用机器人的应用 ID。 若要使用 Emulator 在本地测试机器人,可以使用空字符串 ("")。

测试机器人

  1. 安装 Bot Framework Emulator(如果尚未安装)。
  2. 在计算机本地运行示例。
  3. 启动 Emulator 并连接到机器人。
  4. 加载到机器人的 api/notify 页。 这会在 Emulator 中生成主动消息。

其他信息

要求

在发送主动消息之前,机器人需要一个聊天引用。 机器人可以在它从用户那里收到的任何活动中检索聊天引用,但这通常需要用户至少与机器人交互一次,然后机器人才能发送主动消息。

许多通道禁止机器人向用户发送消息,除非用户至少向机器人发送过消息一次。 某些通道允许例外情况。 例如,Teams 通道允许机器人向已建立的包含该机器人的群组聊天中的个人发送主动(或一对一)消息。

设计注意事项

在机器人中实现主动消息时,不要在短时间内发送多条主动消息。 某些通道强制限制机器人向用户发送消息的频率,并且如果违反这些限制,将禁用机器人。

对于最简单的主动消息类型,机器人在触发时将消息插进对话中,而不考虑对话的当前状态或主题。 在此方案中,主动消息会中断对话的常规流。

若要更顺畅地处理通知,请考虑将通知集成到聊天流中的其他方法,例如在聊天状态中设置标志或将通知添加到队列。

关于主动轮次

continue conversation 方法使用聊天引用和轮次回调处理程序来执行以下操作:

  1. 创建一个轮次,机器人应用程序可在其中发送主动消息。 适配器为此轮次创建一个 event 活动,其名称设置为“ContinueConversation”。
  2. 通过适配器的中间件管道发送轮次。
  3. 调用轮次回调处理程序以执行自定义逻辑。

在“主动消息”示例中,轮次回调处理程序是在通知控制器中定义的,它将消息直接发送到聊天,而不通过机器人的正常轮次处理程序发送主动活动。 示例代码也不会访问或更新机器人在主动轮次中的状态。

许多机器人是有状态的,并使用状态来管理多个轮次的聊天。 当 continue conversation 方法创建轮次上下文时,该轮次将有关联的正确用户和聊天状态,你可以将主动轮次集成到机器人的逻辑中。 如果需要机器人逻辑感知主动消息,可以通过几种方法来实现此目的。 方法:

  • 提供机器人的轮次处理程序作为轮次回调处理程序。 然后机器人将接收“ContinueConversation”事件活动。
  • 首先使用轮次回调处理程序将信息添加到轮次上下文,然后调用机器人的轮次处理程序。

对于这两种情况,都需要设计机器人逻辑来处理主动事件。

后续步骤