I'm trying to create a bot that integrates LUIS whose purpose is to search for recipes and products, but I'm struggling to understand a few concepts.
To begin with, I have an issue regarding the flow of the conversation. Let's say the user asks for a recipe but doesn't specify what products he would like to filter. The bot will check if there are any entities (products) in this utterance and if not, it will reply asking for the specific products. So something like:
User: "I want to see recipes"
Bot: "Please specify the ingredients"
User: "Bananas"
This is where I find my first problem. How will the bot be able to understand that the user's last utterance (bananas) is directed to the Recipes Intent and not the Products one?
To try and work around this, I've trained LUIS to direct these entities to go to the None Intent. I've also created a flag that allows me to detect which was the last Intent the user went through. From this I can redirect the bot to the correct intent.
I feel like there must be a better solution than this. Am I missing something here? Is there a way to keep track of the history of intents used? I've also tried using context.Wait, but I believe the method doesn't receive (or return) a LuisResult, which makes it impossible for me to later detect if there are any entities in the user's message.
My second question is, if it is possible for the user to send a message that won't enter any intent and will just be directed to a certain method?
[LuisIntent("aa")]
[LuisIntent("bb")]
public async Task AaIntentSpecified(IDialogContext context, LuisResult result)
{
}
[LuisIntent("")]
public async Task IntentNotSpecified(IDialogContext context, LuisResult result)
{
}
If you left a method like this in luis dialog, then any intents that not has a function mapped will go to this function. The intent "aa" and "bb", will go to AaaIntentSpecified, any other intents like "cc", "dd" ... will go to IntentNotSpecified.
Related
1- I have created Group in MS teams and added few members into group and had given group name as "IT Bot Helper".
2- Now I have added our custom built Bot into this group and once BOT is added to this group OnTeamsMembersAddedAsync method is triggered in the C# code using BOT framework.
public class ITAssistantBot : TeamsActivityHandler
{
protected override async Task OnTeamsMembersAddedAsync(IList<TeamsChannelAccount> teamsMembersAdded, TeamInfo teamInfo, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
{
// Once Bot is added to teams group this method get triggers
// I got all the members which is part of this member
}
}
3- OnTeamsMembersAddedAsync :- I also got the GroupId of this group using this turnContext.Activity.Conversation.Id but somehow I am not able to find the group name which is "IT Bot Helper".
Can someone help here to find the Group Name in this method.
You can take a look at the docs on Teams contexts in bots to see how to get data about a team. If the built-in functionality does not work for you, you could also look into using the Microsoft Graph APIs instead. Graph lets you do many things that might not be natively supported in the Teams context.
In my Bot Framework project, I'm using a PromptDialog to show a predefined set of valid options, with the below code:
var pickListOptions = new List<Option>();
pickListOptions.AddRange(
_currentQuestion.validValues.Select(x => Option.CreateOption(x)));
PromptDialog.Choice(context, choiceSelected,
pickListOptions,
_currentQuestion.label,
"Sorry, I didn't get that", 3, PromptStyle.Keyboard);
When a free-form answer is typed in which is not in the list of valid values is entered, the "Sorry, I didn't get that" message is automatically displayed and the question is reprompted. However, I want to potentially handle certain invalid answers with a different dialog (i.e. if the user is asking for help). Is there any way to override the automatic reprompt with custom logic?
There are likely two valid answers for this question:
If you want to change the validation, you can inherit from the PromptChoice and override the TryParse or the MessageReceivedAsync methods. For example, the CancelablePromptChoice
For handling global commands, like help, instead of doing what I explained in #1, you might want to consider using Scorables. Take a look to the GlobalMessagesHandlers sample to understand more.
I'm developing a bot using Bot Framework, LUIS and ActionBinding.
In one of my intent handlers I call a new Dialog which has the methods StartAsync(IDialogContext context) and ReceiveMessageAsync(IDialogContext context, IAwaitable<IMessageActivity> result)
As I understand it, the messages typed by the user while waiting (with context.Wait(ReceiveMessageAsync)) won't be sent to LUIS, right?
So if I need to understand what the user is saying without having to parse the string, which are my options? Could call the ILuisService.QueryAsync with the message.Text for each message be an option?
I want to be able to detect entities typed by the user so I can map them to missing fields. For example in this conversation:
User: I want to book a flight. // LUIS detects intent
Bot: Ok. Can you tell me more about your flight? // child dialog is called to handle the rest of the conversation
User: I want to go to Madrid.
Bot: To fly to Madrid you can choose between company A, B or C.
User: I want to go with A tomorrow night
Bot: Ok, searching for available tickets for tomorrow night in A...
In this case there are no initial entities when the intent is detected, but there could be, and in that case the bot would not ask for the already given information.
For my project, a simple Form with one to one question-answer is not enough. I also need to make more validations and confirmations on previously set parameters if the user wants to change one or more parameters (i.e., I need to go back to all parameters and check if the changed parameter affects them). For example:
User: Wait, I want to fly to Barcelona instead.
Bot: Company A does not fly to Barcelona. You can choose between C and D.
User: Ok I want to fly with C.
Bot: There are tickets available for tomorrow night in company C. Keep the flight for tomorrow night?
User: yes.
Any tips or guidance for best practices would help a lot.
Thanks in advance.
Edit:
With the Sub Action solution, where would my validators operate? On the FulfillAsync method? I'd need to validate and then send a question to the user and understand the reply he sent (parsing entities). Would that be possible inside a LuisAction?
I'd like to use the QueryValueFromLuisAsync but after looking at it, I'd need to pass the paramName, which is one of the action properties (if i'm not mistaken) and that is what I'm trying to avoid. I don't want to map one answer (i.e., message.Text) to one field, i want to map one answer to multiple fields.
Let's say i need to fill a model that has 6 properties. If the bot asks one question to the user and in his reply there are 3 entities I want to map those entities to 3 fields and only make questions about the remaining non mapped fields afterwards.
My first reaction to this is to avoid using a custom child dialog and go with SubActions and create your own validators if you want to have complex logic there or even override the IsValid method from the sub action.
However, if that's not a possibility, then I would consider reusing the QueryValueFromLuisAsync method, where the action should be the model you want to interact with. That function will end up calling LUIS and will try to assign the result or return another intent/action depending on the scenario. I would give this a try.
Alright, so assuming I am making a fancy web store.
I have a payment provider (say, paypal) which requires the user to sign into paypal website, confirm the credentials and then to redirect him into my website.
So basically the code behind that would look like this:
class PaymentManager
{
public string AcceptPayment(Payment payment)
{
//return redirect url
}
public bool ConfirmPayment(string paymentToken)
{
//if token is valid the payment succeded
}
}
So basically the usage of this manager from my controller maps into 2 controller methods (each requiring an individual request).
Now, assuming I have a different payment manager, which requires 3 methods being sequentially executed instead of 2. Something like:
class AnotherPaymentManager
{
public string AcceptPayment(Payment payment)
{
//return validation redirect url
}
public string ValidatePayment(string validationCode)
{
//return redirect url
}
public bool ConfirmPayment(string paymentToken)
{
//if token is valid, confirm payment
}
}
Now this class' usage maps into 3 controller methods (we need the client to execute the Accept method to declare payment, then to execute the Validate method to validate it and after all to execute the Confirm method to make sure the server has accepted it).
The question is: provided these managers have different API usage scenarios to do the same thing (as shown above), is there a way to make an abstract layer between them and the controller? I mean something like:
interface IPaymentManager
{
void MakePayment(); //this controls the payment methods flow
//Something like (Accept -> Confirm) in the former case
//and (Accept -> Validate -> Confirm) in the latter
}
I am doing this in ASP.NET WebAPI 2, but I think it may apply to MVC as well.
If I understand correctly, when a user creates a transaction they are redirected to the payment provider (with a redirect url in the response). Once there they confirm their credentials which returns them to your fancy web store (with a confirmation token provided by the payment provider). If that token is valid then the transaction was successful. Also each of those actions require a separate endpoint in your controller.
If those assumptions are correct, I would say it is not necessary, or even recommended, to create an abstraction here. Also there is response data (validationCode, paymentToken, etc.) from the payment provider which your PaymentManger functions and controller endpoints are dependent on in order to proceed in the process.
In my experience, trying to abstract too early can make more work for you down the road. Without more information (more implementations of payment provider clients) you might make abstractions that are too specific - which can not be used for different PaymentManager types you add later.
However, if you already posses this data (validationCode, etc.), then you could abstract here, but I would still say it is unnecessary, and potentially a waste of time.
If you are determined to abstract here, then you can implement your interface in each of your PaymentManager classes. Having each PaymentManger implement the MakePayment function which would call the respective PaymentManager functions.
Again, I would not recommend abstracting here. It doesn't make sense, and really won't be that helpful in my opinion. Wait until you implement a few more PaymentManager classes. Then you will be able to more accurately see the patterns between the different types of PaymentMangers and abstract those patterns out.
If my understanding of the problem was not correct, let me know where I misunderstood the problem, and I will try to answer it again.
On a side note, I would recommend looking into asynchronous functions and the await operator, if you haven't already and are making calls to an external API.
Hope this helps.
We can employ form flow to enable user interactions with the bot in a flow of prompts. Here is a great example for doing this for a simple "Order a sandwich" task. I want to know instead of command line prompts can I use real graphical interfaces? And instead of hard-coding the options can I pull the fields from a database or from some external resources through API calls?
None of the messenger channels (i.e. Facebook / Skype / Slack) support complex GUI elements yet (i.e. radio buttons, combo boxes, etc).
If you use the DynamicField elements, you can indeed populate the options from anywhere you like (including a database if you so wish):
.Field(new FieldReflector<BugReport>(nameof(Product))
.SetType(null)
.SetDefine((state, field) =>
{
foreach (var prod in GetProducts())
field
.AddDescription(prod, prod)
.AddTerms(prod, prod);
return Task.FromResult(true);
}))
Where GetProducts returns you a list of products - this could be from a DB, etc.
Sample taken from Dynamic FormFlow Forms in Bot Builder.