Привет! Меня зовут Вадим Вахрушев, я старший DS-инженер в Авито, занимаюсь задачами поиска. В статье расскажу, как устроено ранжирование в Авито Работе: какие ML-модели помогают создавать выдачу вакансий, и какие инсайты мы вынесли из нескольких показательных АБ-тестов.
Думаю, статья будет в первую очередь интересна ML-инженерам, которым важно разобраться, как поисковое ранжирование адаптируется под конкретную бизнес-вертикаль. Если хочется узнать про поиск в Авито в целом, загляните в обзорную статью — «Как работает поисковое ранжирование для миллионов объявлений Авито». В этом материале я сфокусируюсь именно на специфике вакансий.

Ключевые цели поисковой выдачи
Когда мы формируем результаты поиска для пользователя, преследуем сразу несколько целей:
Показывать релевантные вакансии — такие, с которыми у человека наиболее вероятно случится мэтч, что позволит найти работу. Это главное.
Обеспечивать корректную работу монетизации. Например, на Авито есть услуги продвижения, которые позволяют работодателям увеличивать показы, а вместе с тем и просмотры вакансий. Выдача должна это учитывать.
Создавать справедливые условия. Каждая вакансия имеет право быть показанной хотя бы раз.
Все эти цели мы называем интентами поиска, а алгоритм, который из отобранных кандидатов собирает итоговую выдачу, — блендером.
Пирамида поиска: L1 → L2 → L3
Схему формирования выдачи можно представить как пирамиду из трёх этапов:
Ниже я разберу каждый уровень в подробностях, но общая суть этапов такая:
L1 — отбор кандидатов. Из более чем 1,5 млн вакансий отбираем 1 000 самых релевантных для пользователя.
L2 — вычисление интентов. Для отобранных кандидатов считаем интенты блендера с помощью продвинутых ML-моделей.
L3 — блендер. Перемешиваем кандидатов и формируем итоговую выдачу примерно из 200 вакансий.
L1: максимизируем полноту
Основная задача L1 — полнота. Нам надо отобрать все вакансии, которые потенциально могут быть релевантны пользователю. Здесь работают кандидатогенераторы (кандгены) и лёгкие ML-модели.
Кандгены — это наборы правил, по которым мы отбираем вакансии под конкретный запрос. Бывают двух типов ↓
Горизонтальные, то есть общие для всего Авито:
— Полнотекстовое совпадение. Отбираем вакансии, где встретились все слова запроса. Это база поиска, но её часто недостаточно, если в запросе много слов или они редко встречаются.
— Семантический поиск. Отбираем вакансии, семантически похожие на запрос, даже если слова не совпадают.
— Расширенные регионы. Подмешиваем вакансии из соседних областей — полезно, например, когда пользователь живёт на их границе.
Допустим, получили поисковый запрос «учитель в Москве». Собираем кандидатов сначала из вакансий учителей в Москве, ровно как нас и просили. Ещё возьмём вакансии со словом «педагог» — с точки зрения семантики это тот же «учитель» — плюс покажем учителей в Долгопрудном. Это смежный с Москвой город, славный своими школами.
Вертикальные, то есть специфичные для Авито Работы:
— Удалённая работа. Если запрос про удалёнку, сбрасываем фильтр по локации и показываем вакансии со всей России. Например, если человек ищет вакансию бухгалтера удалённо в Воркуте, локальных предложений там, скорее всего, не окажется — и мы подмешаем подходящие из других городов.
— Смежные профессии. Смежность мы определяем по бизнес-правилам и пользовательскому поведению: анализируем, какие запросы делает человек и на что откликается, кластеризуем профессии. Это особенно важно для нашей основной аудитории — рабочих специальностей, которые готовы рассматривать несколько профессий сразу.
|
Избранные АБ на уровне L1 |
|
|
Добавление кандгена удалённой работы +7.4% пользователей с контактом поиска в сегменте офисной работы +1.7% в тотале вакансий — решили проблему учёта локации при удалёночных запросах |
Добавление кандгена смежных профессий +2% пользователей с контактом из поиска — повысили разнообразие выдачи для людей, которые готовы рассматривать несколько профессий |
Теперь к ML-моделям — на L1 их несколько.
Векторный кандген — двухбашенная (query, item) модель, которая учится на позитивных действиях по вакансии: звонку, сообщению, добавлению в избранное.
Векторы вакансий считаются с задержкой в несколько минут. Модель получает на вход «заголовок» + «текст» + «параметры», например, зарплата, локация и опыт.
Вектор запроса считается онлайн на основе текста в строке поиска, проставленных фильтров и локации, а затем кэшируется.
Clickpred — ранжирующая модель, которая, несмотря на название, учится и калибруется на контакты. Она лёгкая, пока неперсонализированная и использует базовые статистики по объявлению и продавцу (конверсия в контакт, в клик) и фичи запроса (BM25, TF-IDF).
RelPred — модель текстовой релевантности вакансии запросу. Работает на текстовых запросах и учится на LLM-разметке, потому что пользовательская слишком шумная, а нам нужна «идеализированная».
Это бустинг на текстовых статистиках (TF, DF, BM25) плюс тематическая фича — скор двухбашенный DL-модели, обученной на ту же чистую разметку релевантности.
Итоговая формула отбора топ-1000 кандидатов:
score = sigmoid(w1 × clickpred + w2 × relpred + w3 × item_age)
Возраст вакансии (item_age) учитываем сознательно: хотим показывать свежие предложения. Во-первых, это обеспечивает справедливость — каждую свежую вакансию покажем хотя бы раз. Во-вторых, так успеваем накопить по ней статистику, которую потом используем в ранжировании.
L2: считаем интенты
На L2 вычисляются основные интенты блендера — BX и ECPI:
BX — баерское счастье — то, насколько вакансия-кандидат подходит соискателю, то есть вероятность мэтча.
ECPI — ожидаемая выручка от показа вакансии.
Он считается как произведение трёх скоров:
BX = cttr_pred × rel_predk × pess
Здесь RelPred приходит с L1 уровня в некоторой степени k, а pess — пессимизация: она занижает фродовые и накрученные вакансии. А CttrPred — скор CTTR-модели, про неё ниже.
CTTR — это ранжирующая модель, которая учится на событиях контактов, причём успешный контакт, например, пользователя позвали на собеседование, имеет больший вес в таргете. Модель сильно персонализированная, использует разнообразные фичи и своя для каждой вертикали Авито.
Самые важные признаки в вакансиях — фичи персонализации. Это скоры DL-моделей мэтчинга пользователя и вакансии, близость пользователя к месту работы, конверсионные статистики и мэтчи между отдельными признаками пользователя и вакансии. Персонализация очень важна, потому что большинство запросов — широкие, а мэтчинг двусторонний, то есть соискатель и работодатель должны взаимно заинтересоваться друг другом.
Пара инсайтов из АБ-тестов с CTTR-моделью. Небольшая ремарка про методологию: все тесты мы гоняем через внутреннюю систему. Она позволяет точно конфигурировать долю трафика под эксперимент — обычно мы берём не 100%, а, например, 50% и делим контроль и тест 1:1, то есть по 25% на ветку.
💡 Как подобрали таргет. Остановились на связке «контакт + целевой контакт», причём целевой с бóльшим весом: +2,7% пользователей с целевым контактом с поиска.
Крайности работают хуже: учёт только целевых контактов просаживает показатели соискателей и обычные контакты, а добавление целевых кликов нагоняет трафик на накрученные вакансии. Кстати, целевой контакт «вызревает» около трёх дней — чтобы этого не ждать, отдельная модель предсказывает по диалогу, будет ли контакт целевым.
💡 Как изменились показатели после добавления разнообразных фичей:
+2,3% соискателей с целевым контактом дал косинус между векторами соискателя и вакансии из рекомендательной DL-модели. Хороший пример переиспользования фичей из соседних команд.
+1% — интересы пользователя к атрибутам вакансии.
+1,1% — конверсии в целевой контакт по объявлению и работодателю.
💡 Почему статистики важно брать за разные временные окна. По мнению модели, высокая конверсия работодателя за короткое окно в одну неделю — повод дать скор ниже. Скорее всего, работодатель активно нанимает прямо сейчас, и вакансия с большой вероятностью, скоро закроется. Стабильная конверсия за длинное окно, от месяца, — повод дать выше, вероятно, нанимают много и регулярно.
L3: блендер и многокритериальная оптимизация
Полученные на L2 интенты нужно как-то учесть вместе. Формирование выдачи — это задача многокритериальной оптимизации, и в вакансиях мы решаем её жадным алгоритмом.
Интентов на этом уровне четыре:
BX — удовлетворённость пользователя (вероятность, что по вакансии случится мэтч)
ECPI — матожидание выручки от показа (используются свои ML-модели)
ST — свежесть: новые вакансии поднимаем не только на уровне кандгенов, но и здесь, чтобы они не терялись при смешивании
RP — репутация: деприоритизируем недобросовестных работодателей — например, тех, кто не отвечает пользователям
Сам алгоритм можно представить как цикл:
-
Вычисляем интенты для каждой вакансии.
-
Нормируем значения от 0 до 1, чтобы их можно было складывать.
-
Считаем скор как взвешенную сумму: score = w1 × RelIntent1 + w2 × RelIntent2 + …
-
Берём топ-1 по скору и добавляем в выдачу.
-
Дисконтируем веса интентов пропорционально релевантности интентов у отобранной вакансии — и повторяем.
Интуиция на пятом шаге такая: если мы только что отобрали вакансию с высокой релевантностью к ECPI, нужно уменьшить вес этого интента, чтобы следующим показать вариант, с которым у пользователя, скорее всего, случится мэтч, потом — что-то свежее, и так далее.
Веса интентов задаются в конфигурациях исходя из бизнес-приоритетов. А как мы понимаем, что нам нужно, и как переводим это в метрики и веса? Тут несколько источников: отсматриваем поиски и по найденным проблемам выдвигаем гипотезы, проводим опросы аудитории, и вместе с менеджерами выносим метрики, на которые коммитимся. Важно, что метрики со временем меняются — вслед за бизнес-приоритетами.
Выводы
Вертикализация поиска может принести много профита на всех этапах формирования выдачи — от кандгенов до блендера.
В ранжирующих поисковых моделях стоит пробовать самые разнообразные фичи, в том числе переиспользовать из рекомендаций и подбирать таргет исходя из бизнес-целей.
Грамотная балансировка интентов — ключ к успешному поиску, от которого хорошо всем: и работодателю, и соискателю, и нам как бизнесу.
На этом всё. Если есть вопросы или свои истории про ранжирование поиска — буду рад обсудить в комментариях.
А если вам не хватило контента от нас, приходите в телеграм-канал «Доска AI-объявлений» — там и тексты покороче, и вакансии к нам в команды есть, и с инженерами Авито можно в комментах пообщаться. Например, вот о чём уже писали:
📎 Рассуждали, как AI-агенты интегрируются в медицину
📎 Искали ответ на вопрос: куда расти Data Science-инженеру, если он уже синьор?
📎Обсуждали, как изменилась стабильность новых агентов на объёмных задачах
Автор: Vadvak
- Запись добавлена: 31.07.2026 в 13:32
- Оставлено в


