Улучшаем поисковые подсказки — от retrieval к генерации

от автора

Вы начинаете набирать запрос в поисковой строке на Ozon и сразу видите список вариантов. Иногда кажется, что поиск читает мысли. Хотя магии здесь нет. Есть система подсказок или саджестов (от англ. suggest), которая должна за доли секунды понять, что вы хотите, и предложить лучший вариант. На всё — 300 мс. Если она думает дольше, пользователь замечает «подвисание», раздражается и вводит запрос вручную. Но поисковые подсказки — это не просто удобство. Они:

  • ускоряют ввод;

  • помогают точнее выразить запрос;

  • снижают количество ошибок и опечаток;

  • быстрее и эффективнее приводят пользователя к желаемому товару.

Рано или поздно возникает вопрос, как одновременно держать высокое качество и жёсткие ограничения по скорости? Долгое время мы решали это классически. Брали готовые запросы и обучали градиентный бустинг над деревьями решений выбирать лучшие варианты. Работает? Да. Хватает ли этого? Уже нет. В какой-то момент мы упёрлись в потолок качества. Улучшать ранжирование становилось всё сложнее, а эффект был всё меньше. Тогда мы попробовали другой подход и начали генерировать подсказки, а не выбирать из готовых.

В этой статье мы расскажем:

  • почему классической схемы перестало хватать;

  • как мы перешли к генерации;

  • и что из этого получилось в продакшене.

Типы поисковых подсказок

Перед тем как говорить об алгоритмах, выделим два типа подсказок.

  • Саджесты — классические подсказки полноценных поисковых запросов. Пользователь вводит начало запроса, выбирает саджест и сразу попадает на поисковую выдачу. Например, вводит «планш…» и видит «планшет samsung», «планшет с клавиатурой», «планшет 10 дюймов».

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

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

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

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

Классический подход: retrieval + ranking

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

  • Retrieval — поиск кандидатов. Система ищет возможные продолжения запроса в заранее подготовленном индексе. Например, префикс «кросс» может дать подсказки: «кроссовки», «кроссовки Nike», «кроссовки Adidas».

  • Ranking — ранжирование. Найденные кандидаты сортируются по релевантности, популярности и другим признакам.

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

  • Покрытие. Если запрос новый, редкий или слишком специфический, система просто не найдёт подходящих кандидатов. Никто не предложит те самые «кроссовки фиолетовые с единорогами и пайетками», если их нет в индексе.

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

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

Если обобщить, проблемы возникают из-за трёх причин. Первое — ограниченность индекса. Система выбирает только из заранее известных вариантов. Если хорошего кандидата нет в индексе, он никогда не появится в выдаче. Второе — ограниченность префиксного поиска. Поиск кандидатов происходит на основе совпадения префикса с кандидатом. В результате модель ищет либо точные совпадения, либо отличающиеся только на 1–2 буквы. Из-за этого, даже если в индексе есть релевантная подсказка, она не всегда попадёт в пул кандидатов, если в префиксе есть опечатки. Третье — ограниченность факторов ранжирования. Качество подсказок напрямую зависит от того, какие факторы мы придумали. Добавление новых сигналов требует ручной работы, а сложные зависимости поведения пользователя плохо выражаются через фиксированный набор признаков.

В итоге мы упираемся в потолок:

  • расширять индекс становится всё дороже и сложнее;

  • добавление новых факторов даёт всё меньший эффект;

  • качество подсказок растёт всё медленнее.

Генеративный подход: как предсказать продолжение запроса

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

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

  • последовательности запросов: [планшет] → [планшет samsung] → [чехол для планшета samsung];

  • действия с товарами: просмотры, клики, добавления в корзину, покупки;

  • взаимодействия с фильтрами и категориями;

  • исправления и альтернативные написания: [adibas → adidas], [найк → nike].

Так модель учится не просто сопоставлять префикс с частотной подсказкой, а учитывать контекст пользователя. Например, один пользователь после «кросс…» чаще ищет беговую обувь, другой — детские кроссовки, третий — конкретные модели Nike. Для них полезные подсказки будут разными.

За счёт таких данных модель лучше работает с редкими запросами, новыми формулировками, опечатками и транслитерацией. В обучающих сессиях «nike air max», «найк эирмакс» и «naik air maks» могут вести к похожим действиям пользователя. Модель видит это сходство и учится предлагать релевантные варианты, даже если написание различается.

Архитектура и обучение

Мы не брали готовую LLM, а обучали модель под нашу задачу генерации подсказок с нуля. Причина простая: язык маркетплейса отличается от обычного текста. В нём много брендов, артикулов, сокращений, опечаток, транслитераций и пользовательских формулировок: «айфон 17 про», «чехол на 16 pro max», «самсунг s24 ультра», «пауэрбанк 20000». Готовая языковая модель может знать часть таких слов, но при этом не знать, как они связаны с поведением пользователей внутри конкретной платформы.

В основе работы модели — компактный decoder-only transformer на несколько сотен миллионов параметров. Это языковая модель, умеющая работать в нашем домене. Она получает последовательность токенов и предсказывает следующий токен. Только вместо обычного текста мы подаём последовательность действий пользователя внутри маркетплейса: запросы, клики, действия с фильтрами и другие сигналы. Это не новая идея, а устоявшееся решение для генеративных рекомендательных систем. В таких подходах пользовательские действия рассматривают как последовательность, по которой можно предсказывать следующий шаг. Один из близких примеров — работа Actions Speak Louder than Words, где рекомендации формулируются как генеративная задача над последовательностями действий пользователя.

Обучение проходит в два этапа.

Pretrain на пользовательских событиях

Сначала модель учится на большом объёме цепочек событий пользователя. Каждая цепочка может состоять из сотни действий и занимать месяцы реальных взаимодействий с Ozon. Например:

  • [запрос: планшет] → [запрос: планшет samsung] → [фильтр: Встроенная память 1 Тб] → [клик: Samsung Galaxy Tab S11 Ultra] → [запрос: чехол для планшета samsung]

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

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

Fine-tuning на генерацию подсказок

После pretrain мы дообучаем модель на целевой задаче: по контексту и неполному запросу предсказать подсказку:

  • Контекст: [запрос: планшет samsung] → [фильтр: память 1 ТБ] → [клик: Samsung Galaxy Tab S11 Ultra].  

  • Пользователь ввёл: чех…  

  • Целевая подсказка: чехол для samsung galaxy tab s11 ultra.

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

Так два этапа решают разные задачи:

  • pretrain учит модель языку маркетплейса и связям между событиями;

  • fine-tuning учит использовать эти знания для генерации подсказок.

Инференс

Когда пользователь начинает вводить запрос, мы собираем доступный контекст: предыдущие запросы, действия в поиске, взаимодействия с товарами и текущий текстовый префикс. Все эти данные собираются в последовательную цепочку, которая и описывает предыдущее поведение пользователя. Модель продолжает последовательность и предсказывает следующий токен подсказки. Затем — следующий, и так до конца фразы. Чтобы получить несколько вариантов, используем beam search. Он держит несколько вероятных продолжений и помогает собрать набор подсказок под конкретную платформу. В результате получаем несколько вариантов поисковых подсказок и показываем их пользователю.

Преимущества генеративного подхода

Теперь вспомним ограничения классического подхода. Генерация решает их не по отдельности, а все сразу.

  • Покрытие. Модель не ограничена индексом. Она может сгенерировать осмысленный запрос, даже если его никогда не было в логах.

  • Опечатки. Мы больше не ищем совпадение. Мы понимаем намерение, а значит, «кросовки abidas» превращаются в «кроссовки adidas» без костылей.

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

И самое важное — всё это делает одна модель. В классическом подходе это были отдельные компоненты:

  • retrieval;

  • spellcheck;

  • персонализация через факторы ранжирования.

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

Как оценивать генерацию

В классической схеме оценка устроена понятнее. Есть список кандидатов, есть ранжирование, есть метрики вроде NDCG или Precision@K. С генерацией сложнее. У модели нет фиксированного списка правильных ответов и набора кандидатов для ранжирования, а подсказки, в частности, могут являться продолжением текущего запроса, но не вести сразу в поисковую выдачу. Поэтому мы переосмыслили офлайн-оценку вокруг пользовательского сценария. Вместо вопроса «совпала ли подсказка с логом?» стали искать ответ на другой вопрос: «помогает ли подсказка быстрее дойти до целевого поиска?».

Для этого используем метрику expected actions count. Она оценивает, сколько действий понадобится пользователю, чтобы из текущего состояния дойти до целевого запроса, если на каждом шаге он выбирает лучший доступный вариант. Целевой запрос — это запрос из пользовательской сессии, после которого было полезное действие, например добавление товара в корзину. Если подсказка приближает пользователя к такому запросу, путь становится короче. Такая метрика не заменяет A/B-тесты, но даёт более честный офлайн-сигнал. 

Что может пойти не так

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

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

  • Дубли и слишком похожие варианты. Если модель генерирует несколько подсказок, они могут различаться только одним словом или порядком слов. Например: «кроссовки nike мужские» и «кроссовки мужские nike». Формально варианты разные, но для пользователя это один и тот же смысл.

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

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

  • Выдуманные сущности. Генеративная модель не обязана выбирать только из существующих товаров, брендов или категорий. Она может предложить красивую, но бесполезную подсказку, по которой нет нормальной выдачи. Поэтому подсказка должна быть не только грамматически правильной, но и связанной с реальным ассортиментом.

Из-за этого генеративный подход нельзя просто «включить» и оставить без контроля. Мы управляем качеством на двух уровнях: до запуска модели в эксперимент и прямо во время инференса. До запуска смотрим на офлайн-метрики. Для общего качества подсказок используем expected actions count: оцениваем, сокращает ли подсказка путь пользователя до целевого поиска. Для опечаток отдельно считаем качество исправлений на префиксах с ошибками. Считаем, насколько точно модель восстанавливает правильную формулировку. Во время инференса включаем рантайм-проверки. Неприемлемые формулировки фильтруем по словарям и регулярным выражениям. Подсказки, которые гарантированно ведут к пустой выдаче, отбрасываем через проверку размера поисковой выдачи. Удаляем логические дубли, то есть совпадающие по смыслу варианты, чтобы не занимать экран несколькими вариациями одной и той же подсказки. Так контроль качества переезжает с уровня «почистить готовый индекс» на уровень «проверить поведение модели». Это сложнее, зато позволяет генерировать новые полезные подсказки и не терять управляемость продукта.

Генеративный подход в продакшене

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

Как модель работает в продакешне

В онлайне модель получает текущий префикс запроса и контекст сессии: предыдущие запросы, клики, просмотры товаров, взаимодействия с фильтрами и другие доступные сигналы. Эти данные превращаются в последовательность токенов и подаются в трансформер. Модель небольшая по меркам LLM: несколько сотен миллионов параметров. Для инференса используем TensorRT-LLM и запускаем модель на GPU-кластере. Это позволяет держать нагрузку в десятки тысяч запросов в секунду, балансируя запросы между несколькими GPU, а также собирая запросы в группы. На каждый пользовательский ввод модель генерирует не одну подсказку, а несколько кандидатов. После генерации кандидаты проходят постобработку:

  • убираем логические дубли;

  • фильтруем запрещённые формулировки по бан-листам и регуляркам;

  • отбрасываем варианты, которые не проходят проверку на непустую выдачу по запросу.

Только после этого подсказки отображаются пользователю.

Что показали A/B-тесты

Итерация 1: базовая генерация

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

Результат:

  • +10% к CTR подсказок;

  • почти без изменений в конверсионных метриках.

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

Итерация 2: многословные подсказки

Следующий шаг — перейти от коротких подсказок к более содержательным. Мы стали учить модель генерировать более длинные фрагменты целевого запроса — например, не просто «кроссовки», а «кроссовки nike мужские» или «кроссовки для бега». Для этого изменили подготовку таргета. Из целевого запроса выбирали не ближайшее продолжение, а ту часть, которая даёт максимальное ожидаемое количество полезной информации. Грубо говоря, подсказка должна быть достаточно длинной, чтобы помочь пользователю, но не настолько длинной, чтобы увести его в слишком узкий или неверный сценарий.

Результат:

  • ещё +10% к CTR подсказок;

  • по-прежнему слабое влияние на бизнес-метрики.

CTR растёт, но остаётся вопрос: улучшаем ли мы реальный пользовательский сценарий

Итерация 3: работа с опечатками

Дальше мы занялись опечатками. Это важный случай для маркетплейса: пользователь может ошибиться в бренде, раскладке, транслитерации или просто быстро набрать запрос с опечатками. Мы взяли органические исправления из опечаточника, который исправляет запрос на этапе перехода к выдаче, и дополнительно сгенерировали ошибки на основе статистики реальных опечаток. Так обучающая выборка стала богаче: модель увидела больше пар вида «как пользователь написал» → «что он, скорее всего, имел в виду». После этого добавили специальный токен, который обозначает исправление. Он помогает модели не только дописать запрос, но и поправить уже введённую часть. Например, пользователь вводит «adibas крос…». Модель должна понять, что речь, скорее всего, про Adidas, исправить ошибку и предложить полезное продолжение.

Результат:

  • +3% к CTR подсказок;

  • −3% к доле запросов с пустой выдачей;

  • +0,3% пользователей с заказом.

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

Контекстный сигнал за пределами подсказок

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

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

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

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

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

В итоге генеративная модель становится не просто механизмом для подсказок, а источником полезного контекстного сигнала. Его можно встраивать в разные части поиска — от генерации подсказок до ранжирования товаров и фильтров.

Наши выводы

Генеративная модель сама по себе — это не серебряная пуля. Первые итерации в основном оптимизируют поверхностные метрики вроде CTR. Пользователи чаще кликают по подсказкам, но это ещё не гарантирует, что они быстрее приходят к результату. Чтобы повлиять на бизнес-метрики, приходится идти глубже:

  • работать с качеством генерации (опечатки, релевантность);

  • учитывать реальный сценарий пользователя;

  • аккуратно настраивать поведение модели в продакшене.

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

ссылка на оригинал статьи https://habr.com/ru/articles/1040182/