Как мы уменьшили время простоя курьеров. Логистика в Яндекс.Еде

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.


Всем привет! Меня зовут Роман Халкечев, я руковожу отделом аналитики в Яндекс.Еде. Одно из ключевых направлений этого сервиса — логистика. Эффективность алгоритмов логистики во многом и определяет само существование сервисов доставки. Сегодня я расскажу читателям Хабра о нашем новом алгоритме, который помог курьерам сократить время простоя. Вы узнаете, из чего складывается время ожидания доставки заказа и зачем мы считали скорость приготовления килограмма условной еды. Но обо всём по порядку.


Яндекс.Еда представляет собой маркетплейс: на сервисе есть спрос и есть предложение. Спрос — это заказы пользователей. Предложение — курьеры. Разумеется, под предложением мы также понимаем рестораны, но в контексте этого поста остановимся именно на курьерах. Главная задача сервиса — поддерживать баланс: тогда будут счастливы и пользователи (они быстро получат еду), и курьерские службы (заказов хватит всем курьерам). Чтобы сохранять баланс и переживать локальный рост или падение спроса, нам необходимо повышать эффективность доставки. Под эффективностью мы понимаем оборачиваемость — среднее число заказов, которые курьер успевает доставить за час. Чем выше этот показатель, тем эффективнее работает доставка в целом.


Анализируем данные: на что уходит время?


Чтобы понять, как растить оборачиваемость, нужно более глубоко разобраться в том, как работает доставка. В этом нам помогут данные: мы посчитаем среднее время, которое необходимо курьеру для доставки заказа. Эту метрику — время от пользовательского клика до получения заказа — мы называем click to eat. Чем ниже CTE, тем больше заказов в час получится доставлять. Понятно, что при CTE > 30 минут больше двух заказов в час точно не доставить. Разве только если разносить несколько заказов одновременно — но об этом поговорим в следующий раз.



В зависимости от региона и времени года средний CTE лежит в промежутке от 30 до 38 минут. Чем больше заказов и чем лучше погода, тем меньше CTE, и наоборот. Давайте разберёмся, на что у курьера уходит это время.


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


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



Как видно, ещё осенью 2019 года около 45% времени — 15–18 минут — курьеры проводили у ресторана в ожидании, пока блюда приготовят (!), хотя могли бы потратить это время на что-то более полезное. А происходило это из-за схемы назначения: мы получали заказ от клиента, сразу же искали курьера (ближайшего к ресторану) и в тот же момент отправляли его в ресторан. В итоге курьеры приходили рано и больше 15 минут ждали, пока заведение приготовит заказ.


Новый алгоритм назначения курьеров


Чтобы улучшить ситуацию и снизить время, которое курьеры проводят в бесполезном ожидании, мы решили разработать и внедрить новый алгоритм назначения. Основная идея: попробовать назначать курьеров не сразу при создании заказа, а позже — так, чтобы курьер прибывал в ресторан и всего через несколько минут забирал готовые блюда. У курьеров появится больше свободного времени, которое можно потратить с пользой: на доставку других заказов. А это позитивно скажется на оборачиваемости, сделает доставку эффективнее, позволит переживать локальные всплески спроса (при эффективной доставке нужно меньше курьеров, чтобы вывезти пик спроса) и обеспечит курьеров большим числом заказов.



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


Определение времени приготовления


Чтобы приводить курьеров в ресторан к концу приготовления, нужно понимать, когда ресторан, собственно, заканчивает готовить еду. Для первой версии мы взяли эвристику на основе исторических данных и веса корзины (в килограммах!) и считали, что у каждого ресторана есть своё среднее время приготовления.


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



Видно, что модель справляется с предсказанием лучше эвристики. Мы отнормировали предсказания модели так, чтобы зафиксировать долю опозданий на 10 минут и более в рамках SLA, и выкатили её в продакшен.


Определение времени назначения


Окей, предположим, теперь мы знаем, когда каждый заказ будет готов. Следующий вопрос: в какой момент делать назначение?


Можно воспользоваться какой-нибудь простой стратегией. Например, начинать искать курьера за 10 минут до конца приготовления. Но такой подход довольно рискованный. А вдруг мы не найдём курьера, который успеет добраться в ресторан за 10 минут? А что, если в этом районе вообще нет курьеров? В таком случае мы примем заказ, ресторан его приготовит — а мы не сможем доставить. Причём сообщим об этом клиенту не сразу, а через 15–20 минут, а то и больше, если заказ крупный. Пострадают в итоге все: клиент, ресторан и мы сами.


Чтобы свести к минимуму такие ситуации, мы придумали два критерия. Если они выполняются — начинаем искать курьера. Работает это так: мы принимаем заказ, а дальше каждую минуту запускаем ряд проверок, как бы спрашивая себя, можем ли мы отложить поиск курьера. Если ответ «да» — т. е. проверка пройдена — ничего не происходит, в противном случае мы начинаем поиск и назначаем курьера на заказ.


Сами критерии начала поиска очень простые:
— Либо до конца приготовления мало времени (время меньше порога t).
— Либо в окрестностях ресторана мало курьеров (количество тех, кто успеет к концу приготовления, меньше параметра k).


Наглядная гифка за спойлером


Кроме того, мы смотрим на состояние сервиса при создании заказа и можем заказ не принять. Это происходит, если курьеров резко не хватает либо если успевающих (или хотя бы не сильно опаздывающих) курьеров просто нет.


Внедрение и результат


Внедрение нового алгоритма осложнялось тем, что поиск и назначение курьеров — это фича с сетевым эффектом, а значит, нельзя просто так взять и провести честный А/B-эксперимент. С тех пор мы научились продвинутой технике проведения таких экспериментов, которую называют switch back (и о которой, надеюсь, расскажем в будущем). Но тогда мы просто обложили все части сервиса большим количеством метрик и начали постепенно раскатывать алгоритм с очень аккуратными настройками.


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


Классные новости заключаются в том, что это сработало! Осенью мы начали раскатку в одном из городов присутствия — и вот так изменилось распределение среднего времени на заказ:



Как видно из графика выше, время в ресторане сократилось — мы высвободили больше 7 минут на заказ (см. зелёную часть — «До назначения»)! Причём и это ещё не предел — сейчас курьеры ожидают приготовления меньше 10 минут. И такой эффект сохранился, когда мы выкатили новый алгоритм на все города.


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



Видно, что после внедрения мы не стали позже отменять заказы, а в этом был основной риск отложенного назначения. График стал выглядеть иначе, потому что раньше мы пытались найти курьера в первые 8 минут после создания заказа, а если не получалось — отменяли заказ. Теперь же у нас работает многоуровневая схема проверок, о которой я писал выше и которая может приводить к отмене заказов, если курьеров не хватает.


Спасибо за внимание! С удовольствием отвечу на ваши вопросы.

Источник: https://habr.com/ru/company/yandex/blog/507448/


Интересные статьи

Интересные статьи

Недавно мы объявили на Хабре, что начинаем принимать заявки на Яндекс.Алгоритм и другие треки чемпионата по программированию Yandex Cup. Уже много лет онлайн-соревнования Яндекса и др...
Старое конструкторское выражение гласит: «Прибор должен работать не в принципе, а в корпусе». И процесс нахождения, покупки или создания корпуса новой конструкции является одним из главных дл...
В 1С-Битрикс: Управление сайтом (как и в Битрикс24) десятки, если не сотни настраиваемых типов данных (или сущностей): инфоблоки, пользователи, заказы, склады, форумы, блоги и т.д. Стр...
Здравствуйте. Вот и подошел к концу мой трехмесячный ночной режим работы, за это время я потерял здоровье понял многое и хочу с вами поделиться всеми прелестями и ужасными минусами работы в ночно...
Один из самых острых вопросов при разработке на Битрикс - это миграции базы данных. Какие же способы облегчить эту задачу есть на данный момент?