Form Flow 机器人定制问题
Form Flow bot customization issue
我想构建一个可以使用 QnA api 和 google 驱动器搜索 api 的机器人。我会询问用户是想查询知识库还是想在驱动器中搜索文件。为此,我选择了 Bot Framework 的 Form Flow 机器人模板。在这种情况下,如果用户选择查询 qna api 那么我想 post 向 QNA api 提问。我如何在我的机器人中实现它?在哪里可以找到用户在流程中的选择。
这里是MessageController.cs
public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{
if (activity.Type == ActivityTypes.Message)
{
await Conversation.SendAsync(activity, MakeRootDialog);
}
else
{
HandleSystemMessage(activity);
}
var response = Request.CreateResponse(HttpStatusCode.OK);
return response;
}
private Activity HandleSystemMessage(Activity message)
{
if (message.Type == ActivityTypes.DeleteUserData)
{
// Implement user deletion here
// If we handle user deletion, return a real message
}
else if (message.Type == ActivityTypes.ConversationUpdate)
{
// Handle conversation state changes, like members being added and removed
// Use Activity.MembersAdded and Activity.MembersRemoved and Activity.Action for info
// Not available in all channels
}
else if (message.Type == ActivityTypes.ContactRelationUpdate)
{
// Handle add/remove from contact lists
// Activity.From + Activity.Action represent what happened
}
else if (message.Type == ActivityTypes.Typing)
{
// Handle knowing tha the user is typing
}
else if (message.Type == ActivityTypes.Ping)
{
}
return null;
}
internal static IDialog<UserIntent> MakeRootDialog()
{
return Chain.From(() => FormDialog.FromForm(UserIntent.BuildForm));
}
表单生成器
public static IForm<UserIntent> BuildForm()
{
return new FormBuilder<UserIntent>()
.Message("Welcome to the bot!")
.OnCompletion(async (context, profileForm) =>
{
await context.PostAsync("Thank you");
}).Build();
}
首先,您需要通过 LUIS 门户创建您的 LUIS 应用程序,将您的意图与话语一起添加到那里,例如:- 意图是 "KnowlegeBase",您可以在里面创建尽可能多的话语,这些话语将 return这个意图。
在应用程序内部,创建一个 LUISDialog class 并从您的 BuildForm 中调用它:-
await context.Forward(new MyLuisDialog(), ResumeDialog, activity, CancellationToken.None);
LuisDialog class:-
public class MyLuisDialog : LuisDialog<object>
{
private static ILuisService GetLuisService()
{
var modelId = //Luis modelID;
var subscriptionKey = //Luis subscription key
var staging = //whether point to staging or production LUIS
var luisModel = new LuisModelAttribute(modelId, subscriptionKey) { Staging = staging };
return new LuisService(luisModel);
}
public MyLuisDialog() : base(GetLuisService())
{
}
[LuisIntent("KnowledgeBase")]
public async Task KnowledgeAPICall(IDialogContext context, LuisResult result)
{
//your code
}
要利用用户传递的消息,您可以在参数的上下文中使用它。
FormFlow 更适合引导对话流程。在我看来它不符合您的要求。您可以只使用 PromptDialog 来获取用户对他们喜欢的搜索类型的回答,然后将下一条消息转发到相应的对话框。类似于:
[Serializable]
public class RootDialog : IDialog<object>
{
const string QnAMakerOption = "QnA Maker";
const string GoogleDriveOption = "Google Drive";
const string QueryTypeDataKey = "QueryType";
public Task StartAsync(IDialogContext context)
{
context.Wait(MessageReceivedAsync);
return Task.CompletedTask;
}
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<object> result)
{
var activity = await result as Activity;
if(context.UserData.ContainsKey(QueryTypeDataKey))
{
var userChoice = context.UserData.GetValue<string>(QueryTypeDataKey);
if(userChoice == QnAMakerOption)
await context.Forward(new QnAMakerDialog(), ResumeAfterQnaMakerSearch, activity);
else
await context.Forward(new GoogleDialog(), ResumeAfterGoogleSearch, activity);
}
else
{
PromptDialog.Choice(
context: context,
resume: ChoiceReceivedAsync,
options: new[] { QnAMakerOption, GoogleDriveOption },
prompt: "Hi. How would you like to perform the search?",
retry: "That is not an option. Please try again.",
promptStyle: PromptStyle.Auto
);
}
}
private Task ResumeAfterGoogleSearch(IDialogContext context, IAwaitable<object> result)
{
//do something after the google search dialog finishes
return Task.CompletedTask;
}
private Task ResumeAfterQnaMakerSearch(IDialogContext context, IAwaitable<object> result)
{
//do something after the qnamaker dialog finishes
return Task.CompletedTask;
}
private async Task ChoiceReceivedAsync(IDialogContext context, IAwaitable<object> result)
{
var userChoice = await result;
context.UserData.SetValue(QueryTypeDataKey, userChoice);
await context.PostAsync($"Okay, your preferred search is {userChoice}. What would you like to search for?");
}
}
我想构建一个可以使用 QnA api 和 google 驱动器搜索 api 的机器人。我会询问用户是想查询知识库还是想在驱动器中搜索文件。为此,我选择了 Bot Framework 的 Form Flow 机器人模板。在这种情况下,如果用户选择查询 qna api 那么我想 post 向 QNA api 提问。我如何在我的机器人中实现它?在哪里可以找到用户在流程中的选择。
这里是MessageController.cs
public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{
if (activity.Type == ActivityTypes.Message)
{
await Conversation.SendAsync(activity, MakeRootDialog);
}
else
{
HandleSystemMessage(activity);
}
var response = Request.CreateResponse(HttpStatusCode.OK);
return response;
}
private Activity HandleSystemMessage(Activity message)
{
if (message.Type == ActivityTypes.DeleteUserData)
{
// Implement user deletion here
// If we handle user deletion, return a real message
}
else if (message.Type == ActivityTypes.ConversationUpdate)
{
// Handle conversation state changes, like members being added and removed
// Use Activity.MembersAdded and Activity.MembersRemoved and Activity.Action for info
// Not available in all channels
}
else if (message.Type == ActivityTypes.ContactRelationUpdate)
{
// Handle add/remove from contact lists
// Activity.From + Activity.Action represent what happened
}
else if (message.Type == ActivityTypes.Typing)
{
// Handle knowing tha the user is typing
}
else if (message.Type == ActivityTypes.Ping)
{
}
return null;
}
internal static IDialog<UserIntent> MakeRootDialog()
{
return Chain.From(() => FormDialog.FromForm(UserIntent.BuildForm));
}
表单生成器
public static IForm<UserIntent> BuildForm()
{
return new FormBuilder<UserIntent>()
.Message("Welcome to the bot!")
.OnCompletion(async (context, profileForm) =>
{
await context.PostAsync("Thank you");
}).Build();
}
首先,您需要通过 LUIS 门户创建您的 LUIS 应用程序,将您的意图与话语一起添加到那里,例如:- 意图是 "KnowlegeBase",您可以在里面创建尽可能多的话语,这些话语将 return这个意图。
在应用程序内部,创建一个 LUISDialog class 并从您的 BuildForm 中调用它:-
await context.Forward(new MyLuisDialog(), ResumeDialog, activity, CancellationToken.None);
LuisDialog class:-
public class MyLuisDialog : LuisDialog<object>
{
private static ILuisService GetLuisService()
{
var modelId = //Luis modelID;
var subscriptionKey = //Luis subscription key
var staging = //whether point to staging or production LUIS
var luisModel = new LuisModelAttribute(modelId, subscriptionKey) { Staging = staging };
return new LuisService(luisModel);
}
public MyLuisDialog() : base(GetLuisService())
{
}
[LuisIntent("KnowledgeBase")]
public async Task KnowledgeAPICall(IDialogContext context, LuisResult result)
{
//your code
}
要利用用户传递的消息,您可以在参数的上下文中使用它。
FormFlow 更适合引导对话流程。在我看来它不符合您的要求。您可以只使用 PromptDialog 来获取用户对他们喜欢的搜索类型的回答,然后将下一条消息转发到相应的对话框。类似于:
[Serializable]
public class RootDialog : IDialog<object>
{
const string QnAMakerOption = "QnA Maker";
const string GoogleDriveOption = "Google Drive";
const string QueryTypeDataKey = "QueryType";
public Task StartAsync(IDialogContext context)
{
context.Wait(MessageReceivedAsync);
return Task.CompletedTask;
}
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<object> result)
{
var activity = await result as Activity;
if(context.UserData.ContainsKey(QueryTypeDataKey))
{
var userChoice = context.UserData.GetValue<string>(QueryTypeDataKey);
if(userChoice == QnAMakerOption)
await context.Forward(new QnAMakerDialog(), ResumeAfterQnaMakerSearch, activity);
else
await context.Forward(new GoogleDialog(), ResumeAfterGoogleSearch, activity);
}
else
{
PromptDialog.Choice(
context: context,
resume: ChoiceReceivedAsync,
options: new[] { QnAMakerOption, GoogleDriveOption },
prompt: "Hi. How would you like to perform the search?",
retry: "That is not an option. Please try again.",
promptStyle: PromptStyle.Auto
);
}
}
private Task ResumeAfterGoogleSearch(IDialogContext context, IAwaitable<object> result)
{
//do something after the google search dialog finishes
return Task.CompletedTask;
}
private Task ResumeAfterQnaMakerSearch(IDialogContext context, IAwaitable<object> result)
{
//do something after the qnamaker dialog finishes
return Task.CompletedTask;
}
private async Task ChoiceReceivedAsync(IDialogContext context, IAwaitable<object> result)
{
var userChoice = await result;
context.UserData.SetValue(QueryTypeDataKey, userChoice);
await context.PostAsync($"Okay, your preferred search is {userChoice}. What would you like to search for?");
}
}