惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

云风的 BLOG
云风的 BLOG
阮一峰的网络日志
阮一峰的网络日志
有赞技术团队
有赞技术团队
小众软件
小众软件
P
Proofpoint News Feed
P
Proofpoint News Feed
Apple Machine Learning Research
Apple Machine Learning Research
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
The Last Watchdog
The Last Watchdog
O
OpenAI News
Security Latest
Security Latest
博客园 - Franky
Forbes - Security
Forbes - Security
N
Netflix TechBlog - Medium
H
Hacker News: Front Page
Cloudbric
Cloudbric
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Hugging Face - Blog
Hugging Face - Blog
Microsoft Security Blog
Microsoft Security Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
S
Security Affairs
Recent Announcements
Recent Announcements
The GitHub Blog
The GitHub Blog
S
Schneier on Security
MongoDB | Blog
MongoDB | Blog
WordPress大学
WordPress大学
Last Week in AI
Last Week in AI
博客园 - 【当耐特】
Attack and Defense Labs
Attack and Defense Labs
C
Cyber Attacks, Cyber Crime and Cyber Security
F
Fortinet All Blogs
Webroot Blog
Webroot Blog
S
Secure Thoughts
Spread Privacy
Spread Privacy
Blog — PlanetScale
Blog — PlanetScale
T
Troy Hunt's Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
V
V2EX
Security Archives - TechRepublic
Security Archives - TechRepublic
P
Privacy & Cybersecurity Law Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Simon Willison's Weblog
Simon Willison's Weblog
C
Check Point Blog
L
LINUX DO - 最新话题
NISL@THU
NISL@THU
博客园_首页
罗磊的独立博客
A
Arctic Wolf
U
Unit 42

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Как закешировать интеллект: LLM Cache в поиске Яндекс Лавки
AShchekalev · 2026-04-30 · via Все публикации подряд на Хабре

Меня зовут Алексей Щекалёв, я работаю в команде машинного обучения Яндекс Лавки. Как думаете, что общего между пакетом молока, айфоном, дрелью и лабубу? Похоже на начало анекдота, но для нас это серьёзный технический вопрос. Ответ на него определяет, найдут ли пользователи то, что ищут, или разочарованно закроют приложение.

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

Как всё начиналось

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

На старте, в 2020–2021 годах, когда Лавка была сервисом доставки продуктов за 15 минут, сложные решения не требовались. Каталог даркстора (маленького склада, откуда курьер везёт товары клиенту) составлял порядка 10³ позиций. Хлеб, молоко, чипсы — всё было на виду. 

Тогда поиск прекрасно работал на классических алгоритмах: BM25 и Ахо — Корасик. Это был надёжный, быстрый, но абсолютно «деревянный» полнотекстовый поиск. Система смотрела исключительно на токены, и, если пользователь вводил «молоко», мы искали товары, в названии или описании которых есть эта последовательность букв. Искать по смыслу мы тогда не умели. 

В 2022 году за поиск взялась профильная ML-команда. Как и многие, мы начали с классики — CatBoost. Механизм отбора кандидатов остался текстовым, но мы добавили этап ранжирования: брали найденных кандидатов и с помощью CatBoost поднимали вверх более кликабельные и конверсионные позиции. Выдача стала точнее, метрики подросли, но «слепота» к смыслу запроса никуда не делась.

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

В 2024 году мы внедрили многостадийный поиск с персональным ранжированием в три этапа: 

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

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

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

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

Кризис масштабирования

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

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

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

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

Генерация кандидатов

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

Разметка релевантности

Мы составили инструкцию, в которой чётко описали критерии релевантности, и отправили задачу в Яндекс Задания. Пользователи разметили несколько сотен тысяч пар «запрос — товар» на четыре класса. 

Вот, например, запрос «шоколадное мороженое»:

  • Рел+ — точное совпадение. Товар «Мороженое „48 копеек“ шоколадное» полностью соответствует интенту.

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

  • Рел− — созвучие, но промах, например шоколадка. С точки зрения текста здесь чёткое пересечение, но пользователь хотел мороженое, а не плитку шоколада. В поиске такое скорее показывать не нужно.

  • Нерел — например, салфетки, то есть мусор, который не имеет никакой связи с запросом.

Обучение BERT-модели

Получив размеченный датасет, мы обучили модель с ранним связыванием на базе BERT. Архитектурно она работает так: на вход модель получает единую последовательность «поисковый запрос + описание товара». На выходе BERT выдаёт векторный эмбеддинг текста. Поверх этого эмбеддинга стоит полносвязная нейронная сеть, которая превращает его в скаляр от 0 до 1. Получается оценка релевантности, где единица — очень релевантный товар, а ноль — нерелевантный.

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

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

Дистилляция в DSSM

Что делать, если модель хорошая, но медленная? Мы можем провести дистилляцию: передать то, что «знает» тяжёлая модель, в лёгкую.

Мы взяли обученный BERT и с его помощью собрали разметку примерно из 107 релевантных пар «запрос — товар». На этих данных обучили DSSM — двухбашенную модель. Одна «башня» независимо обрабатывает текст запроса и сжимает его в вектор (эмбеддинг запроса), вторая делает то же самое с описанием товара. Релевантность определяется через скалярное произведение двух получившихся векторов.

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

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

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

Неизбежный (?) компромисс

Получается дилемма. Модели с ранним связыванием дают отличное качество на новом ассортименте и новых запросах, но неприменимы в рантайме. В то же время двухбашенные модели (DSSM) работают быстро и применимы в рантайме, но плохо обобщают.

Оба подхода работают, оба нужны, но каждый имеет критический недостаток именно в нашей ситуации. По сути, мы столкнулись с компромиссом «качество — latency — гибкость». Модель с ранним связыванием даёт качество и гибкость (понимает новый ассортимент без переобучения), но проигрывает по latency. DSSM даёт скорость, но жертвует и качеством на сложных запросах, и гибкостью — каждый новый ассортимент требует цикла переобучения.

Чтобы DSSM начала понимать новый ассортимент, её нужно переобучить на новых данных. Но недостаточно переобучить только модель генерации кандидатов, придётся переобучить и фильтрующую формулу, и ранжирующую. Это достаточно долгая, рутинная работа. А если придёт ещё один новый ассортимент? Скажем, начнёт продаваться компьютерная электроника — тогда всё нужно делать по новой.

Вместо этого мы задались вопросом: а какие вообще особенности лавкового поиска? Что отличает его, скажем, от поиска на маркетплейсах или от большого Яндекс Поиска?

Концентрация спроса

Мы посмотрели на сырую статистику и увидели несколько важных цифр: 10⁵ товаров в общем ассортименте (по сравнению с маркетплейсами или веб-поиском это, по сути, ничто); 10³ товаров в конкретном дарксторе — ещё на два порядка меньше; 10⁶ уникальных поисковых запросов в месяц — число большое, но стоит посмотреть на их распределение.

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

  • 200 запросов генерируют 50% всех добавлений в корзину. Половина ценности поиска: «молоко», «хлеб», «яйца», «сыр», «вода» и ещё пара сотен подобных слов. Если подумать, это достаточно понятно, ведь в Лавку приходят за продуктами повседневного спроса. Запросы вроде «лабубу» или «дрель» тоже бывают, но значительно реже.

  • 2000 запросов покрывают 80% добавлений в корзину.

  • ~10⁵ запросов покрывают 97% добавлений в корзину.

Оставшиеся 3% — длинный хвост из сотен тысяч уникальных, редких запросов.

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

LLM Cache

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

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

Офлайн-этап: подготовка кеша

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

Сначала мы приводим запросы к нижнему регистру и обрезаем пробелы (trim). Опечатки не исправляем. Затем для каждого запроса считаем количество добавлений в корзину и сортируем по убыванию. И наконец, оставляем лишь те запросы, которые кумулятивно дают 98% добавлений в корзину.

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

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

  1. Декартово произведение — самый ресурсоёмкий этап. Берём топ запросов и строим пары с каждым товаром из ассортимента: Pairs = Queries_top × Items_all. При 10⁵ запросов и 10⁵ товаров получается 10¹⁰ пар, образуются миллиарды комбинаций. В рантайме это было бы нереально, а в офлайне вычислимо, хотя и требует серьёзных ресурсов. Тут возможны разные оптимизации, но самый простой способ — скорить все пары полным перебором.

  2. Каждую пару прогоняем через LLM. Модель выдаёт для каждой пары скор релевантности — число от 0 до 1, где значения ближе к 1 соответствуют классам «рел+» и «рел» из нашей разметки, а ближе к 0 — классам «рел−» и «нерел». Берём товары, попавшие в «рел+» и «рел», а всё остальное просто отбрасываем. Как именно скор связан с классами и где проходит порог отсечения, разберём чуть ниже.

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

Порог отсечения выбираем по разметке. Когда дообучали BERT, мы присваивали классам REL+, REL, REL−, NON-REL скоры от 0 до 1 (REL+ = 1, NON-REL = 0, промежуточным классам — значения между ними) и обучали модель на MSELoss. Благодаря этому выходные скоры модели лежат примерно в том же диапазоне и группируются вокруг значений, соответствующих каждому классу разметки.

Это даёт простое правило выбора порога: берём скор, соответствующий классу «рел», и немного опускаем его вниз, чтобы компенсировать разброс модели. В кеш попадают все пары со скором выше этого порога — так мы гарантированно забираем REL+ и REL, отсекая REL− и NON-REL.

Модель, определяющая релевантность, может быть любой. Мы используем BERT на несколько сотен миллионов параметров, дообученный на вручную собранной разметке, но можно взять локальную LLM на ~10⁹ параметров или даже обратиться к LLM через API. Модель покрупнее, предобученная на большом объёме знаний о мире, и без дообучения будет хорошо понимать, какие товары релевантны каким запросам.

Рантайм: быстрая отдача результатов

В рантайме схема проще.

Когда приходит поисковый запрос, мы первым делом проверяем, есть ли он в кеше.

  • Если он попал в кеш, то мы достаём из кеша список товаров со скорами релевантности и отправляем на персональное ранжирование. Модель сортирует товары с учётом предпочтений конкретного пользователя. Путь короткий: кандидаты из кеша → ранжирование.

  • Если запрос не попал в кеш, включается фолбэк: классический рантайм-пайплайн — DSSM и BM25 для генерации кандидатов, затем фильтрация, затем ранжирование. Качество выдачи чуть ниже, чем у модели с ранним связыванием, но мы хотя бы что-то находим. А поскольку таких запросов меньшинство, нагрузка на этот пайплайн остаётся в пределах нормы.

Важный момент: при попадании в кеш мы пропускаем фильтрацию и рантайм-генерацию кандидатов. Они уже отобраны тяжёлой моделью в офлайне — остаётся только персонализировать выдачу.

Результаты

С этим пайплайном нам удалось достичь заметных результатов. 

Лавка: качество и скорость

Мы улучшили качество поиска даже на типичных запросах. Модели, обученные на лавочную выдачу, всё равно проигрывают тяжёлым моделям с ранним связыванием. Благодаря кешу мы получили выдачу уровня BERT-модели без ограничений по скорости.

Мы адаптировали поиск под супермаркеты без переобучения всего стека. Раньше для поддержки новых запросов вроде «iPhone», «лабубу», «чайник» пришлось бы заново переобучать модели генерации кандидатов, фильтрации и ранжирования, дожидаясь накопления логов. Теперь достаточно забросить новое множество товаров в офлайн-пайплайн и проскорить. Декартово произведение порождает миллиарды или десятки миллиардов пар. Это требует вычислительных ресурсов, но, в отличие от рантайма, жёсткого ограничения по latency здесь нет.

Мы сократили тайминги: если запрос попал в кеш, не нужно скорить весь ассортимент и искать подмножество кандидатов, мы просто достаём готовых из кеша.

Товары по пути

В поиске появились товары из аптек, зоомагазинов, цветочных магазинов и других ритейлов по пути следования курьера. Чтобы понять, какие запросы характерны для нового ассортимента, мы подсмотрели у коллег из Яндекс Еды, по каким запросам пользователи добавляют подобные товары в корзину. Именно логи Яндекс Еды стали источником топ-запросов, так как в Лавке таких запросов ещё просто не было. Мы отправили их в механизм кеширования, и модель, обученная на большом объёме знаний, прекрасно справилась: определила, какие товары релевантны аптечным запросам, а какие — зоотоварам и так далее.

Эксперимент с ритейлом Яндекс Еды

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

Это подтвердило, что подход универсален и не привязан к конкретному ассортименту.

Где можно переиспользовать LLM Cache

Описанный подход применим, если выполняются три условия:

  1. Небольшой ассортимент — порядка 10⁵ SKU. При таком ассортименте полный перебор пар «запрос — товар» в офлайне считается прямолинейно. Если каталог значительно больше — десятки миллионов товаров, как у крупных маркетплейсов, — полный перебор становится слишком дорогим даже в офлайне, но подход всё равно применим: вместо перебора по всему ассортименту можно офлайн отобрать топ-N кандидатов на запрос более простым методом и прогонять через LLM уже их. Покрытие кеша будет ниже, и появятся дополнительные компромиссы, но общая схема сохраняется. Для продуктовых ритейлеров, аптек, зоомагазинов, магазинов электроники декартово произведение работает без таких ухищрений.

  2. Концентрированный спрос, когда малая часть запросов приносит основную долю добавлений в корзину. Это типично для большинства e-commerce-сервисов с ограниченным каталогом.

  3. Доступ к модели для оценки релевантности. Подойдёт BERT, локальная LLM на ~10⁹ параметров, LLM через API. Главное — чтобы модель умела определять релевантность пары «запрос — товар». Чем мощнее модель, тем лучше качество кеша, но даже относительно небольшой дообученный BERT даёт хорошие результаты.

Заключение: третий путь

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

LLM Cache — промежуточный вариант. Он уже понимает смыслы, но при этом так же прост в интеграции, как обычный полнотекстовый поиск. Он даёт хорошее качество поверх BM25 и достаточно прост в реализации. Выдачу из кеша можно дополнительно переранжировать персонально, докидывать кандидатов, есть масса возможностей для развития и тонкой настройки.

Интеллект модели и скорость её работы совсем необязательно должны быть связаны в одном моменте времени. Можно быть умными в офлайне и быстрыми в онлайне.