【发布时间】:2017-04-07 20:33:07
【问题描述】:
我有一个机器人,我正在做这样的事情:
1) 一个新的 Activity(消息)到达。
2) 我将消息发送到 RootDialog。
3) 根据某些逻辑,RootDialog 可以:
a) Call a LuisDialog (handling natural language)
b) Call a CustomDialog (handles some business logic).
但是当用户状态被重置,并且流程导致 LuisDialog 内部的意图时,它会调用意图方法两次。只是第一次状态为空,然后就可以正常工作了。
让我给你看代码:
MessagesController:
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)
{
...
}
}
RootDialog:
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 的意图是这样的:
[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);
}
}
最后是 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); 之后,机器人调用该函数(而不是等待事件叫它)
有什么想法吗?
提前致谢。
【问题讨论】:
-
你能创建一个可以复制的小样本吗?
-
0) 正如我在帖子中所说,当机器人状态为空时(用户第一次使用它或我执行 /deleteprofile 时)会发生这种情况。 1) 用户键入“hi”。 2)我所说的逻辑被评估(在我的例子中 DoSomeLogic() 调用一个 QnA 服务,并根据分数它是否回复问题)。由于“hi”不是一个有效的问题,它会将 msg 转发到 LuisDialog。 3)Salute Intent 被调用并显示一个小英雄卡。它以 context.Wait(AfterUserChoseOptionInSalute) 结束; 4) AfterUserChoseOption 本身被调用,并重新调度活动(导致另一个调用)。
-
是的,我阅读了这篇文章,但除非您希望有人编写完全相同的场景来尝试重现该问题,否则这将很难......转发/呼叫对话框按预期工作;所以问题出在你的代码中。
-
另一件事 context.Done(true);在intent方法被执行但之前也执行了一个等待。所以或者你等待或者你结束对话;你不能两者都做。上下文助手正在等待,因此您应该删除 context.Done
标签: botframework azure-language-understanding