Мы добавили LLM в поиск, но не стали заменять им Elasticsearch. Почему?. ai.. ai. ai search.. ai. ai search. elasticsearch.. ai. ai search. elasticsearch. llm.. ai. ai search. elasticsearch. llm. search.. ai. ai search. elasticsearch. llm. search. искусственный интеллект.. ai. ai search. elasticsearch. llm. search. искусственный интеллект. Машинное обучение.. ai. ai search. elasticsearch. llm. search. искусственный интеллект. Машинное обучение. Поисковые технологии.
Мы добавили LLM в поиск, но не стали заменять им Elasticsearch. Почему? - 1

Привет! Меня зовут Дмитрий, я лид отдела семантики в Профи.ру. Мы отвечаем за то, чтобы сервис понимал, какую услугу ищет клиент: когда он вводит «репетитор по английскому», «починить стиральную машину» или «нужен электрик».

На первый взгляд, поиск в сервисе услуг — это довольно типовая задача. Что там может быть особенного? Под капотом лежит каталог услуг, Elasticsearch, нормализация запросов, ранжирование, кеширование. Ничего такого, чего не было бы в других продуктах. Но это только на первый взгляд.

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

Что для нас значит поиск

Немного о том, как работает поиск на сайте и в приложениях Профи.ру.

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

У него есть три основных режима.

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

Уроки аниме — это уже отдельный разговор

Уроки аниме — это уже отдельный разговор
Мы добавили LLM в поиск, но не стали заменять им Elasticsearch. Почему? - 3

Второй режим — поиск конкретного специалиста. Например, клиент вводит имя или фамилию специалиста и пытается найти его профиль.

Ищем:

Мы добавили LLM в поиск, но не стали заменять им Elasticsearch. Почему? - 4

Получаем страничку специалиста:

Мы добавили LLM в поиск, но не стали заменять им Elasticsearch. Почему? - 5

И третий — определить услугу, которую Профи.ру не оказывает. Это не обязательно что-то незаконное: некоторые работы мы не берём из-за требований к документам, рисков или специфики услуги. Например, няня для детей. Если начать искать няню, поиск распознает такой запрос и не поведёт человека по стандартному сценарию оформления заказа.

Мы добавили LLM в поиск, но не стали заменять им Elasticsearch. Почему? - 6

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

То есть поиск — это у нас не отдельный экран. Это сервис, который сопровождает клиента на разных этапах пути.

Как устроен базовый поиск

С точки зрения архитектуры всё довольно предсказуемо. Веб, мобильный веб и приложения отправляют запрос в корпоративную шину данных — GraphQL-контур. Через gate он доходит до сервиса поиска, а дальше мы обращаемся к Elasticsearch — основному источнику данных для поиска по услугам.

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

До поиска в Elastic запрос проходит нормализацию. Пользователь может написать одну и ту же потребность десятками способов: «уроки английского», «английский с преподавателем», «нужен репетитор англ», — а иногда ещё и переключить раскладку или ошибиться в одном-двух словах.

Поэтому мы:

  • приводим слова к базовой форме;

  • учитываем порядок слов для финального скоринга, но не делаем его жёстким ограничением;

  • пытаемся исправить неверную раскладку;

  • игнорируем часть опечаток;

  • используем синонимы и другие варианты написания услуг;

  • сравниваем запрос с формулировками из каталога.

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

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

Математика сильно популярнее языков

Математика сильно популярнее языков

Наша цель проста: клиент должен увидеть нужный вариант в первых строках. Больше 90% выборов должны приходиться на первые пять результатов, а в идеале — на первые три.

Почему поиск должен быть душным и надёжным

Поиск в Профи.ру — не самый тяжёлый сервис в классическом понимании загрузки. В пике он обрабатывает около 150 тысяч запросов в час, то есть чуть более 60 запросов в секунду. При этом нагрузка днём и ночью различается больше чем в десять раз.

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

Мы добавили LLM в поиск, но не стали заменять им Elasticsearch. Почему? - 8

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

Сейчас базовый поиск обычно отвечает примерно за 150 мс. Порог, после которого мы считаем, что с сервисом что-то не так, — 250 мс на 95-м перцентиле. Ошибки отслеживаем с нулевой терпимостью: уже одна ошибка создаёт warning, а при десяти в команду приходит алерт.

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

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

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

Наконец, у нашего поиска много fallback-сценариев. Если сервис не нашёл подходящую услугу, он не должен оставить пользователя перед пустым экраном. Например, если запрос не похож на название услуги, система может предположить, что клиент искал специалиста, и переключиться в соответствующий режим. 

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

Как изменился поиск из-за ИИ

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

Несколько лет назад в этот сценарий попадало меньше 1% пользователей. Казалось, что проблема есть, но она не настолько велика, чтобы менять устойчивую архитектуру поиска. Но я постоянно об этом думал и понимал, что недалёк тот день, когда это число увеличится и нам придётся что-то менять. 

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

Вместо «ремонт комнаты» писали «хочу привести в порядок комнату на даче, стены кривые, пол скрипит, а с проводкой, кажется, тоже что-то не так». Вместо «репетитор по английскому» — «ребёнку надо подтянуть английский перед экзаменом, но заниматься хочется индивидуально». Стало понятно: если мы хотим понимать такие запросы, нужна дополнительная логика.

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

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

Поэтому LLM сейчас работает как интерпретатор между языком клиента и языком нашего каталога.

Мы добавили LLM в поиск, но не стали заменять им Elasticsearch. Почему? - 9

С кейсом несуществующей услуги моделька тоже справляется отлично

Каждая услуга в Профи.ру описывается набором признаков. В упрощённом виде это действие, объект и дополнительные свойства. Например:

  • «Репетитор по английскому языку». Действие — репетиторство. Объект — английский язык.

  • «Ремонт компьютера». Действие — ремонт. Объект — компьютер.

  • «Обучение вождению». Действие — обучение. Объект — вождение.

Когда обычный поиск не находит достаточно хорошего совпадения, подключается LLM. Она разбирает свободное описание на эти признаки, формирует гипотезу о нужной услуге и отправляет её обратно в привычный алгоритмический поиск.

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

Упрощённо пайплайн выглядит так:

Запрос клиента
    ↓
Обычный алгоритмический поиск
    ↓
Есть уверенный мэтч? -- да → показываем результат
    ↓ нет
LLM выделяет признаки услуги и строит гипотезу
    ↓
Алгоритмический поиск проверяет гипотезу
    ↓
Ранжируем результаты и ведём клиента дальше

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

Что в итоге

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

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

Автор: Pumppeedd

Источник