- PVSM.RU - https://www.pvsm.ru -

Алиса, Google Assistant, Siri, Alexa. Как писать приложения для голосовых ассистентов

image

Рынок голосовых ассистентов расширяется, особенно для русскоязычных пользователей. 2 недели назад Яндекс рассказала впервые про платформу Яндекс.Диалоги, 2 месяца назад Google представила возможность писать диалоги для Google Assistant на русском языке, 2 года назад со сцены Bill Graham Civic Auditorium Apple выпустила в открытое плавание SiriKit. Фактически, появляется новая отрасль разработки, где должны быть свои проектировщики, архитекторы и разработчики. Идеальный момент, чтобы поговорить про голосовые помощники и api для них.

В этой статье не будет подробных туториалов. Это статья об идеях и интересных технических деталях, на которых построены инструменты для сторонних разработчиков основных игроков рынка: Apple Siri, Google Assistant и Алисы от Яндекса.

Теорию без практики изучать скучно. Представим, что перед нами стоит задача от только что придуманной пиццерии «ДоРеМи». Руководство компании хочет, чтобы покупатель имел возможность узнать меню пиццерии и заказать пиццу голосом. Заказ еды оставим на вторую итерацию, а сейчас займемся меню. Добавим команду «Что входит в состав <Название пиццы>?». Если пользователь вводит некорректную команду, то вывод будет состоять из списка пицц. Задача простая. Идеально подходит, чтобы изучить технологию и подготовиться к дальнейшему расширению.

Первый пункт — поднять бэкэнд

ВНИМАНИЕ! Android разработчик поднимает сервер на node.js. Слабонервным лучше пропустить эту часть.

Сервер нам нужен для хранения информации о пиццах и для дальнейшего взаимодействия с апи ассистентов. Бэкэнд будет написан на node.js вместе с фреймворком express для настройки веб-приложения. Разворачивать будем на платформе Now от компании Zeit [1]. Платформа бесплатна и проста в использовании. Вводим команду «now» в терминале для старта скрипта деплоя и в ответ получаем ссылку на наше веб-приложение.

Для инициализации проекта используем Express Generator. Результатом генерации будет отличный каркас для веб приложений, но здесь много лишнего для простого апи: шаблоны для страниц, страницы ошибок, папки для ресурсов. Оставим только самое необходимое.

image

Базу данных использовать не будем. Данные у нас статические, достаточно будет одного объекта в js — список пицц с названиями и ингредиентами.

const pizzas = [
   {
       name: "Маргарита",
       ingredients: ["Тесто", "Томатный соус", "Сыр Моцарелла", "Томаты", "Базилик"]
   },
   {
       name: "Пепперони",
       ingredients: ["Тесто", "Томатный соус", "Сыр Моцарелла", "Пепперони"]
   },
   {
       name: "Вегетарианская",
       ingredients: ["Тесто", "Томатный соус", "Сыр Моцарелла", "Грибы", "Маслины", "Зеленый перец", "Сыр Фета", "Томаты", "Орегано"]
   },
   {
       name: "Четыре сыра",
       ingredients: ["Тесто", "Томатный соус", "Сыр Моцарелла", "Сыр пармезан", "Сыр Чеддер", "Сыр Блючиз"]
   },
   {
       name: "Гавайская",
       ingredients: ["Тесто", "Томатный соус", "Сыр Моцарелла", "Куриное филе", "Ананасы"]
   },
];

Добавим метод, который по объекту пиццы будет выводить ее состав. Если же пицца не найдена, то ответом будет меню пиццерии.

const pizzaInfo = {

   getPizzaInfoByPizzaName: function (pizza) {
   const wrapName = name => `"${name}"`

   if (!pizza) {
       const pizzaNames = pizzas.map(pizza => wrapName(pizza.name)).join(", ")
       return `В ассортимент пиццерии "ДоРеМи" входят пиццы ${pizzaNames}. Могу рассказать состав каждой пиццы.`
   }

   const ingredients = pizza.ingredients.map(ingredient => ingredient.toLowerCase()).join(", ")
   return `В пиццу ${wrapName(pizza.name)} входят ${ingredients}.`
},
};

Яндекс.Алиса. Начнем с простого

Яндекс.Диалоги — базовая комплектация машины, на которой можно ездить. Но все равно не хватает кондиционера. Платформа от Яндекса идеальна для изучения азов: простая, как три копейки, но при этом содержит в себе концепции, на которых построено большинство ассистентов.

Основная единица платформы — диалоги. Диалоги — скиллы, созданные сторонними разработчиками. Добавить новый функционал в основной разговор с ассистентом не получится. Хотелось бы взять фразу «Алиса, закажи мне пиццу», но пиццерий много. Пользователю придется сказать активационную команду: «Алиса, вызови мне ДоРеМи». Тогда сервис понимает, что нужно переключиться на диалог от «ДоРеМи». Мы принимаем власть в свои руки и управляем процессом на своем сервере, через реквесты и респонсы, используя технологию вебхуков.

Что такое вебхуки?

Вебхук, по своей сути, POST запрос, который отправляется на сервер. Сервер настроен на прием запроса, его обработку и отправление ответа на url, который указал клиент. Клиент при этом не тратит время на ожидание ответа.

Работает примерно так.

Вы приходите в магазин, набираете тележку товаров. Очередь в магазине одна, очень длинная. В обычных супермаркетах вам пришлось бы ее отстоять и потерять огромное количество времени. В параллельной вселенной вы оставляете свою тележку в очереди, а сами занимаетесь другими делами. Персонал магазина вас находит и отдает пакеты. Первый подход — аналогия API, второй — вебхуков.

Настройки, которые нужно прописать для создания диалога в личном кабинете: название, тематика диалога, активационное имя и url на сервер.
image
Дальше только настройка сервера на обработку запросов. Принимаем json, отправляем json. А еще проще, если отбросить все шелуху с приемом json, его парсингом, с извлечением данных и обратными действиями в процессе отправки ответа, то мы принимаем текст пользователя и возвращаем текст Алисы. Добро пожаловать в 70-е, во времена текстовых интерфейсов.

У нас есть строка с командой пользователя. Чтобы возвращать состав пиццы по команде пользователя, нам нужно вычленить название пиццы и в ответ прислать фразу. Вычленять будем обычным string.contains(фраза). Чтобы идея сработала, модернизируем наш список пицц, добавив к ним список основ (морфема слова без окончания), которые могут встречаться в запросе.

const pizzas = [
   {
       name: "Маргарита",
       base_name: ["маргарит"],
       ingredients: ["Тесто", "Томатный соус", "Сыр Моцарелла", "Томаты", "Базилик"]
   },
   {
       name: "Пепперони",
       base_name: ["пепперони", "пеперони", "пепирони"],
       ingredients: ["Тесто", "Томатный соус", "Сыр Моцарелла", "Пепперони"]
   },
   {
       name: "Вегетарианская",
       base_name: ["вегетариан"],
       ingredients: ["Тесто", "Томатный соус", "Сыр Моцарелла", "Грибы", "Маслины", "Зеленый перец", "Сыр Фета", "Томаты", "Орегано"]
   },
   {
       name: "Четыре сыра",
       base_name: ["четыр", "сыр"],
       ingredients: ["Тесто", "Томатный соус", "Сыр Моцарелла", "Сыр пармезан", "Сыр Чеддер", "Сыр Блючиз"]
   },
   {
       name: "Гавайская",
       base_name: ["гавай"],
       ingredients: ["Тесто", "Томатный соус", "Сыр Моцарелла", "Куриное филе", "Ананасы"]
   },
];

Немного изменим функцию, которая возвращает состав пиццы по команде пользователя.

getPizzaInfoByUserCommand: function (command) {
   command = command.toLowerCase();
   const pizza = pizzas.find(pizza => (
       pizza.base_name.some(base => (command.indexOf(base) !== -1))
   ))

   return this.getPizzaInfoByPizzaName(pizza)
},

Обрабатываем JSON, отправляем корректный ответ и добавляем кнопку, которая перенаправит пользователя на сайт пиццерии. Кнопки в Алисе — единственная возможность привнести разнообразие в обычный текстовый вывод. На кнопку можно назначить реплику пользователя либо открытие браузера по url. Используйте deep linking, чтобы связать ассистент и приложение в один удобный процесс. Например, при заказе пиццы можно настроить переход на экран оплаты в приложении, где уже сохранены платежные данные или есть возможность оплатить через Google/Apple Pay.

var express = require('express');
var pizzaInfo = require('../pizza/pizza_info.js');
var router = express.Router();

/* GET home page. */

router.use('/', function (req, res, next) {
   const body = req.body;

   const commandText = body.request.command;
   const answer = pizzaInfo.getPizzaInfoByUserCommand(commandText);

   res.json({
       "response": {
           "text": answer,
           "buttons": [{
               "title": "Заказать",
               "url": "https://doremi.fake/"
           }
           ],
           "end_session": false
       },
       "session": {
           "session_id": body.session.session_id,
           "message_id": body.session.message_id,
           "user_id": body.session.user_id
       },
       "version": body.version
   })

});

С помощью параметра tts(text-to-speach) можно настроить фонотеку голосового ответа Алисы: ударение, произношение и пробелы. В tts лучше передавать транскрипцию вместо орфографически правильного написания. Например, «пажалуста». Так речь Алисы будет естественнее.

Тестировать диалог проще некуда. В личном кабинете можно поговорить через консоль со своим диалогом и почитать json’ы.

image

На данный момент диалог в стадии черновика. Следующим шагом будет публикация в каталоге Яндекса. Перед публикацией он проходит проверку на соответствие требованиям Яндекса: достоверность информации, грамотность, этичность и остальные формальные качества.

Google Assistant. Новый уровень

Если Диалоги — это базовая комплектация автомобиля, то Actions on Google — комплектация с массажным креслом, автопилотом и персональным водителем, инструкция к которым прилагается на китайском языке. Инструмент от Google сильнее, богаче, но сложнее. И входной порог в технологию выше. У Яндекса гениально лаконичная и простая документация. Про Google сказать такое не могу. Actions on Google построен на тех же аксиомах, что и Диалоги: активационная команда, общение через апи, использование вебхуков, отделение стороннего диалога от основного.

Простота — главное преимущество и проблема Диалогов. Проблема в том, что всю архитектуру нужно строить самому. Простейший алгоритм вычленения частей из текста пользователя, реализованный выше, нельзя расширить на новые команды. Приходиться изобретать велосипеды. В такие моменты понимаешь, почему графический UI до сих рулит. Но Google реализовал продукты, которые освобождают разработчика от скучных скриптуемых процессов: классификация команд пользователя и работа с реквестами и респонсами. Первая задача решается фреймворком DialogFlow aka Api.Ai, вторая — объемной библиотекой под node.js. Нам остается подсоединить апи к Actions через node.js. На первый взгляд, что это лишнее усложнение, но сейчас я покажу, что этот подход выигрывает в проектах, где команд больше, чем одна.

DialogFlow решает типичную задачу машинного обучения — задачу классификации, в нашем случае классификация пользовательских команд по категориям. Для понимания и настройки работы фреймворка разберем два понятия из терминологии DialogFlow. Первое — Entities или сущности. Например, марки машин, города или названия пиццы. В настройках сущности мы указываем примеры сущности и ее синонимы. Алгоритм будет пытаться зацепиться за сущность на уровне основ слов. В случае успеха Google пришлет ее на сервер в качестве аргумента.

image

Второе понятие — Intents или действия — категории, по которым DialogFlow будет классифицировать команды пользователя. Мы добавляем примеры команд, по которым будет определяться интент. В примерах команд лучше использовать примеры сущностей, которые были добавлены на первом шаге. Так алгоритму будет проще научиться вычленять нужные нам аргументы. Главная фишка DialogFlow — на основе введенных шаблонов нейросети Google тренируются и генерируют новые ключевые фразы. Чем больше шаблонов мы добавим, тем корректнее будет определяться интент. Не забудем добавить идентификационное имя для интента, которым мы будем дальше пользоваться в коде.

image

У интента есть имя, есть список параметров. Не хватает возвращаемого значения. В настройках можно добавить статические ответы. Динамические ответы — зона ответственности js кода. Далее я буду расхваливать вторую вещь, которая делает подход гугла еще круче — официальная библиотека к node.js. Она лишает радости парсить json и заниматься маршрутизацией интентов через длинные if’ы или switch-case блоки.

Инициализируем объект DialogflowApp, в конструктор передадим request и response. Через метод getArgument() мы получаем сущность из команды, с помощью tell() передаем ответ помощника, через handleRequest() настраиваем маршрутизацию в зависимости от интента.

const express = require('express');
const Assistant = require('actions-on-google').DialogflowApp;
const pizzaInfo = require('../pizza/pizza_info.js');
const app = express.Router();

// запрос на обработку вебхуков
app.use('/', function (req, res, next) {
   // Инициализируем API.AI assistant объект.
   const assistant = new Assistant({request: req, response: res});

   const ASK_INGREDIENTS_ACTION = 'listOfIngredients';  // Название интента
   const PIZZA_PARAMETER = 'pizza'; // Название сущности

   function getIngredients(assistant) {
       let pizzaName = assistant.getArgument(PIZZA_PARAMETER);
       // Respond to the user with the current temperature.
       assistant.tell(pizzaInfo.getPizzaInfoByUserCommand(pizzaName));
   }

   // Настраиваем маршрутизацию
   let actionRouter = new Map();
   actionRouter.set(ASK_INGREDIENTS_ACTION, getIngredients);
   assistant.handleRequest(actionRouter);
});

module.exports = app;

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

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

image

Для более скрупулезного тестирования есть симулятор или девайс с Google now.

image

Ответ в Google Assistant может состоять не только из текста, но и разных ui элементов: кнопки, карточки, карусели, списки.

image

На этом стоит остановиться. Дальнейшие тонкости технологии — материал на несколько статей. Те основы, которые были рассказаны сейчас, уже дают огромную пользу в построении своего приложения для Google Assistant. Закон Парето в действии.

Apple SiriKit. Кратко о том, почему Siri отстает

Если Диалоги — это базовая комплектация автомобиля, а Actions on Google — полная комплектация, то SiriKit — это метро с двумя станциями на всю Москву.

Две особенности, которые делают подход Apple не таким, как все — привязанность к основному приложение и обязательное соответствие одному из сценариев использования, прописанных Apple, то есть полное отсутствие кастомизации разговора. По первому пункту все понятно — без основного приложение на девайсе не будет диалога в Siri. Ваш диалог лишь дополнение к приложению.

Второй пункт является главным недостатком SiriKit. Все диалоги, весь текст уже прописан. Можно только добавить немного синонимов в вокабуляр Siri или сверстать виджет, который появится по запросу. Это единственная свобода, которую дает Apple.

Вам повезло, если вы хотите сделать что-то похожее на команды из скриншота ниже. Нам не повезло.

image

Если на WWDC 2018 Apple не поменяет кардинально подход к кастомным диалогам, то тогда Siri так и останется внизу топа. Голосовые помощники — это операционные системы будущего. Систему делают крутой приложения. Когда их нельзя нормально сделать, система проигрывает. Именно из-за этого IOS в топе. Именно из-за этого Siri отстает в гонке.

Экспертное мнение. Про Amazon Alexa, продакшн и будущее.

Думаю, что голосовая разработка на нашем рынке в ближайшее время перейдет из развлекательной штуки во что-то серьезное, в продакшн. Точкой отсчета, скорее всего, будет официальный анонс русскоязычного Google Assistant, то есть Google I/O 2018. Надо морально подготовиться и поучиться у западных коллег. Расспросил нашего друга, Максима Кокоша, Team Lead-а из Omnigon. Он работал с Assistant и Alexa.

image
Максим Кокош, Team Lead Omnigon

Расскажи в общих словах, что ты разрабатывал?
Я занимался доработкой одного скилла для Alexa и портированием другого c Alexa на Actions on Google при использовании DialogFlow. Причем в очень сжатые сроки, неделя была на портирование, неделя на доработку Alexa скила.

Про Амазоновский проект мы ничего не знаем.
Вот ты в статье написал про Алису, Siri, Google Assistant, а про Alexa нет. Это как сравнивать Android и Symbian и забыть про iOS.

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

Кстати, я бы не стал подход Алисы называть автомобилем. Двухколесная повозка на ослиной тяге, это максимум. По сравнению с Google Actions и Alexa, там все совсем плохо. Парсить руками string'и в 2018 году звучит как дикость.

Как ты думаешь, почему аудитория у Alexa намного больше?
Мне кажется, это из-за того, что Google позже вступил в игру. Очень мало вкладывается в рекламу. Хотя судя по тому, что ассистент есть или может быть установлен почти на каждом Android устройстве, они могли бы стать популярней.

В чем особенности Alexa?
В Alexа удобно работать с состоянием в рамках сессии. Например, ты просишь включить свет в ванной. Получается Intent «Включить свет», а entity — ванная. Затем ты говоришь: «Выключить». И вот тут нам пригодится контекст внутри сессии. Во время обработки интента мы можем выставить состояние «bathroom» и использовать его при получении следующих интентов. У Google есть Follow-up интенты, отвечающие аналогичным целям, но они не такие гибкие.

У Alexa явно говорится, как установить скиллы. Это знакомый пользователю подход — магазин скиллов. У Google скилы ставятся автоматически.

Review процесс очень строгий на обеих платформах. Ревьюверы следят, чтобы каждый response заканчивался точкой, чтобы взаимодействие с пользователем выглядело натурально, для этого у каждой платформы есть свои гайдлайны, чтобы не было грамматических ошибок, даже в описании, а текста там очень много. Ревью от Amazon обычно занимало 2-3 дня, в Google справились за 1 день.

Сама разработка Google Actions показалось более простой: захостил на Firebase action, подключил его в 2 клика, и вот у тебя уже все готово к разработке. Если ты хочешь делать запросы наружу, нужно платить. Если будешь обращаться только к сервисам Google, то можно и бесплатно. AWS, ввиду большей загруженности, выглядит запутанней.

У Google можно [2] ботов делать при помощи Google таблиц, функциональность весьма ограничена, однако позволяет писать ботов без скилов программирования и подойдет для мелких задач.

DialogFlow также в теории позволяет в один клик подключать твоих ботов к слаку, Telegram, Cortana и тому подобному. Там куча интеграций, правда коллаборация с Alexa не работает.

image

В принципе, имея знания об Actions on Google, можешь работать с Alexa.

Мы с тобой люди из мобильной сферы. Отличаются ли бизнес-процессы мобильной и голосовой разработки?
На мой вкус, процесс весьма похож, интенты можно рассматривать как экраны, entity — как данные в них, также есть система состояний. С точки зрения тестирования тоже все похоже: требуется корректная обработка случаев с потерей интернета, неожиданных респонсов от API и т.д.

Может ли разработка диалогов стать такой же популярной, как сейчас мобильная разработка?
Мне кажется, ассистенты не смогут достичь популярности мобильных приложений, ибо не все сценарии можно переложить на устное взаимодействие + с экрана информация воспринимается намного быстрее, чем на слух. Также не всегда у пользователя есть возможность устно вести диалог с ассистентом, особенно когда это касается sensitive данных. Голосовые ассистенты могут быть достаточно утилитарны, например, «закажи такси» или «закажи пиццу», но надолго увлечь пользователя вряд ли удастся. Это, скорее, эффектное дополнение к дому или автомобилю.

Какое будущее у голосовых ассистентов?
Голосовые ассистенты займут свою нишу и достаточно большую. И станут таким же обычным делом, как Android Auto и CarPlay среди машин. Уже продано 20 миллионов Amazon Echo девайсов и 4.6 миллиона Google Home девайсов. Не стоит забывать, что множество Android-телефонов оборудованы Google Assistant.

Макс, спасибо большое за подробные ответы.
Надеюсь, они хоть как-то помогут. :)

Что нас ждет впереди

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

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

  1. Весь код из статьи [3]
  2. Документация по Алисе [4]
  3. Короткое видео про то, как сделать диалог для Google Assistant [5]
  4. Как настроить DialogFlow для Telegram бота [6]
  5. Google vs Amazon [7]

Автор: Максим Бачинский

Источник [8]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/apple/277216

Ссылки в тексте:

[1] Now от компании Zeit: https://zeit.co/now

[2] можно: https://developers.google.com/actions/templates/first-app

[3] Весь код из статьи: https://github.com/TouchInstinct/VoiceAssistantsBackend

[4] Документация по Алисе: https://tech.yandex.ru/dialogs/alice/doc/about-docpage/

[5] Короткое видео про то, как сделать диалог для Google Assistant: https://youtu.be/qCY0cG9R3H4

[6] Как настроить DialogFlow для Telegram бота: https://habrahabr.ru/post/342762/

[7] Google vs Amazon: https://medium.com/archieai/google-actions-vs-alexa-skill-a-comparative-study-4e7bdd3c3c62

[8] Источник: https://habrahabr.ru/post/352982/?utm_source=habrahabr&utm_medium=rss&utm_campaign=352982