Хакатон Google, и все, что нужно, чтобы начать разрабатывать свои приложения для ассистента. Google организовал хакатон, посвященный технологии Actions On Google. Это хорошая возможность получить опыт и подумать, как начать делать conversation user interface (CUI) для наших приложений. Поэтому мы собрали команду из двух Android-разработчиков: shipa_o, raenardev и дизайнера comradeguest и отправились участвовать.  Actions On Google (AoG) — это способ добавить свое действие в ассистент.
Сделать это можно с помощью 4 инструментов: На хакатоне мы делали навык — приложение, расширяющее возможности ассистента, поэтому на нем и остановимся. После обращения «Окей, гугл. Я хочу поговорить с ${название_приложения}”, ассистент открывает навык, с которым пользователь и ведет диалог: Вам понадобятся два скилла:
— понимание работы Conversational User Interface (CUI), умение их проектировать;
— умение работать с Natural Language Processing (NLP), например, Dialogflow. Чтобы у вашего навыка когда-нибудь появился белковый собеседник, лучше подумать о будущем уже сейчас. Востребованными будут те, которые учитывают контекст использования. Диалоговыми интерфейсами будут пользоваться тогда, когда есть возможность говорить вслух, и взаимодействовать с устройствами голосом удобнее и быстрее, чем руками, глазами и прочими частями тела.  Голосовой интерфейс последовательный. Если на графическом можно показать всю форму оформления заказа, а человек сам будет выбирать, на что посмотреть сначала, а на что потом, то в голосовом задавать вопросы можно только один за другим. Чтобы придумать востребованное и удобное приложение, найдите пересечение между потребностями пользователя и возможностью использования голосового интерфейса (или невозможностью использовать другие).  Первое, что приходит в голову — голосовой помощник для слепых, который помогает решить бытовые задачи. Например, оформить заказ в магазине, вызвать такси, позвонить родственникам. Второе — говорящая книга рецептов для домохозяек, у которых руки в муке. Третье — игры, в которых нужно что-то объяснять. Мы решили начать с простого и разработали робота, который советует людям хорошие фильмы. Мы обыграли несовершенство голосовых синтезаторов: наш помощник даже не притворяется человеком и всячески подчеркивает свою яркую электронную индивидуальность. Google написали отличные гайдлайны о том, как разрабатывать диалоговые интерфейсы. А мы расскажем о том, как проектировали своего говорящего первенца. 1. Обращение (Invocation) Для начала помощника надо позвать. Вызов может быть явным (Explicit Invocation) и косвенным (Implicit Invocation). Явное обращение люди будут использовать, когда уже знают приложение. Косвенное нужно, чтобы Google Assistant мог порекомендовать подходящее приложение в определенной ситуации. Правильно подобранные варианты косвенного обращения — как правильные ключевые слова в контекстной рекламе, только более «человеческие». Описание 
Окей, Гугл, я хочу поговорить с Красным страстным кинороботом.
Позови мне киноробота.
Где там красный и страстный? Важно, чтобы косвенные обращения не были слишком общими. Как и общие ключевые слова в контекстной рекламе, они только мешают найти нужное приложение и понижают рейтинг приложения в выдаче Ассистента. Вызовы могут содержать deep link к отдельным функциям голосового помощника. Например, обычно наш киноробот начинает общение с того, что предлагает человеку выбрать какой-нибудь жанр. Но если его вызовут по косвенному обращению «Хочу посмотреть смешную комедию», логично начать диалог с предложения гарантированно хорошего фильма упомянутого жанра. 2. Первое приветствие Первое приветствие — это то, что говорит человеку приложение сразу после вызова.
Сначала нужно дать пользователю понять, что помощник уже тут: А потом — подсказать, что делать дальше. Наш робот ищет фильмы по жанрам, поэтому мы подсказываем, с каким запросом человек может обратиться дальше:
Что ты хочешь посмотреть: может, комедию, боевик или ужасы? Новых и опытных пользователей можно приветствовать по-разному. Если человек в первый раз общается с вашим помощником, можно немного рассказать о себе. Если не в первый — длинное приветствие будет его раздражать. Поэтому можно сразу перейти к делу: Повторно 3. Разговор по-людски Учите помощника понимать естественную речь и поддерживать беседу. Самый простой способ сделать это — ещё до начала разработки пообщаться с людьми из целевой аудитории. Причем желательно устно, а не письменно, потому что письменная разговорная речь более скудная, чем устная. Сыграйте роль робота, а собеседника попросите представить, что он пользуется вашим будущим приложением. Запишите все диалоги на диктофон, а потом расшифруйте. Это поможет спроектировать схему типовой беседы и найти, где могут появиться ответвления. Разрабатывать свой action для ассистента можно несколькими способами: Ниже нарисовано взаимодействие ассистента с вашим навыком.
Диалог выглядит примерно так: Ассистент переводит речь в текст и отправляет его в ваш action. Текст обрабатывается одним из указанных выше способов. На этой схеме — через Dialogflow. Dialogflow определяет intent (конкретное намерение пользователя) и получает
из него entities (параметры). (Опционально) Dialogflow может вызвать соответствующий webhook, обработать данные на backend и получить ответ. Dialogflow формирует ответ. Ассистент озвучивает ответ, включает микрофон и слушает, что скажет пользователь. Схема устройства action для ассистента Не будем подробно расписывать основы Dialogflow — Google выпустили хорошие обучающие видео.  Будем считать, что вы уже посмотрели видео и разобрались с консолью Dialogflow. Давайте разберем вопросы, которые возникали у нас по каждой из частей в процессе реализации, и что интересного можно отметить.  Помните также о правилах построения хорошего диалога, когда будете переходить к реализации — это повлияет на связку intents, набор entities и использование их в ответах, на использование контекстов и все остальное. Есть рекомендации — сделать более подробное приветствие нового пользователя, а для остальных делать его более кратким. Как это реализовать? В консоли Dialogflow определить такую логику не получится. Это можно делать внутри fulfillment для welcome intent. Иначе говоря, сделать это нужно будет руками.  Это касается и обработки ошибок. Например, в первый раз можно просто переспросить, а во второй — рассказать, какого ответа вы ждете от пользователя.  Через responses это не сделать — будет выбран случайный ответ. Можно сделать через fulfillment или чуть хитрей, завязав на контекст (об этом ниже). "Allow automated expansion" и sys.Any Если фраза похожа по структуре, то при включенном "Allow automated expansion" в качестве распознанной сущности может попасть что-то, что все равно обработать с пользой не получится.  Например — ваше приложение распознает фразу "Посоветуй мне что-нибудь из <жанр>" для советов по фильмам. Если сказать "Посоветуй мне что-нибудь из еды", то в качестве параметра "жанр" вы получите не то, что ожидали бы. Это имеет смысл, если набор жанров у вас постоянно динамически обновляется на бекенде, и там же обрабатывается ответ пользователя, но вы не успеваете обновлять entity. Другое решение — использование sys.Any. В этом случае он передаст все, что было после ожидаемой конструкции, что может быть целым предложением. Это дает более широкие возможности чем automated expansion, но и недостатков больше. Единственное и множественное число, падежи, род Давайте придумаем простой пример. Мы будем говорить про любимые фрукты, а приложение будет отвечать какие они классные. В примерах далее П — пользователь, А — наш action. В этом примере у нас есть entity: Ананасы
Но что если пользователь говорит «Ананас»? Мы можем добавить синоним. По умолчанию используется reference form — т.е. именно исходная форма, не ее синонимы. Именно поэтому приложение не говорит “Да, ананас — самые крутые фрукты!” Стоит это также учитывать при написании ответов. Если бы у нас был ответ “Да, $fruit самый классный!“, то мы бы получили следующее: И не забываем про падежи: Поэтому следует писать как первоначальные значения entity, так и ответы в согласованной форме. Например, все исходные формы entity для фруктов мы можем сделать во множественном числе, а синонимы — в любом. Жанры на русском будем делать в именительном падеже, но в любом числе (драма, ужасы). Когда дойдем до ответов — не будем одновременно писать “Да, $fruit — самые крутые фрукты!” и “Да, $fruit самый классный!” — фрукты у нас всегда во множественном числе. В случае с русским языком стоит пойти еще дальше и подумать как написать согласованный ответ — у нас есть не только падежи (драма/драму), но и род (драма/вестерн). Но как возвращать именно ту форму entity, которую сказал пользователь? В случае с жанрами, синонимом "научной фантастики" могло бы быть "инопланетяне". Тогда если бы пользователь говорил "инопланетяне", в качестве параметра возвращался "научная фантастика".
Если мы хотим получить entity в той форме, в которой сказал пользователь то стоит выбрать значение $entity.original Но тогда возможны проблемы с несогласованностью численности и (особенно) несогласованностью падежей. Действительно ли это нужно? Если да, создавайте entity для единственного, множественного числа и падежей. Ответы также должны быть согласованы с формой entity, которая в них используется. Пожалуй, с этим больше всего проблем.  Input context Это контекст, к которому привязан конкретный intent. На одну и ту же фразу могут реагировать несколько intent'ов, и скорее всего сработает тот, у которого активен входящий контекст.
Таким образом, можно, например, привязать ответ "да/нет" к конкретному вопросу, что и делается при использовании follow-up intent в Dialogflow Output context Это контекст, который активируется при срабатывании intent. Именно так активируются контексты в консоли Dialogflow (в fulfillment это тоже можно делать). Мы указываем число витков диалога, в течении которых он будет активен, а после обнуления счетчика либо по истечению 20 минут он деактивируется. Это значит что данные внутри этого контекста станут больше недоступны и intent'ы, для которых он является входным не будут срабатывать.  На этом же завязан другой трюк: вы можете одним intent активировать контекст, а другим вручную его деактивировать, просто проставив его как output контекст для второго intent с числом ответов 0. Если не хотите писать код в fulfillment, то таким образом можно реализовать интересную логику, например, используя контекст как счетчик, реализовать обработку ошибок, когда ассистент не понимает пользователя. Не нужно перезапускать страничку с assistant preview — когда вы внесли изменения в агент dialogflow, можете дождаться завершения его обучения и сразу же повторить нераспознанную фразу в симуляторе. Dialogflow можно рассматривать как backend, к которому обращается ассистент. Пользуйтесь prebuilt agents — там вы сможете посмотреть, как реализовать типовой сценарий. Будьте осторожны с разделом Small talk. Его использование не выключает микрофон в конце беседы, и такие ответы обычно не содержат call-to-action. Вы не направляете пользователя к следующему витку диалога, и ему не совсем понятно, что следует сказать далее. С большой вероятностью из-за этого вы можете не пройти ревью. Лучше сделать отдельные intents для этого, если вы сможете вписать их в диалог. Не стоит редактировать один и тот же intent вдвоем одновременно. Сейчас одновременная работа нескольких человек не поддерживается — неизвестно, чьи изменения перезапишутся. Если необходимо распараллелить работу с intent — ее можно вести в отдельных проектах, а затем просто выбрать нужные и перенести. Также импорт и экспорт entities в json/xml и импорт/экспорт для intent. Сразу стоит учесть, что вы пишите action для конкретного языка. Написание ответов на русском языке имеет дополнительные нюансы. Так что локализация action выглядит более сложной задачей, чем в случае с GUI мобильных приложений. Учитывайте правила дизайна голосовых интерфейсов — они влияют не только на набор реплик, но и на структуру в целом. Вы строите диалог, поэтому каждый ответ должен оставлять call to action, чтобы пользователь понимал, что сказать. После того, как все будет готово, и вы начнете тестирование, не бойтесь отказываться от отдельных ветвей диалога или форм вопросов. Возможно, на этапе тестирования вы поймете, как связать intents и чего не хватает для удобства использования. Для подключения сервера нужно использовать fulfillment. Для этого есть два варианта:  Рассмотрим самый простой — Inline Editor.  На звание экспертов в node.js мы не претендуем, исправление ошибок в комментариях приветствуется. Важно обращать внимание на версию API Dialogflow.
Последняя версия v2. Все, что написано для версии v1 с ней не работает.
Подробнее про миграцию можно почитать тут. Полезные ссылки: Первым делом, обновите зависимости alpha и beta версий, до последних стабильных.  А теперь давайте разберемся подробнее с кодом. Этот callback будет вызываться для тех intent, у которых Вы активируете fullfilment. При этом код полностью заменяет ответ intent-а из раздела Responses.
Responses вызовется только если в callback отработает с ошибкой, поэтому там можно сделать обработку ошибок. Вынесем функции обработки intent-а из callback.
Функции welcome и fallback находятся в замыкании.  Итак, теперь вы готовы к тому, чтобы написать свой первый навык для Google Assistant. База есть, а к хардкору перейдем в следующей части.