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

中间件

适用于:SDK v4

中间件只是一个位于适配器和机器人逻辑之间的类,它是在初始化期间添加到适配器的中间件集合的。 SDK 可让用户自行编写中间件或添加其他人创建的中间件。 进出机器人的每个活动都流经中间件。

适配器处理传入的活动并通过机器人中间件管道将其定向到机器人的逻辑,然后再返回。 当每个活动流入和流出机器人时,每个中间件都可以在机器人逻辑运行前后对其进行检查或执行操作。

在跳转到中间件之前,请务必 了解机器人的一般 情况及其 处理活动的方式

中间件用法

问题经常出现:“何时应将操作作为中间件实现,而不是使用普通机器人逻辑?中间件提供额外的机会来与用户的对话流交互之前和之后,处理每个 会话轮次 。 中间件还允许你存储和检索有关聊天的信息,并在需要时调用其他处理逻辑。 下面是一些常见的场景,展示了中间件可能有用的地方。

观察或操作每个活动

很多情况下,需要机器人对每个活动或某种类型的每个活动执行某些操作。 例如,如果机器人未生成此轮次的响应,则可能需要记录机器人接收的每个消息活动或提供回退响应。 中间件是此类进程的绝佳位置,它能够在执行其他机器人逻辑之前和之后执行操作。

修改或增强回合上下文

如果机器人知道的信息比活动中提供的多,这会让某些聊天将更富成效。 在这种情况下,中间件可以查看截至目前的聊天状态信息、查询外部数据源,还可在将执行传递给机器人逻辑之前将其附加到轮次上下文对象。

SDK 定义了可记录传入和传出活动的日志记录中间件,但你也可以定义自己的中间件。

机器人中间件管道

对于每个活动,适配器都按照其添加顺序调用中间件。 适配器传入回合的上下文对象和下一个委托,而中间件会调用该委托,将控制权传递给管道中的下一个中间件 。 在完成方法之前,中间件仍有机会在下一个委托返回后执行操作 。 你可以将其视为每个中间件对象始终都有机会对管道中的下一个中间件对象执行操作。

例如:

  • 第一个中间件对象的轮次处理程序在调用 下一步之前执行代码。
    • 第二个中间件对象的轮次处理程序在调用 下一步之前执行代码。
      • 机器人的轮次处理程序执行操作并返回结果。
    • 第二个中间件对象的轮次处理程序在返回之前执行任何剩余的代码。
  • 第一个中间件对象的轮次处理程序在返回之前执行任何剩余的代码。

如果中间件不调用下一个委托,适配器不会调用任何后续中间件或机器人轮次处理程序,以及管道短路。

机器人中间件管道完成后,回合结束,且回合上下文超出范围。

中间件或机器人可以生成响应并注册响应事件处理程序,但请记住,响应是在单独的进程中处理的。

中间件的顺序

由于添加中间件的顺序决定了中间件处理活动的顺序,因此有必要确定添加中间件的顺序。

注意

这意味着为你提供一种适用于大多数机器人的通用模式,但一定要根据自己的情况考虑每个中间件将如何与其他中间件进行交互。

首先应将处理每个机器人的最低级别任务的中间件添加到中间件管道。 相关示例包括日志记录、异常处理和转换。 根据需求对这些消息进行排序,例如,是否希望先转换传入消息,然后再存储消息,或者消息存储应先进行,这可能意味着不会转换存储的消息。

机器人特定的中间件应最后添加到中间件管道,你实现的中间件会在发送到机器人的每个消息上执行一些处理。 如果中间件使用状态信息或机器人上下文中设置的其他信息,请将其添加到中间件管道中用于修改状态或上下文的中间件的后面。

短路

有关中间件和响应处理程序的一个重要概念是“短路”。 如果要通过后续的层继续执行,需使用中间件(或响应处理程序)调用该执行的下一个委托,将执行传递下去。 如果未在该中间件中调用下一个委托 (或响应处理程序) ,则不会执行关联的管道短路和后续层。 这意味着将跳过所有机器人逻辑以及管道中后面的所有中间件。 中间件与响应处理程序短路轮次之间存在细微差异。

当中间件短路轮次时,不会调用机器人轮次处理程序,但在管道中此点之前执行的所有中间件代码仍将运行到完成。

对于事件处理程序,不调用 下一 步意味着取消事件,这与跳过逻辑中间件大不相同。 如果不处理事件的其余部分,则适配器永远不会发送该事件。

提示

如果响应事件(如 SendActivities)短路,请确保这是意料中的行为。 否则,它可能会导致难以修复 bug。

响应事件处理程序

除了应用程序和中间件逻辑以外,还可将响应处理程序(有时也称为事件处理程序或活动事件处理程序)添加到上下文对象中。 在执行实际响应之前,当前上下文对象上出现相关响应时,将调用这些处理程序。 当知道要在实际事件之前或之后对其余当前响应的该类型的所有活动执行某些操作时,这些处理程序非常有用。

警告

注意,请勿从它的相应响应事件处理程序中调用活动响应方法,例如,从发送活动处理程序中调用发送活动方法。 执行此操作可以生成一个无限循环。

请记住,每个新活动都会获得一个要执行的新线程。 创建处理活动的线程后,该活动的处理程序列表将复制到该新线程。 不会针对该特定活动事件执行在此之后添加的任何处理程序。 在上下文对象上注册的处理程序处理方式与适配器管理中间件管道的方式类似。 也就是说,处理程序按照它们添加的顺序进行调用,并且调用下一个委托将控制权传递给下一个已注册的事件处理程序。 如果处理程序不调用下一个委托,则不会调用任何后续事件处理程序、事件短路和适配器不会将响应发送到通道。

处理中间件中的状态

保存状态的一种常用方法是在轮次处理程序的末尾调用 save changes 方法。 下面是一个关系图,重点介绍通话。

机器人轮次的序列图,其中状态已从机器人的轮次处理程序保存。

此方法的问题是,从某些自定义中间件进行的任何状态更新在机器人轮次处理程序返回后发生的任何状态更新都不会保存到持久存储。 解决方法是通过将“自动保存更改”中间件的实例添加到中间件堆栈的开头,或至少添加到可能更新状态的任一中间件之前,在自定义中间件完成后,将调用转移到 save changes 方法。 执行情况如下所示。

机器人轮次的序列图,状态已从中间件保存。

添加需要更新机器人状态集对象的状态管理对象,然后在创建“自动保存更改”中间件时使用这些对象。

其他资源

可以查看一下用 Bot Framework SDK [C# | JS] 实现的脚本记录器中间件。