На сегодняшний день существует много интегрированных информационных систем и клиентских приложений, и при работе с ними у пользователей возникают проблемы различной степени сложности, и чтобы разгрузить и улучшить качество взаимодействия с ними, в разрабатываются диалоговые помощники и виртуальные консультанты с использованием искусственного интеллекта и технологиями 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/
Добавить комментарий