问题
I'm creating a bot for FAQ . When bot start conversations send a PromptDialog with 2 options: english, french.
I want to forward the dialog to EnglishLuis when user chooses English button, and FrenchLuis when choosing French.
Here is my code :
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);
}
}
}
EnglishLuis.cs :
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);
}
My problem is that when i choose English ( even French ) i got this error : here was an error sending this message to your bot: HTTP status code InternalServerError
Can you please help me how to start luis dialog ? P.S if i start from MessagesController.cs directly works good...but my intentions is to let people choose between two languages.
I try to call the luis using : await context.Forward(new EnglishLuis(), ResumeAfterDialog, message, CancellationToken.None); but without result .
new file RootDialog.cs ( updated ) :
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);
}
}
}
回答1:
First, doing
context.Wait(this.MessageReceived);
context.Done(true);
it's wrong. You need to choose: or you wait for a new message in the EnglishDialog
or you end the EnglishDialog
(with Done
)
Then, you are trying to send a string in the context.Forward
and you need to forward an IMessageActivity. And I suspect you want to send the original message, so you will need to save that in global variable before continuing with the prompt. Try with:
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);
}
}
回答2:
This is how I would have implemented your method for calling the different dialogs. I tend to use dependency injection of dialogs so I don't have to constantly new them up.
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;
}
}
There is an issue in your EnglishLuis dialog where you are using:
context.Wait(this.MessageReceived);
context.Done(true);
The issue is that both of these lines will execute when it passes through the dialog. The context.Done will cause this dialog to leave the stack so you'll end up going to the previous dialog instead, which clashes with the fact you're trying to wait for a response.
There shouldn't really be a context.Done in your luis dialog unless you want to go back to the previous Dialog. So if you're choosing to use context.Done either put it in the resumeAfter method with an appropriate condition or under a single intent for exiting this part of your program.
You didn't include a stack trace, but something that can cause issues when using Luis is if you're using one from a region other than the US. In which case you need to set the properties accordingly where the domain points to the right Luis service.
public EnglishLuis(ConstructorParameters parameters)
: base(new LuisService(new LuisModelAttribute(
"<AppId>",
"<SubscriptionKey>",
domain: "westeurope.api.cognitive.microsoft.com")))
{
// Constructor Stuff...
}
来源:https://stackoverflow.com/questions/45464455/how-to-forward-from-rootdialog-to-luisdialog-in-bot-framework