I have been asked a question a lot recently – is it possible to pass messages / activities between dialogs in Microsoft Bot Framework? By doing this you could have a root dialog handling your conversation, but then hand off the message activity to another dialog. One common example of this is using the LUIS service to recognise a user’s intent, but handing off to a dialog powered by the QnA Maker service if no intent is triggered.
Thankfully this is very simple to do.
Normally to add a new dialog to the stack we would use context.call which adds a dialog to the top of the stack. However, there is another method which was added some time ago but is not as widely known, context.forward, allowing us to not only call a child dialog and add it to the stack, but also let us pass an item to the dialog as well, just as if it was the root dialog receiving a message activity.
The example code below shows you how to forward to fallback to a dialog that uses the QnA Maker if no intent is identified within a LUIS dialog.
1 2 3 4 5 6 7 |
[LuisIntent("")] public async Task None(IDialogContext context, IAwaitable<IMessageActivity> message, LuisResult result) { var faqDialog = new FaqDialog(); var messageToForward = await message; await context.Forward(faqDialog, AfterFAQDialog, messageToForward, CancellationToken.None); } |
In the example above, a new instance of the FaqDialog class is created and the forward method takes the incoming message (which you can get as a parameter from the LUIS intent handler), passes it to the new dialog and also specifies a callback for when the new child dialog has completed, in this case AfterFAQDialog.
Once it has finished, the AfterFAQDialog will call context.Done and in the example will pass a Boolean to indicate if an FAQ answer was found – if the dialog returns false then we can provide an appropriate message to the user.
1 2 3 4 5 6 7 8 9 10 |
private async Task AfterFAQDialog(IDialogContext context, IAwaitable<bool> result) { var messageHandled = await result; if (!messageHandled) { await context.PostAsync("Sorry, I wasn't sure what you wanted."); } context.Wait(MessageReceived); } |
That’s it, it is super simple and unlocks the much asked for scenario of using LUIS and QnAMaker together, falling back from one to the other.
When I forward my messages from LUIS to Qna Maker Dialog, I cannot use active learning on QnA dialog.
Do you know if it possible to use it ?
(When I enable active learning, the bot doesn’t respond to questions with multiple answers).
I haven’t updated my QnA Dialog to include active learning yet, but it is on the list. There is an official dialog for QnA Maker on NuGet.
Hello Gary
in this example AfterFAQDialog method takes an IAwaitable but the context.Forward expects a callback method witch takes and IAwaitable …
However, if I change the AfterFAQDialog to:
Task AfterFAQDialog(IDialogContext context, IAwaitable result)
I receive an exception when I try to await the ‘result’ parameter telling me this:
“invalid type: expected Microsoft.Bot.Connector.IMessageActivity, have Boolean”
Do you have a clue on what I’m missing ?
I’m not sure. Can you share some basic code so I can see? Follow me on twitter and send me a message and I can share an email address via DM for you.
Hello Gary,
Thanks for the post and it’s really helpful & useful. I have one simple query:-
QnA Maker and LUIS is two different services and both use NLP internally. QnA Maker used for Question and Answer, LUIS used to create intent, entity, and utterance.
Is LUIS target to fall back purpose only or can we use in below format also.
Query -> Luis -> Pick Logical Intent (Using Dialog or API) -> Pass to QnA Maker -> Get Answer. It means only “None” intent will be used as fall back.
Is my understanding correct here? If not please share your blogpost on this.
You are correct. You can use all of the parts in whatever order you wish, just passing context using context.forward or context.call between the dialogs.