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?");
    }
}