Добавить в корзинуПозвонить
Найти в Дзене
Mirorstar — ShiftSchedule

Я решил добавить Алису в своё Android-приложение. Думал, дело на пару дней, а в итоге словил 15 граблей и три модерации.

Когда я решил добавить Яндекс Алису в своё Android-приложение, мне казалось, что задача будет сравнительно простой. У меня уже было приложение ShiftSchedule — календарь смен для людей, которые работают не по обычной пятидневке. Пользователь настраивает график 2/2, 3/3, день/ночь, сутки через трое или свой цикл, а приложение считает рабочие дни, выходные, отпуск, больничные, подработки, второй график и общие выходные с партнёром. Оставалось, как мне тогда казалось, просто добавить голосовой слой. Пользователь спрашивает Алису: «Я сегодня работаю?» Алиса берёт данные из приложения и отвечает по его графику. В голове план выглядел красиво: написать команды, подключить Яндекс Диалоги, отправить навык на модерацию, подождать пару дней и выкатить обновление. На практике всё растянулось на недели. Алиса странно распознавала коды, русские буквы ломались в коде, данные между репликами не всегда сохранялись, симулятор работал не так, как реальный навык, а модерация два раза возвращала всё на дор
История о том, как я подключал Яндекс Алису к Android-приложению для сменных графиков: Cloudflare, кириллица, коды привязки, состояния в Яндекс Диалогах и модерация с третьей попытки.
История о том, как я подключал Яндекс Алису к Android-приложению для сменных графиков: Cloudflare, кириллица, коды привязки, состояния в Яндекс Диалогах и модерация с третьей попытки.

Когда я решил добавить Яндекс Алису в своё Android-приложение, мне казалось, что задача будет сравнительно простой.

У меня уже было приложение ShiftSchedule — календарь смен для людей, которые работают не по обычной пятидневке. Пользователь настраивает график 2/2, 3/3, день/ночь, сутки через трое или свой цикл, а приложение считает рабочие дни, выходные, отпуск, больничные, подработки, второй график и общие выходные с партнёром.

Оставалось, как мне тогда казалось, просто добавить голосовой слой.

Пользователь спрашивает Алису:

«Я сегодня работаю?»

Алиса берёт данные из приложения и отвечает по его графику.

В голове план выглядел красиво: написать команды, подключить Яндекс Диалоги, отправить навык на модерацию, подождать пару дней и выкатить обновление.

На практике всё растянулось на недели.

Алиса странно распознавала коды, русские буквы ломались в коде, данные между репликами не всегда сохранялись, симулятор работал не так, как реальный навык, а модерация два раза возвращала всё на доработку.

В итоге функция всё-таки заработала. Теперь можно сказать:

«Алиса, я сегодня работаю?»
«Когда следующий выходной?»
«Сколько смен в этом месяце?»

И получить ответ по своему графику.

Но путь к этому оказался сильно менее прямым, чем я ожидал.

Расскажу, как я подключал Яндекс Алису к Android-приложению для сменных графиков и какие грабли собрал по дороге.

## Как это должно было работать

Навык должен был работать по простой схеме:

приложение сохраняет график в облако, а Алиса потом берёт эти данные и отвечает на вопросы пользователя.

То есть Алиса не читает график напрямую с телефона. Она не может просто заглянуть внутрь приложения на смартфоне. Ей нужны данные, заранее отправленные в облако.

Из-за этого появилось важное условие для старых пользователей: нужно обновиться до версии 1.9.0 или новее, войти в аккаунт и сделать первую синхронизацию.

Без этого навык просто не увидит актуальный график.

Для подключения нужно:

  1. Обновить ShiftSchedule до версии 1.9.0 или новее.
  2. Войти в аккаунт через почту, Яндекс или Google.
  3. Сделать первую синхронизацию в облако.
  4. Сказать: «Алиса, запусти навык Мой график смен».
  5. Назвать шестизначный код из приложения.

После этого навык связывается с графиком и начинает отвечать на вопросы.

Звучит просто. Но дальше началась техническая кухня.

## Русские буквы умеют ломаться

Первая неожиданная проблема прилетела оттуда, откуда я её вообще не ждал: из русского текста.

Часть логики навыка я редактировал через браузерный интерфейс сервиса, где был размещён код. И в какой-то момент русские буквы в коде превратились в набор странных символов.

То есть текст визуально был сломан, команды не совпадали, а навык начинал вести себя непредсказуемо.

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

Для обычного пользователя это звучит как мелочь, но в голосовом помощнике почти всё завязано на текст. Если одна буква распозналась или сохранилась не так, команда может просто не сработать.

## Алиса по-своему слышит коды

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

7HTZCA

Пользователь говорит этот код Алисе, а навык должен понять, какой график подключать.

Но Алиса может распознать такой код не как один цельный набор символов, а с пробелом внутри. Например:

7[пробел]HTZCA

Для человека это почти одно и то же. Для программы — уже нет. Она ждёт шесть символов подряд, а получает часть до пробела и часть после пробела.

В итоге код вроде бы правильный, пользователь его говорит правильно, но навык его не принимает.

Решение оказалось простым: перед проверкой кода нужно убирать все пробелы. Но до этого ещё надо было понять, почему правильный код не работает.

## Состояние навыка не сохранялось

Следующая проблема была ещё неприятнее.

Навык должен помнить, что пользователь уже ввёл код. Иначе каждый раз пришлось бы заново привязывать график.

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

Причин оказалось несколько.

Во-первых, в кабинете Яндекс Диалогов нужно отдельно включить хранилище данных для навыка. Это такая настройка, без которой навык просто не сохраняет нужную информацию.

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

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

Это не самая красивая часть, но она помогла сделать работу стабильнее.

## Симулятор — это не всегда реальность

Когда делаешь навык, хочется всё быстро проверять в симуляторе. Написал команду, отправил, посмотрел ответ.

Но оказалось, что симулятор не всегда ведёт себя точно так же, как реальная Алиса.

Где-то состояние между фразами сохраняется, где-то нет. Где-то команда выглядит правильно, но в реальном использовании приходит немного иначе.

Это неприятный момент: вроде бы тест прошёл, а потом в реальности всё равно всплывает проблема.

После этого я стал относиться к симулятору как к полезному, но не окончательному тесту. Проверять в нём удобно, но полностью доверять ему нельзя.

## Русский язык добавил отдельный слой проблем

Голосовые команды на русском языке — это не просто “сравнить одну строку с другой”.

Человек может сказать:

«следующий выходной»
«ближайший выходной»
«когда выходной»
«через сколько дней выходной»

Смысл похожий, слова разные.

Плюс есть буквы, которые могут по-разному обрабатываться внутри программы. Например, буква «й». Визуально она одна, но технически иногда может храниться разными способами. Для человека разницы нет, а программа может считать, что это разные символы.

Из-за этого часть команд сначала не срабатывала, хотя глазами в тексте всё выглядело правильно.

Пришлось отдельно приводить команды к одному виду, чтобы навык меньше зависел от таких мелочей.

Вот здесь я в очередной раз убедился: когда работаешь с русским языком, особенно в голосовом интерфейсе, простое “сравнить текст” быстро превращается в отдельную задачу.

## Команда «Помощь» сломала модерацию

Одна из самых показательных ошибок была связана с командой «Помощь».

В навыке были команды:

«Помощь»
«Что ты умеешь»

Они должны были показывать пользователю инструкцию. Но я поставил их обработку не в самое начало.

Сначала навык проверял, есть ли у пользователя привязанный код. Если кода нет, он сразу просил ввести код. И только потом где-то ниже по логике находилась помощь.

Для обычного пользователя это ещё можно понять: навык хочет сначала подключиться к графику.

Но модератор Яндекса проверяет навык с чистого старта. Он запускает навык, говорит «Помощь» и ожидает получить инструкцию. А вместо этого навык отвечает: введите код.

И навык отклоняют.

Решение было простым: команды «Помощь» и «Что ты умеешь» должны обрабатываться самыми первыми, ещё до проверки кода и других условий.

После этого помощь стала доступна всегда — даже если пользователь ещё не подключил график.

## Длинные ссылки голосом — плохая идея

Сначала мне хотелось просто говорить пользователю ссылку на инструкцию.

Но если Алиса начинает зачитывать длинный адрес сайта голосом, это превращается в мучение. Слушать длинный URL посимвольно — удовольствие сомнительное.

Поэтому я разделил сценарии.

Если устройство с экраном, например телефон или умный экран, можно показать кнопку со ссылкой.

Если это колонка без экрана, лучше не зачитывать длинный адрес, а дать короткую голосовую подсказку.

Это мелочь, но для пользователя такие мелочи очень заметны.

## Данные в облаке не бесконечные

Ещё один важный момент связан с самим графиком.

Приложение не отправляет в облако бесконечное расписание на годы вперёд. Оно отправляет данные на ограниченный период — примерно на ближайшие 60 дней.

Это значит, что Алиса отвечает по тем данным, которые есть в облаке.

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

Особенно если вы меняете расписание, добавляете отпуск, больничный, подработки или второй график.

## Выходной — это не всегда просто выходной

Была и логическая ошибка.

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

Если человек в отпуске спрашивает «когда выходной?», странно отвечать ему датой после отпуска. Для него отпуск — это тоже отдых.

Пришлось учитывать не только обычные выходные, но и отпуск, больничный и другие нерабочие статусы.

Это хороший пример того, как технически вроде всё правильно, но по-человечески ответ получается неправильным.

## Слишком умный поиск команд тоже мешает

Ещё одна проблема — слишком широкие проверки.

Если навык реагирует просто на одно слово, он может ошибиться. Например, слово «вместе» может встретиться в разных фразах, но не каждая такая фраза относится к общим выходным с партнёром.

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

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

Так стало меньше странных ответов.

## Модерация прошла только с третьего раза

В итоге навык прошёл модерацию с третьей попытки.

Первая отправка — отклонение. Проблемы были с активационными именами и обработкой помощи.

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

Вторая отправка — снова отклонение. Осталась проблема с помощью: при первом запуске без кода команда «Помощь» всё ещё не доходила до нужного ответа.

Третья отправка — одобрено.

Вот так задача “быстро прикрутить Алису” превратилась в отдельную историю с кодами, русским языком, сохранением данных, модерацией и кучей мелких правок.

## Что я понял после этой интеграции

Голосовой навык — это не просто набор фраз и ответов.

Нужно учитывать:

  • как Алиса распознаёт речь;
  • как хранить данные пользователя;
  • что делать, если пользователь ещё не ввёл код;
  • как показать инструкцию;
  • как не зачитывать длинные ссылки;
  • как не ловить ложные команды;
  • как честно объяснить ограничения данных.

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

Несмотря на все грабли, результат мне нравится.

ShiftSchedule постепенно перестаёт быть просто календарём смен. Он становится помощником, который знает расписание и может отвечать на бытовые вопросы.

Иногда проще спросить Алису, чем открывать календарь и искать нужный день вручную.
Иногда проще спросить Алису, чем открывать календарь и искать нужный день вручную.

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

## Маленький лайфхак

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

Если просто сказать:

«Алиса, запусти навык Мой график смен»

то навык откроется, поздоровается и начнёт спрашивать, что именно вы хотите узнать. Это нормально для первого запуска, но в повседневном использовании хочется быстрее.

Поэтому можно задавать вопрос сразу одной фразой:

«Алиса, спроси у навыка “Мой график смен”, работаю ли я завтра?»

В этом случае Алиса сразу передаёт вопрос навыку и отвечает по существу, без лишнего вступления.

Так же можно спрашивать:

«Алиса, спроси у навыка “Мой график смен”, когда следующий выходной?»
«Алиса, спроси у навыка “Мой график смен”, сколько смен в этом месяце?»
«Алиса, спроси у навыка “Мой график смен”, когда отпуск?»
«Алиса, спроси у навыка “Мой график смен”, какие заметки на сегодня?»

Для меня это как раз тот сценарий, ради которого вся интеграция и затевалась. Не открывать приложение, не искать нужный экран, не вспоминать, где что отмечено, а просто спросить голосом и получить ответ.

Не всем это нужно. Кто-то будет по-прежнему открывать приложение и смотреть календарь руками. Это нормально.

Но для тех, кто уже пользуется Яндекс Станцией или часто общается с Алисой, такая интеграция может оказаться удобной.

Если вы уже пользуетесь ShiftSchedule, для подключения навыка нужно обновиться до версии 1.9.0 или новее, войти в аккаунт и сделать первую синхронизацию в облако.

После этого можно сказать:

«Алиса, запусти навык Мой график смен»

А для быстрого запроса сразу используйте формат:

«Алиса, спроси у навыка “Мой график смен”, работаю ли я завтра?»

Полная инструкция и список команд: https://mirorstar.github.io/shiftschedule/articles/alice-navyk.html

Сайт проекта:
https://mirorstar.github.io/shiftschedule/index.html