嵌套对话框:消息被发送两次
Nested dialogs: message being sent twice
我有一个机器人,我正在做这样的事情:
1) 一条新的 Activity(消息)到达。
2) 我将消息发送到 RootDialog。
3) 根据某些逻辑,RootDialog 可以:
a) Call a LuisDialog (handling natural language)
b) Call a CustomDialog (handles some business logic).
但是当用户状态被重置,并且流程导致LuisDialog内部的意图时,它调用了两次意图方法。只是第一次状态为空,然后就正常了。
让我给你看代码:
消息控制器:
public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{
if (activity.Type == ActivityTypes.Message)
{
ConnectorClient connector = new ConnectorClient(new Uri(activity.ServiceUrl));
try
{
await Conversation.SendAsync(activity, () => new RootDialog());
}
catch (HttpRequestException e)
{
...
}
}
根对话框:
public class RootDialog : IDialog<object>
{
public async Task StartAsync(IDialogContext context)
{
await MessageReceivedAsync(context, Awaitable.FromItem(context.Activity.AsMessageActivity()));
}
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> awaitable)
{
bool value = DoSomeCustomLogic();
if (value)
{
string message = DoSomething();
await context.PostAsync(message);
} else {
bool value2 = DoSomeCustomLogic2();
if (value2)
{
var answerValidationDialog = new ValidateAnswerWithUserDialog();
context.Call(answerValidationDialog, ValidateAnswerWithUserDialogCompletedCallback);
} else {
var luisDialog = new LuisDialog();
await context.Forward(luisDialog,LuisDialogCompletedCallback, context.Activity, CancellationToken.None);
}
}
}
回调只做 context.Done(true);
而 LuisDialog 有一个 Intention 是这样的:
[LuisIntent(LuisUtils.INTENT_MENU_SALUTE)]
public async Task SaluteOrMenu(IDialogContext context, LuisResult result)
{
if (LuisUtils.IntentScoreIsHighEnough(result))
{
string userName = context.Activity.From.Name;
ContextHelper helper = new ContextHelper(MessageReceived);
await helper.AskUserToDoSomeInitialAction(context, saluteWord, userName);
context.Done(true);
}
else
{
await None(context, result);
}
}
最后 class ContextHelper:
public class ContextHelper
{
private Func<IDialogContext, IAwaitable<IMessageActivity>, Task> MessageReceived;
public ContextHelper(Func<IDialogContext, IAwaitable<IMessageActivity>, Task> messageReceived)
{
MessageReceived = messageReceived;
}
public async Task AskUserToDoSomeInitialAction(IDialogContext context, string saluteWord, string userName)
{
var reply = context.MakeMessage();
List<CardAction> buttons = BuildInitialOptionActions();
List<CardImage> images = BuildInitialOptionImages();
string initialText = $"Hi stranger!"
var card = new HeroCard
{
Title = "Hello!"
Text = initialText,
Buttons = buttons,
Images = images
};
reply.Attachments = new List<Attachment> { card.ToAttachment() };
await context.PostAsync(reply);
context.Wait(AfterUserChoseOptionInSalute);
}
private async Task AfterUserChoseOptionInSalute(IDialogContext context, IAwaitable<IMessageActivity> result)
{
await ReDispatchMessageReceivedToDialog(context);
}
private async Task ReDispatchMessageReceivedToDialog(IDialogContext context)
{
await MessageReceived(context, Awaitable.FromItem(context.Activity.AsMessageActivity()));
}
}
SaluteOrMenu Intention 被调用两次(仅在我第一次与机器人交互或删除状态时调用。调试后我看到在执行 context.Wait(AfterUserChoseOptionInSalute);
后,机器人调用该函数(而不是等待用于调用它的事件)
有什么想法吗?
提前致谢。
我找到了错误的那一行。它在第一个对话框(RootDialog)上:
public async Task StartAsync(IDialogContext context)
{
await MessageReceivedAsync(context, Awaitable.FromItem(context.Activity.AsMessageActivity()));
}
那是用传入 activity 重新发送消息的行。我把它放在一些代码块中的某个地方并且(不知道为什么)认为在 StartAsync 上使用它是个好主意。因此,正因为如此,发生了两次呼叫。
我傻了。
我只是把它改成了这个并工作:
public async Task StartAsync(IDialogContext context)
{
context.Wait(MessageReceivedAsync);
}
我有一个机器人,我正在做这样的事情:
1) 一条新的 Activity(消息)到达。
2) 我将消息发送到 RootDialog。
3) 根据某些逻辑,RootDialog 可以:
a) Call a LuisDialog (handling natural language)
b) Call a CustomDialog (handles some business logic).
但是当用户状态被重置,并且流程导致LuisDialog内部的意图时,它调用了两次意图方法。只是第一次状态为空,然后就正常了。
让我给你看代码:
消息控制器:
public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{
if (activity.Type == ActivityTypes.Message)
{
ConnectorClient connector = new ConnectorClient(new Uri(activity.ServiceUrl));
try
{
await Conversation.SendAsync(activity, () => new RootDialog());
}
catch (HttpRequestException e)
{
...
}
}
根对话框:
public class RootDialog : IDialog<object>
{
public async Task StartAsync(IDialogContext context)
{
await MessageReceivedAsync(context, Awaitable.FromItem(context.Activity.AsMessageActivity()));
}
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> awaitable)
{
bool value = DoSomeCustomLogic();
if (value)
{
string message = DoSomething();
await context.PostAsync(message);
} else {
bool value2 = DoSomeCustomLogic2();
if (value2)
{
var answerValidationDialog = new ValidateAnswerWithUserDialog();
context.Call(answerValidationDialog, ValidateAnswerWithUserDialogCompletedCallback);
} else {
var luisDialog = new LuisDialog();
await context.Forward(luisDialog,LuisDialogCompletedCallback, context.Activity, CancellationToken.None);
}
}
}
回调只做 context.Done(true);
而 LuisDialog 有一个 Intention 是这样的:
[LuisIntent(LuisUtils.INTENT_MENU_SALUTE)]
public async Task SaluteOrMenu(IDialogContext context, LuisResult result)
{
if (LuisUtils.IntentScoreIsHighEnough(result))
{
string userName = context.Activity.From.Name;
ContextHelper helper = new ContextHelper(MessageReceived);
await helper.AskUserToDoSomeInitialAction(context, saluteWord, userName);
context.Done(true);
}
else
{
await None(context, result);
}
}
最后 class ContextHelper:
public class ContextHelper
{
private Func<IDialogContext, IAwaitable<IMessageActivity>, Task> MessageReceived;
public ContextHelper(Func<IDialogContext, IAwaitable<IMessageActivity>, Task> messageReceived)
{
MessageReceived = messageReceived;
}
public async Task AskUserToDoSomeInitialAction(IDialogContext context, string saluteWord, string userName)
{
var reply = context.MakeMessage();
List<CardAction> buttons = BuildInitialOptionActions();
List<CardImage> images = BuildInitialOptionImages();
string initialText = $"Hi stranger!"
var card = new HeroCard
{
Title = "Hello!"
Text = initialText,
Buttons = buttons,
Images = images
};
reply.Attachments = new List<Attachment> { card.ToAttachment() };
await context.PostAsync(reply);
context.Wait(AfterUserChoseOptionInSalute);
}
private async Task AfterUserChoseOptionInSalute(IDialogContext context, IAwaitable<IMessageActivity> result)
{
await ReDispatchMessageReceivedToDialog(context);
}
private async Task ReDispatchMessageReceivedToDialog(IDialogContext context)
{
await MessageReceived(context, Awaitable.FromItem(context.Activity.AsMessageActivity()));
}
}
SaluteOrMenu Intention 被调用两次(仅在我第一次与机器人交互或删除状态时调用。调试后我看到在执行 context.Wait(AfterUserChoseOptionInSalute);
后,机器人调用该函数(而不是等待用于调用它的事件)
有什么想法吗?
提前致谢。
我找到了错误的那一行。它在第一个对话框(RootDialog)上:
public async Task StartAsync(IDialogContext context)
{
await MessageReceivedAsync(context, Awaitable.FromItem(context.Activity.AsMessageActivity()));
}
那是用传入 activity 重新发送消息的行。我把它放在一些代码块中的某个地方并且(不知道为什么)认为在 StartAsync 上使用它是个好主意。因此,正因为如此,发生了两次呼叫。
我傻了。
我只是把它改成了这个并工作:
public async Task StartAsync(IDialogContext context)
{
context.Wait(MessageReceivedAsync);
}