Search code examples
botframeworkazure-language-understanding

How to call Dialog Class From a LUIS Intent


I am using Bot Framework V4 dispatch model to invoke LUIS and QnA services. I can call the code inside of the top scoring intents class.

However, I couldn't find a way to call an external dialog from it. How can I do that?

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

    namespace Microsoft.BotBuilderSamples
    {
        public class DispatchBot : ActivityHandler
        {
            private ILogger<DispatchBot> _logger;
            private IBotServices _botServices;

            public DispatchBot(IBotServices botServices, ILogger<DispatchBot> logger)
            {
                _logger = logger;
                _botServices = botServices;
            }

            protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
            {
                // First, we use the dispatch model to determine which cognitive service (LUIS or QnA) to use.
                var recognizerResult = await _botServices.Dispatch.RecognizeAsync(turnContext, cancellationToken);

                // Top intent tell us which cognitive service to use.
                var topIntent = recognizerResult.GetTopScoringIntent();

                // Next, we call the dispatcher with the top intent.
                await DispatchToTopIntentAsync(turnContext, topIntent.intent, recognizerResult, cancellationToken);
            }

            protected override async Task OnMembersAddedAsync(IList<ChannelAccount> membersAdded, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
            {
                const string WelcomeText = "Type a greeting, or a question about the weather to get started.";

                foreach (var member in membersAdded)
                {
                    if (member.Id != turnContext.Activity.Recipient.Id)
                    {
                        await turnContext.SendActivityAsync(MessageFactory.Text($"Welcome to Dispatch bot {member.Name}. {WelcomeText}"), cancellationToken);
                    }
                }
            }

            private async Task DispatchToTopIntentAsync(ITurnContext<IMessageActivity> turnContext, string intent, RecognizerResult recognizerResult, CancellationToken cancellationToken)
            {
                switch (intent)
                {
                    case "l_HomeAutomation":
                        //WANT TO CALL THE EXTERNAL DIALOG CLASS FROM HERE
                        //await ProcessHomeAutomationAsync(turnContext, recognizerResult.Properties["luisResult"] as LuisResult, cancellationToken);
                        break;
                    case "l_Weather":
                        await ProcessWeatherAsync(turnContext, recognizerResult.Properties["luisResult"] as LuisResult, cancellationToken);
                        break;
                    case "q_sample-qna":
                        await ProcessSampleQnAAsync(turnContext, cancellationToken);
                        break;
                    default:
                        _logger.LogInformation($"Dispatch unrecognized intent: {intent}.");
                        await turnContext.SendActivityAsync(MessageFactory.Text($"Dispatch unrecognized intent: {intent}."), cancellationToken);
                        break;
                }
            }

            private async Task ProcessHomeAutomationAsync(ITurnContext<IMessageActivity> turnContext, LuisResult luisResult, CancellationToken cancellationToken)
            {
                _logger.LogInformation("ProcessHomeAutomationAsync");

                // Retrieve LUIS result for Process Automation.
                var result = luisResult.ConnectedServiceResult;
                var topIntent = result.TopScoringIntent.Intent; 

                await turnContext.SendActivityAsync(MessageFactory.Text($"HomeAutomation top intent {topIntent}."), cancellationToken);
                await turnContext.SendActivityAsync(MessageFactory.Text($"HomeAutomation intents detected:\n\n{string.Join("\n\n", result.Intents.Select(i => i.Intent))}"), cancellationToken);
                if (luisResult.Entities.Count > 0)
                {
                    await turnContext.SendActivityAsync(MessageFactory.Text($"HomeAutomation entities were found in the message:\n\n{string.Join("\n\n", result.Entities.Select(i => i.Entity))}"), cancellationToken);
                }
            }

            private async Task ProcessWeatherAsync(ITurnContext<IMessageActivity> turnContext, LuisResult luisResult, CancellationToken cancellationToken)
            {
                _logger.LogInformation("ProcessWeatherAsync");

                // Retrieve LUIS results for Weather.
                var result = luisResult.ConnectedServiceResult;
                var topIntent = result.TopScoringIntent.Intent;
                await turnContext.SendActivityAsync(MessageFactory.Text($"ProcessWeather top intent {topIntent}."), cancellationToken);
                await turnContext.SendActivityAsync(MessageFactory.Text($"ProcessWeather Intents detected::\n\n{string.Join("\n\n", result.Intents.Select(i => i.Intent))}"), cancellationToken);
                if (luisResult.Entities.Count > 0)
                {
                    await turnContext.SendActivityAsync(MessageFactory.Text($"ProcessWeather entities were found in the message:\n\n{string.Join("\n\n", result.Entities.Select(i => i.Entity))}"), cancellationToken);
                }
            }

            private async Task ProcessSampleQnAAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
            {
                _logger.LogInformation("ProcessSampleQnAAsync");

                var results = await _botServices.SampleQnA.GetAnswersAsync(turnContext);
                if (results.Any())
                {
                    await turnContext.SendActivityAsync(MessageFactory.Text(results.First().Answer), cancellationToken);
                }
                else
                {
                    await turnContext.SendActivityAsync(MessageFactory.Text("Sorry, could not find an answer in the Q and A system."), cancellationToken);
                }
            }
        }
    }

Solution

  • You'll need to initialize instances of the required dialogs in your DispatchBot, and then invoke them like this: await this.weatherDialog.RunAsync(turnContext, dialogStateAccessor, cancellationToken)

    Here is some sample code. It's slightly different from your design since I am delegating intent detection to a parent dialog and then do the logic branching from there (to keep the bot class logic simple), but how to invoke dialogs is the same.

    Update based on discussion below: If you are using multi turn dialogs, I would suggest you follow the pattern in my code. Otherwise you'll have to deal dialog continue by yourself and good luck with it.