Как работает многогранный (faceted) поиск в RAG с фильтрами?

Краткий тезис

Многогранный (faceted) поиск — это техника, при которой результаты поиска фильтруются по нескольким атрибутам (фасетам), таким как автор, дата, категория, источник. В RAG-системах faceted поиск реализуется через метаданные, которые прикрепляются к каждому чанку. Фильтрация может выполняться до векторного поиска (pre-filter) или после него (post-filter). Pre-filter эффективнее при высокой селективности фильтров, post-filter проще в реализации, но может быть дорогим. Правильное применение фасетов повышает релевантность retrieval и снижает шум.


1. Термин: Фасет (facet) и метаданные

Фасет — это атрибут или категория, по которой можно отфильтровать данные. В контексте RAG фасеты хранятся как метаданные чанка: например, { "author": "Иванов", "date": "2024-01-15", "category": "техническая документация", "language": "ru" }. Метаданные добавляются на этапе индексации и позволяют сужать область поиска без изменения векторного представления.

Зачем нужны фасеты в RAG

  • Уменьшают количество нерелевантных чанков, особенно когда база разнородна (документы разных типов, языков, периодов).
  • Позволяют пользователю явно указать контекст (например, «только статьи за последний год»).
  • Улучшают precision (точность) retrieval, не снижая recall (полноту) для нужного подмножества.

2. Pre-filtering (фильтрация до векторного поиска)

Pre-filter — сначала применяются фильтры по метаданным, затем поиск|векторный поиск выполняется только в отфильтрованном подмножестве.

Как работает

  1. Пользовательский запрос содержит как текстовую часть, так и фильтры (например, category = "новости" AND year >= 2023).
  2. Система извлекает из векторной БД все чанки, удовлетворяющие фильтрам (используя индекс по метаданным).
  3. На этом подмножестве выполняется поиск по косинусной близости или другому векторному расстоянию.
  4. Возвращаются top-k результатов.

Преимущества

  • Высокая производительность, если фильтры селективны (отсекают >90% данных).
  • Гарантируется, что все результаты соответствуют фильтрам.
  • Не тратится время на вычисление расстояний для заведомо неподходящих чанков.

Недостатки

  • Если фильтры слишком узкие, может не остаться чанков для поиска → пустой результат.
  • Требует поддержки фильтрации на уровне векторной БД (не все БД умеют эффективно комбинировать фильтры и векторный поиск).

3. Post-filtering (фильтрация после векторного поиска)

Post-filter — сначала выполняется векторный поиск по всей базе, затем из top-N результатов отбрасываются те, что не проходят фильтры.

Как работает

  1. Векторный поиск возвращает, скажем, top-100 чанков по близости к запросу.
  2. Из этих 100 отбираются только те, у которых метаданные соответствуют фильтрам.
  3. Если после фильтрации осталось меньше k, можно либо вернуть меньше результатов, либо увеличить N.

Преимущества

  • Простая реализация — фильтрация на стороне приложения или через SQL-подобные условия после поиска.
  • Не требует специальной поддержки в векторной БД (подходит для FAISS без метаданных).

Недостатки

  • Низкая производительность при больших N и жёстких фильтрах (приходится искать много лишнего).
  • Риск потерять релевантные чанки, которые не попали в top-N из-за фильтра (если фильтр отсекает большинство результатов, нужное может быть на 101-м месте).
  • Увеличение latency из-за двойного прохода.

4. Сравнение pre-filter и post-filter

КритерийPre-filterPost-filter
ПроизводительностьВысокая при селективных фильтрахНизкая при жёстких фильтрах
Гарантия соответствия фильтрамДа (все результаты)Да (после отсева)
Риск пустого результатаЕсть (если фильтр слишком узкий)Меньше (можно увеличить N)
Сложность реализацииТребует поддержки БДПроще, часто на стороне клиента
Точность (precision)Высокая (фильтр до поиска)Может быть ниже (фильтр после)
Пример БДPinecone (с filter), Weaviate, QdrantFAISS + внешний фильтр, Elasticsearch

Когда что выбирать

  • Pre-filter — когда фильтры обязательны и селективны (например, «только документы на русском языке»).
  • Post-filter — когда фильтры опциональны или слабо селективны (например, «автор = Иванов», если Иванов написал 80% базы).

5. Реализация в популярных векторных БД

Pinecone — поддерживает pre-filter через параметр filter в запросе. Пример:

index.query(
    vector=query_embedding,
    filter={"category": {"$eq": "news"}, "year": {"$gte": 2023}},
    top_k=10
)

Weaviate — использует синтаксис where для фильтрации. Можно комбинировать с nearVector или nearText. Pre-filter по умолчанию.

Qdrant — поддерживает filter в search_points. Есть режимы pre-filter и post-filter (настраивается).

Elasticsearchгибридный поиск: сначала фильтр по метаданным (через bool query), затем knn по отфильтрованным документам.

FAISS — не имеет встроенной фильтрации. Для post-filter нужно хранить метаданные отдельно (например, в pandas DataFrame) и отсеивать после поиска.


6. Пример кода: faceted поиск с pre-filter (Pinecone)

import pinecone
from sentence_transformers import SentenceTransformer

# Инициализация
pinecone.init(api_key="...", environment="...")
index = pinecone.Index("my-rag-index")
model = SentenceTransformer('all-MiniLM-L6-v2')

# Запрос пользователя
query_text = "Как настроить VPN?"
query_vector = model.encode(query_text).tolist()

# Фильтры: только документы за 2024 год, категория "инструкции"
filters = {
    "year": {"$gte": 2024},
    "category": {"$eq": "инструкции"}
}

# Pre-filter поиск
results = index.query(
    vector=query_vector,
    filter=filters,
    top_k=5,
    include_metadata=True
)

# Вывод результатов
for match in results['matches']:
    print(f"ID: {match['id']}, Score: {match['score']}, Author: {match['metadata']['author']}")

7. Продвинутые техники

Динамические фасеты — фасеты, которые вычисляются на лету на основе запроса. Например, если пользователь ищет «отчёты за прошлый квартал», система может автоматически определить фасет date и применить фильтр.

Комбинированные фильтры — логические комбинации (AND, OR, NOT) нескольких фасетов. Например: (category = "техника" OR category = "программирование") AND language = "ru".

Гибридный поиск с фасетами — сочетание векторного поиска и полнотекстового (BM25) с фильтрацией. В Elasticsearch это делается через knn + bool query.

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


8. Проблемы и ограничения

  • Curse of dimensionality при фильтрации — если фильтр отсекает очень мало данных (например, 1%), pre-filter может быть неэффективен, так как построение индекса по метаданным и поиск по малому подмножеству могут быть медленнее, чем полный поиск.
  • Несбалансированные фасеты — если один фасет (например, language) имеет очень неравномерное распределение (99% документов на английском), фильтр на редкий язык может дать пустой результат.
  • Сложность поддержки метаданных — нужно аккуратно заполнять метаданные при индексации, иначе фильтры будут работать некорректно.
  • Ограничения БД — не все векторные БД поддерживают сложные фильтры (например, $in, $regex). Приходится делать post-filter.

9. Когда использовать faceted поиск в RAG

  • Мультиязычные базы — фильтр по языку.
  • Разнородные источники — фильтр по типу документа (PDF, HTML, видео-транскрипт).
  • Временные ряды — фильтр по дате (последние новости, архивные данные).
  • Персонализация — фильтр по автору или отделу (только документы, созданные пользователем или его командой).
  • Безопасность — фильтр по уровню доступа (public, internal, confidential).

Пет-проект для закрепления

Задача Реализовать RAG-систему для корпоративной базы знаний, где пользователь может фильтровать результаты по отделу (HR, IT, Finance) и по дате (последние 30 дней, последний год, всё время).

Инструменты

Шаги:

  1. Собрать 100–200 текстовых документов, разбить на чанки, присвоить каждому метаданные: department (HR/IT/Finance) и date (YYYY-MM-DD).
  2. Создать векторный индекс (FAISS) и отдельный DataFrame с метаданными.
  3. Реализовать pre-filter: по выбранным фильтрам отфильтровать DataFrame, получить ID чанков, затем выполнить поиск по FAISS только по этим ID (используя faiss.IndexIDMap).
  4. Реализовать post-filter для сравнения: сначала поиск по всему индексу, затем отсев по метаданным.
  5. Сделать Streamlit-интерфейс: поле ввода, чекбоксы для отделов, выпадающий список для периода дат, кнопка поиска.
  6. Вывести результаты с указанием метаданных и сходства.

Ожидаемый результат Работающее приложение, где можно выбрать «IT + последние 30 дней» и получить только релевантные чанки из IT-документов за последний месяц. Сравнить latency pre-filter и post-filter при разных объёмах данных.


Связь с другими вопросами

ВопросТема
377Как организовать индексацию метаданных в RAG?
379Как реализовать гибридный поиск (векторный + BM25) в RAG?
380Что такое Self-Querying Retriever и как он работает?
381Как использовать агентные паттерны для динамического выбора источника?
382Как обрабатывать запросы, требующие объединения данных из нескольких источников?
383Как реализовать многошаговый retrieval с обратной связью?

Навигация