【问题标题】:Nested dialogs: message being sent twice嵌套对话框:消息被发送两次
【发布时间】: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


【解决方案1】:

我找到了错误的行。它在第一个对话框(RootDialog)上:

public async Task StartAsync(IDialogContext context)
{
   await MessageReceivedAsync(context, Awaitable.FromItem(context.Activity.AsMessageActivity()));
}

那是一条重新发送带有传入活动的消息的行。我在一些代码中的某个地方有它(不知道为什么),认为在 StartAsync 上使用它是个好主意。因此,因此发生了两个调用。

骗我。

我只是把它改成这个并工作了:

public async Task StartAsync(IDialogContext context)
{
    context.Wait(MessageReceivedAsync);
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-11-09
    • 1970-01-01
    • 2017-08-15
    • 1970-01-01
    • 2020-06-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多