如何在Bot框架下从RootDialog转发到LuisDialog

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (100)

我在为FAQ创造一个机器人。当BOT开始对话发送一个PromptDialog与两个选项:英语,法语。

当用户选择“英语”按钮时,我想将对话框转发给“English”Luis,在选择“法语”时将其转发给“法语”。

这是我的代码:

Rootdialog.cs

public class RootDialog : IDialog<object>
{
    private const string EnglishMenu = "English";
    private const string FrenchMenu = "French";
    private const string QAMenu = "Q&A";

    private List<string> mainMenuList = new List<string>() { EnglishMenu, FrenchMenu, QAMenu };
    private string location;

    public async Task StartAsync(IDialogContext context)
    {
        await context.PostAsync("Welcome to Root Dialog");
        context.Wait(MessageReceiveAsync);
    }

    private async Task MessageReceiveAsync(IDialogContext context, IAwaitable<IMessageActivity> result)
    {
        var reply = await result;
        if (reply.Text.ToLower().Contains("help"))
        {
            await context.PostAsync("You can implement help menu here");
        }
        else
        {
            await ShowMainmenu(context);
        }
    }

    private async Task ShowMainmenu(IDialogContext context)
    {
        //Show menues
        PromptDialog.Choice(context, this.CallDialog, this.mainMenuList, "What do you want to do?");
    }

    private async Task CallDialog(IDialogContext context, IAwaitable<string> result)
    {
        //This method is resume after user choise menu
       // this.luisResult = result;
       // var message = await result;
        var selectedMenu = await result;
        var message = await result;
        switch (selectedMenu)
        {
            case EnglishMenu:
                //Call child dialog without data
               //  context.Call(new EnglishLuis(),ResumeAfterDialog);
                //  context.Call(new EnglishLuis(), ResumeAfterDialog);

               await Conversation.SendAsync(context.MakeMessage(), () => new EnglishLuis());
                break;
            case FrenchMenu:
                //Call child dialog with data
                context.Call(new HotelDialog(location), ResumeAfterDialog);
                break;
            case QAMenu:
                context.Call(new LuisCallDialog(),ResumeAfterDialog);
                break;
        }

    }



    private async Task ResumeAfterDialog(IDialogContext context, IAwaitable<object> result)
    {
        //Resume this method after child Dialog is done.
        var test = await result;
        if (test != null)
        {
            location = test.ToString();
        }
        else
        {
            location = null;
        }
        await this.ShowMainmenu(context);
    }
}

}

英国人:

 public class EnglishLuis : LuisDialog<object>
{
    private string location;



    //   string message = $"welcome to english dialog";

    public async Task None(IDialogContext context, LuisResult result)
    {
        string message = $"Sorry, I did not understand '{result.Query}'. Please try again";

        await context.PostAsync(message);

        context.Wait(this.MessageReceived);
        context.Done(true);
    }


    [LuisIntent("gretting")]
    [LuisIntent("intentfr")]
    public async Task Greeting(IDialogContext context, IAwaitable<IMessageActivity> activity, LuisResult result)
    {


        await context.PostAsync("Welcome :) ");


        context.Wait(MessageReceived);
        context.Done(true);
    }



    [LuisIntent("test")]
    public async Task test(IDialogContext context, IAwaitable<IMessageActivity> activity, LuisResult result)
    {
        await context.PostAsync("Do you want to test our bot ? We suggest to type : hi or who are you, help etc..");
        // context.Done(true);
        context.Wait(MessageReceived);
        context.Done(true);
    }

我的问题是,当我选择英语(甚至法语)时,我得到了这个错误:这里有一个错误,将这条消息发送给您的bot:http状态代码InternalServerError

你能帮我启动路易斯对话吗?如果我直接从MessagesController.cs开始,我的意图是让人们在两种语言之间做出选择。

我试着调用Luis:Waitingcontext.Forward(新英语Luis(),ResumeAfterDialog,Message,CancementToken.None);但没有结果。

新文件RootDialog.cs(更新):

   using System;
   using System.Collections.Generic;
   using System.Threading.Tasks;
    using Microsoft.Bot.Builder.Dialogs;
   using Microsoft.Bot.Connector;
    using System.Threading;

   namespace TeamsBot.Dialogs
    {
[Serializable]
public class RootDialog : IDialog<object>
{
    private const string EnglishMenu = "English";
    private const string FrenchMenu = "French";
    private const string QAMenu = "Q&A";

    private List<string> mainMenuList = new List<string>() { EnglishMenu, 
        FrenchMenu, QAMenu };
    private string location;

    private string originalMessage;

    public async Task StartAsync(IDialogContext context)
    {
        await context.PostAsync("Welcome to Root Dialog");
        context.Wait(MessageReceiveAsync);
    }

    private async Task MessageReceiveAsync(IDialogContext context, 
     IAwaitable<IMessageActivity> result)
    {
        var reply = await result;

        this.originalMessage = reply.Text;




        if (reply.Text.ToLower().Contains("help"))
        {
            await context.PostAsync("You can implement help menu here");
        }
        else
        {
            await ShowMainmenu(context);
        }
    }

    private async Task ShowMainmenu(IDialogContext context)
    {
        //Show menues
        PromptDialog.Choice(context, this.CallDialog, this.mainMenuList, 
   "What do you want to do?");
    }

    private async Task CallDialog(IDialogContext context, IAwaitable<string> 
    result)
    {

        var selectedMenu = await result;
        switch (selectedMenu)
        {
            case EnglishMenu:
                //Call child dialog without data
                var newMessage = context.MakeMessage();
                newMessage.Text = reply.Text; 
                 await context.Forward(new EnglishLuis(), ResumeAfterDialog, newMessage, CancellationToken.None);
                break;
            case FrenchMenu:
                //Call child dialog with data
                //   context.Call(new HotelDialog(location), ResumeAfterDialog);

                var frenchLuis = new FrenchLuis();
                var messageToForward = await result;
             //   await context.Forward(new FrenchLuis(), ResumeAfterDialog, messageToForward, CancellationToken.None);
                break;
            case QAMenu:
                context.Call(new LuisCallDialog(),ResumeAfterDialog);
                break;
        }

    }



    private async Task ResumeAfterDialog(IDialogContext context, IAwaitable<object> result)
    {
        //Resume this method after child Dialog is done.
        var test = await result;
        if (test != null)
        {
            location = test.ToString();
        }
        else
        {
            location = null;
        }
        await this.ShowMainmenu(context);
    }
}

}

提问于
用户回答回答于

首先,做

context.Wait(this.MessageReceived);
context.Done(true);

这是不对的。需要选择:或者在EnglishDialog否则你就结束了EnglishDialog(与Done)

然后,将尝试在context.Forward你需要转发一个IMessageActivity。我怀疑希望发送原始消息,因此在继续提示之前,需要将其保存在全局变量中。试着:

var newMessage = context.MakeMessage();
newMessage.Text = this.originalMessageText //the variable that contains the text of the original message that you will have to save at MessageReceiveAsync
await context.Forward(new EnglishLuis(), ResumeAfterDialog, newMessage, CancellationToken.None);


MessageReceivedAsync in RootDialog should looks like:

 private async Task MessageReceiveAsync(IDialogContext context, IAwaitable<IMessageActivity> result)
    {
        var reply = await result;
        if (reply.Text.ToLower().Contains("help"))
        {
            await context.PostAsync("You can implement help menu here");
        }
        else
        {
            this.originalMessage = reply.Text;
            await ShowMainmenu(context);
        }
    }
用户回答回答于

这就是我如何实现你的方法来调用不同的对话框,我倾向于使用依赖注入的对话框,所以我不需要不断更新它们。

private async Task CallDialog(IDialogContext context, IAwaitable<string> result)
{
    //These two variables will be exactly the same, you only need one     
    //var selectedMenu = await result;
    var message = await result;
    switch (selectedMenu)
    {
        case EnglishMenu:
            // Forward the context to the new LuisDialog to bring it to the top of the stack.  
            // This will also send your message to it so it gets processed there.
            await context.Forward<object>(new EnglishLuis(), ResumeAfterDialog, message , CancellationToken.None);
            break;
        case FrenchMenu:
             await context.Forward<object>(new HotelDialog(location), ResumeAfterDialog, message , CancellationToken.None);
            break;
        case QAMenu:
             await context.Forward<object>(new LuisCallDialog(), ResumeAfterDialog, message , CancellationToken.None);
            context.Call(new LuisCallDialog(),ResumeAfterDialog);
            break;
    }

}
context.Wait(this.MessageReceived);
context.Done(true);

问题是,这两行在经过对话框时都会执行。context.Done将导致此对话框离开堆栈,因此最终会转到前面的对话框,这与试图等待响应的事实相冲突。

除非你想回到以前的对话框中,否则不应该有一个Conext.Done在你的Luis对话框中。所以如果你选择使用context.Done或者把它放在resumeAfter方法中,有一个适当的条件,或者在一个单一的意图下退出你的程序的这一部分。

没有包含堆栈跟踪,但是在使用Luis时可能会引起问题的是,如果使用的是来自美国以外地区的跟踪,则需要相应地将域指向正确的Luis服务的属性设置为相应的属性。

public EnglishLuis(ConstructorParameters parameters) 
    : base(new LuisService(new LuisModelAttribute(
        "<AppId>",
        "<SubscriptionKey>",
        domain: "westeurope.api.cognitive.microsoft.com")))
    {
        // Constructor Stuff...
    }

扫码关注云+社区

领取腾讯云代金券