Happy Halloween! Hello, Ada +19


Сегодня хэллоуин! Считается, что это день, когда души умерших свободно путешествуют по миру живых. Именно сегодня, спешите познакомиться с духом Ады Лавлейс – первого программиста в истории, которая спустя столетия обрела новую жизнь в Telegram. Это стало возможным благодаря двум моим коллегам, которые провели бессонную ночь, воссоздавая по цитатам и историческим текстам виртуальный образ Ады. Для тех же, кто хочет похожим образом оживить кого-то из других исторических персонажей, мы подготовили подробную инструкцию по созданию бота с помощью Microsoft Bot Framework и wit.ai, которую вы найдете под катом.


Disclaimer: Предлагаемый вашему вниманию бот является демонстрацией того, как создавать ботов с помощью Bot Framework и Wit.ai, и не претендует на прохождение теста Тьюринга или даже на способность поддерживать сложную беседу. Поэтому предлагаю не ругать интеллектуальность бота в комментариях. Кроме того, создание данного бота не означает, что мы приветствуем и поощряем различные технологии потустороннего общения.

Мы уже писали про то, как начать разрабатывать своего бота на Microsoft Bot Framework. С тех пор версия Bot Framework немного обновилась, но принципы остались те же. Тем не менее, в прошлый раз мы оставили за пределами статьи самый главный вопрос — как написать интеллектуального бота, способного общаться на естественном языке.

Для этого нам нужно научиться сначала распознавать фразы пользователя, т.е. чего именно он хочет достичь. Такое намерение пользователя, которое он выражает своей фразой, называется интент. Для выделения интентов из естественного языка служат специальные инструменты, такие, как LUIS (входит в состав Microsoft Cognitive Services) или wit.ai. Про использование LUIS мы уже писали, сейчас же мы решили интегрировать бота с wit.ai.

Используем wit.ai


Для начала необходимо зарегистрироваться на сайте wit.ai и создать там приложение.



В рамках приложения необходимо перечислить все интенты, которые мы хотим распознавать в диалоге. Интенты могут иметь сложную природу, различные параметры (например, интент «узнать погоду в городе» может иметь местоположение и дату в качестве параметров) и т.д. Однако в нашем случае для простоты мы ограничимся простыми интентами.



Далее неоходимо обучить логику боты, предоставляя ему примеры фраз, и указывая, какому интенту та или иная фраза соответствует. Чем больше разных фраз вы дадите в качестве примеров, тем лучше будет бот в итоге понимать вашу речь.

Создаем бота в bot framework



Для создания бота вам понадобится:

  1. Microsoft Visual Studio 2015 (с последними обновлениями).
  2. Bot Application Template (необходимо скачать этот файл и скопировать его, не распаковывая, в %USERPROFILE%\Documents\Visual Studio 2015\Templates\ProjectTemplates\Visual C#).
  3. Bot Framework Channel Emulator.

Далее создаем нового бота в Visual Studio: Visual Studio > File > New > Project > Templates > Visual C# > Bot Application.



Name: AdaBot > Ok.

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

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

  • Debug > Start Debugging (или клавиша F5), в результате чего в браузере открывается веб-страница.
  • Запускаем Bot Framework Channel Emulator, если поле Bot URL пустое, копируем в него адрес открывшейся ранее веб-страницы и добавляем «api/messages».
  • Набираем текст и отправляем боту.
  • В ответ получаем повторение отправленной фразы и число символов, из которых она состоит.



Для написания бота осталось лишь поменять пару строчек, отвечающих за формирования ответа. В public async Task<HttpResponseMessage> Post([FromBody]Activity activity) за это отвечают строчки:

// calculate something for us to return
int length = (activity.Text ?? string.Empty).Length;

Нам нужно вместо этого распознать интент пользователя и выбрать в ответ один из вариантов сообщений, соответствующих этому интенту.

Для задания вариантов ответа добавим в приложение файл Responses.xml примерно такого содержания:



<Responses>
 <Response intent="greeting">
    <Text>Здравствуй, мой собеседник!</Text>
    <Text>Приветствую вас!</Text>
    <Text>Добрый день.</Text>
    <Text>Рада с вами побеседовать.</Text>
    <Text>Какая встреча!</Text>
    <Text>Рада вас поприветствовать!</Text>
    <Text>Позвольте поприветствовать вас!</Text>
    <Text>Добрый день. Холодно сегодня.</Text>
    <Text>Кто вы?</Text>
    <Text>Зачем вы потревожили меня?</Text>
    <Text>Здравствуйте! Я живая!</Text>
    <Text>Что происходит?</Text>
 </Response>
...
</Responses>

Так нужно задать несколько вариантов ответа для каждого интента, чтобы бот радовал вас разнообразием.

Чтобы использовать API wit.ai в нашем проекте, подключим к нему соответствующий пакет nuget Wit.ai.net. В Solution Explorer вызываем для AdaBot контекстное меню -> Manage NuGet Packages -> вкладка Browse.



Далее необходимо реализовать логику бота. В файле проекта MessagesController.cs подключаем пространства имен:

using com.valgut.libs.bots.Wit;
using System.Text;
using System.Xml.Linq;


Для поиска интента используем следующий код:

var wit = new WitClient("YOUR WIT SERVER ACCESS TOKEN");
var msg = wit.Converse(activity.From.Id, activity.Text);
var intent = string.Empty; double conf = 0;
try
{
    var a = msg.entities["intent"];
    if (a != null)
    {
        foreach (var z in msg.entities["intent"])
        {
            if (z.confidence > conf)
            {
                conf = z.confidence;
                intent = z.value.ToString();
            }
        }
    }
}
catch (System.Collections.Generic.KeyNotFoundException exc)
{
                    
}

Мы видим, что wit.ai возвращает нам несколько вероятных интентов и их степени достоверности, поэтому мы перебираем их все и выбираем наиболее вероятный из всех. К слову, интентов может и не быть вовсе, тогда в переменной intent остаётся пустая строка, и будет выбран ответ, которому в нашем XML-файле соответствует пустой интент.

Далее осталось лишь выбрать один из возможных ответов, соответствующих найденному интенту. Для этого используем LINQ:

string res = "Я вас не понимаю...";
var doc = XDocument.Load(System.Web.HttpContext.Current.Request.MapPath("~/Responses.xml"));
var r = (from x in doc.Descendants("Response")
         where x.Attribute("intent").Value == intent
         select x).FirstOrDefault();
if (r!=null)
{
    var arr = (from x in r.Descendants("Text") select x.Value).ToArray();
    if (arr!=null && arr.Length>0)
    {
         var rnd = new Random();
        res = arr[rnd.Next(0, arr.Length)];
     }
}

Теперь, получив новое сообщение, бот будет заниматься не подсчетом числа символов в нем, а классификацией интентов и выбором ответа. Для того, чтобы он на входящие сообщения давал выбранный ответ, заменяем строчки:

// return our reply to the user
Activity reply = activity.CreateReply($"You sent {activity.Text} which was {length} characters");

на
Activity reply = activity.CreateReply(res);

Теперь бот умеет разговаривать! Конечно, если вы предварительно натренировали его на распознавание большого количества фраз, и задали множество ответов.

Пример кода вы можете найти тут, правда, без всего разнообразия фраз и без тренировочной выборки для wit.ai. Это пусть останется нашим know how.

Если же вы хотите накануне хеллоуина поговорить с самим ботом Ады Лавлейс — используйте @Ada_Lovelace_Bot в телеграме.

С праздником!
-->


К сожалению, не доступен сервер mySQL