基于MS Bot Framework中的响应的分支对话框/表单

问题描述:

我们正在尝试使用MS Bot框架,但尚未完全弄清楚如何实现此方案:

We're experimenting with the MS Bot Framework and haven't quite worked out how to do this scenario:

我们有一个LUIS对话框(类型为<object>),该对话框工作正常并且经过了适当的培训.为了使用常见的三明治示例,LUIS意图要寻找的基本知识是用户询问订单的状态.如果问题中提供了订单号("1234号订单的状态是什么?"),则LUIS对话框将进行查找并直接报告状态(当前所有状态).

We have a LUIS Dialog (type <object>), which is working correctly and is trained properly. To use the common sandwich example, the basics of what LUIS intent is looking for is the user asking for the status of an order. If the order number was provided in the question ("What is the status of order 1234?"), then the LUIS dialog does the lookup and reports the status directly (which is all currently working).

但是,如果用户只是在不提供订单号的情况下触发了意图(我想查询订单的状态."),我想启动另一个对话框/表单来询问用户是否他们想按地址或订单号查找订单,然后根据他们的回答方式进行适当的数据库查找.

However, if the user just triggers the intent without providing the order number ("I'd like to look up the status of an order."), I'd like to launch another dialog/form to ask the user if they'd like to look up the order by address or order number, and then do the appropriate DB lookup based on how they answer.

我只是不确定如何配置表单/对话框(或者在这种情况下最好),以便根据他们选择地址还是数字查找来进行不同的查找.

I'm just not sure how to configure the Form/Dialog (or even which is best in this case) to do a different lookup based on if they choose address or number lookup.

到目前为止,这是意图:

Here's the intent so far:

private readonly BuildFormDelegate<OrderStatusDialog> OrderStatusDelegate;

[LuisIntent(nameof(LuisIntents.OrderStatus))]
public async Task OrderStatus(IDialogContext context, LuisResult result)
{
    // Order number(s) were provided
    if (result.Entities.Any(Entity => Entity.Type == nameof(LuisEntityTypes.OrderNumber)))
    {
        // Loop in case they asked about multiple orders
        foreach (var entity in result.Entities.Where(Entity => Entity.Type == nameof(LuisEntityTypes.OrderNumber)))
        {
            var orderNum = entity.Entity;

            // Call webservice to check status
            var request = new RestRequest(Properties.Settings.Default.GetOrderByNum, Method.GET);
            request.AddUrlSegment("num", orderNum);
            var response = await RestHelper.SendRestRequestAsync(request);

            var parsedResponse = JObject.Parse(response);

            if ((bool)parsedResponse["errored"])
            {
                await context.PostAsync((string)parsedResponse["errMsg"]);
                continue;
            }

            // Grab status from returned JSON
            var status = parsedResponse["orderStatus"].ToString();

            await context.PostAsync($"The status of order {orderNum} is {status}");
        }

        context.Wait(MessageReceived);
    }
    // Order number was not provided
    else
    {
        var orderStatusForm = new FormDialog<OrderStatusDialog>(new OrderStatusDialog(), OrderStatusDelegate,
            FormOptions.PromptInStart);
        context.Call<OrderStatusDialog>(orderStatusForm, CallBack);
    }
}

private async Task CallBack(IDialogContext context, IAwaitable<object> result)
{
    context.Wait(MessageReceived);
}

和表格:

public enum OrderStatusLookupOptions
{
    Address,
    OrderNumber
}

[Serializable]
public class OrderStatusDialog
{
    public OrderStatusLookupOptions? LookupOption;

    public static IForm<OrderStatusDialog> BuildForm()
    {
        return new FormBuilder<OrderStatusDialog>()
            .Message("In order to look up the status of a order, we will first need either the order number or your delivery address.")
            .Build();
    }
}

FormFlow路由是有效的选项.表单流中缺少的是在选择了查找选项后要求提供地址/订单号.

The FormFlow route is a valid option. What is missing in your form flow is asking for the address/order number after the lookup option is selected.

在这种情况下,您可以做的是向OrderStatusDialog类中再添加两个字段:OrderNumberDeliveryAddress.

What you can do in that case is adding two more fields to the OrderStatusDialog class: OrderNumber and DeliveryAddress.

然后,您需要使用选择的OrderStatusLookupOptions激活/停用下一个字段.

Then you need to use the selected OrderStatusLookupOptions to activate/deactivate the next field.

从我的头顶上看,代码将是这样的:

The code, from the top of my head, would be something like:

[Serializable]
public class OrderStatusDialog
{
    public OrderStatusLookupOptions? LookupOption;

    public int OrderNumber;

    public string DeliveryAddress

    public static IForm<OrderStatusDialog> BuildForm()
    {
        return new FormBuilder<OrderStatusDialog>()
            .Message("In order to look up the status of a order, we will first need either the order number or your delivery address.")
            .Field(nameof(OrderStatusDialog.LookupOption))
            .Field(new FieldReflector<OrderStatusDialog>(nameof(OrderStatusDialog.OrderNumber))
                .SetActive(state => state.LookupOption == OrderStatusLookupOptions.OrderNumber))
            .Field(new FieldReflector<OrderStatusDialog>(nameof(OrderStatusDialog.DeliveryAddress))
                .SetActive(state => state.LookupOption == OrderStatusLookupOptions.Address))
            .Build();
    }
}

然后在您的Callback方法上,您将收到填写的表单,并且可以执行数据库查找.

Then on your Callback method you will receive the form filled and you can do the DB lookup.

或者,您可以只使用PromptDialogs并指导用户进行相同的体验.看一下 MultiDialogs 示例,看看不同的选择.

Alternatively, you can just use PromptDialogs and guide the user through the same experience. Take a look to the MultiDialogs sample to see the different alternatives.

我在此处上添加了一个工作示例.

I added a working sample on this here.