Как добавить несколько ComponentDialogs? - PullRequest
0 голосов
/ 22 июня 2019

Я создал новый ComponentDialog, создав для него класс, который расширяет ComponentDialog примерно так:

public class GetPersonInfoDialog : ComponentDialog
{
        protected readonly ILogger Logger;

        public GetPersonInfoDialog(IConfiguration configuration, ILogger<GetPersonInfoDialog> logger)
            : base("get-person-info", configuration["ConnectionName"])
        { }
        // code ommitted
    }
}

Затем я добавил его в Startup.cs:

public void ConfigureServices(IServiceCollection services)
    {

        // ...

        services.AddSingleton<GreetingDialog>();
        services.AddTransient<IBot, AuthBot<GreetingDialog>>();

        // My new component dialog:
        services.AddSingleton<GetPersonInfoDialog>();
        services.AddTransient<IBot, AuthBot<GetPersonInfoDialog>>();
    }
}

Но я заметил, что используется только последний диалог. GreetingDialog больше не работает, говоря:

DialogContext.BeginDialogAsync (): диалог с идентификатором приветствия не найден. Диалог должен быть включен в текущий или родительский DialogSet. Например, при создании подкласса ComponentDialog вы можете вызвать AddDialog () в своем конструкторе.

Однако я заметил, что диалоговое окно GetPersonInfo действительно начинается, а диалоговое окно приветствия - больше. Как будто я могу использовать только один или другой. Я заметил, что используется только последний добавленный «переходный процесс», как если бы он перекрывал предыдущие переходные процессы.

Как добавить многокомпонентные диалоги в мой файл Startup.cs? Или я вообще об этом правильно говорю? Я не нашел никакой документации, объясняющей, как иметь несколько ComponentDialogs.

1 Ответ

3 голосов
/ 22 июня 2019

В файле Startup.cs

Я начну с вашего файла Startup.cs, поскольку именно там находится первая проблема, затем я предложу альтернативный дизайн.

То, что вы эффективно делаете со следующим блоком кода:

services.AddSingleton<GreetingDialog>();
services.AddTransient<IBot, AuthBot<GreetingDialog>>();

services.AddSingleton<GetPersonInfoDialog>();
services.AddTransient<IBot, AuthBot<GetPersonInfoDialog>>();
  1. Регистрация одного экземпляра GreetingDialog для вашего бота (по сути, статический).
  2. Регистрация интерфейса IBot для возврата нового AuthBot типа GreetingDialog каждый раз, когда запрашивается IBot.
  3. Регистрация одного экземпляра GetPersonInfoDialog для вашего бота(по сути, статический).
  4. Регистрация интерфейса IBot (снова) для возврата нового AuthBot типа GetPersonInfoDialog каждый раз, когда запрашивается IBot (который перезапишет регистрацию на шаге 2).

Вы можете больше узнать о сроке службы здесь .

Итак, что вы на самом деле хотите, больше похоже на ниже:

public void ConfigureServices(IServiceCollection services)
{
    // Other code

    // Register dialogs
    services.AddTransient<GreetingDialog>();
    services.AddTransient<GetPersonInfoDialog>();

    // Some more code

    // Configure bot
    services.AddTransient<IBot, DialogBot<GreetingDialog>>();
}

Сообщение об ошибке

DialogContext.BeginDialogAsync (): Aдиалог с идентификатором приветствия не найден.Диалог должен быть включен в текущий или родительский DialogSet.Например, при создании подкласса ComponentDialog вы можете вызвать AddDialog () в своем конструкторе.

Это сообщение об ошибке вызвано тем, что ваш GetPersonInfoDialog не знает о вашем GreetingDialog (и не должент).Я считаю, что это ошибка во время выполнения, потому что я помню, как столкнулся с подобной проблемой сам.Поскольку вы не предоставили полную реализацию для вашего GetPersonInfoDialog класса, я должен предположить, что где-то там вы пытаетесь сделать что-то вроде следующего:

dialogContext.BeginDialogAsync("greeting");

or

dialogContext.BeginDialogAsync(nameof(GreetingDialog));

согласно документации первый параметр - это идентификатор диалога для запуска , этот идентификатор также используется для извлечения диалога из стека диалогов.Чтобы вызвать один диалог из другого, вам нужно добавить его в родительский диалог DialogSet.Принятый способ сделать это - добавить вызов внутри конструктора для родительского диалога, например, так:

public ParentDialog(....)
    : base(nameof(ParentDialog)
{
    // Some code

    // Important part
    AddDialog(new ChildDialog(nameof(ChildDialog)));
}

При этом используется метод AddDialog , предоставленный Microsoft.Bot.Builder.Диалоги пакета NuGet и доступны через класс ComponentDialog.

Затем, когда вы хотите отобразить ChildDialog, вы бы позвонили:

dialogContext.BeginDialogAsync(nameof(ChildDialog));

В вашем случае вы можете заменить ParentDialogс GetPersonInfoDialog и ChildDialog с GreetingDialog.Поскольку ваш GreetingDialog может быть использован только один раз (это не служебный диалог, который можно вызывать несколько раз, а с разными аргументами - в этом случае вы захотите указать конкретный идентификатор вместо использования nameof(GreetingDialog)), этоМожно использовать строковое представление имени класса в качестве DialogId, вы можете использовать «приветствие» внутри вызова AddDialog, но вам также придется обновить вызов BeginDialogAsync, чтобы также использовать «приветствие».


Альтернативный дизайн

Поскольку я не верю, что вы хотите, чтобы GreetingDialog или GetPersonInfoDialog были вашими фактическими отправными точками, я бы предложил добавить еще один диалогназывается MainDialog, который наследуется от класса RouterDialog (пакет Microsoft.Bot.Builder.Solutions.Dialogs NuGet).Основываясь на архитектуре (Virtual Assistant Template) здесь , вы получите MainDialog spawn от ваших GreetingDialog и GetPersonInfoDialog.

Предполагая, что ваш GreetingDialog - это только один этап, когда он отправляет карту или какой-либо текст пользователю, чтобы приветствовать их, он может быть полностью заменен методом OnStartAsync, который отправляет вашу карту / сообщение.Получение вашего пользователя к вашему GetPersonInfoDialog будет затем обрабатываться с помощью RouteAsync метода , например, здесь .

Изменения, которые вам нужно будет внести в существующий проект, чтобы подключить это,(при условии, что вы сохраняете GreetingDialog):

  • Добавить временные регистрации в Startup.cs для GreetingDialog, GetPersonInfoDialog и MainDialog.
  • Добавить временную регистрациюдля отображения IBot в AuthBot<MainDialog>
  • Добавить вызовы внутри конструктора MainDialog, чтобы добавить дочерние диалоги GreetingDialog и GetPersonInfoDialog.
  • В OnBeginDialog или OnStartAsync из MainDialog начать свой GreetingDialog.
  • В RouteAsync из MainDialog обрабатывайте любые условия вокруг отображения GetPersonInfoDialog перед его отображением.
  • Могут быть некоторые дополнительные шаги, которые я пропустил.

Полезные ссылки:


Редактировать

Чтобы достичь того, что вы хотите в OAuth-примере , вы можете сделать следующее:

В LogoutDialog.cs изменить:

private async Task<DialogTurnResult> InterruptAsync(DialogContext innerDc, CancellationToken cancellationToken = default(CancellationToken))

до

protected virtual async Task<DialogTurnResult> InterruptAsync(DialogContext innerDc, CancellationToken cancellationToken = default(CancellationToken))

В MainDialog.cs добавить:

protected override async Task<DialogTurnResult> InterruptAsync(DialogContext innerDc, CancellationToken cancellationToken = default(CancellationToken))
{
    if (innerDc.Context.Activity.Type == ActivityTypes.Message)
    {
        var text = innerDc.Context.Activity.Text.ToLowerInvariant();

        if (text == "check email")
        {
            //
            return innerDc.BeginDialogAsync(/*TODO*/);
        }
        else if (text == "check calender")
        {
            //
            return innerDc.BeginDialogAsync(/*TODO*/);
        }
        // etc


        return await base.InterruptAsync(innerDc, cancellationToken);
    }

    return null;
}

вместе с регистрацией ваших диалогов календаря, электронной почты и т. Д. В конструкторе для MainDialog с использованием метода AddDialog.

Я бы посоветовал вам обратить внимание на использование шаблона Virtual Assistant . Поскольку он использует LUIS для определения намерений пользователя (проверьте электронную почту, проверьте календарь) и т.д.), затем направьте их соответствующим образом, соответствующий код находится в , этот метод . Преимущество использования LUIS для определения намерений заключается в возможности связать несколько способов запроса одного и того же намерения, поэтому вы не полагаетесь на то, что ваши пользователи явно набирают «проверить календарь», вы можете «показать мне мой календарь». "," какова моя готовность к следующему понедельнику "," свободен ли я сегодня днем ​​"," проверьте, есть ли у меня какие-либо встречи завтра "и т. д. Фактически Microsoft уже создала Skills для электронной почты и календарь, который Для работы с шаблоном Virtual Assistant достаточно просто перенести код входа в систему для этого шаблона.

...