首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Bot框架:通过Prompts循环

Bot框架:通过Prompts循环
EN

Stack Overflow用户
提问于 2017-11-24 16:02:10
回答 1查看 1.5K关注 0票数 2

我最近进入了微软的Bot,这将是我第一次接触C#的异步编程。我正在创建一个作为选择树设计的提示符。使用一个XML文档,我设计了一个用户可以选择的主题层次结构--然后我使用一个HelpTopicSelector类抽象了这个XML的解析。

流动情况如下:

  • 用户键入“帮助”
  • 上下文转发到HelpDialog
  • 帮助对话框使用HelpTopicSelector提供的选项列表创建提示符
  • 当用户选择提示选项时,HelpTopicSelector“选择”选择并从子树更新新的选项列表。
  • 使用更新的主题创建另一个提示
  • 重复,直到最后选定的主题是最后一个节点调用Context.Done。

帮助对话框从基本对话框中调用如下:

代码语言:javascript
运行
复制
    private async Task ActivityRecievedAsync(IDialogContext context, IAwaitable<object> result)
    {
        Activity activity = await result as Activity;

        if (activity.Text == "test")
        {
            await context.PostAsync("works");
        }
        else if(activity.Text == "help")
        {
            await context.Forward(new HelpDialog(), this.ResumeAfterHelp, activity.AsMessageActivity(), System.Threading.CancellationToken.None);
            await context.PostAsync("Done Selection!");
        }

        context.Wait(ActivityRecievedAsync);
    }

我几乎可以肯定,我的代码中的问题在于我的HelpDialog的“循环”性质,但我真的不知道为什么它会失败。

代码语言:javascript
运行
复制
class HelpDialog : IDialog
{
    public async Task StartAsync(IDialogContext context)
    {
        await context.PostAsync("Reached Help Dialog!");
        context.Wait(ActivityRecievedAsync);
    }

    private async Task ActivityRecievedAsync(IDialogContext context, IAwaitable<object> result)
    {
        var message = await result;
        await context.PostAsync("HelpDialog: Activity Received");
        await HandleTopicSelection(context);

        context.Wait(ActivityRecievedAsync);
    }

    private async Task HandleTopicSelection(IDialogContext context)
    {
        List<string> topics = HelpTopicSelector.Instance.Topics;
        PromptDialog.Choice<string>(context, TopicSelectedAsync, topics, "Select A Topic:");

        // Unecessary?
        context.Wait(ActivityRecievedAsync);
    }

    private async Task TopicSelectedAsync(IDialogContext context, IAwaitable<string> result)
    {
        string selection = await result;

        if (HelpTopicSelector.Instance.IsQuestionNode(selection))
        {
            await context.PostAsync($"You asked: {selection}");
            HelpTopicSelector.Instance.Reset();
            context.Done<string>(selection);
        }
        else
        {
            HelpTopicSelector.Instance.SelectElement(selection);
            await HandleTopicSelection(context);
        }

        // Unecessary?
        context.Wait(ActivityRecievedAsync);
    }
}

我所期望的:

  • 我相信等待关键字应该保持一个任务的执行,直到等待的任务完成。
  • 类似地,我相信Context.Wait在任务结束时被调用回AcitivtyReceived方法,这有效地使bot等待用户输入。
  • 假设逻辑为真,帮助对话框进入StartAsync方法并将控件交给响应父对话框的Context.Forward传递的“消息”的StartAsync。然后,它等待负责提示符的HandleTopic方法。如TopicSelectedAsync参数所示,提示符继续在ResumeAfter中执行。
  • TopicSelectedAsync方法检查所选主题是否位于XML树的末尾,如果是,则以调用Context.Done结束对话框。否则,它将等待另一个HandleTopic方法,该方法递归地创建另一个提示--有效地创建一个循环,直到对话框结束。

考虑到这看起来是多么的烦人,我并不惊讶于面对一个错误。bot仿真器引发“堆栈为空”异常。

。在尝试使用断点进行调试之后,我注意到HelpDialog在进入HelpDialog方法时突然结束和退出(特别是当它等待结果时)。Visual引发以下异常:

无效需要:期待呼叫,有民意测验。

额外注意:,我尝试在我的BasicDialog类中编码这个逻辑,最初没有转发到任何其他对话框。令我惊讶的是,它几乎完美无缺。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2017-11-25 01:36:42

此调查对话框示例类似于您的景物:https://github.com/Microsoft/BotBuilder-Samples/blob/45d0f8767d6b71b3a11b060c893521d5150ede7f/CSharp/core-proactiveMessages/startNewDialogWithPrompt/SurveyDialog.cs

将其修改为帮助对话框:

代码语言:javascript
运行
复制
[Serializable]
public class HelpDialog : IDialog
{
     public async Task StartAsync(IDialogContext context)
    {
        PromptDialog.Choice<string>(context, TopicSelectedAsync, HelpTopicSelector.Instance.Topics, "Select A Topic:", attempts: 3, retry: "Please select a Topic");
    }

    private async Task TopicSelectedAsync(IDialogContext context, IAwaitable<object> result)
    {
        try
        {
            string selection = await result as string;

            if (HelpTopicSelector.Instance.IsQuestionNode(selection))
            {
                await context.PostAsync($"You asked: {selection}");
                HelpTopicSelector.Instance.Reset();
                context.Done<string>(selection);
            }
            else
            {
                await this.StartAsync(context);
            }
        }
        catch (TooManyAttemptsException)
        {
            await this.StartAsync(context);
        }                
    }
}

从这样的父对话框(使用context.Call()而不是.Forward())调用它:

代码语言:javascript
运行
复制
 private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<object> result)
    {
        Activity activity = await result as Activity;

        if (activity.Text == "test")
        {
            await context.PostAsync("works");
            context.Wait(MessageReceivedAsync);
        }
        else if (activity.Text == "help")
        {
            context.Call(new HelpDialog(), ResumeAfterHelp);
            await context.PostAsync("Called help dialog!");
        }            
    }

    private async Task ResumeAfterHelp(IDialogContext context, IAwaitable<object> result)
    {
        var selection = await result as string;
        context.Wait(MessageReceivedAsync);
    }

在为Context.Wait()提供方法时,实际上提供了一个连续委托。从用户收到的下一条消息将发送给最后一个.Wait() ed的方法。如果您正在转发,或者调用一个单独的对话框,那么父程序也不应该调用.Wait()。另外,在调用context.Done()时,也不应该在同一个对话框中出现.Wait()。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/47476712

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档