Как интегрировать виртуального помощника на Rasa?

от автора

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

Одним из инструментов создания диалоговых помощников является Rasa — сценарная платформа машинного обучения с открытым исходным кодом.

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

Вводная

В рамках задачи нужно было интегрировать помощника Rasa в социальную сеть VK. В этой статье и поговорим о том как это сделать.

Руководство предполагает, что вы уже создали проект Rasa и обучили модель.

Rasa Connector

В Rasa уже имеется модуль для вывода взаимодействия с моделью за пределами командной строки и он называется Rasa Connector.

Коннектор — это модуль, который предоставляет внешним ресурсам делать запросы к Rasa. Он принимает внешний запрос обрабатывает его и приводит к нужному формату, затем отправляя запрос на сервер Rasa. Далее он обрабатывает ответ и отправляет обратно во внешний ресурс.

Архитектура

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

Создание коннектора

Перейдем к написанию кода. Мы рассмотрим создание коннектора на примере VK с использованием Callback API. Но вы также можете использовать это руководство при создании коннекторов для других сервисов.

Шаг 1. Создание структуры файлов

Создаем директорию, например connectors, в корне проекта Rasa и далее в ней создаем файл vk.py в нём мы и будем создавать наш коннектор

Структура файлов

Структура файлов

Шаг 2. Добавление учётных данных

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

Далее добавляем их в credentials.yml, но также нужно написать путь до InputChnnel коннектора: <директория>.<файл>.<название_класса>, так Rasa будет знать к чему относить эти данные.

connectors.vk.VkInputChannel:  access_token: '<YOUR_ACCESS_TOKEN>'  # ключ доступа  confirmation_token: '<YOUR_CONF_TOKEN>'  # строка подтверждение

Шаг 3. Создаем InputChannel

Первое что нам необходимо сделать, это создать свой класс входного канала VkInputChannel, он будет наследоваться от базового класса InputChannel, который предлагает нам Rasa, в нем объявлены методы для определения префикса URL-адреса веб-хука, создания схемы Sanic, а также получения токенов из credentials.yml.

Есть и другие методы, которые мы не будем использовать в данном коннекторе, их можно посмотреть в документации.

Файл vk.py

Шаг 4. Определяем создание обьекта

Объект класса VkInputChannel будет содержать всю информацию которую мы указали для этого канала в credentials.yml. Это необходимо чтобы мы могли отправлять обратные ответы с помощью API.

   def __init__(        self,        access_token: Optional[Text],        confirmation_token: Optional[Text],    ) -> None:        self.access_token = access_token  # ключ доступа        self.confirmation_token = confirmation_token # строка подтверждение

Шаг 5. Переопределение методов

Так как InputChannel предоставляет нам только описание методов, необходимо переопределить их для конкретного канала.

Метод name определяет префикс URL-адреса веб-хука коннектора, итоговый адрес будет выглядеть http://<host>:<port>/webhooks/vk/webhook , где хост и порт это соответствующие значения работающего сервера Rasa.

   @classmethod    def name(cls) -> Text:        return 'vk'

Переопределим еще и метод получения данных из credentials.yml, Rasa сама загружает нужные данные и преобразует их в Dict.

   @classmethod    def from_credentials(        cls,        credentials: Optional[Dict[Text, Any]],    ) -> InputChannel:    # Базовый метод вызова ошибки, при отсутствии credentials.        if not credentials:            cls.raise_missing_credentials_exception()        # Возвращаем __init__ с полученными токенами.        return cls(            credentials.get('access_token'),            credentials.get('confirmation_token'),        )

Шаг 6. Создание OutputChannel

Второй класс который мы создадим это VkOutputChannel, он наследуется от базового класса OutputChannel. Последний реализует методы отправки сообщений разного формата(текст, картинка и тп.) обратно в канал обращения.

class VKOutputChannel(OutputChannel):

Здесь мы тоже определим метод name.

   @classmethod    def name(cls) -> Text:        return 'vk'

В объекте VkOutputChannel, нам потребуется использовать VkAPI для отправки ответного сообщения.

   def __init__(        self,        access_token: Optional[Text],    ) -> None:        self.vk = VkApi(token=access_token)

Переопределяем метод отправки ответа, здесь мы вызываем у обьекта класса VkApi метод отправки текстового сообщения, и задаём user_id и text , а также random_id необходимый для того, чтобы ваш бот не отравлял одно и то же сообщение несколько раз.

   async def send_text_message(        self,        recipient_id: Text,        text: Text,        **kwargs: Any,    ) -> None:        for message_part in text.strip().split('\n\n'):            self.vk.method(                'messages.send',                {                    'user_id': recipient_id,                    'message': message_part,                    'random_id': get_random_id(),                }            ) 

Шаг 7. Добавление метода blueprint в InputChannel

Этот метод будет создает схему Sanic, и закрепляет ее за сервером Sanic, который будет обрабатывать входящие маршруты. В методе мы также создадим объект класса VKOutputChannel .

Sanic — быстрый асинхронный веб‑сервер и веб‑фреймворк, использующий синтаксис async/await.

   def blueprint(        self,        on_new_message: Callable[[UserMessage], Awaitable[Any]]    ) -> Blueprint:        # webhooks/vk        webhook = Blueprint('webhook', __name__)        output_channel = VKOutputChannel(self.access_token)

Нам необходимо создать минимум два маршрута / и /webhook (подробнее в документации).

       # webhooks/vk/        @webhook.route('/', methods=['GET'])        async def health(request: Request) -> HTTPResponse:            return response.json({'status': 'ok'})

На этот маршрут будут прилетать запросы от VK.

   # webhooks/vk/webhook        @webhook.route('/webhook', methods=['POST'])        async def recieve(request: Request) -> HTTPResponse:            # Берем из запроса json            request = request.json            # Если запрос в виде строки, то преобразуем            if isinstance(request, Text):                request = json.loads(request)

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

           # Если пришел некорректный запрос            if 'type' not in request.keys():                return response.text('not vk')

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

          # Обработка запроса строки-подтверждения           if request['type'] == 'confirmation':               return response.text(self.confirmation_token)                      # Обработка нового сообщения от пользователя           if request['type'] == 'message_new':               sender_id = request['object']['message']['from_id']               text = request['object']['message']['text']

И наконец мы говорим Rasa обработать запрос, и формируем UserMessage. Важно отметить, в случае с VK, для каждого события сервер должен отвечать ‘ok’ в случае успешного запроса, подробнее тут.

             metadata = self.get_metadata(request)              # Говорим Rasa обработать сообщение пользователя              await on_new_message(                  UserMessage(                      text,                      output_channel,                      sender_id,                      input_channel=self.name(),                      metadata=metadata,                  )              )            return response.text('ok')      return webhook

Шаг 8. Запуск сервера Rasa

Далее запустите сервер раса c вашей обученой моделью командой:

rasa run

Шаг 9. Настройка Ngrok

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

Установка ngrok

1. Установим и добавим токен ngrok по инструкции.

2. Создаем статический домен ngrok здесь.

3. Далее идём в терминал и открываем туннель с портом 5005

ngrok http --domain=master-positively-flounder.ngrok-free.app 5005

Шаг 10. Настройка сервиса CallbackAPI

1. Для начала создадим сообщество в VK, и в его настройках включим «Сообщения сообщества».

2. Далее переходим в раздел «Работа с API», создаем ключ доступа, его и записываем в credentials.yml.

3. Идём в «CallbackAPI» в разделе «Типы событий» выберем «Входящие сообщения», так бот не будет реагировать на другие типы событий.

4. В «Настройках сервера» нужно скопировать строку которую будет возвращать сервер и записать в credentials.

5. В адрес указываем созданный ранее публичный адрес и подтверждаем.

https://master-positively-flounder.ngrok-free.app/webhooks/vk/webhook

Результат работы

Переходим в диалог с сообществом и теперь мы можем вести общение с нашим помощником.

Как происходит взаимодействие?

На диаграмме ниже я представил взаимодействие всех компонентов.Пользователь обращается к помощнику с приветствием, далее запрос отправляется на публичный адрес ngrok, который перенаправляет его на наш сервер. Запрос обрабатывается классом InputChannel и потом отправляет запрос в Rasa для получения ответа от модели. Rasa возвращает ответ и OutputChannel отправляет его в качестве ответа помощнику, а тот пользователю.

Итог

Надеюсь это руководство было вам полезно! Теперь вы сможете самостоятельно интегрировать чат-бота Rasa, и улучшить user experience во взаимодействии в с диалоговым искусственным интеллектом. Если возникнут проблемы, приглашаю обсудить их в комментариях.

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


ссылка на оригинал статьи https://habr.com/ru/articles/821833/


Комментарии

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *