Search code examples
c#dependency-injectionbotframeworkasp.net-core-2.1azure-language-understanding

Is the best practice alwayas to start a dialog in bot?


I am migrating a bot V3 to V4 and it is working well, but the bot has some rules and I am having some difficulties to implement these rules.

One of these rules starts dialog after call Luis, in this Luis recognizes the need.

My doubt is: what is the best practice to bot? Always starts a dialog or only starts if necessary?

PS: in my mind, I have to start if necessary, but I am in doubt.

if (Luis.CheckDialogNeed)
{
   await Dialog.RunAsync(turnContext, ConversationState.CreateProperty<DialogState>(nameof(DialogState)), cancellationToken)
}

If the answer is to start dialog if necessary, how to get dialog's texts outside dialog class. For example, in bot class?


Solution

  • Dialogs are started, continued, and ended using a dialog context, which is a combination of a turn context and a dialog set. A dialog set is created from a dialog state property accessor. Please familiarize yourself with the dialogs documentation. I think you'll be particularly interested in this when it comes to using bot state and creating property accessors. This also contains some code examples for using dialogs.

    Like I said, most of the current Bot Builder samples are built in such a way that all bot logic gets executed inside a dialog. Starting a new dialog from within a dialog is easy because the new dialog just gets added to the dialog stack. If you're using a component dialog then you can even have dialogs inside your dialog.

    The old samples and templates worked a bit differently. Bot state objects (like conversation state and user state) were passed to the bot class using dependency injection, and those bot state objects were used to create state property accessors and then dialog sets. The bot class would then create a dialog context in its OnTurnAsync handler and try to continue the current dialog or begin a new one or just send a one-turn message depending on the dialog stack and the incoming message. You can check out older commits in the git repo to see this in action, or you can check out the new active learning sample because it hasn't been updated to match the pattern of the other samples yet. The bot's constructor looks like this:

    public ActiveLearningBot(ConversationState conversationState, UserState userState, IBotServices botServices)
    {
        botServices = botServices ?? throw new ArgumentNullException(nameof(botServices));
        if (botServices.QnAMakerService == null)
        {
            throw new ArgumentException($"Invalid configuration. Please check your '.bot' file for a QnA service.");
        }
    
        ConversationState = conversationState;
        UserState = userState;
    
        // QnA Maker dialog options
        QnaMakerOptions = new QnAMakerOptions
        {
            Top = 3,
            ScoreThreshold = 0.03F,
        };
    
        _dialogs = new DialogSet(ConversationState.CreateProperty<DialogState>(nameof(DialogState)));
    
        _dialogHelper = new DialogHelper(botServices);
    
        _dialogs.Add(_dialogHelper.QnAMakerActiveLearningDialog);
    }
    

    And then this is the relevant code in OnTurnAsync:

    var dialogContext = await _dialogs.CreateContextAsync(turnContext, cancellationToken);
    var results = await dialogContext.ContinueDialogAsync(cancellationToken);
    switch (results.Status)
    {
        case DialogTurnStatus.Cancelled:
        case DialogTurnStatus.Empty:
            await dialogContext.BeginDialogAsync(_dialogHelper.ActiveLearningDialogName, QnaMakerOptions, cancellationToken);
            break;
        case DialogTurnStatus.Complete:
            break;
        case DialogTurnStatus.Waiting:
            // If there is an active dialog, we don't need to do anything here.
            break;
    }
    
    // Save any state changes that might have occured during the turn.
    await ConversationState.SaveChangesAsync(turnContext, false, cancellationToken);
    await UserState.SaveChangesAsync(turnContext, false, cancellationToken);