2

I want to build a bot which can make use of QnA api and google drive's search api. I will ask user if he wants to query Knowledge base or he wants to search a file in drive. For this, I chose Form Flow bot template of Bot Framework. In this case, if user chooses to query qna api then I want to post the question to QNA api. How can I implement this in my bot? Where can I find user's selection in flow.

Here is 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));
        }

Form Builder

public static IForm<UserIntent> BuildForm()
 {
   return new FormBuilder<UserIntent>()
     .Message("Welcome to the bot!")
     .OnCompletion(async (context, profileForm) =>
      {
         await context.PostAsync("Thank you");
      }).Build();
 }
Sonali
  • 2,223
  • 6
  • 32
  • 69

2 Answers2

1

FormFlow is more for a guided conversation flow. It doesn't seem to me like it meets your requirements. You can just use a PromptDialog to get the user's answer for which type of search they prefer, then forward the next message to the corresponding dialog. Something like:

[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?");
    }
}
Eric Dahlvang
  • 8,252
  • 4
  • 29
  • 50
0

First you need to create your LUIS application over the LUIS portal, add your intents over there with the utterances, for eg:- intent is "KnowlegeBase", inside that you can create as many utterances which will return this intent.

Inside the application, create a LUISDialog class and call it from your 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
    }

To utilize the message passed by user, you can use it from the context in the parameter.

  • how can I call `await context.Forward(new MyLuisDialog(), ResumeDialog, activity, CancellationToken.None);` from form builder. First, I want to detect the selection of user first. – Sonali Mar 12 '18 at 11:12
  • Using LUIS here may not be useful as the OP explicitely asked for a QnA service, and just need to know if the user wants to use this service or use another service (Google Drive Search) – Nicolas R Mar 13 '18 at 09:00