TL;DR
HyST — техника поиска, которая разделяет запрос на две части: жёсткие требования (бренд, категория, цена) и мягкие предпочтения (атмосфера, стиль, тон). Сначала LLM извлекает из запроса структурированные ограничения и превращает их в фильтры. Потом по оставшимся кандидатам идёт семантический поиск на основе неструктурированных предпочтений. Это как двухступенчатое сито: грубое отсекает явно не подходящее, тонкое ранжирует по нюансам.
Проблема чисто семантического поиска: если просто превратить всё в текст и искать похожее по смыслу, модель путает обязательное и желательное. Запрос "итальянский ресторан в Нью-Йорке с уютной атмосферой" может вернуть французский ресторан с очень уютной атмосферой — семантически похоже, но нарушено жёсткое требование. Причина: эмбеддинги ловят смысловую близость, но не понимают разницу между "must have" (кухня) и "nice to have" (атмосфера). Линеаризация всех полей в один текст стирает эту границу.
Решение HyST: LLM читает запрос и вытаскивает явные ограничения (категория = итальянская, локация = Нью-Йорк), превращая их в точные фильтры. Эти фильтры отсекают всё, что не подходит по обязательным критериям. По тому, что осталось, модель уже ищет семантически близкое к "уютная атмосфера". Два шага вместо одного — но каждый делает своё дело, и точность взлетает.
Схема метода
ШАГ 1 (LLM): Извлечь из запроса структурированные ограничения → JSON-фильтры
Вход: "Show me Italian restaurants in NYC with cozy atmosphere under $100"
Выход: {"category": "Italian", "location": "NYC", "price": {"$lt": 100}}
ШАГ 2 (LLM): Убрать из запроса уже зафиксированные ограничения → очищенный семантический запрос
Вход: тот же запрос
Выход: "restaurant with cozy atmosphere"
ШАГ 3 (Vector DB): Применить фильтры → найти семантически близкое среди отфильтрованных
Фильтрация: оставить только Italian + NYC + price < $100
Семантический поиск: ранжировать по "cozy atmosphere"
Все три шага можно сделать в одном промпте для LLM, если векторной БД нет — просто попросить модель сначала отфильтровать, потом ранжировать.
Пример применения
Задача: Ты консультант по переезду в Казахстан. Клиент написал: "Хочу снять квартиру в Астане в районе Есиль или Сарыарка, 2-3 комнаты, до 300к тенге, желательно с видом на реку и рядом с метро".
Тебе нужно найти подходящие варианты из базы объявлений. В базе есть поля: район, количество комнат, цена, описание (там упоминаются вид, инфраструктура).
Промпт (двухэтапный с явным разделением):
Задача: найти квартиры по запросу клиента.
Запрос клиента: "Хочу снять квартиру в Астане в районе Есиль или Сарыарка,
2-3 комнаты, до 300к тенге, желательно с видом на реку и рядом с метро"
База объявлений: [здесь вставляешь список объявлений в формате:
ID, район, комнаты, цена, описание]
Шаг 1: ЖЁСТКАЯ ФИЛЬТРАЦИЯ
Извлеки из запроса обязательные критерии:
- Район: ...
- Количество комнат: ...
- Цена: ...
Отбери только объявления, которые ТОЧНО соответствуют этим критериям.
Шаг 2: РАНЖИРОВАНИЕ ПО ПРЕДПОЧТЕНИЯМ
Из отобранных на шаге 1, ранжируй по мягким предпочтениям:
- вид на реку
- близость к метро
Выдай топ-3 с обоснованием почему именно они.
Результат: Модель сначала выдаст список объявлений после жёсткой фильтрации (все из Есиль/Сарыарка, 2-3 комнаты, до 300к). Потом — топ-3 с пояснениями типа "№12 — есть вид на Есиль, 5 минут до станции метро" или "№8 — рядом с метро, но вида нет". Ты увидишь прозрачную логику: сначала отсечка несоответствующих, потом оценка по предпочтениям.
Почему это работает
Слабость LLM: Семантический поиск ловит смысловую близость, но не видит разницы между обязательным и желательным. Если в запросе 10 слов, а 2 из них — жёсткие требования (бренд, категория), а 8 — описание атмосферы, эмбеддинг смешает всё в одну кашу. Результат: модель может проигнорировать требование к бренду ради более похожей семантики в описании.
Сильная сторона LLM: Модель отлично извлекает структурированную информацию из текста — может вычленить "цена до 100", "категория = итальянский" и превратить в точные фильтры. И ещё лучше — может ранжировать по субъективным критериям типа "уютный", "с хорошей атмосферой", если кандидаты уже отфильтрованы.
Как HyST использует это: Вместо того чтобы мешать всё вместе, HyST делегирует задачи. LLM парсит запрос → вытаскивает жёсткие критерии → формирует фильтры → отсекает несоответствующее. Только после этого включается семантический поиск, но уже по узкому пулу кандидатов. Два инструмента, каждый делает своё дело — и точность взлетает.
Рычаги управления:
- Если запрос простой (1-2 критерия) → можно обойтись без второго шага, сразу искать по полному запросу
- Если поля в данных дублируют структурированную инфу (например, бренд упоминается и в названии, и в описании) → очистка запроса может навредить, лучше оставить полный текст
- Если данные плохо структурированы → можно попросить LLM сначала структурировать их (извлечь атрибуты), потом уже фильтровать
Шаблон промпта
Задача: найти {что ищем} по запросу.
Запрос: {текст запроса пользователя}
База данных: {список элементов с полями: название, категория, бренд, описание, отзывы}
ШАГ 1: ИЗВЛЕЧЬ ЖЁСТКИЕ КРИТЕРИИ
Из запроса выдели обязательные требования по структурированным полям:
- Категория: ...
- Бренд: ...
- Цена/диапазон: ...
- Другие точные ограничения: ...
ШАГ 2: ОТФИЛЬТРОВАТЬ
Оставь только элементы, которые ПОЛНОСТЬЮ соответствуют критериям из шага 1.
ШАГ 3: РАНЖИРОВАТЬ ПО ПРЕДПОЧТЕНИЯМ
Из оставшихся после фильтрации ранжируй по неструктурированным предпочтениям из запроса
(описание, атмосфера, стиль, качество и т.д.).
Выдай топ-{N} с обоснованием.
Подставляй:
{что ищем}— тип элементов (рестораны, квартиры, товары){текст запроса}— запрос пользователя как есть{список элементов}— твои данные в любом формате (таблица, JSON, просто список){N}— сколько результатов нужно
Важно: Если твои данные НЕ имеют чётких полей (нет явной колонки "бренд" или "категория"), попроси LLM сначала структурировать их:
ШАГ 0: СТРУКТУРИРОВАТЬ БАЗУ
Для каждого элемента извлеки:
- Категория: ...
- Бренд: ...
- Ключевые атрибуты: ...
Потом переходи к шагу 1.
Ограничения
⚠️ Зависимость от качества данных: Если структурированные поля (категория, бренд) имеют сотни разных значений с пересекающейся семантикой, LLM может ошибиться в маппинге. Например, пользователь пишет "Nike trainers", а в базе есть и "Nike", и "Nike Running", и "Nike Sport" — модель может выбрать не ту категорию.
⚠️ Простые фильтры: Метод показан на простых условиях (равенство, списки). Если нужны сложные условия (диапазоны, вложенные логические выражения, агрегация типа "больше 100 отзывов") — в исследовании не проверяли. Скорее всего сработает, но могут быть косяки в генерации фильтров.
⚠️ Очистка запроса не всегда помогает: Исследователи проверили вариант с удалением структурированных частей из семантического запроса (шаг 2). Оказалось, что иногда полный запрос работает лучше, чем очищенный. Причина: если бренд или категория упоминаются и в названии, и в описании продукта, удаление их из запроса теряет важные сигналы. Поэтому шаг 2 — опциональный, зависит от данных.
⚠️ Семантический поиск остаётся слабым звеном: Большинство ошибок было НЕ в генерации фильтров (LLM справлялась отлично), а в семантическом ранжировании. Если эмбеддинг-модель плохо ловит нюансы (тон, стиль, субъективные качества) — результат будет посредственный, даже при идеальной фильтрации.
Как исследовали
Взяли STaRK Amazon — датасет с товарами и вопросами пользователей. Изначально он был графовым (связи "также купили", "также смотрели"), но исследователи переделали в табличный формат: оставили 5 полей — название, бренд, категория, описание, отзывы. Из них бренд и категория использовали как структурированные (для фильтров), остальные — как текст для эмбеддингов.
Из датасета вручную отобрали 76 запросов, где нужны и жёсткие критерии, и семантика. Например: "Can you suggest a high-quality fishing line from Sufix that offers good value for money?" (бренд = Sufix — жёсткий фильтр, "quality", "value" — семантика). База сократилась с 1 млн товаров до 3335, чтобы ускорить эксперименты, но сложность сохранили.
Сравнили HyST с классикой: BM25 (лексический поиск), DPR и ColBERTv2 (плотные эмбеддинги), гибриды (BM25 + DPR), и чисто семантический поиск (всё в один текст → эмбеддинг). Метрики: precision@1, @5, @10, recall@20, MRR.
Результат: HyST победил по всем метрикам. Особенно сильно — на precision@5 (+14 пунктов против чисто семантического). Почему? Потому что семантический поиск часто возвращал товары с похожим описанием, но другим брендом — а HyST такое отсекал фильтрами сразу.
Неожиданность: Когда убрали шаг очистки запроса (оставили полный текст для семантики), точность выросла на некоторых метриках. Причина: в названиях и описаниях товаров часто дублируются бренд и категория. Удаляя их из запроса, теряли полезные сигналы для матчинга.
Инсайт для практики: Если твои данные уже содержат структурированную инфу в неструктурированных полях (бренд в названии, категория в описании) — не парься с очисткой запроса, оставь как есть. Фильтры всё равно отработают, а полный контекст поможет семантике.
Оригинал из исследования (промпт для генерации фильтров)
Контекст: Исследователи использовали GPT-4o для генерации метаданных-фильтров. Вот их промпт (упрощённая версия из статьи):
You are an assistant helping to parse user queries into structured filters
for a product recommendation system.
Given a user query, extract the following structured attributes:
- brand: exact brand name mentioned (if any)
- category: product category mentioned (if any)
- price range: any price constraints (if mentioned)
Output the result as a JSON object compatible with Pinecone metadata filters.
Use operators:
- "$eq" for exact matches (e.g., brand = "Nike")
- "$in" for list membership (e.g., category in ["Italian", "French"])
- "$lt", "$gt" for numeric comparisons (e.g., price < 100)
If a constraint is not mentioned, omit it from the output.
Example:
Query: "Show me Italian or French restaurants in New York under $100"
Output:
{
"category": {"$in": ["Italian", "French"]},
"location": {"$eq": "New York"},
"price": {"$lt": 100}
}
Now process this query: [USER_QUERY]
Ключевая деталь: Они использовали temperature = 0.3 и top-p = 0.8 для генерации фильтров — это даёт баланс между детерминированностью (нужна для точных фильтров) и гибкостью (если в запросе есть синонимы или неточности).
Адаптации и экстраполяции
💡 Адаптация для поиска вакансий
Вместо товаров — вакансии. Клиент пишет: "Хочу работу Python-разработчиком в Алматы, удалёнка или гибрид, зарплата от 500к тенге, желательно с ДМС и в продуктовой компании".
ШАГ 1: ЖЁСТКИЕ КРИТЕРИИ
- Специализация: Python
- Город: Алматы
- Формат: удалёнка ИЛИ гибрид
- Зарплата: от 500 000 тенге
ШАГ 2: ФИЛЬТРАЦИЯ
[Оставить только вакансии, соответствующие шагу 1]
ШАГ 3: РАНЖИРОВАНИЕ
Среди оставшихся ранжируй по:
- Наличие ДМС (приоритет)
- Тип компании: продуктовая vs аутсорс/аутстаф
Топ-5 вакансий с обоснованием.
🔧 Техника: Прозрачность через промежуточный вывод
Добавь в промпт: "После каждого шага выводи, сколько элементов осталось и почему отсеяли остальные".
ШАГ 1: ФИЛЬТРАЦИЯ
[Применить критерии]
Результат: осталось {N} элементов из {M}.
Отсеяно {M-N} по причинам:
- {X} не подошли по бренду
- {Y} не подошли по категории
- ...
ШАГ 2: РАНЖИРОВАНИЕ
[...]
Эффект: Видишь где теряются кандидаты — можешь скорректировать критерии, если фильтр слишком жёсткий.
🔧 Техника: Мягкая vs жёсткая фильтрация
Если критерий допускает гибкость, укажи это явно:
ШАГ 1: ЖЁСТКИЕ (строгое соответствие)
- Бренд: Nike (точное совпадение)
ШАГ 2: МЯГКИЕ (могут быть исключения)
- Цена: предпочтительно до 100$, но можно до 120$ если качество очень высокое
Модель будет учитывать нюансы, а не отсекать всё подряд.
Ресурсы
Статья: HyST: LLM-Powered Hybrid Retrieval over Semi-Structured Tabular Data
Авторы: Jiyoon Myung, Jihyeon Park, Joohyung Han (PrompTart LAB, MODULABS, Seoul, South Korea)
Конференция: EARL '25 (2nd EARL Workshop on Evaluating and Applying Recommender Systems with LLMs), October 2025
Датасет: STaRK Amazon benchmark (https://arxiv.org/abs/2404.13207)
Векторная БД: Pinecone
Embedding модель: OpenAI text-embedding-3-small
