如何从 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
中只有 Nombre
和 Apellido
)并且它工作正常 - ResumeAfterCallback
被调用成功。
我正在使用 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
中只有 Nombre
和 Apellido
)并且它工作正常 - ResumeAfterCallback
被调用成功。