在MS Bot Framework中,当用户输入“exit”,“quit”等内容时,终止所有对话并退出对话。

29

我不知道如何在MS Bot Framework中完成一件非常简单的事情:允许用户通过键入“quit”、“exit”或“start over”来退出任何对话,离开当前的对话框并返回主菜单。

这是我的主要对话设置方式:

    public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
    {
        try
        {
            if (activity.Type == ActivityTypes.Message)
            {
                UserActivityLogger.LogUserBehaviour(activity);

                if (activity.Text.ToLower() == "start over")
                {
                    //Do something here, but I don't have the IDialogContext here!
                }
                BotUtils.SendTyping(activity); //send "typing" indicator upon each message received
                await Conversation.SendAsync(activity, () => new RootDialog());
            }
            else
            {
                HandleSystemMessage(activity);
            }
        }

我知道如何使用context.Done<DialogType>(this);来终止对话,但在这个方法中,我无法访问IDialogContext对象,因此无法调用.Done()
除了在所有对话的每个步骤中添加检查之外,是否有其他方法可以在用户输入特定消息时终止整个对话堆栈?
我需要一种方法来终止所有IDialog,而不使用我发布的荒谬方法(它会删除所有用户数据,而我需要这些数据,例如用户设置和首选项)。
基本上,当用户键入“退出”或“退出”时,我需要退出当前正在进行的任何IDialog并返回到初始状态,就像用户刚刚启动对话一样。
我需要能够从执行此操作,但我仍然无法访问。我唯一有用的数据似乎是对象。如果有人指出其他方法来做到这一点,我将感到高兴。
另一种方法是找到某种其他方式在bot的其他地方检查“退出”和“退出”关键字,而不是在Post方法中进行检查。
但它不应该是在每个IDialog的每个步骤中都进行的检查,因为那是太多的代码,甚至不总是可能的(当使用时,我无法访问用户输入的文本)。
我没有探索的两种可能的方法:
1.不是终止所有当前的IDialog,而是开始一个新的与用户的对话(新的)
2.获取对象并对其进行某些操作以管理对话堆栈。
Microsoft文档对此对象保持沉默,因此我不知道如何获取它。我在bot中没有使用允许.Switch()的对象,但如果您认为可以重写它来使用它,则可以成为解决此问题的方法之一。然而,我还没有找到如何在各种类型的对话框(和普通的)之间进行分支,这些对话框又调用自己的子对话框等。
5个回答

29

问题细分

从我对您的问题的理解,您想实现的是在不完全销毁机器人状态的情况下重置对话堆栈


事实(来自我从Github存储库中读取的内容)

  1. 框架如何保存对话堆栈:

BotDataStore > BotData > DialogStack

  1. BotFramework使用AutoFac作为DI容器
  2. DialogModule是他们的Autofac模块,用于Dialog组件

如何做

根据上述事实,我的解决方案是

  1. 注册依赖项以便我们可以在控制器中使用:

// in Global.asax.cs
var builder = new ContainerBuilder();
builder.RegisterModule(new DialogModule());
builder.RegisterModule(new ReflectionSurrogateModule());
builder.RegisterModule(new DialogModule_MakeRoot());

var config = GlobalConfiguration.Configuration;
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
builder.RegisterWebApiFilterProvider(config);
var container = builder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
  1. 获取Autofac容器(可以放置在您觉得舒适的任何代码位置)

private static ILifetimeScope Container
{
    get
    {
        var config = GlobalConfiguration.Configuration;
        var resolver = (AutofacWebApiDependencyResolver)config.DependencyResolver;
        return resolver.Container;
    }
}
  1. scope 中加载 BotData
  2. 加载 DialogStack
  3. 重置 DialogStack
  4. 将新的 BotData 推回到 BotDataStore
using (var scope = DialogModule.BeginLifetimeScope(Container, activity))
{
    var botData = scope.Resolve<IBotData>();
    await botData.LoadAsync(default(CancellationToken));
    var stack = scope.Resolve<IDialogStack>();
    stack.Reset();
    await botData.FlushAsync(default(CancellationToken));
}

希望能对您有所帮助。


更新1 (27/08/2016)

感谢 @ejadib 指出,Container 已经在 conversation 类中被公开。

我们可以删除上面回答中的步骤2,在最终代码中将如下所示

using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, activity))
{
    var botData = scope.Resolve<IBotData>();
    await botData.LoadAsync(default(CancellationToken));
    var stack = scope.Resolve<IDialogStack>();
    stack.Reset();
    await botData.FlushAsync(default(CancellationToken));
}

1
这里只是小问题:您不需要进行所有的注册。容器已经在Conversation类中公开,所以您可以直接使用 =>using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, activity)) { ... } - Ezequiel Jadib
@ejadib:感谢您指出,我会更新答案。 - Kien Chu

9

这里有一个非常丑陋的方法可以解决问题。它基本上会删除所有用户数据(这可能是你实际需要的),这将导致对话重新开始。

如果有更好的方法,而不需要删除用户数据,请务必分享。

    public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
    {
        try
        {
            if (activity.Type == ActivityTypes.Message)
            {
                //if the user types certain messages, quit all dialogs and start over
                string msg = activity.Text.ToLower().Trim();
                if (msg == "start over" || msg == "exit" || msg == "quit" || msg == "done" || msg =="start again" || msg == "restart" || msg == "leave" || msg == "reset")
                {
                    //This is where the conversation gets reset!
                    activity.GetStateClient().BotState.DeleteStateForUser(activity.ChannelId, activity.From.Id);
                }

                //and even if we reset everything, show the welcome message again
                BotUtils.SendTyping(activity); //send "typing" indicator upon each message received
                await Conversation.SendAsync(activity, () => new RootDialog());
            }
            else
            {
                HandleSystemMessage(activity);
            }
        }

2

我知道这篇文章有点老,但我遇到了同样的问题,而且已经发布的解决方案不再是最佳方法。

从3.8.1版本开始,您可以注册IScorable服务,以便在对话框的任何地方触发。

有一个示例代码展示了它的工作原理,并且它确实具有“取消”全局命令处理程序:

https://github.com/Microsoft/BotBuilder-Samples/tree/master/CSharp/core-GlobalMessageHandlers

代码的一部分将如下所示:

protected override async Task PostAsync(IActivity item, string state, CancellationToken token)
{
    this.task.Reset();
}

你的链接已经失效。 - Rohan Rao
我已经有一段时间没有使用Bot Framework了,所以我不能确定这是否仍然适用于2020年。无论如何,您可以在https://github.com/microsoft/BotBuilder-Samples/tree/9b4f74d3c95f68b67f77a0ca22ab2bd658dca099/CSharp/core-GlobalMessageHandlers找到我在2017年提到的代码。 - Ivo Udelsmann

0

我知道这是一个旧问题,但是我到这里来了...似乎处理中断的“推荐”方式是使用主对话框作为你的机器人的入口点,然后使该主对话框继承某种取消/帮助对话框,其OnContinueDialogAsync函数首先被调用。在那里,您可以从所提供参数中的DialogContext调用CancelAllDialosAsync。

在这里阅读更多https://learn.microsoft.com/en-us/azure/bot-service/bot-builder-howto-handle-user-interrupt?view=azure-bot-service-4.0&tabs=csharp


0

以下是其他人使用过的附加代码:

private async Task _reset(Activity activity)
    {
        await activity.GetStateClient().BotState
            .DeleteStateForUserWithHttpMessagesAsync(activity.ChannelId, activity.From.Id);

        var client = new ConnectorClient(new Uri(activity.ServiceUrl));
        var clearMsg = activity.CreateReply();
        clearMsg.Text = $"Reseting everything for conversation: {activity.Conversation.Id}";
        await client.Conversations.SendToConversationAsync(clearMsg);
    }

这段代码是由用户mmulhearn在这里发布的:https://github.com/Microsoft/BotBuilder/issues/101#issuecomment-316170517

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接