如何解决对话框将其变量值保留在MS BotFramework v4中的新对话中
我正在使用MS BotFramework v4。有一个RootDialog
以Dialog_A
或Dialog_B
开头,具体取决于用户输入的内容。
TL; DR
如果在对话之后开始新的对话并且机器人没有重新启动,则已经为其分配了值(不是初始值)的对话框的私有变量不会重置为初始值,从而导致意外行为。如何避免这种情况?
详细
让我们假设以下情况: 这些对话框中的每一个都有一些私有变量来控制是输出长介绍消息还是简短介绍消息。长对话框仅应在第一次启动此对话框时输出。如果对话再次到达对话框,则只应打印短消息。
实现看起来像这样:
RootDialog.cs
public class RootDialog : ComponentDialog
{
private bool isLongWelcomeText = true;
// Some more private variables follow here
public RootDialog() : base("rootId") {
AddDialog(new WaterfallDialog(nameof(WaterfallDialog),new WaterfallStep[] {
WelcomeStep,DoSomethingStep,FinalStep
});
}
private async Task<DialogTurnContext> WelcomeStep(WaterfallStepContext ctx,CancellationToken token) {
if(isLongWelcomeText) {
await ctx.SendActivityAsync(MessageFactory.Text("A welcome message and some detailed bla bla about the bot"));
isLongWelcomeText = false;
} else {
await ctx.SendActivityAsync(MessageFactory.Text("A short message that hte bot is waiting for input"));
}
}
private async Task<DialogTurnContext> DoSomethingStep(WaterfallStepContext ctx,CancellationToken token) {
// call Dialog_A or Dialog_B depending on the users input
// Dialog X starts
await ctx.BeginDialogAsync("Dialog_X",null,token);
}
private async Task<DialogTurnContext> FinalStep(WaterfallStepContext ctx,CancellationToken token) {
// After dialog X has ended,RootDialog continues here and simply ends
await ctx.EndDialogAsync(null,token);
}
}
Dialog_A
和Dialog_B
的结构相同。
问题
如果漫游器处理了第一次对话,则一切都会按预期进行(将长欢迎文本显示给用户,并且isLongWelcomeText
中的false
设置为WelcomeStep
。新的对话(新的对话ID和用户ID)isLongWelcomeText
仍设置为false
,这会导致漫游器将新对话中的简短欢迎文本输出给新用户。
在BotFramework v3中,对话框与所有变量值一起被序列化和反序列化。
如果我对BF v4的要求正确,对话框将不再序列化。
问题
如何解决?有更好的方法吗?
备注
我正在使用UserState
和ConversationState
,它们已序列化并在新对话中重置。但是我不想在状态下存储每个对话框的每个私有变量值。这不是要走的路。
感谢advace
解决方法
通常,您应该将其视为将实例成员变量放入对话框类的错误。在某些情况下它可能会起作用,但是这些情况不会涉及尝试在转弯之间保持某种状态。使用您的bot类的任何类型的内存变量来保持转弯之间的状态存在三个主要问题:
- 范围不正确。这是您已经注意到的问题。您已经明确地将
isLongWelcomeText
定义为特定于用户和/或对话的内容,但是由于它是在您自己的机器人内存中用于处理每个用户的每次对话,因此它无法区分不同的对话和用户。 - 它无法正确扩展。这意味着,即使您的机器人只是在一次对话中与一个用户交谈,如果该机器人部署在某些托管服务(如Azure)中,也可以横向扩展,然后将多个您的机器人实例可能正在运行。机器人的不同实例将具有不同的内存,因此,如果您要正确地设计机器人,则需要采取行动,好像每个回合都将由完全不同的机器人实例(也许是在完全不同的服务器上)进行处理。一个实例无法访问另一实例的内存。
- 应用重新启动时,它将会丢失。即使您只有一个用户,一个对话,和一个机器人实例,您仍然希望能够停止您的漫游器,然后在不破坏对话的情况下再次启动它。如果您正在使用漫游器的内存,则无法这样做。
后两个问题甚至在您使用MemoryStorage
时也适用,而不仅仅是在使用内存中变量时。您可能已经猜到解决方案是使用漫游器状态(并且在部署漫游器时将漫游器状态连接到MemoryStorage
以外的其他存储类)。
您是正确的,在v3中,对话框类的整个实例对象将被序列化为持久状态。这带来了自己的问题,并且并不总是合乎逻辑,因此在v4中,要序列化的对象是DialogInstance
对象。 (了解有关对话框实例here的信息)。您想让对话框保持跟踪的所有内容,都应放入关联的对话框实例的状态对象,而查看如何执行此操作的示例的最佳位置是SDK源代码本身。例如,您可以看到waterfall dialog如何跟踪诸如其自定义值之类的内容以及其执行的步骤:
// Update persisted step index var state = dc.ActiveDialog.State; state[StepIndex] = index;
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。