• AppTractor.ru
  • Главная
  • iTunes
  • SoundCloud
  • Podster
  • Чат в Telegram
Подкасты Android Dev
Подкасты Android Dev
  • AppTractor.ru
  • Главная
  • iTunes
  • SoundCloud
  • Podster
  • Чат в Telegram
Как создать приложение для Google Home или Google Assistant
Home
Статья

Как создать приложение для Google Home или Google Assistant

01/11/2017 comments 5100

Facebook Twitter Google+ LinkedIn VKontakte

Главный разработчик в компании Azimo Мирек Станек рассказал о процессе создания простого приложения для голосовых интерфейсов Google.

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

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

Приложение WaterLog

Мы начнем с создания простой программы. WaterLog – это приложение, которое помогает нам отследить ежедневное потребление воды на основе разговора или переписки с Google Assistant. Первая версия приложения сможет отследить, сколько литров или миллилитров воды мы выпили за день. Для простоты мы пропустим теорию дизайна голосовых интерфейсов и сфокусируемся на технической стороне создания работающего приложения.

Вот возможные сценарии бесед:

Новый пользователь

Пользователь: Окей, Google, поговорить с WaterLog.

WaterLog: Добро пожаловать в WaterLog. Вы знаете, что вам следует пить около 3 литров воды в день, чтобы оставаться здоровыми? Сколько вы уже выпили?

Пользователь: Я выпил 500 миллилитров воды.

WaterLog: Ок, я добавил 500 миллилитров воды в ваш дневной журнал. В сумме вы выпили за сегодня 500 миллилитров воды. Дайте мне знать, когда вы будете пить воду в следующий раз! Увидимся позже.

Вернувшийся пользователь

Пользователь: Окей, Google, поговорить с WaterLog.

WaterLog: Добрый день! Сегодня вы выпили 500 мл воды. Какое количество воды мне добавить?

Пользователь: 100 мл.

WaterLog: Ок, я добавил 100 мл воды в ваш дневной журнал. В сумме вы выпили за сегодня 600 мл воды. Дайте мне знать, когда вы будете пить воду в следующий раз! Увидимся позже.

Вернувшийся пользователь спрашивает о записанной воде

Пользователь: Окей, Google, спросить WaterLog, сколько воды я сегодня выпил.

WaterLog: В сумме вы выпили за сегодня 600 мл воды. Дайте мне знать, когда вы будете пить воду в следующий раз! Увидимся позже.

Вы можете протестировать это приложение на своем устройстве.

Начало

Это приложение очень простое, но даже такой проект требует связи некоторых частей, чтобы он заработал. Хотя у нас много свободы в выборе платформы (мы можем создать приложение на разных языках и поместить его в разные облачные хранилища, такие как Google Cloud или AWS), для начала мы возьмем самый распространенный стек:

  • Cloud Functions и база данных Firebase для бэкенда;
  • Dialogflow для понимания естественного языка и диалогов;
  • JavaScript/Node.js для кода приложения (на данный момент Firebase Cloud Functions поддерживает только этот язык);
  • Google Actions SDK для интеграции с Google Assistant (в будущем мы попробуем использовать платформы вроде Amazon Alexa или Facebook Messenger).

Я не буду подробно писать о том, как связать все это вместе. На сайте Google Actions есть хороший пошаговое руководство.

Использование Cloud Functions в Firebase

Если вкратце:

  1. Начните новый проект в консоли Google.
  2. Затем вас попросят выбрать инструмент или платформу для создания приложения для Assistant. Как я и сказал, это будет Dialogflow. Если вы сделаете все верно, ваши приложения Actions и Dialogflow будут соединены. Вы можете проверить это в настройках агента Dialogflow:

Агент Dialogflow

Первой большой частью вашего приложения станет агент для разговоров, в нашем случае он будет создан на платформе Dialogflow. Его самая важная роль – понять, что пользователь говорит приложению, и определить свойства, которыми может управлять наш код. Именно это делают Dialogflow Intents.

Согласно документации:

Intent (намерение) представляет собой связь между тем, что сказал пользователь, и действием, которое должно выполнить приложение.

Давайте определим наши намерения. Вот список предложений, которые мы хотим учесть:

Стандартный запасной вариант

Его мы пока не будем трогать. Как и предполагает название, этот intent вступает в силу, если входные данные от пользователя не соответствуют обычным (документация). Стоит упомянуть, что этот intent даже не указывается в коде нашего приложения, он полностью управляется платформой Dialogflow.

welcome_user

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

— Config —
Action name
: input.welcome
Events: 
WELCOME, GOOGLE_ASSISTANT_WELCOME — events are additional mappings which allow to invoke intents by an event name instead of a user query.
Fulfillment: ✅ Use webhook — Intent welcome_user will be passed to our backend.

log_water

Событие используется, чтобы сохранить, какое количество воды хочет записать пользователь во время диалога. Будет несколько вариантов, которыми мы хотели бы управлять одинаково. Вот некоторые из них:

  • Окей, Google, сказать WaterLog записать 1 литр воды – событие происходит сразу же, как только пользователь вызывает действие. В этом случае приветствие пропускается. Подробнее о вызове ассистента вы можете найти в документации.
  • Записать 500 мл воды – может быть сказано в середине диалога, когда приложение ждет ввода данных.
  • 500 мл – как ответ на вопрос ассистента.

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

Кроме того, нам необходимо комментировать фрагменты наших примеров, которые нужно обрабатывать особым образом, так наше приложение узнает нужное высказывание. Например: “Я выпила 500 миллилитров воды”. Высказывание содержит количество и единицы объема выпитой воды. Всё, что нам нужно сделать, это выделить фрагмент и выбрать нужный объект (смотрите документацию).

— Config —
Action name
: log_water
User says (should be much more examples, esp. in more complex apps):

Fulfillment: ✅ Use webhook
Google assistant: ✅ End conversation — pick this to let Google Assistant know that conversation should be finished here.

get_logged_water

Событие, которое пользователь использует, чтобы узнать сколько воды он или она выпили в текущий день. Как и в случае log_water, есть несколько способов вызвать это событие:

  • Окей, Google, спросить WaterLog, сколько воды я выпил сегодня? – вызывать вместо приветственного события, когда действие известно;
  • Сколько я воды я выпила? – задается в середине беседы с приложением.

— Config —
Action name
: get_logged_water
User says:

Fulfillment: ✅ Use webhook
Google assistant: ✅ End conversation

Это все для конфигурации Dialogflow. Если вы хотите увидеть полный файл конфигурации, вы можете скачать его и импортировать в агент из репозитория (WaterLog.zip).

Код

Если вы следовали гиду Google (Build fulfillment), у вас уже должна быть базовая структура кода, развернутые облачные функции Firebase и связь с агентом Dialogflow через конфигурацию выполнения.

Теперь давайте создадим код для приложения WaterLog. Репозиторий с финальным решением доступен на GitHub.

По сути нам нужно определить все Intents в приложении Dialogflow. Мы определим их в файле functions/assistant-actions.js:

1
2
3
4
5
module.exports = {
ACTION_WELCOME: 'input.welcome',
ACTION_LOG_WATER: 'log_water',
ACTION_GET_LOGGED_WATER: 'get_logged_water'
};

Ядро нашего приложения – файл index.js, который также вызывает HTTP для Firebase Cloud Function (конечная точка):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//...
firebase.initializeApp(functions.config().firebase);
 
exports.waterLog = functions.https.onRequest((request, response) => {
    //Initialise app dependencies
    const dialogflowApp = new DialogflowApp({request, response});
    const userManager = new UserManager(firebase);
    const waterLog = new WaterLog(firebase, userManager);
    const conversation = new Conversation(dialogflowApp, userManager, waterLog);
 
    //Define map of Dialogflow agent Intents
    let actionMap = new Map();
    actionMap.set(Actions.ACTION_WELCOME, () => conversation.actionWelcomeUser());
    actionMap.set(Actions.ACTION_LOG_WATER, () => conversation.actionLogWater());
    actionMap.set(Actions.ACTION_GET_LOGGED_WATER, () => conversation.actionGetLoggedWater());
    
    //Handle request from Dialogflow (will be dispatched into appropriate action defined above)
    dialogflowApp.handleRequest(actionMap);
});

В нашей облачной функции мы определим связи Intents с функциями, которые должны быть вызваны для окончания беседы. Как пример рассмотрим conversation.actionLogWater()(выполнение намерения log_water).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
const ARG_WATER_VOLUME = 'water_volume';
 
//...
class Conversation {
  
  //...
  
  actionLogWater() {
      //Get argument extracted by Dialogflow
      let waterToLog = this.dialogflowApp.getArgument(ARG_WATER_VOLUME);
      //Save logged water into Firebase Realtime Database
      this.waterLog.saveLoggedWater(this._getCurrentUserId(), waterToLog);
      //Load sum of logged water for current user and reply user
      //with how much water he or she logged so far.
      //End the conversation.
      return this.waterLog.getLoggedWaterForUser(this._getCurrentUserId())
          .then(loggedWater => {
              this.dialogflowApp.tell(
                  util.format(Str.WATER_LOGGED_NOW,
                      waterToLog.amount,
                      waterToLog.unit,
                      loggedWater
                  )
              );
          });
  }
}

Что происходит:

  1. Приложение получает аргумент, выделенный из высказывания Dialogflow. Для входных данных Записать 500 мл воды мы получим объект {“amount”:500,”unit”:”ml”} .
  2. Приложение сохраняет эти данные в базу данных Firebase.
  3. В конце приложение получает сумму записанной воды и отправляет её как ответ в объект dialogflowApp. Функция tell() отвечает пользователю и закрывает беседу (документация).

Полный код класса Conversation: conversation.js.

Остальное код не делает ничего интересного. Класс conversation несет ответственность за управление вводными данными пользователя. WaterLog сохраняет данные и извлекает их из базы данных Firebase. UserManager добавляет несколько хелперов для (анонимного) управления пользователями.

Юнит-тестирование

Хотя этот абзац не связан напрямую с приложениями Assistant или голосовыми интерфейсами, я думаю, что он очень важен в каждом типе приложений. Просто представьте, что при каждом изменении кода вам нужно разворачивать функцию и начинать диалог с приложением. В WaterLog это было довольно просто (хотя все равно требует десятки развертываний). В более крупных приложениях юнит-тесты необходимы. Это на порядки ускорит разработку.

Все юнит-тесты для наших классов можно найти в директории functions/test/. В этом проекте тесты не были сложными (они используют библиотеки sinon.js и chai без расширений), но они во многом помогли сделать приложение за короткий срок.

Вот результат $ npm test :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
Conversation
    actionWelcomeUser
      ✓ Should create new anonymous user
      ✓ Should greet new user
      ✓ Should greet existing user
    actionGetLoggedWater
      ✓ Should tell about logged water for given user
    actionLogWater
      ✓ Should save given amount of water and response with saved value
 
  Cloud Functions
    waterLog
 
  UserManager
    isFirstUsage
      ✓ Should return first usage when user doesnt exist in DB
      ✓ Shouldnt return first usage when user exists in DB
    saveAssistantUser
      ✓ Should save assistant user into DB
    ensureAuthUser
      ✓ Should authenticate anonymous user when user isnt authenticated
      ✓ Should return authenticated user
 
  WaterLog
    saveLoggedWater
      ✓ Should save logged mililiters of water
      ✓ Should save logged liters of water
    getLoggedWaterForUser
      ✓ Should load logged water for given user and present data from recent day
 
 
  14 passing (44ms)

Полный исходный код приложения с Firebase Cloud Functions, конфигурацией агента Dialogflow и необходимыми для распространения приложениями активами можно найти на GitHub.

  • Tags
  • Google Assistant
  • разработка
Facebook Twitter Google+ LinkedIn VKontakte
Следующая запись Видео Droidcon NYC 2017
Предыдущая запись Интересные материалы для Android-разработчика #95

Facebook Comments

Другие записи

Трудоустройство Android-разработчиков в России и за рубежом: собеседования, знания, деньги — часть 1.1 Статья
18/06/2018

Трудоустройство Android-разработчиков в России и за рубежом: собеседования, знания, деньги — часть 1.1

38 лучших Open Source Android-проектов на Java Статья
13/04/2018

38 лучших Open Source Android-проектов на Java

Соединяем Android Things со смартфоном при помощи Nearby Connections 2.0 Статья
23/03/2018

Соединяем Android Things со смартфоном при помощи Nearby Connections 2.0

Слушайте
itunes-podcast-app-logo soundcloud-icon thumb17
Рассылка Android Dev
Facebook
Apptractor

Вакансии
  • ASO-специалист

  • Frontend-разработчик

  • iOS-разработчик

  • iOS-разработчик

  • Архитектор чат-бота

  • Контент-креатор

  • AppTractor.ru
  • Главная
  • Обратная связь
  • О проекте
  • RSS
  • Back to top
Android Dev — подкаст о разработке под Android и всем, что с этим связано. Гости программы — разработчики с большим опытом и стажем, которые помнят Android, когда он еще был версии 1.5, и за годы написали приложения для миллионов пользователей по всему миру.
Последние записи
Интересные материалы для Android-разработчика #157

Интересные материалы для Android-разработчика #157

Android Dev Podcast #87. JNI, NDK, private API, инструменты низкоуровневой разработки

Android Dev Podcast #87. JNI, NDK, private API, инструменты низкоуровневой разработки

Метки
Android Android O Android Pay Android Things ConstraintLayout Continuous Delivery Continuous Integration Dagger Droidcon Firebase Flutter Gitflow Google Assistant Google I/O Gradle I/O Instant apps Java Jrebel Kotlin Mobius MVP NDK QA React Native RXJava SDK 23 TensorFlow анимация архитектура безопасность видео дайджест дизайн дополненная реальность интернет вещей конференция кроссплатформенная разработка машинное обучение новости программирование работа разработка статья тестирование
© AppTractor.ru 2017