RAG: фильтрация вместо поиска – новая ментальная модель с таблицами | AiManual
AiManual Logo Ai / Manual.
23 Июн 2026 Гайд

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

Переосмысление RAG: используйте структурированные таблицы (line_df, toc_df) и фильтрацию вместо эмбеддингов. Концепция anchor/context для enterprise RAG.

Реклама
cliv1

Почему векторный поиск в RAG — это как искать иголку в стоге сена с закрытыми глазами

Вы построили RAG-систему. Залили тысячи документов, натренировали эмбеддинги, запустили семантический поиск. А потом задаёте простой вопрос: «Какая версия библиотеки X использовалась в проекте Y во втором квартале 2025?». И модель выплёвывает что-то из соседнего документа, путая даты и контекст. Знакомо? Это не баг — это архитектурная ошибка. Мы пытаемся натянуть векторное сходство на задачи, где нужна точная фильтрация по полям. Enterprise-данные — это не художественная литература, это таблицы, регламенты, логи. Настало время сменить ментальную модель: RAG должен работать как фильтрация, а не как поиск.

Ключевая идея: Вместо того чтобы искать «похожие» чанки по эмбеддингам, мы превращаем весь корпус в структурированную таблицу (line_df + toc_df) и применяем фильтры по точным полям (заголовок, раздел, дата, ID). LLM получает не вероятностный контекст, а точную вырезку из «оглавления».

Проклятие семантического поиска в Enterprise

Эмбеддинги прекрасно работают, когда нужно найти «похожие новости» или «связанные статьи». Но в корпоративных RAG-системах точность важнее похожести. Я уже писал о том, как математический потолок embedding-моделей не даёт найти редкие, но важные документы. А когда база растёт, деградация поиска становится неизбежной — эмбеддинги начинают путать похожие, но разные сущности. Результат: LLM галлюцинирует, пользователь теряет доверие.

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

Ментальный сдвиг: от «поиска» к «фильтрации»

Представьте, что ваш корпус документов — это не мешок слов, а книга с оглавлением (table of contents). Каждый элемент — это строка в таблице с полями: ID, заголовок, раздел, параграф, дата, автор. Вместо того чтобы искать «похожие» абзацы, вы фильтруете строки по точным критериям. Вопрос «Какая версия библиотеки X в проекте Y во втором квартале 2025?» превращается в фильтр: project=Y AND lib=X AND date BETWEEN '2025-04-01' AND '2025-06-30'. Никакой семантики — только точное совпадение.

Это и есть новая ментальная модель: RAG как фильтрующая база данных. Вы создаёте две таблицы: toc_df — оглавление (структура документа) и line_df — строки с контентом. Каждая строка line_df привязана к toc_df через anchor-поле (например, section_id). LLM получает «контекст» — набор строк, отфильтрованных по anchor-полям из запроса. Эмбеддинги если и используются, то только для первичного ранжирования, но не для отбора.

Ошибка новичка: Пытаться заменить эмбеддинги полнотекстовым поиском (BM25) — это тоже поиск, хоть и без векторов. Фильтрация принципиально другая: вы не ищете «лучшее совпадение», а выбираете строки, где поле status = 'active'. Разница как между покупкой товара по названию и по артикулу.

Концепция Anchor/Context: как это работает на практике

Разберём на примере технической документации. У вас есть PDF с описанием API. Традиционный RAG нарежет его на чанки, построит эмбеддинги и при запросе «Как вызвать метод login?» вернёт чанк, где встречается слово «login». Но если в документации 10 методов с похожими параметрами, модель ошибётся.

С табличным подходом вы сначала парсите PDF в структуру: разделы, подразделы, строки. Каждая строка line_df содержит текст, но главное — anchor-поля: method_name, http_method, endpoint. toc_df содержит иерархию разделов. При запросе вы извлекаете из вопроса сущности (NER или LLM-парсинг) и накладываете фильтр: method_name = 'login'. Всё. Контекст для LLM — строки, прошедшие фильтр, плюс их родительские разделы из toc_df для понимания структуры.

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

Пошаговый план: как внедрить табличную фильтрацию в RAG

1 Спроектируйте схему таблиц

Определите, какие поля будут anchor (используются для фильтрации), а какие context (передаются LLM). Пример для базы знаний продукта: anchor — product_id, version, feature; context — описание, пример кода, примечания. Для документации: anchor — chapter, section, tag.

2 Парсинг и линтинг исходных документов

Используйте парсеры (Unstructured, LlamaParse, или кастомные) для извлечения структуры. Сохраняйте иерархию: если документ — PDF с главами, каждая глава становится строкой в toc_df. Каждый абзац — строка в line_df с ссылкой на toc_id. Важно: не теряйте заголовки и метаданные. Elasticsearch/OpenSearch отлично подходят для хранения таких таблиц с поддержкой фильтрации.

3 Маршрутизация запроса: извлечение фильтров

Первый шаг в production — определить, какие anchor-поля можно извлечь из вопроса. Используйте LLM (небольшую, типа GPT-4o-mini) или rules для парсинга сущностей. Например: «Покажи ошибки в версии 2.3 для Windows» -> version=2.3, os=Windows, type=error. Если извлечение неуверенное, можно подстраховаться гибридным поиском (BM25 + эмбеддинги) — но это компромисс, а не основа.

4 Фильтрация и сбор контекста

Выполните запрос к line_df с фильтрами. Дополнительно подтяните строки из toc_df для иерархии. Если строк много — примените ранжирование (BM25 по тексту anchor-родителя или по самому контексту). Отдайте LLM топ-K строк как контекст.

5 Обработка пограничных случаев

Что делать, если фильтры не дали результатов? Тогда можно упасть на семантический поиск, но лучше добавить fallback-запрос с ослаблением фильтров (например, искать по частичному совпадению). Три причины плохого поиска часто связаны именно с отсутствием fallback-стратегии.

Типичные ошибки и как их избежать

  • Слепое доверие парсингу. Если структура документа не размечается (сканы, сложные таблицы), anchor-поля будут пустыми. Используйте OCR и LLM для разметки, но всегда проверяйте качество.
  • Жесткая фильтрация без ранжирования. Даже отфильтрованные строки могут быть нерелевантны — добавьте BM25 по контекстному полю для сортировки.
  • Игнорирование гибридных случаев. Иногда запрос не содержит явных anchor-полей (например, «Расскажи про архитектуру»). В таких случаях можно использовать эмбеддинги, но как второй этап. Roadmap RAG 2026 показывает, что гибрид с приоритетом фильтрации — самый надёжный путь.
  • Забыли про безопасность. Фильтрация позволяет легко внедрить row-level security: ограничить доступ пользователя к строкам по anchor-полю (департамент, роль). В 2026 году атаки на RAG через инъекции стали нормой, и табличная модель даёт естественную защиту.

Когда эмбеддинги всё ещё нужны? (спойлер: редко)

Табличная фильтрация не отменяет векторы полностью. Они полезны для:
- Первичного поиска, когда вы не знаете точные anchor-значения (например, «найди что-то похожее на этот баг-репорт»).
- Мультимодальных данных (изображения, аудио).
- Задач, где важна семантическая близость, но не точность фильтра.
Однако для 80% enterprise-запросов (вопросы по версиям, статусам, датам, именам) фильтрация выигрывает вчистую. Современные исследования RAG подтверждают: гибрид с доминированием структурированного поиска показывает стабильно высокие метрики.

Прогноз: будущее за «табличным RAG»

В ближайшие год-два мы увидим, как все major RAG-фреймворки (LangChain, LlamaIndex) добавят встроенную поддержку анкорных таблиц. Эмбеддинги останутся для «похожего поиска», а точные ответы будет давать фильтрация. Уже сейчас иерархический RAG провалился именно из-за того, что пытался искать по иерархии через эмбеддинги, а не через фильтрацию по структуре. Не повторяйте чужих ошибок — переключайтесь на фильтрацию уже сегодня.

Подписаться на канал