聊天机器人项目中使用Dialog实现循环任务
分类: Azure机器人 ◆ 标签: #Azure Bot Framework SDK #Azure Bot Service #机器人 ◆ 发布于: 2023-08-07 23:08:04

我们之前有三篇文章已经讲述了Dialog
的基本使用和基本的原理以及应用场景,我们这一篇看看如何利用Dialog
实现循环任务。阅读本章前,需要使用如下的文章先创建好项目:
- https://www.azuredeveloper.cn/article-create-a-full-function-chat-bot-project
- https://www.azuredeveloper.cn/article-use-dialog-control-chat-bot-session-flow
- https://www.azuredeveloper.cn/article-implement-branch-task-by-dialog
请根据上述三篇文章一步一步完成,就有了一个实现了Dialog
,并且实现了主线任务以及分支任务的代码了。
什么是循环任务?例如在聊天的会话中,我们需要对某些场景进行分页,显示更多的数据,或者需要用户重复的确认某一件合同,收集不同的信息,等等都可以实现为一个循环任务。我们本例继续延续之前的场景,当用户年龄大于35岁,询问他是否会购买保险,如果用户选择了购买保险,那么我们需要给用户看一个3页的保险说明书,案例中使用循环任务实现了从第一页保险说明循环到最后一页退出,或者用户选择特定的按钮退出循环。
开始之前一定要按照上面的三遍文章一步一步的创建好项目CoreBot
, 我们现在继续实现循环任务。
开始代码
在已有的CoreBot
项目中,我们已经有一个Dialog
- MainDialog.cs
, 为了完成循环任务我们需要另外一个新的Dialog
, 我们可以命名为:InsuranceDialog
, 专门用于处理保险的循环任务。
先给代码,再解释:
Dialogs\InsuranceDialog.cs
:
using Microsoft.Bot.Builder.Dialogs; using Microsoft.Bot.Builder; using Microsoft.Bot.Builder.Dialogs.Choices; namespace CoreBot.Dialogs; public class InsuranceDialog : ComponentDialog { public InsuranceDialog() : base(nameof(InsuranceDialog)) { AddDialog(new ChoicePrompt(nameof(ChoicePrompt))); AddDialog(new WaterfallDialog( nameof(WaterfallDialog), new WaterfallStep[] { ReadInsuranceDetail, CheckReadPage } )); InitialDialogId = nameof(WaterfallDialog); } public async Task<DialogTurnResult> ReadInsuranceDetail(WaterfallStepContext waterfallStepContext, CancellationToken cancellationToken) { waterfallStepContext.Values["PageOptions"] = null; var pageOptions = waterfallStepContext.Options as PageOptions ?? new PageOptions() { TotalPage = 3, CurrentPage = 1 }; var reply = waterfallStepContext.Context.Activity.CreateReply(); reply.Text = $"保险协议,您当前阅读的在{pageOptions.CurrentPage}页"; await waterfallStepContext.Context.SendActivityAsync(reply,cancellationToken); var options = new PromptOptions() { Prompt = MessageFactory.Text("请阅读保险协议"), RetryPrompt = MessageFactory.Text("请仔细阅读保险协议"), Choices = GetChoices(), }; waterfallStepContext.Values["PageOptions"] = pageOptions; return await waterfallStepContext.PromptAsync(nameof(ChoicePrompt), options, cancellationToken); } public async Task<DialogTurnResult> CheckReadPage(WaterfallStepContext waterfallStepContext, CancellationToken cancellationToken) { var checkValue = ((FoundChoice)waterfallStepContext.Result).Value; if ( checkValue.Equals("下页")) { PageOptions pageOptions = (PageOptions)waterfallStepContext.Values["PageOptions"]; if (pageOptions.TotalPage <= pageOptions.CurrentPage) { //直接退出了. var reply = waterfallStepContext.Context.Activity.CreateReply(); reply.Text = "已经浏览完所有搜索结果。"; await waterfallStepContext.Context.SendActivityAsync(reply, cancellationToken); return await waterfallStepContext.EndDialogAsync(null, cancellationToken); } else { pageOptions.CurrentPage += 1; return await waterfallStepContext.ReplaceDialogAsync(nameof(InsuranceDialog), pageOptions, cancellationToken); } } else { var reply = waterfallStepContext.Context.Activity.CreateReply(); reply.Text = "退出浏览搜索结果。"; await waterfallStepContext.Context.SendActivityAsync(reply, cancellationToken); return await waterfallStepContext.EndDialogAsync(null, cancellationToken); } } private IList<Choice> GetChoices() { var cardOptions = new List<Choice>() { new Choice() { Value = "下页", Synonyms = new List<string>() { "下一页" } }, new Choice() { Value = "退出", Synonyms = new List<string>() { "退出" } }, }; return cardOptions; } }
代码比较长,但是核心的价值主要是两个:
- 通过
waterfallStepContext.Values
数组在不同的Turn
之间传递状态和状态数据。 - 通过方法
waterfallStepContext.ReplaceDialogAsync
来显式的调用需要的Dialog
, 注意根据DialogId
来调用,也就是在构造函数里输入的nameof()
。
其他的更改
更改一下`MainDialog.cs':
更改构造函数,添加新的Dialog
:
public class MainDialog: ComponentDialog { public MainDialog() : base(nameof(MainDialog)) { AddDialog(new TextPrompt("get-nick-name", NickNameNotNull)); AddDialog(new NumberPrompt<int>("get-age")); AddDialog(new ChoicePrompt(nameof(ChoicePrompt))); AddDialog(new InsuranceDialog()); AddDialog(new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[]{ GetNickName, GetAge, SetAgeForHealth, ShowNickNameAndAge, })); InitialDialogId = nameof(WaterfallDialog); }
更改方法ShowNickNameAndAge
:
public async Task<DialogTurnResult> ShowNickNameAndAge(WaterfallStepContext waterfallStepContext, CancellationToken cancellationToken) { var Age = Convert.ToInt32(waterfallStepContext.Values["Age"]); var NickName = waterfallStepContext.Values["NickName"]; var reply = waterfallStepContext.Context.Activity.CreateReply(); reply.Text = $"您好,您输入的昵称是:{NickName}, 您输入的年龄是: {Age}"; if (Age > 35) { var choice = ((FoundChoice)waterfallStepContext.Result).Value; if (choice.Equals("购买保险")) { reply.Text = "您已经购买了保险。"; return await waterfallStepContext.BeginDialogAsync(nameof(InsuranceDialog), null, cancellationToken); } } await waterfallStepContext.Context.SendActivityAsync(reply, cancellationToken); return await waterfallStepContext.EndDialogAsync(null, cancellationToken); }
主要是更改这一句:return await waterfallStepContext.BeginDialogAsync(nameof(InsuranceDialog), null, cancellationToken);
, 开始调用下一个Dialog
, 这样我们就完成了整个的代码更改。
您可以尝试运行一下,查看结果:
dotnet run
