Как помочь вашему RAG адаптироваться? Вместо top_k — DRAG with KNEE

Как помочь вашему RAG адаптироваться? Вместо top_k — DRAG with KNEE

Многие сталкивались с проблемой: RAG-система получает сложный PDF на 50 страниц, а в ответ либо галлюцинирует, либо отправляет в LLM огромный объём нерелевантного текста, быстро съедая бюджет на токены. Проблема — в классическом статическом подходе с top_k: он либо не даёт достаточно контекста, либо перегружает модель шумом. Нужно, чтобы RAG умел адаптироваться.

Решение — DRAG with KNEE (Dynamic RAG with Knee-point pruning). Это алгоритм, который не просто ищет похожие фрагменты, а строит иерархию документов и отсекает лишнее с помощью геометрического анализа «точки колена». В этой статье — как сделать RAG адаптивным с помощью Qdrant, Python и математики.

DRAG with KNEE: как это работает

Алгоритм использует иерархическое дерево (Hierarchical Vector Tree), где:

  • Корни — краткие сводки документов.
  • Листья — конкретные страницы с детальным содержанием.

Такой подход позволяет эффективно искать релевантный контекст, не перегружая LLM.

Компоненты системы

Система объединяет три ключевых элемента:

  • Vector-Dense Complex — для глубокого понимания семантики.
  • Sparse-Keyword Extract — для точного поиска по ключевым словам.
  • RRF-Enzymes — ферменты слияния, которые объединяют результаты dense и sparse поиска в единый ранжированный список с помощью Reciprocal Rank Fusion (RRF).

Механизм действия

При поступлении запроса запускается «дышащий луч» (Adaptive Beam). Он проходит от корней к листьям, подавляя родительские узлы, если их дочерние узлы оказываются более релевантными.

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

Индексация: построение иерархии

Иерархия строится в Qdrant по следующему алгоритму:

  1. Документ разбивается на страницы. Для каждой LLM генерирует краткое смысловое описание и ключевые слова (с использованием структурированного вывода, чтобы избежать галлюцинаций). Эти данные становятся листьями дерева.
  2. Листья объединяются в узлы (по 3–5 штук). На основе их описаний и ключевых слов формируется описание родительского узла. Ключевые слова наследуются полностью.
  3. Процесс повторяется до тех пор, пока не останется один корневой узел с меткой parent_id=-1. Это позволяет легко находить стартовые точки поиска.

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

Алгоритмы поиска

Были рассмотрены три подхода:

  • Поиск по веткам (branch_search): спуск от корня к листьям. Если ребёнок релевантнее родителя — спускаемся. Если нет — фиксируем родителя. Плюс — полнота. Минус — риск переноса нерелевантного контекста из-за top_k.
  • Поиск по фиксированному лучу (beam_search, fixed): на каждой итерации сравниваются все кандидаты (свои и чужие дети). Если ребёнок лучше родителя — родитель исключается. Если родитель лучше всех детей — дети исключаются. Проблема — фиксированная ширина луча может пропустить важный контекст или захватить лишнее.
  • Адаптивный луч с точкой колена (beam_search, adaptive_with_knee): ширина луча определяется динамически. На каждом шаге строится график RRF-оценок, и с помощью метода колена отсекается шум. Это позволяет избежать жёсткого top_k и адаптироваться под запрос.

Чувствительность отсечения

Введён параметр чувствительности (от 0 до 1):

  • При 0 — отсечение мягкое, возвращаются почти целые документы (до max_num_roots).
  • При 1 — стандартное отсечение по колену.

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

Важное замечание по реализации

При реализации важно сначала применять логику сжатия (исключение родителей/детей), а затем — отсечение по колену. Иначе алгоритм может преждевременно остановиться, выдав всего 2 точки, что сводит пользу от адаптивности на нет.

Результаты и выводы

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

В следующей части — тесты на бенчмарках и сравнение с другими подходами. Автор приглашает к обсуждению и PR на GitHub для совместного развития адаптивного RAG.

Читать оригинал