你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn。
管理状态
适用于:SDK v4
机器人中的状态遵循与新式 Web 应用程序相同的模式,Bot Framework SDK 提供一些抽象用于简化状态管理。
与 Web 应用一样,机器人在本质上也是无状态的;机器人的不同实例可以处理任意给定的聊天轮次。 某些机器人倾向于采用这种简单设计—要么不提供附加的信息就使机器人能够正常运行,要么保证在传入的消息中提供所需的信息。 对于其他一些机器人而言,必须提供状态(例如,会话中断的位置,或者以前收到的有关用户的数据)才能让机器人提供有用的聊天信息。
为何需要状态?
维护状态可以记住有关用户或聊天的某些信息,使机器人能够进行更有意义的聊天。 例如,如果你以前与某个用户交谈过,则可以事先保存有关该用户的信息,这样,下次就不再需要请求提供这些信息。 状态还能以长于当前轮次的时间保留数据,使机器人能够在整个多轮次聊天过程中保留信息。
由于它与机器人相关,使用状态有几个层:存储层、状态管理(包含在下图所示的机器人状态中)和状态属性访问器。 此图演示了这些层之间的交互序列部分,实线箭头表示方法调用,虚线箭头表示响应(包括或不包括返回值)。
以下部分解释了此图的流程,并详细描述了其中的每个层。
存储层
从后端开始,实际存储状态信息的位置是存储层。 可将存储层视为物理存储,例如内存中服务器、Azure 服务器或第三方服务器。
Bot Framework SDK 包含存储层的某些实现:
- 内存存储实现内存中存储用于测试。 内存中数据存储仅用于本地测试,因为它是易失性的临时存储。 每次重启机器人时都会清除数据。
- Azure Blob 存储连接到 Azure Blob 存储对象数据库。
- Azure Cosmos DB 分区存储 连接到分区的 Cosmos DB NoSQL 数据库。
重要
Cosmos DB 存储类已弃用。 最初使用 CosmosDbStorage 创建的容器未设置分区键,而是指定了默认分区键 "/_partitionKey"。
使用 Cosmos DB 存储创建的容器可与 Cosmos DB 分区存储一起使用。 有关详细信息,请阅读 Azure Cosmos DB 中的分区。
另请注意,与旧版 Cosmos DB 存储不同,Cosmos DB 分区存储不会自动在 Cosmos DB 帐户中创建数据库。 需要手动创建新的数据库,但请跳过手动创建容器的步骤,因为 CosmosDbPartitionedStorage 将为你创建容器。
有关如何连接到其他存储选项的说明,请参阅直接写入存储。
状态管理
状态管理可以自动在基础存储层中读取和写入机器人的状态。 状态以状态属性的形式存储。状态属性实际上是机器人可以通过状态管理对象读取和写入的键值对(不管具体的基础实现是什么)。 这些状态属性定义信息的存储方式。 例如,当你检索某个定义为特定类或对象的属性时,便知道数据的建构方式。
这些状态属性集结到有范围的“桶”中,这些桶不过是一些帮助组织这些属性的集合。 SDK 包含其中的三个“桶”:
- 用户状态
- 聊天状态
- 私人聊天状态
所有这些桶都是 bot state 类的子类,可以派生该类来定义具有不同范围的其他类型的桶。
这些预定义的桶已限定到特定的可见范围内,具体取决于桶:
- 不管聊天内容如何,机器人在该通道中与该用户展开的任何聊天轮次都会提供用户状态
- 无论用户如何(例如在组对话中)都可以在任何轮次的特定对话中使用聊天状态
- 私人聊天状态的范围限定为特定聊天和特定用户
提示
用户状态和聊天状态的范围根据通道进行限定。 使用不同的通道访问机器人的同一个人显示为不同的用户,每个通道有一个用户,并且每个用户具有不同的用户状态。
用于其中每个预定义桶的键特定于用户和/或聊天。 设置状态属性的值时,密钥在内部定义,其中包含轮次上下文中包含的信息,以确保每个用户或对话都放置在正确的存储桶和属性中。 具体而言,将按如下所述定义键:
- 用户状态使用通道 ID 和源 ID 创建键。 例如 {Activity.ChannelId}/users/{Activity.From.Id}#YourPropertyName
- 聊天状态使用通道 ID 和聊天 ID 创建键。 例如 {Activity.ChannelId}/conversations/{Activity.Conversation.Id}#YourPropertyName
- 私人聊天状态使用通道 ID、源 ID 和聊天 ID 创建键。 例如 {Activity.ChannelId}/conversations/{Activity.Conversation.Id}/users/{Activity.From.Id}#YourPropertyName
何时使用每种类型的状态
聊天状态非常适合用于跟踪聊天的上下文,例如:
- 机器人是否向用户提出了问题,问题是什么
- 聊天的当前主题或最后一个主题是什么
用户状态非常适合用于跟踪有关用户的信息,例如:
- 非关键性用户信息,例如姓名和首选项、警报设置或警报首选项
- 有关用户与机器人上次展开的聊天的信息
- 例如,产品支持机器人可以跟踪用户咨询过的产品。
私人聊天状态非常适合用于支持群组聊天的通道,但在其中需要同时跟踪用户和聊天特定的信息。 例如,如果你有一个课堂抢答机器人:
- 该机器人可以聚合并显示学生对给定问题的回答。
- 该机器人可以聚合每位学生的成绩,并在会话结束时,以私密方式将该信息中继回到相应的学生。
有关使用这些预定义桶的详细信息,请参阅状态操作方法文章。
连接到多个数据库
如果机器人需要连接到多个数据库,请为每个数据库创建一个存储层。 如果机器人收集具有不同的安全性、并发性或数据位置需求的信息,可以选择使用多个数据库。
对于每个存储层,创建支持状态属性所需的状态管理对象。
状态属性访问器
状态属性访问器用于实际读取或写入某个状态属性,并提供 get、set 和 delete 方法用于从轮次内部访问状态属性。 若要创建访问器,必须提供属性名称(通常是在初始化机器人时提供)。 然后,可以使用该访问器来获取和处理机器人状态的该属性。
访问器允许 SDK 从基础存储获取状态并更新机器人的状态缓存。 状态缓存是机器人维护的本地缓存,用于存储状态对象,并允许在不访问基础存储的情况下执行读取和写入操作。 如果状态尚未进入缓存,则调用访问器的 get 方法可以检索状态并将其放入缓存。 检索后,可以像处理本地变量一样处理状态属性。
访问器的 delete 方法会从缓存和基础存储中删除属性。
重要
首次调用访问器的 get 方法时,必须提供一个工厂方法用于创建对象(如果状态中尚不存在该对象)。 如果未提供工厂方法,则会出现异常。 在状态操作方法文章中可以找到有关如何使用工厂方法的详细信息。
若要保存对你从访问器获取的状态属性所做的任何更改,必须更新状态缓存中的属性。 为此,可以调用访问器的 set 方法,以便设置缓存中属性的值;如果以后需要在该轮次中读取或更新该属性,也可以使用此方法。 若要将该数据实际保存到基础存储(从而使该数据在当前轮次结束后可供使用),必须保存状态。
状态属性访问器方法的工作原理
访问器方法是机器人与状态交互的主要方法。 下面介绍了每个方法的工作原理以及基础层的交互方式:
- 访问器的 get 方法:
- 访问器从状态缓存请求属性。
- 如果该属性在缓存中,则返回它。 否则,从状态管理对象获取该属性。 (如果尚未处于状态,请使用访问器中提供的工厂方法 get call.)
- 访问器的 set 方法:
- 使用新属性值更新状态缓存。
- 状态管理对象的 save changes 方法:
- 检查对状态缓存中属性所做的更改。
- 将属性写入存储。
对话框中的状态
对话框库使用在机器人的会话状态上定义的对话框状态属性访问器来保留对话在会话中的位置。 "对话框状态" 属性还允许每个对话框在轮次之间存储临时信息。
自适应对话框具有更复杂的内存范围结构,使你可以更轻松地访问配置和识别结果。 对话框管理器使用用户和会话状态管理对象提供这些内存范围。
有关对话框的信息,请参阅对话框库一文。
- 有关特定于这些类型的对话框的信息,请参阅关于组件和瀑布对话框。
- 有关特定于自适应对话的信息,请参阅 自适应对话 简介和管理 自适应对话文章中的状态 。
保存状态
调用访问器的 set 方法来记录更新的状态时,该状态属性尚未保存到持久性存储,而只是保存到了机器人的状态缓存。 若要将状态缓存的任何更改保存到持久性状态,必须调用状态管理对象的 save changes 方法。可以针对上述机器人状态类的实现(例如用户状态或聊天状态)使用此方法。
为状态管理对象调用保存更改方法 ((例如上述存储桶),) 将所有属性保存在你为该存储桶设置到该存储桶的该点的状态缓存中,但对于可能处于机器人状态的任何其他存储桶而言,都不是这样。
提示
机器人状态实现“最后一次写入优先”行为,即最后一次写入会改写上一次写入的状态。 此行可能适合许多应用场合,但也会带来负面影响,尤其是横向扩展方案中需要提供某种并发度或控制延迟时。
如果某些定制中间件在轮次处理程序完成后可能需要更新状态,请考虑在中间件中处理状态。