如何从 BotFramework 中的 LuisIntent 触发 FormFlow

How to Fire FormFlow from LuisIntent in BotFramework

我正在使用 C# 中的 Microsoft BotFramework。我有一个 LUIS Intent,我想从那里启动一个 FormFlow,但老实说我不知道​​该怎么做。

[LuisModel("35554cdf-92ae-4413-*", "*")]
[Serializable]
public class LuisIntegration : LuisDialog<Object>
{
    public BuildFormDelegate<Cotizacion> _configurecotizacion;
    public BuildFormDelegate<Cotizacion> LuisDialog(BuildFormDelegate<Cotizacion> configureCotizacion) => _configurecotizacion = configureCotizacion;

    [LuisIntent("Cotizar")]
    public async Task CotizarAsync(IDialogContext context, LuisResult result)
    {
        var entitiesArray = result.Entities;

        var form = new FormDialog<Cotizacion>(new Cotizacion(), Cotizacion.BuildForm, FormOptions.PromptInStart, result.Entities);

        context.Call(new FormDialog<Cotizacion>(new Cotizacion(), this._configurecotizacion, FormOptions.PromptInStart), ResumeAfterCallback);
    }

    private async Task ResumeAfterCallback(IDialogContext context, IAwaitable<Cotizacion> result)
    {
        Cotizacion coti = null;

        try
        {
            coti = await result;
        }
        catch (OperationCanceledException)
        {
            await context.PostAsync("Cancelaste la cotización.");
            return;
        }

        //context.Wait(MessageReceived);
    }
}


public static IForm<Cotizacion> BuildForm()
    {
        return new FormBuilder<Cotizacion>()
                .Message("¡Listo! Necesito algunos datos para cotizar, empecemos.")
                .Field(nameof(Nombre))
                .Field(nameof(Apellido))
                .Field(nameof(Provincia))
                .Field(nameof(Localidad))
                .Field(nameof(Edad))
                .Field(new FieldReflector<Cotizacion>(nameof(Marca))
                    .SetType(null)
                    .SetDefine(async (state, field) =>
                    {
                        foreach (var prod in await Gets.ObtenerMarcasAsync())
                            field
                                .AddDescription(prod, prod)
                                .AddTerms(prod, prod);

                        return await Task.FromResult(true);
                    }))
                .Field(new FieldReflector<Cotizacion>(nameof(Modelo))
                    .SetType(null)
                    .SetDefine(async (state, field) =>
                    {
                        foreach (var prod in await Gets.ObtenerModelosAsync(state.Marca))
                            field
                                .AddDescription(prod, prod)
                                .AddTerms(prod, prod);

                        return await Task.FromResult(true);
                    }))
                .Field(nameof(Anio))
                .Field(nameof(Version))
                .Field(nameof(Email))
                .Field(nameof(Telefono))
                .Field(nameof(Sexo))
                .Confirm("Estamos a punto de cotizar tu auto, ¿está correcta la información que ingresaste?")
                .OnCompletion(async (context, state) => { ... }

FormFlow 单独工作完美,问题是当我添加 LuisIntent 时。

我修复了调用FormFlow的问题,现在的问题是没有提示确认,所以没有完成。它挂在表格的最后 属性 处。我更新了代码。

编辑 2:

private async Task ResumeAfterCallback(IDialogContext context, IAwaitable<Cotizacion> result)
    {
        Cotizacion coti = null;

        try
        {
            coti = await result;
        }
        catch (OperationCanceledException)
        {
            await context.PostAsync("Cancelaste la cotización.");
            return;
        }

        context.Wait(MessageReceived);
    }

编辑 3:

public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
    {
        if (activity.Type == ActivityTypes.Message)
        {
            await Conversation.SendAsync(activity, () => new LuisIntegration());
        }
        else
        {
            HandleSystemMessage(activity);
        }
        var response = Request.CreateResponse(HttpStatusCode.OK);
        return response;
    }

更新:(最终)问题是 FieldReflector 的 SetDefine 方法的 DefineAsyncDelegate 参数的定义:

public delegate Task<bool> DefineAsyncDelegate<T>(T state, Field<T> field) where T : class;

没有明确要求它是一个 async 方法,但是由于上面的实现在其中使用了异步调用 ('Gets.ObtenerXXXAsync()'),该方法应该 return 一个 bool 而不是包裹 Task.FromResult<bool>.

如果省略了 async,并且使用 Task.Run(...) 调用了 Gets.ObtenerXXXAsync()。例如,结果是正确的return一个Task.FromResult(真)。

.....

也许您的机器人沉迷于 'Sexo'... :)

为什么需要 BuildFormDelegate<Cotizacion> 成员变量?您的 CotizarAsync 方法可能如下所示:

    [LuisIntent("Cotizar")]
    public async Task CotizarAsync(IDialogContext context, IAwaitable<IMessageActivity> activity, LuisResult result)
    {
        context.Call(new FormDialog<Cotizacion>(new Cotizacion(), Cotizacion.BuildForm, FormOptions.PromptInStart, result.Entities), 
            ResumeAfterCallback);
    }

它只是将 Cotizacion.BuildForm 传递给 FormDialog 构造函数。

我在我的 LUIS 机器人中尝试了一个缩减版本(在 Cotizacion 中只有 NombreApellido)并且它工作正常 - ResumeAfterCallback 被调用成功。