Попытки найти альтернативу векторному поиску в RAG продолжаются. После GraphRAG появился новый кандидат — PageIndex.
Идея проста. Документ разбивается на страницы. С помощью LLM и специального кода строится расширенная таблица содержания (TOC) в виде дерева узлов, для каждого из которых генерируется саммари. При поступлении запроса эта структура передаётся в промпт, и LLM определяет релевантные узлы. Каждому узлу соответствуют страницы документа, которые затем используются как контекст для финального ответа.
Не нужны чанки, эмбеддинги и векторные хранилища. Выглядит привлекательно. Добавлю критический взгляд и расскажу, как запустить PageIndex локально.
Плюсы и минусы
В README проекта приведён пример с одним документом. Но что делать, если их сотни или тысячи? Авторы предлагают три стратегии:
- Поиск по метаданным. Перспективно, но метаданные нужно получить. Эта функция обещана в ближайшей бета-версии. Однако фильтрация по метаданным уже есть в большинстве векторных хранилищ.
- Поиск по сгенерированному описанию документов. Работает, но эффективно только при небольшом количестве документов.
- Семантический поиск. Векторный поиск возвращается — без него не обойтись. Можно искать по TOC и саммари. Коэффициент сжатия контекста у PageIndex — в 4–5 раз меньше исходного текста.
Можно использовать и другие методы: TF-IDF, BM25, поиск по ключевым словам. Но индексы всё равно придётся где-то хранить. А где хранить TOC при большом числе документов? Пока PageIndex не предлагает рабочего решения для масштабных коллекций.
LLM давно используется в RAG: для транскрипции, управления коллекциями, адаптивной обработки. PageIndex добавляет ещё одну функцию — замену поиска по сходству на поиск по релевантности. Это имитирует поведение человека: сначала смотришь в оглавление, находишь нужный раздел, потом читаешь текст.
Retrieval is based on reasoning — traceable and interpretable, with page and section references. No more opaque, approximate vector search...
Заявлено 98,7% правильных ответов на FinanceBench — впечатляюще. Но цена высока: ресурсоёмкость. Допустим, векторный поиск с хорошим эмбеддингом даёт 90% точности. Дополнительные 8,7% могут стоить в десятки раз больше. Для некоторых проектов это оправдано. Для других — нет.
Судя по конфигурации, авторы экспериментировали с GPT-4o и GPT-5. Я же попробовал запустить PageIndex на локальных моделях.
Эксперименты
Текст для экспериментов
Сначала взял рассказ Уильяма Гибсона «New Rose Hotel» — PageIndex не справился. Скорее всего, из-за отсутствия структуры: это повествование без заголовков. Затем выбрал академическую статью: «A Cyberpunk 2077 perspective on the prediction and understanding of future technology» от Miguel Bordallo López и Constantino Álvarez Casado (University of Oulu).
Варианты пайплайна
PageIndex поддерживает Markdown и PDF — это два разных пайплайна.
Для Markdown текст разбивается по строкам с заголовками. Сегментация происходит от одного заголовка до следующего. Работает быстро, но это стандартный подход, аналогичный MarkdownTextSplitter из LangChain. Если заголовки не помечены — вся структура сводится к одному узлу: названию файла.
С PDF сложнее. PageIndex пытается выделить неотмеченные заголовки. Но не всегда успешно, особенно на слабых моделях.
Проблема I. Пайплайн падает из-за ошибок вывода в JSON. Пришлось обработать текст: убрать список литературы, кавычки и преобразовать нумерованные списки в маркированные.
Проблема II. Ошибка при недостаточной точности (accuracy) построения структуры. Порог — 60%. Для PDF система оценивает, сколько найденных узлов действительно находятся в указанных фрагментах. Можно снизить порог — коррекция ошибок идёт дальше. Я вынес значение в переменную окружения ACCURACY_THRESHOLD.
Проблема III. Низкая точность из-за перегрузки LLM. Связывание узлов и фрагментов выполняет LLM. Если модель слабая, она путает нумерацию. В коде предусмотрена группировка фрагментов, но параметр max_tokens по умолчанию — 20 тысяч токенов. Для текста в 17k токенов это одна группа. Я вынес MAX_TOKENS в переменную окружения.
На начальном этапе LLM ищет разделы:
- gemma3:27b — нашла оглавление, которого нет в документе. Пришлось остановить.
- gpt-oss:20b — справилась. Точность — от 57 до 63%. Seed не стабилизирует результат.
- qwen3:14b — точность 69,57%, но «вылетала» из-за JSON. Структура разделов — бедноватая.
На GPU 16 ГБ процесс занял около получаса: несколько минут на пайплайн, остальное — на коррекцию ошибок.
Как запустить
Внимание: PageIndex использует liteLLM. В requirements указана версия 1.82.0. Установите именно её — версии 1.82.7 и 1.82.8 скомпрометированы.
Не устанавливайте pageindex через pip — это версия с внешним API, не подходящая для локальной работы.
Я подготовил два ноутбука: MinimalPageindexLocalPDF.ipynb и MinimalPageindexLocalMD.ipynb — они в корне репозитория. Часть кода взята из примера с внешним API.
В utils.py добавил переменные OLLAMA_HOST и OLLAMA_TIMEOUT. В page_index.py — возможность настройки ACCURACY_THRESHOLD и MAX_TOKENS через переменные окружения. Этого достаточно для локального запуска.
Пример запроса: «What model was used in preparing this article and why?»
Ещё один запрос: «What does a dystopian future look like in this game?»
Инструкция по установке
- Клонируйте репозиторий:
git clone https://github.com/khmelkoff/PageIndex_local.git - Создайте и активируйте окружение:
conda create --name pageindex python==3.11, затемconda activate pageindex - Установите зависимости:
pip install -r requirements.txt - Проверьте версию litellm:
pip show litellm— должна быть 1.82.0 - Установите Jupyter:
pip install notebook - Запустите Jupyter и откройте MinimalPageindexLocalPDF.ipynb
- Загрузите модель:
ollama pull gpt-oss:20b - Остановите ollama, установите переменные и запустите:
set OLLAMA_HOST=0.0.0.0:11434,set OLLAMA_CONTEXT_LENGTH=32000, затемollama serve - В ноутбуке проверьте переменные окружения и название модели. Для litellm 1.82.0 префикс openai обязателен.
- Текст для экспериментов — в папке data: 2077.pdf, 2077.md и примеры TOC в формате pickle.
Выводы
Код пайплайна неидеален: функции похожи, слабо документированы, промпты вшиты в код. Для MVP — нормально, для продакшена — требуется доработка.
Лучший результат, вероятно, достигается в гибридном режиме: например, использовать расширенную TOC в векторном поиске или применять семантический поиск на этапе выбора документов. PageIndex — не альтернатива векторному RAG по эффективности. Ответы в реальном времени маловероятны, а обработка документов остаётся на порядок дороже.