导航如何与LUIS子对话框一起使用?

时间:2017-11-02 14:16:23

标签: bots botframework microsoft-cognitive luis azure-cognitive-services

我有一个问题......不幸的是,网上的所有样本都太浅了,并没有真正涵盖这个问题:

我有一个扩展LuisDialog的RootDialog。 RootDialog负责确定用户想要做什么。它可能是多件事,但其中一个将启动新订单。为此,RootDialog会将调用转发给NewOrderDialog,NewOrderDialog的职责是找出一些基本细节(用户想要订购什么,他喜欢使用哪个地址),最后它将确认订购并返回RootDialog。

RootDialog的代码非常简单:

[Serializable]
public class RootDialog : LuisDialog<object>
{
    public RootDialog() : base(new LuisService(new LuisModelAttribute(ConfigurationManager.AppSettings["LuisAppId"], ConfigurationManager.AppSettings["LuisAPIKey"], domain: "westus.api.cognitive.microsoft.com")))
    {
    }

    [LuisIntent("Order.Place")]
    public async Task PlaceOrderIntent(IDialogContext context, LuisResult result)
    {
        await context.Forward(new NewOrderDialog(), OnPlaceOrderIntentCompleted, context.Activity, CancellationToken.None);

        context.Wait(MessageReceived);
    }

    private async Task OnPlaceOrderIntentCompleted(IDialogContext context, IAwaitable<object> result)
    {
        await context.PostAsync("Your order has been placed. Thank you for shopping with us.");

        context.Wait(MessageReceived);
    }
}

我还为NewOrderDialog考虑了一些代码:

[Serializable]
public class NewOrderDialog : LuisDialog<object>
{
    private string _product;
    private string _address;

    public NewOrderDialog() : base(new LuisService(new LuisModelAttribute(ConfigurationManager.AppSettings["LuisAppId"], ConfigurationManager.AppSettings["LuisAPIKey"], domain: "westus.api.cognitive.microsoft.com")))
    {
    }

    [LuisIntent("Order.RequestedItem")]
    public async Task RequestItemIntent(IDialogContext context, LuisResult result)
    {
        EntityRecommendation item;
        if (result.TryFindEntity("Item", out item))
        {
            _product = item.Entity;
            await context.PostAsync($"Okay, I understood you want to order: {_product}.");
        }
        else
        {
            await context.PostAsync("I couldn't understand what you would like to buy. Can you try it again?");
        }

        context.Wait(MessageReceived);
    }

    [LuisIntent("Order.AddedAddress")]
    public async Task AddAddressIntent(IDialogContext context, LuisResult result)
    {
        EntityRecommendation item;
        if (result.TryFindEntity("Address", out item))
        {
            _address = item.Entity;
            await context.PostAsync($"Okay, I understood you want to ship the item to: {_address}.");
        }
        else
        {
            await context.PostAsync("I couldn't understand where you would like to ship the item. Can you try it again?");
        }

        context.Wait(MessageReceived);
    }
}

列出的代码不起作用。输入Order.Place意图后,它会立即执行“成功”操作。回调,然后抛出此异常:

  

异常:通过IDialogStack指定的多个简历处理程序完成IDialog方法执行。   [文件类型&#39; text / plain&#39;]

所以我有几个问题:

  1. 如何解决我得到的错误?
  2. 我如何在进入NewOrderDialog时检查我们是否已经知道产品和地址是什么,如果没有提示他们输入正确的信息?
  3. 为什么即使我没有调用context.Done()之类的内容,NewOrderDialog也会关闭?我只希望在收集完所有信息并确认订单后关闭它。

2 个答案:

答案 0 :(得分:3)

所以第一个问题是你在&#34; Order.Place&#34;中做了context.Forwardcontext.Wait,根据定义,这是错误的。您需要选择:转发到新对话框或等待当前。根据你的帖子,你想转发,所以只需删除等待电话。

除此之外,您还有1个LUIS对话框,并且您正在尝试转发到新的LUIS对话框...我怀疑这是否会起作用;我可以想象这些是两种不同的LUIS模型,否则就会出错。

根据您的评论,我现在明白您要对第二个对话框执行的操作。问题(这与你的第二个问题有关)是使用LUIS可能会让人感到困惑。例如:

  • 用户:我想下订单
  • bot =&gt;转到新对话框。由于它是前锋,activity.Text可能会再次进入LUIS(到第二个对话框的模型),并且不会检测到任何内容。第二个对话框将处于Wait状态,供用户输入。

现在,用户如何知道他需要输入地址或产品?你在哪里提示用户?看到这个问题?

我怀疑你的第三个问题是你在#1中遇到的错误的副作用,我已经提供了解决方案。

如果你澄清一点,我可能会更有帮助。你在第二个对话框中尝试用LUIS做什么看起来不行,但也许有一个解释可能有意义。

通常情况是:我从LUIS获得意图(&#34; Order.Place&#34;)然后我启动FormFlow或一组提示以获取信息以下订单(地址,产品等)或者如果您想继续使用LUIS,您可能需要查看Luis Action Binding。您可以在https://blog.botframework.com/2017/04/03/luis-action-binding-bot/上阅读更多内容。

答案 1 :(得分:-1)

你知道Bing Location Control for Microsoft Bot Framework吗?它可以用来处理'他喜欢使用哪个地址'来获取和验证用户的地址。

以下是一些示例代码:

[LuisModel("xxx", "yyy")]
[Serializable]
public class RootDialog : LuisDialog<object>
{
    ...

    [LuisIntent("Find Location")]
    public async Task FindLocationIntent(IDialogContext context, LuisResult result)
    {
        try
        {
            context.Call(new FindUserLocationDialog(), ResumeAfterLocationDialog);
        }
        catch (Exception e)
        {
            // handle exceptions
        }
    }

    public async Task ResumeAfterLocationDialog(IDialogContext context, IAwaitable<object> result)
    {
        var resultLocation = await result;

        await context.PostAsync($"Your location is {resultLocation}");

        // do whatever you want

        context.Wait(this.MessageReceived);
    }
}

对于“FindUserLocationDialog()”,您可以从第58行开始关注sample

修改

1)你能尝试使用:

[LuisIntent("Order.Place")]
public async Task PlaceOrderIntent(IDialogContext context, LuisResult result)
{
    context.Forward(new NewOrderDialog(), OnPlaceOrderIntentCompleted, context.Activity, CancellationToken.None);

    // or this
    // context.Call(new NewOrderDialog(), OnPlaceOrderIntentCompleted);
}

2)我会说这取决于你如何构建你的意图。您的“Order.Place”意图是否包含实体?这意味着,如果您的用户说“我想订购产品X 的订单发送到地址Y ”,您的意图是否已经接收了这些实体?

我建议您检查“Order.Place”意图下的产品和地址。获得并验证产品和地址后,您可以将其转发到另一个对话框(非LUIS)以处理剩余的订单处理。

[LuisIntent("Order.Place")]
public async Task PlaceOrderIntent(IDialogContext context, LuisResult result)
{
    EntityRecommendation item;
    if (result.TryFindEntity("Item", out item))
    {
        _product = item.Entity;
    }

    if (result.TryFindEntity("Address", out item))
    {
        _address = item.Entity;
    }

    if (_product == null)
    {
        PromptDialog.Text(context, this.MissingProduct, "Enter the product");   
    }
    else if (_address == null)
    {
        PromptDialog.Text(context, this.MissingAddress, "Enter the address");   
    }

    // both product and address present
    context.Forward(new NewOrderDialog(), OnPlaceOrderIntentCompleted, **object with _product and _address**, CancellationToken.None);
}

private async Task MissingProduct(IDialogContext context, IAwaitable<String> result)
{
    _product = await result;
    // perform some validation

    if (_address == null)
    {
        PromptDialog.Text(context, this.MissingAddress, "Enter the address");
    }
    else
    {
        // both product and address present
        context.Forward(new NewOrderDialog(), OnPlaceOrderIntentCompleted, **object with _product and _address**, CancellationToken.None);
    }
}

private async Task MissingAddress(IDialogContext context, IAwaitable<String> result)
{
    _address = await result;
    // perform some validation

    // both product and address present
    context.Forward(new NewOrderDialog(), OnPlaceOrderIntentCompleted, **object with _product and _address**, CancellationToken.None);
}

这些方面的东西。可能需要包含try / catch。

3)我认为它与'Order.Place'LUIS意图中的'context.Wait(MessageReceived)'有关