Пора действовать в отношении действий Google (… каламбур 😀 )
На Google I / O 2017 компания продемонстрировала Actions on Google (или Google Action) и объявила Developer Challenge по созданию новых Actions для Google Assistant на Google Home.
С этого момента все еще относительно новая технология, я хотел бы поделиться с вами коротким руководством о том, как вы можете создавать свои собственные действия. Я буду использовать Dialogflow (также известный как api.ai), платформу взаимодействия с пользователем, чтобы помочь нам создать агента / бота, с которым мы можем общаться на человеческом языке (голосом или текстом), и Node.js для создания веб-перехватчика. / на стороне сервера.
Наше новое действие: тренер по математике!
Идея этого действия пришла ко мне, когда Google Home присоединился к нашей семье. Дети сразу же усыновили «ее» и стали ежедневно с ней общаться. Поскольку мы обучаем своих детей на дому, многие запросы к Google Home носили образовательный характер. Я подумал, что если бы я мог совместить развлечения, которые дети играли с Google Home, с возможностью для них практиковать то, что они изучали, это было бы идеально. Так началась работа с Math Trainer.
Я считаю, что у каждого продукта должна быть четкая цель, которая поможет определить его развитие. Итак, цель Math Trainer - создать действие Google, которое побуждает детей заниматься математикой.
Имея это в виду, мы можем начать:
Определение нашего действия
Архитектура этого решения основана на использовании Dialogflow для обработки человеческого естественного языка (ввод) и преобразования его на наш сервер в виде структурированных данных (вывод). Затем сервер (Node.js) решит, как продолжить разговор, и ответит правильным ответом.
Поскольку мы имеем дело с беседой, давайте начнем с определения потоков и сценариев беседы:
- Приветственное приветствие: когда пользователь просит Google Assistant поговорить с агентом (нашим «ботом действий»), агент должен будет начать разговор. Было бы хорошо дать пользователю краткое введение, объяснить, что такое агент, и начать разговор. Наш тренер по математике начнет с «Привет» и задаст первый вопрос.
- Прощальное приветствие: пользователи могут разговаривать только с одним агентом за раз, поэтому, когда пользователь хочет завершить разговор, он вернется в Google Assistant. Было бы неплохо попрощаться с пользователем и, возможно, поощрить его вернуться в будущем.
- Пользователь дает ответ: сейчас я разделю этот сценарий на 2 варианта:
1. Правильный ответ: агент ответит положительно и задаст следующий вопрос, чтобы можно было продолжить разговор.
2. Неправильный ответ: агент ответит отрицательно и попросит пользователя повторить попытку, чтобы продолжить разговор. - Резервное намерение по умолчанию: когда Dialogflow не может сопоставить ввод пользователя ни с одним из сценариев, он отправит его на сервер в качестве резервного по умолчанию. Это позволит справиться с ситуацией, когда пользователь потерялся в разговоре и его нужно вернуть в нужное русло.
Позже я конвертирую эти сценарии в намерения, чтобы настроить их в Dialogflow.
Теперь, когда мы определили наш продукт, давайте настроим его и напишем код.
Создание тренера по математике
Мы начнем с создания двух учетных записей, одну на https://console.actions.google.com, а другую на https://console.dialogflow.com. Учетная запись Google позволит нам создавать действия, тестировать и отправлять их в Google Assistant, а учетная запись Dialogflow позволит нам создать агента, который будет переводить человеческий язык в структуру данных, которую может обрабатывать наш сервер.
После того, как мы настроили учетные записи, мы можем начать процесс:
- В actions.google создайте новый проект. Дайте ему имя и нажмите кнопку Создать проект.
- В разделе «Добавить действия в приложение» выберите Dialogflow.
- Нажмите кнопку Создать действия в Dialogflow, чтобы открыть новую вкладку с консолью Dialogflow.
- Там вы можете добавить детали, если хотите, и нажмите Сохранить.
Создание намерений
Как только действие будет создано в Dialogflow, консоль перенесет вас в окно намерений. Это сердце вашего приложения (душа будет в коде JS). Намерения будут собирать вводимые пользователем данные на человеческом языке и переводить их в действия, которые может обрабатывать код.
Начните с удаления намерения «Добро пожаловать по умолчанию» и создайте новое с именем «math_trainer» (вы можете увидеть кнопку «Создать намерение» вверху).
- В Контекстах добавьте «викторину» к выходным контекстам. Контексты помогают нам понять, в каком состоянии мы находимся в разговоре.
- В поле Пользователь говорит мы добавим текст, который ожидаем получить от пользователя, чтобы начать действие. А пока просто добавьте предложение: «Тренер по математике». Я объясню больше о том, что говорит пользователь, в следующем намерении.
- В События добавьте «GOOGLE_ASSISTANT_WELCOME», чтобы помощник Google знал, что это первое намерение, которое запускается при обращении пользователя к приложению.
- В Действие напишите «generate_question». Это будет действие, которое код будет использовать для этого намерения.
Пришло время сохранить намерение, нажав кнопку Сохранить в правом верхнем углу. (Кнопка Сохранить - это то, что вам всегда нужно помнить при работе в Dialogflow).
Поздравляю! Вы только что сделали свое первое намерение! Теперь нам просто нужно создать еще несколько, чтобы обеспечить полноценный диалог.
Следующее намерение будет называться «quit_trainer», и вы можете догадаться, что оно будет делать.
- В Контекстах добавьте «викторину» к входным и выходным контекстам.
- Пользователь говорит, будет следующее:
Я сдаюсь
Стоп
Выйти
Здесь мы просто приводим несколько примеров. о том, что пользователь может сказать, если он / она хотел бы завершить разговор. Dialogflow будет использовать свое машинное обучение, чтобы «перевести» больше предложений в это намерение. - Действие будет «выйти».
Таким образом, мы позаботимся об этом случае (не забудьте нажать «Сохранить»!), Так что теперь нам нужно еще одно намерение, прежде чем мы сможем погрузиться в реальное кодирование.
Затем мы создадим намерение под названием «provide_answer».
- Еще раз добавьте «викторину» во входные и выходные Контексты.
- (Мы заполним Пользователь говорит через мгновение)
- В разделе Действие напишите check_answer в названии действия и заполните первую строку в таблице ниже:
ТРЕБУЕТСЯ: необходимо отметить. < br /> ИМЯ ПАРАМЕТРА: answer
ENTITY: @sys .number
VALUE: $ answer
Теперь вернемся к Пользователь говорит и добавим строку «это 45». Как только вы нажмете Enter, строка будет добавлена в список, платформа отметит «45» желтым цветом, и появится новая всплывающая строка, которая покажет вам, что она понимает, что это значение 45.
Это настоящая магия Dialogflow: он может слушать человеческий естественный язык и преобразовывать его в структурированные данные, с которыми мы затем можем играть в коде.
Приведем еще несколько примеров. Добавьте следующие строки:
Думаю, это 12
27
Мой ответ 42
Примечание. Если по какой-то причине система не отметила номер, вы можете сделать это вручную, выделив номер мышью и выбрав @sys .number: answer из всплывающего списка.
Опять же, не забудьте сохранить новое намерение.
На самом деле у нас есть еще одно намерение в списке, «Резервное намерение по умолчанию», которое обрабатывает все события «потеряны в разговоре», но поскольку мы получаем это намерение от системы, нам не нужно с ним ничего делать ( теперь).
Написание «души» приложения (кодирование на JS)
Итак, теперь, когда у нас есть намерение входа, намерение выхода и намерение ответа, мы можем начать писать некоторый код.
Для Webhook я буду использовать Node.js всего с двумя файлами: index.js и package.json. Вы можете скачать их с github: bit.ly/MathTrainerV1.
(Примечание: объяснение о package.json на самом деле не входит в объем этой публикации, но если вы хотите, чтобы я объяснил это более подробно, дайте мне знать в комментариях.)
Теперь приступим к работе с нашим основным файлом: index.js. Начнем с некоторых объявлений среды:
'use strict'; process.env.DEBUG = 'actions-on-google:*'; let ApiAiAssistant = require('actions-on-google').ApiAiAssistant; let sprintf = require('sprintf-js').sprintf;
Установите константы:
// The context and actions as we declare them in api.ai const QUIZ_CONTEXT = 'quiz'; const GENERATE_QUESTION_ACTION = 'generate_question'; const CHECK_ANSWER_ACTION = 'check_answer'; const QUIT_ACTION = 'quit'; const DEFAULT_FALLBACK_ACTION = 'input.unknown';
А теперь давайте настроим несколько строк для обработки разговора:
const WRONG_ANSWER = 'No, it\'s not %s. Try again.'; const CORRECT_ANSWER = 'Correct!'; const WELCOME_MESSAGE = 'Welcome to the math trainer!'; const ASK_QUESTION = 'How much is %s'; const QUIT_MESSAGE = 'See you later.';
И, наконец, зададим логическую константу:
const min = 0; const max = 10;
Затем давайте создадим функцию для подключения к Dialogflow:
exports.math_trainer = function (request, response) { // print the request to the log //console.log('headers: ' + JSON.stringify(request.headers)); //console.log('body: ' + JSON.stringify(request.body)); const assistant = new ApiAiAssistant({request: request, response: response});
Давайте оставим эту функцию открытой и добавим некоторые другие функции, которые будут обрабатывать ввод / вывод нашего действия.
Для простоты я решил не хранить никаких данных пользователя или сеанса. Чтобы отслеживать разговор, я отправлю все необходимые данные через помощника. В данном случае это будет вопрос и ответ, поэтому, когда мы получим ответ пользователя, мы сможем сравнить его с тем, что содержится в файле assistant.data.
Чтобы Math Trainer мог вести правильный «разговор», мы создадим функцию для обработки каждого намерения и закроем функцию math_trainer:
function generateQuestion (assistant) { console.log('generateQuestion'); let newQuestion = getNextQuestion(); assistant.data.answer = newQuestion.answer; assistant.data.question = newQuestion.question; assistant.setContext(QUIZ_CONTEXT); assistant.ask(printf(WELCOME_MESSAGE + ' ' + ASK_QUESTION, newQuestion.question)); } function checkAnswer (assistant) { console.log('checkAnswer'); let answer = assistant.data.answer; //let question = assistant.data.question; let userAnswer = assistant.getArgument("number")? parseInt(assistant.getArgument("number")): ''; if (answer! = userAnswer) { assistant.ask( printf(WRONG_ANSWER, userAnswer)); } else { let newQuestion = getNextQuestion(); assistant.data.answer = newQuestion.answer; assistant.data.question = newQuestion.question; assistant.ask(printf(CORRECT_ANSWER + ' ' + ASK_QUESTION, newQuestion.question)); } } function defaultFallback (assistant) { console.log('defaultFallback: ' + assistant.data.fallbackCount); if (assistant.data.fallbackCount === undefined) { assistant.data.fallbackCount = 0; } assistant.data.fallbackCount++; assistant.ask("WHAT? I asked how much is "+ assistant.data.question); } /** * Use Tell to send goodbye message and close the mic * @param assistant */ function quit (assistant) { console.log('quit'); assistant.tell(QUIT_MESSAGE); } /** * Use sprintf to reformat the string and add params to it * @param line * @returns {*} */ function printf(line) { console.log('printf: ' + line); return sprintf.apply(this, arguments); } // Map all the actions that create on api.ai to the function in this file let actionsMap = new Map(); actionsMap.set(GENERATE_QUESTION_ACTION, generateQuestion); actionsMap.set(CHECK_ANSWER_ACTION, checkAnswer); actionsMap.set(DEFAULT_FALLBACK_ACTION, defaultFallback); actionsMap.set(QUIT_ACTION, quit); assistant.handleRequest(actionsMap); };
Наконец, нам нужна еще одна функция для генерации вопросов:
/** * Randomize the next question * @returns {{answer: number, question: string}} */ function getNextQuestion (){ let value1 = Math.floor(Math.random() * (max - min + 1)) + min; let value2 = Math.floor(Math.random() * (max - min + 1)) + min; let res = { answer: (value1 + value2), question: sprintf(PLUS_QUESTION, value1, value2) }; console.log(JSON.stringify(res)); return res; }
Теперь мы готовы к запуску!
Запуск кода
Обычно я сначала запускаю код локально, но для экономии времени давайте запустим код на общедоступном сервере.
Вам нужно будет запустить экземпляр node.js в общедоступном домене с SSL. Если у вас нет под рукой, вы можете использовать Google App Engine. Чтобы узнать, как это сделать, см .: https://cloud.google.com/functions/docs/tutorials/http.
Когда у вас есть рабочий URL-адрес с запущенным на нем кодом, перейдите в свой проект в Dialogflow и выберите Выполнение. Включите Webhook, установите URL-адрес в поле URL-адреса и сохраните его.
Теперь перейдите к намерениям и в каждом из них найдите раздел «Выполнение» внизу и включите Использовать веб-перехватчик.
Теперь мы установили соединение между Dialogflow и нашим сервером Node.js, поэтому мы можем перейти к настройке соединения между Dialogflow и Actions в Google.
Откройте раздел Интеграции в интерфейсе Dialogflow и нажмите Действия в Google. В окне настроек убедитесь, что для параметра «Добро пожаловать» установлено значение «math_trainer», и нажмите Обновить.
Используйте кнопку Посетить консоль, чтобы вернуться на console.actions.google.com.
Заполните всю информацию о приложении, которую запрашивает система. Затем в разделе Возможности поверхности задайте для всех вопросов ответ «Нет».
Нажмите Проверить, чтобы убедиться, что он выполняет то, что мы хотим, затем следуйте инструкциям в Симуляторе действий и протестируйте новое действие.
Поздравляем, вы только что создали новую акцию!
Но если вы немного поиграете с этим, вы согласитесь со мной, что он на самом деле не соответствует определению ״ Math Trainer - это действие Google, которое побуждает детей заниматься математикой. ״ Это скучно и не требует создать приятный опыт для разговора. Повторение одних и тех же предложений ни у кого не вызовет желания продолжить разговор, особенно у ребенка.
Итак, теперь нам нужно спроектировать личность агента и спроектировать разговор агента так, чтобы он соответствовал человеку, но мы поработаем над всем этим в следующем посте.
На случай, если вам было интересно, мое вдохновение для этой статьи пришло из статьи Google о действии числа-джинна. Вы можете попробовать это, сказав Поговори с джинном в Google Home.