Microsoft Bot Framework、LUIS 和瀑布
Microsoft Bot Framework, LUIS and waterfalling
我正在尝试构建一个机器人。
起初我使用的是 Node.js 但因为显然 c# 更适合我的客户,所以我开始在 c# 中创建 Bot。我还创建了一个 LUIS 应用程序。
现在,这个 Bot 背后的想法是它会问你一系列问题,然后在最后组合你的结果并根据你的答案选择一个解决方案。我遇到的问题是我不知道如何设置机器人来做到这一点。在节点中,它谈到了瀑布流,但我在 c# 版本中看不到类似的东西。
另外,我不确定它是否应该在我应该集中注意力的 LUIS 中。
例如,假设我希望用户选择一个对象,然后根据该对象提出 3 个问题,例如:
- Is it small?
- Is it light?
- What colour do you prefer?
最后说 "You want a black, small, light camera." 我似乎无法让它工作。我知道你们中的一些人会说使用 FormFlow,但问题是动态的,对象也是动态的,这就是我使用 LUIS 的原因。
有谁知道我在哪里可以找到关于如何使用 LUIS 创建类似流程的好文章?
这是另一种选择,如果你想手动处理这个,你可以为每个问题设置不同的 context.Wait 方法,有一种瀑布方法。
假设您有一个带有 ChooseCategory 意图的 LuisDialog。在该方法中,您将确定类别并基于您将提出新问题的一些自定义逻辑。
这可以通过以下方式完成:PromptDialog/ResumeAfter 或手动 context.PostAsync/context.Wait(基本上定义了将侦听下一条消息的方法)。
我不知道你的自定义逻辑如何,但你应该能够动态决定哪个是下一个方法来监听即将到来的消息。
[LuisIntent("Choose category")]
public async Task ChooseCategory(IDialogContext context, LuisResult result)
{
// get category logic..
await context.PostAsync("This is my first question?");
context.Wait(CaptureFirstQuestionAnswerAsync);
}
public async Task CaptureFirstQuestionAnswerAsync(IDialogContext context, IAwaitable<IMessageActivity> argument)
{
IMessageActivity message = await argument;
switch (message.Text.ToLower())
{
case "answer 1":
// do something
break;
case "answer 2":
// do something different
break;
default:
// do something ...
break;
}
await context.PostAsync($"This is my second question?");
context.Wait(CaptureSecondQuestionAnswerAsync);
}
public async Task CaptureSecondQuestionAnswerAsync(IDialogContext context, IAwaitable<IMessageActivity> argument)
{
//...
}
如果 FormFlow 限制太多,您应该考虑使用对话框。他们允许进行更开放的对话。
我会做以下事情:
1) 要求用户选择类别。
如果可能,请使用按钮为用户提供可供选择的类别列表。这意味着您不必向 LUIS 询问用户选择的类别。
否则,允许用户键入类别并将其传递给 LUIS。来自 LUIS 的响应将包含一个实体。实体将保存类别的名称。
2) 转发到该类别的对话框
根据从 LUIS 返回的实体,转发到负责提出下一个问题的适当对话框
[LuisIntent("AskQuestionAboutCategory")]
public async Task AskQuestion(IDialogContext context, LuisResult result)
{
//get Entity from LUIS response
string category = result.Entities.FirstOrDefault(e => e.Type == "Category")?.Entity;
switch (category)
{
case "Category 1":
//forward to Dialog for Category1
await
context.Forward(new Category1Dialog(), ResumeAfter,
new Activity {Text = result.Query}, CancellationToken.None);
break;
case "Category 2":
//forward to Dialog for Category2
await
context.Forward(new Category2Dialog(), ResumeAfter,
new Activity {Text = result.Query}, CancellationToken.None);
break;
}
}
private async Task ResumeAfter(IDialogContext context, IAwaitable<object> result)
{
context.Wait(MessageReceived);
}
3) 在对话框中提问
在对话框中,使用 PromptDialog 来询问问题 1。使用开关来回答问题 1,以确定问题 2 的询问内容。
像这样继续对话。
[Serializable]
public class Category1Dialog : IDialog<object>
{
public async Task StartAsync(IDialogContext context)
{
context.Wait(MessageReceivedAsync);
}
public virtual async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> argument)
{
var prompt = "what is the answer to question 1 ?";
//ASK QUESTION 1
PromptDialog.Text(context, ResumeAfterQuestion1, prompt);
}
private async Task ResumeAfterQuestion1(IDialogContext context, IAwaitable<string> result)
{
var input = await result;
switch (input)
{
//ASK QUESTION 2, DEPENDING ON WHAT WAS ANSWERED FOR QUESTION 1
case "Answer A":
PromptDialog.Text(context, ResumeAfterQuestion2, "what is the answer to question 2 ?");
break;
case "Answer B":
PromptDialog.Text(context, ResumeAfterQuestion2, "what is the answer to question 2 ?");
break;
}
}
private async Task ResumeAfterQuestion2(IDialogContext context, IAwaitable<string> result)
{
var input = await result;
switch (input)
{
//ASK QUESTION 3
case "Answer C":
PromptDialog.Text(context, ResumeAfterQuestion3, "what is the answer to next question after Answer C ?");
break;
case "Answer D":
PromptDialog.Text(context, ResumeAfterQuestion3, "what is the answer to next question after Answer D ?");
break;
}
}
看来您需要使用 switch 语句来确定接下来要问的问题。
我正在尝试构建一个机器人。 起初我使用的是 Node.js 但因为显然 c# 更适合我的客户,所以我开始在 c# 中创建 Bot。我还创建了一个 LUIS 应用程序。
现在,这个 Bot 背后的想法是它会问你一系列问题,然后在最后组合你的结果并根据你的答案选择一个解决方案。我遇到的问题是我不知道如何设置机器人来做到这一点。在节点中,它谈到了瀑布流,但我在 c# 版本中看不到类似的东西。 另外,我不确定它是否应该在我应该集中注意力的 LUIS 中。
例如,假设我希望用户选择一个对象,然后根据该对象提出 3 个问题,例如:
- Is it small?
- Is it light?
- What colour do you prefer?
最后说 "You want a black, small, light camera." 我似乎无法让它工作。我知道你们中的一些人会说使用 FormFlow,但问题是动态的,对象也是动态的,这就是我使用 LUIS 的原因。
有谁知道我在哪里可以找到关于如何使用 LUIS 创建类似流程的好文章?
这是另一种选择,如果你想手动处理这个,你可以为每个问题设置不同的 context.Wait 方法,有一种瀑布方法。
假设您有一个带有 ChooseCategory 意图的 LuisDialog。在该方法中,您将确定类别并基于您将提出新问题的一些自定义逻辑。
这可以通过以下方式完成:PromptDialog/ResumeAfter 或手动 context.PostAsync/context.Wait(基本上定义了将侦听下一条消息的方法)。
我不知道你的自定义逻辑如何,但你应该能够动态决定哪个是下一个方法来监听即将到来的消息。
[LuisIntent("Choose category")]
public async Task ChooseCategory(IDialogContext context, LuisResult result)
{
// get category logic..
await context.PostAsync("This is my first question?");
context.Wait(CaptureFirstQuestionAnswerAsync);
}
public async Task CaptureFirstQuestionAnswerAsync(IDialogContext context, IAwaitable<IMessageActivity> argument)
{
IMessageActivity message = await argument;
switch (message.Text.ToLower())
{
case "answer 1":
// do something
break;
case "answer 2":
// do something different
break;
default:
// do something ...
break;
}
await context.PostAsync($"This is my second question?");
context.Wait(CaptureSecondQuestionAnswerAsync);
}
public async Task CaptureSecondQuestionAnswerAsync(IDialogContext context, IAwaitable<IMessageActivity> argument)
{
//...
}
如果 FormFlow 限制太多,您应该考虑使用对话框。他们允许进行更开放的对话。
我会做以下事情:
1) 要求用户选择类别。
如果可能,请使用按钮为用户提供可供选择的类别列表。这意味着您不必向 LUIS 询问用户选择的类别。
否则,允许用户键入类别并将其传递给 LUIS。来自 LUIS 的响应将包含一个实体。实体将保存类别的名称。
2) 转发到该类别的对话框
根据从 LUIS 返回的实体,转发到负责提出下一个问题的适当对话框
[LuisIntent("AskQuestionAboutCategory")]
public async Task AskQuestion(IDialogContext context, LuisResult result)
{
//get Entity from LUIS response
string category = result.Entities.FirstOrDefault(e => e.Type == "Category")?.Entity;
switch (category)
{
case "Category 1":
//forward to Dialog for Category1
await
context.Forward(new Category1Dialog(), ResumeAfter,
new Activity {Text = result.Query}, CancellationToken.None);
break;
case "Category 2":
//forward to Dialog for Category2
await
context.Forward(new Category2Dialog(), ResumeAfter,
new Activity {Text = result.Query}, CancellationToken.None);
break;
}
}
private async Task ResumeAfter(IDialogContext context, IAwaitable<object> result)
{
context.Wait(MessageReceived);
}
3) 在对话框中提问
在对话框中,使用 PromptDialog 来询问问题 1。使用开关来回答问题 1,以确定问题 2 的询问内容。 像这样继续对话。
[Serializable]
public class Category1Dialog : IDialog<object>
{
public async Task StartAsync(IDialogContext context)
{
context.Wait(MessageReceivedAsync);
}
public virtual async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> argument)
{
var prompt = "what is the answer to question 1 ?";
//ASK QUESTION 1
PromptDialog.Text(context, ResumeAfterQuestion1, prompt);
}
private async Task ResumeAfterQuestion1(IDialogContext context, IAwaitable<string> result)
{
var input = await result;
switch (input)
{
//ASK QUESTION 2, DEPENDING ON WHAT WAS ANSWERED FOR QUESTION 1
case "Answer A":
PromptDialog.Text(context, ResumeAfterQuestion2, "what is the answer to question 2 ?");
break;
case "Answer B":
PromptDialog.Text(context, ResumeAfterQuestion2, "what is the answer to question 2 ?");
break;
}
}
private async Task ResumeAfterQuestion2(IDialogContext context, IAwaitable<string> result)
{
var input = await result;
switch (input)
{
//ASK QUESTION 3
case "Answer C":
PromptDialog.Text(context, ResumeAfterQuestion3, "what is the answer to next question after Answer C ?");
break;
case "Answer D":
PromptDialog.Text(context, ResumeAfterQuestion3, "what is the answer to next question after Answer D ?");
break;
}
}
看来您需要使用 switch 语句来确定接下来要问的问题。