Сегодня очень любят использовать слово «умный»: умный дом, умный телевизор, умный пылесос. Хотя чаще всего речь идёт не о некой мифической «умности» прибора или приложения, а об автоматизации определённых рутинных действий. Вот и мы сделали «умным» наш Календарь: автоматизировали добавление в него событий на основе важных входящих писем. Я - Дима, тимлид команды машинного обучения Почты Mail.ru, и на примере этого функционала хочу рассказать о том, какой путь проходит фича от рождения до передачи в эксплуатацию.
Так как статья получилась по объёму полноценным лонгридом, для удобства восприятия её решили разбить на 2 части. В первой части обсудим продуктовые вопросы о том, как происходит проработка идей до того, как фича пойдёт в разработку, и технические - как быстро собрать прототип для проверки гипотез. Во второй части коснёмся того, как дорабатывали и масштабировали прототип, как готовили данные, какие ML-модели пробовали и какие финальные результаты получили.
Появление идеи
Начинается всё как обычно с того, что "а было бы неплохо, чтобы …" - то есть с появления идеи.
![Как нейросеть Kandinsky видит появление идеи Как нейросеть видит появление идеи](https://habrastorage.org/getpro/habr/upload_files/0bf/ede/984/0bfede9844ee3a794fd5a7fc6756f1ac.png)
Идея может прийти из разных источников: через бета-программу, в результате мозгового штурма, через глубинное интервью с пользователями, от коллеги в кафетерии и т.д. В случае с добавлением событий из писем идея появилась после нескольких отзывов от коллег и анализа конкурентов.
Случай, с которого всё началось, был связан с доставкой посылок в почтовое отделение. Срок хранения посылки обычно составляет 15-30 дней, а Почта России отдельно не напоминала о том, что срок заканчивается. Если не поставить напоминание самостоятельно, то можно легко забыть, что посылку пора забирать. Так и произошло.
Недовольные коллеги передали идею менеджерам, и закрутилось…
Проработка идеи
После того, как идея попала к менеджеру, начинается следующий этап — проработка. Продакт-менеджер идёт к аналитикам и разработчикам и вместе с ними проясняет ряд вопросов:
Какую долю аудитории покроет эта фича?
Как она повлияет на основные метрики продукта (аудиторные, метрики удовлетворённости и т.п.)?
Могут ли быть другие сценарии, которые расширят аудиторию и будут полезны для бизнеса и пользователей?
Какова трудоемкость разработки?
На этом этапе становится понятно, стоит ли идея дальнейшей проработки. Если функциональность полезная, но её будет видеть всего лишь пара сотен человек в день, то разработка того не стоит. Точно также не стоит вкладываться в фичу, которая никак не повлияет ни на метрики бизнеса, ни на пользовательский опыт. Таким образом у продакт-менеджеров появляется инструмент скоринга идей (в том числе для приоритизации бэклога).
В этом случае мы решили расширить список сценариев использования, которые могут быть потенциально полезны пользователю: записи к врачу, походы в кино и театры, авиаперелёты и поездки на поездах и автобусах, бронирование жилья. В Почте Mail.ru есть категоризация входящего потока: все важные пользователю письма разделяются на смысловые категории (финансы, путешествия, культурные мероприятия и т.д.), поэтому прикинуть DAU/MAU по категориям было довольно просто.
Исследования
Далее требовалось выяснить, насколько эта функциональность будет полезна пользователю. Для этого опросили активных пользователей почты, получающих письма с нужными категориями. Выяснилось, что большинство респондентов хотят получать напоминания о событиях, но при этом не хотят самостоятельно ставить напоминания в календарь.
![Исследование пользователей календаря. Kandinsky 2.1, нейрохолст, нейромасло. Исследование пользователей календаря. Нейрохолст, нейромасло.](https://habrastorage.org/getpro/habr/upload_files/dd6/250/ab3/dd6250ab3ccd1ac220c3d62af8044f13.png)
Также в результате UX-исследований стало понятно, что в зависимости от типа события возможны различные пользовательские сценарии. Например, в кинотеатре надо быстро найти вложение с билетом, чтобы посмотреть зал и места. При посещении поликлиники полезно иметь под рукой ФИО врача и номер кабинета, а иногда и адрес поликлиники. При посадке на поезд — номер поезда и свои места в вагоне. В случае бронирования отеля — адрес и телефон. Таким образом мы выяснили, что гораздо полезнее не просто напомнить о событии, а ещё и сохранить полезную информацию по нему в календарь.
На второй итерации исследований UX-лаборатория выяснила, когда именно пользователи хотели бы получать уведомления в зависимости от сценария. Например, напоминание о посещении кинотеатра удобно получать за 1-2 часа, чтобы успеть собраться и выехать. А уведомление об истечении срока хранения заказа можно получить и за пару дней.
После проведённых опросов и исследований стало понятно, что:
Большинство пользователей хотело бы получать напоминания о предстоящих событиях, но не хочет самостоятельно их ставить.
Часто нужен быстрый доступ к информации о событии в зависимости от сценария (адрес, места, телефон и т.д.).
Итоговая функциональность должна выглядеть примерно так: пользователю приходит письмо с событием; определяется, к какому сценарию оно относится; в зависимости от сценария выделяется информация о событии; оно ставится в календарь и в нужный момент отправляется push-уведомление. Момент прихода уведомления конфигурируется автоматически в зависимости от сценария (кино, авиаперелёт и т.п.).
Проектирование системы
Как правило, до начала разработки нового сервиса разработчики пишут дизайн-документ будущей системы. Это позволяет заранее ответить на ряд важных технических вопросов (тайминги, стандартная и пиковая нагрузка, масштабируемость и т.п.) и выстроить примерный план разработки с контрольными точками.
У нас есть шаблон дизайн-документа с вопросами, на которые надо ответить перед началом разработки (вдохновлялись этой статьей). Там же прописываются альтернативные решения, продумываются риски разработки и эксплуатации системы. После написания дизайн-документа он обсуждается и «прожаривается» сотрудниками соседних команд, чтобы получить более широкий взгляд на проблему.
![Программист вносит правки в дизайн-документ (да, не самое веселое занятие) (с) Kandinsky 2.1 Программист пишет дизайн-документ (да, не самое веселое занятие)](https://habrastorage.org/getpro/habr/upload_files/cc8/f16/78a/cc8f1678a9d5fabcdb8a3c9d0d34a2da.png)
В нашем случае всё тоже начиналось с дизайн-документа. Для начала требовалось решить, будет система работать online или near-online.
В Почте есть ML-сервисы, работающие как online на потоке, так и в формате отложенной обработки. Например, категоризация писем (”умная сортировка”) работает в онлайне, то есть письма классифицируются до доставки в ящик пользователю. Логично, что требования по таймингам в случае онлайн-обработки достаточно жёсткие, так как через почтовые сервера ежеминутно проходит огромное количество писем.
В нашем случае подходит отложенная (near-online) обработка. Во-первых, нам не нужно обрабатывать весь поток писем (в котором подавляющую часть составляют рекламные рассылки), а только его небольшую часть с событиями. Во-вторых, поставить событие в календарь можно и спустя какое-то время после доставки письма в ящик пользователю. К тому же относительно мягкие требования к таймингам и нагрузке позволят нам использовать более сложные модели машинного обучения.
Для отложенной обработки писем в почтовой команде уже сложилась проверенная временем схема. Структурно она состоит из ML-сервиса, который обрабатывает письма, выделяет нужную информацию и передаёт ответ в сервис бэкенда Почты, который сохраняет эту информацию куда-то, например, в метаданные письма или в календарь.
Также после предварительного анализа данных стало ясно, что нужно уметь не только ставить событие в календарь, но и в ряде случае удалять его. Не нужно напоминать о событии, если пользователь отменил перелёт, сдал билеты в кино или уже забрал свой заказ из пункта выдачи. Таким образом, ML-сервис должен для события прислать его статус: нужно ли поставить его в календарь или удалить из календаря.
Ещё один нюанс, который проявился на этапе проектирования, связан с часовыми поясами. Дело в том, что календарь должен корректно присылать уведомления пользователю вне зависимости от его местонахождения. То есть в случае перелета Москва-Новосибирск-Москва push с напоминанием должен всегда приходить за три часа до вылета по местному времени. А это значит, что нужно каким-то образом определять и часовой пояс пользователя.
Вкратце, до начала разработки:
Составили дизайн-документ системы, в котором ответили на основные вопросы (нагрузка, тайминги и т.п.).
Выбрали схему отложенной обработки с уже обкатанной связкой «ML-сервис + бэкенд почты».
Поняли, что ML-сервис должен выделять из писем определённый набор сущностей в зависимости от сценария. Зафиксировали, какие поля при каком сценарии надо отдавать, какие из них обязательные, а какие дополнительные.
Поняли, что ML-сервис должен присылать ещё и статус события (поставить или удалить). Соответственно, каждому событию нужно формировать уникальный идентификатор.
Поняли, что нужно учитывать часовой пояс, в котором происходит событие, чтобы корректно его отображать в календаре и корректно присылать уведомления.
Набросали и согласовали формат общения между ML-сервисом и почтовым бэкендом.
Разработка MVP
Сразу делать сложную систему, которая выделяет из всего многообразия писем нужную информацию о событиях, выглядело довольно дорогой затеей. Хотелось быстро проверить, будут ли люди будут пользоваться таким функционалом, позитивно реагируя на уведомления. То есть нужно было сделать MVP (Minimal Viable Product) - минимально жизнеспособную версию продукта, которая решает часть пользовательских сценариев.
Каноническая картинка про MVP ниже: самокат/велосипед в данном случае и являются MVP, тогда как машина - финальной версией продукта
![Источник Источник](https://habrastorage.org/getpro/habr/upload_files/844/e00/e83/844e00e83d486159dd748d9d26d50fc2.png)
Самую первую версию функционала проще всего было сделать, если бы отправители сами присылали нам информацию о событии, а мы просто вытаскивали ее и заполняли пользовательский календарь. На самом деле есть ряд случаев, когда отправители могут самостоятельно добавлять структурированные данные в письмо. Например, это можно сделать, встраивая в HTML-тело письма микроразметку формата JSON-LD. Таким образом можно удобно передавать и отображать в интерфейсе данные о предстоящем событии, перелёте, заказе и т.д. Например, первую версию прототипа плашки заказов над письмами мы делали на JSON-LD, а потом уже масштабировали с помощью моделей машинного обучения.
![Плашка заказа в моем ящике, отрисованная на основе встроенного в письме JSON-LD Плашка заказа в моем ящике, отрисованная на основе встроенного в письме JSON-LD](https://habrastorage.org/getpro/habr/upload_files/366/8f0/ef8/3668f0ef827f60cf5c22acb75ac5e03d.png)
Еще один способ встроить структурированные данные в письмо - это файлы с расширением “.ics”, которые представляют собой универсальный формат вложения для календаря.
Есть четыре ICS-метода:
publlish — добавление события в календарь пользователю (например, событие с авиаперелётом);
request — приглашение добавить событие в календарь. Пользователь может согласиться и отказаться;
reply — реакция от участника на встречу, в которой вы — организатор (участник принял приглашение или отклонил его);
cancel — владелец встречи удалил событие, и пользователю приходит уведомление об отмене.
Если отправитель прислал в письме файл с расширением “.ics” и типом “request”, то при открытии письма у пользователя появляется плашка «Вас пригласили на событие», как на рисунке ниже:
![Плашка календаря в письме с ICS с методом Request Плашка календаря в письме с ICS с методом Request](https://habrastorage.org/getpro/habr/upload_files/bae/c8a/d9f/baec8ad9f346c180dbe15097303d5c96.png)
Если пользователь подтвердит участие, то событие встанет в календарь. Однако люди редко самостоятельно подтверждают события, поэтому конверсия показов плашки в добавление в календарь в этом случае невелика.
Самым простым способом проверки на пользователях нового функционала автоматического добавления событий было использование информации из ICS, которую уже предоставил отправитель. Для этого на этапе MVP решили вместо ML-сервиса, который мог бы выделять структурированную информацию из любого письма с событиями, сделать сервис, который берёт уже готовые поля из ICS-файла.
Архитектура MVP. Взгляд сверху
Пайплайн обработки состоит из нескольких крупных этапов:
Выделить из всего потока писем только письма с ICS-вложениями.
Достать из ICS-файла информацию о событии.
Сохранить полученную информацию о событии в календарь.
![Упрощённая схема обработки писем для MVP на основе ICS. Упрощённая схема обработки писем для MVP на основе ICS.](https://habrastorage.org/getpro/habr/upload_files/b82/5f6/370/b825f6370b518b4b4a1e9df568a1f828.png)
Разберём весь путь от отправки письма до создания напоминания.
Пользователь купил билет на поезд, и отправитель прислал ему письмо с билетом. В письме есть ICS-файл, который содержит основные сведения о событии (время отправления, номер поезда и т.п.).
Письмо приходит в Почту, проверяется Антиспамом (через Календарь тоже могут слать спам под видом приглашения на встречи) и сохраняется в хранилище-сторадже.
Сторадж отправляет сообщение в сервис-обработчик (Hander), который имеет набор правил фильтрации. Если письмо подпадает под определённое правило, то оно отправляется в очередь, где будет ждать обработки. В нашем случае в обработчике настроено правило формата «есть вложение с расширением .ics», таким образом все письма с вложениями Календаря собираются в отдельной очереди Queue.
Разгребает очередь сервис-«разгребальщик» ics-processor, который берёт письмо из очереди и отправляет его в микросервис-парсер.
Парсер получает письмо, парсит ics и возвращает разгребальщику ответ, который содержит ics-метод (publlish/request/reply/delete) и описание события.
Разгребальщик ics-processor получает ответ от парсера ics и производит с ним целевое действие через API Календаря (ставит/изменяет/удаляет событие).
Итоги запуска MVP
Таким образом, мы на коленке собрали MVP-прототип, позволяющий быстро проверить гипотезу о полезности, выкатили его в эксперимент на группу пользователей и приступили к оценке. Успешность MVP оценивали по нескольким критериям, главными из которых были CTR пушей (доля открытий пушей из показов) и результаты опросов. По итогам исследований около 70% пользователей, получавших уведомления с напоминаниями, оценили их как “очень полезно” и “скорее полезно” и хотели бы получать напоминания и дальше. Для остальных пользователей, которым данный функционал не нужен, решили сделать опцию, позволяющую отключить автоматическое добавление событий в календарь.
После подтверждения полезности MVP мы начали думать о том, как распространить эту функциональность на большее количество пользователей. Дело в том, что лишь небольшая доля отправителей присылает файлы ICS в своих письмах. Соответственно, хочется для всего потока писем с событиями (в идеале) уметь выделять нужные поля. То есть от прототипа пора двигаться к решению на ML-основе.
О том, как устроен ML-сервис, как готовили данные, учили модели и какие результаты в конечном счете получили вы узнаете во второй части статьи. Не переключайтесь!