Обзор Bot Builder SDK v4

Уже доступна версия Bot Builder SDK 4 . На момент написания данной статьи версия находится в стадии preview, но начинать работать с ней можно уже сейчас. Данная версия нацелена на работу c .NET Core, поэтому следует учитывать, что это не простое добавление дополнительного функционала, не спешите использовать новую версию в ваших существующих проектах без необходимости, для новых же проектов уже можно начинать присматриваться к ней. В статье не будет дано описание всего доступного функционала, а лишь основные моменты, для понимания различий в использовании по сравнению с третьей версией, если вы не работали с третьей версией, думаю, будет просто интересно увидеть принципы работы с Bot Builder SDK, доступным под .NET Core. Давайте же посмотрим, как это всё работает на текущий момент.

Для быстрого старта необходимо скачать Bot Builder SDK v4 template по ссылке.  После установке данного шаблона он станет доступен в шаблонах Visual Studio. Создадим новый проект, на основе данного шаблона:

Шаблон чат-бота для Visual Studio

Созданный проект содержит следующие файлы:

Файлы шаблонного проекта Bot Builder SDK v4

Что есть интересного среди имеющихся файлов:

appsettings.json — данный файл содержит настройки проекта, по умолчанию имеются поля для заполнения MicrosoftAppId и MicrosoftAppPassword

Задание конфигурации чат-бота в Bot Builder SDK v4

BotBuilderV4.bot — данный файл служит для открытия в Bot Framework Emulator, в нём уже прописаны необходимые данные для начала работы с чат-ботом через эмулятор.

Содержимое файла для открытие чат-бота в эмуляторе

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

Работа шаблонного чат-бота в эмуляторе

Бот отображает какое по счету это было сообщение и его текст. Логика работы данного чат-бота описана в файле EchoBot.cs в методе OnTurn

public class EchoBot : IBot
{   
    public async Task OnTurn(ITurnContext context)
    {
        if (context.Activity.Type == ActivityTypes.Message)
        {
            var state = context.GetConversationState();

            state.TurnCount++;

            await context.SendActivity($"Turn {state.TurnCount}: You sent '{context.Activity.Text}'");
        }
    }
}

Как можно заметить, класс описывающий чат-бот сейчас реализует интерфейс IBot, сам интерфейс сейчас содержит только один метод OnTurnAsync:

Task OnTurnAsync(ITurnContext turnContext);

Так как сейчас всё работает в .NET Core, то и вызываться класс чат-бота должен соответственно. Подключение происходит в методе ConfigureServices класса Startup, как добавление дополнительного сервиса, опционально задаётся конфигурация:

services.AddBot(options =>
{
    options.CredentialProvider = new ConfigurationCredentialProvider(Configuration);

    options.Middleware.Add(new CatchExceptionMiddleware(async (context, exception) =>
    {
        await context.TraceActivity("EchoBot Exception", exception);
        await context.SendActivity("Sorry, it looks like something went wrong!");
    }));
    IStorage dataStore = new MemoryStorage();
    options.Middleware.Add(new ConversationState(dataStore));
});

Отслеживание состояния беседы

Фактически во всех чат-ботах необходимо отслеживать состояние беседы, так как получив от пользователя данные необходимо их сохранить и дальнейшие шаги взаимодействия строить уже исходя из полученных данных. В четвёртой версии Bot Builder SDK для получения состояния беседы используется метод context.GetConversationState для получения/задания состояния используется класс, содержащий нужные параметры. Сам метод GetConversationState представляет из себя следующее:

public static TState GetConversationState(this ITurnContext context)
    where TState : class, new()
{
    return ConversationState.Get(context);
}

Место хранение состояние можно задавать при подключении сервиса чат-бота. В примере кода выше можно увидеть строки подключения хранения в памяти:

IStorage dataStore = new MemoryStorage();
options.Middleware.Add(new ConversationState(dataStore));

Хранить состояние можно не только в оперативной памяти, но и в файлах или базе данных. В простейшем случае, для получения каких-то данных, состояние можно использовать следующим образом:

public async Task OnTurn(ITurnContext context)
{

    if (context.Activity.Type == ActivityTypes.ConversationUpdate)
    {
        var newUserName = context.Activity.MembersAdded.FirstOrDefault()?.Name;
        if (!string.Equals("Bot", newUserName))
        {
            await context.SendActivity($"Здравствуйте, {newUserName}, введите ваше реальное имя");
        }
    }

    if (context.Activity.Type == ActivityTypes.Message)
    {
        var state = context.GetConversationState();
        var message = context.Activity.Text;
        if (string.IsNullOrEmpty(state.FirstName))
        {
            state.FirstName = message;
            await context.SendActivity($"Сейчас введите фамилию");
            return;
        }
        if (string.IsNullOrEmpty(state.LastName))
        {
            state.LastName = message;
            await context.SendActivity($"Сейчас введите телефон");
            return;
        }
        if (string.IsNullOrEmpty(state.Phone))
        {
            state.Phone = message;
        }

        await context.SendActivity($"Спасибо, ваши данные получены: {state.FirstName} {state.LastName} - {state.Phone}");
    }
}

Результат работы будет следующим:

Пример сбора информации через чат-бот

Работа с диалогами

Диалоги используются для выделения функционала взаимодействия с пользователем в отдельную область, на подобие функций в языках программирования. Каждый диалог может выполнять определённые действия, предоставляя полученный результат. В четвёртой версии SDK принцип вызова диалогов изменился, на мой взгляд, логически всё стало более правильным, хотя и не привычным. Для работы с диалогами необходимо установить библиотеку Microsoft.Bot.Builder.Dialogs. Для того чтобы работать с диалогами необходимо использовать класс DialogSet Данный класс может принимать диалоги, реализующие интерфейс IDialog, для диалогов должно быть задано уникальное текстовое имя, по которому в дальнейшем они смогут быть вызваны. Добавление диалогов в DialogSet не достаточно для того чтобы они были запущены в чат-боте. Запуск происходит только после того как нужный диалог попадает в стек диалогов (dialog stack), вызовами методов begin или replace.

В настоящий момент поддерживаются 2 типа диалогов:

Prompt — диалог использует как минимум 2 функции, одна из которых используется для получения пользовательских данных, а вторая для их обработки.

Waterfall — позволяет определить последовательность диалогов, которые будет выполняться один за одним. Для данного типа диалога можно задать один шаг, в таком виде он будет работать аналогично prompt-диалогам. Простой пример работы с диалогами (привожу полный листинг класса TestBot):

public class TestBot : IBot
{
    private DialogSet _dialogs;
    public TestBot()
    {
        _dialogs = new DialogSet();

        _dialogs.Add("adduserInfo", new WaterfallStep[]
        {
            async (dc, args, next) =>
            {
                dc.ActiveDialog.State = new Dictionary<string, object>();
                await dc.Prompt("textPrompt", "Введи имя.");
            },
            async (dc, args, next) =>
            {
                dc.ActiveDialog.State["name"] = args["Value"];
                await dc.Prompt("textPrompt", "Введи фамилию?");
            },
            async (dc, args, next) =>
            {
                dc.ActiveDialog.State["lastName"] = args["Value"];
                await dc.Context.SendActivity($"Имя {dc.ActiveDialog.State["name"]}, фамилия {dc.ActiveDialog.State["lastName"]}");
                await dc.End();
            }
        });
        _dialogs.Add("textPrompt", new TextPrompt());
    }

    public async Task OnTurn(ITurnContext context)
    {
        var state = ConversationState<Dictionary<string, object>>.Get(context);
        var dialogCtx = _dialogs.CreateContext(context, state);

        switch (context.Activity.Type)
        {
            case ActivityTypes.Message:
                await dialogCtx.Continue();
                if (!context.Responded)
                {
                    await dialogCtx.Begin("adduserInfo");
                }
                break;
        }
    }
}
Пример работы диалогов в чат-боте используя Bot Builder SDK v4

Константы для работы с ботом, конечно, лучше выносить в отдельный класс. Также для выделения диалогов в отдельные классы можно использовать DialogContainer. Диалоги имеют много возможностей и особенностей работы, но описывать их здесь было бы избыточным для обзорной статьи, постараюсь раскрыть эту тему детальнее в ближайших публикациях.

Работа с карточками

Работа с карточками в Bot Builder SDK четвертой версии осталось такой же. Типы доступных карт можно увидеть на изображении ниже:

Типы доступных карточей в Bot Builder SDK v4

А вот пример простого вызова:

//...здесь пропущен код...
case ActivityTypes.Message:
await context.SendActivity(GetSuggestedActions());
break;
//...здесь пропущен код...
private IActivity GetSuggestedActions()
{
    var suggestedActions = new CardAction[]
    {
        new CardAction
        {
            Type = ActionTypes.ImBack,
            Title = "ImBack",
            Value = "Сообщение из ImBack"
        },
        new CardAction
        {
            Type = ActionTypes.PostBack,
            Title = "PostBack",
            Value = "Сообщение из PostBack"
        },
        new CardAction
        {
            Type = ActionTypes.OpenUrl,
            Title = "Открытие URL",
            Value = "https://dev.botframework.com/"
        }
    };
    return MessageFactory.SuggestedActions(suggestedActions, "Выберите нужный тип действия");
}
Вывод карточек в чат-боте используя Bot Builder SDK v4

Заключение:

На первый взгляд, главная особенность четвёртой версии Bot Builder SDK заключается в её работе под .NET Core. Но, помимо этого, разработчики переработали принципы взаимодействия с чат-ботом, сделав его более удобным, хотя это, конечно, субъективное мнение. В любом случае движение в сторону .NET Core это правильно. Также ваши старые чат-боты, написанные на третьей версии SDK можно запускать под четвертой (только как .NET Framework, а не .NET Core), не знаю насколько безболезненно пройдёт процесс миграции для сложных чат-ботов, но пример подобного уже описан в документации.

На этом всё, приятного программирования.

Добавить комментарий