Головна / Статті

PageIndex: Безвекторний RAG, заснований на міркуваннях, із дерев’яним індексом

PageIndex відмовляється від бази даних векторів на користь індексу дерева, керованого LLM. Як працює цей RAG, заснований на логічних міркуваннях, із прикладом коду на Python, підключенням через MCP та показниками тестування.

Видавець: REACTAPP.TOP14 хв читання3118 слів

Джерела: PageIndex: The RAG Framework That Threw Out Vector Databases and Still Hit 98.7% Accuracy

Уявіть собі фреймворк RAG, який повністю відмовляється від векторних баз даних та досягає результатів тестування, до яких жодна система, заснована на ембеддингах, не може навіть наблизитися. Це PageIndex — проєкт з відкритим кодом від VectifyAI. У FinanceBench, стандартному тесті для відповідей на запитання щодо фінансових документів, звичайні системи RAG на основі векторів досягають результатів близько 30–50 відсотків. PageIndex показує 98,7 відсотка. Він досяг цього не завдяки кращій моделі ембеддингів, а шляхом їх повного відмовлення.

Цей результат заслуговує на уважніше розглядання, адже обробка фінансових запитань на основі документів SEC є одним із найскладніших тестів пошуку в промислових системах штучного інтелекту. Він вимагає багатоетапного міркування, переходу між розділами та точних чисел. Найкраща система, яка існує, не використовує ні векторів, ні розділення на частини, ні векторні бази даних.

Ось що робить PageIndex, як він побудований, приклади робочого коду на Python, де він проявляє себе найкраще та де — ні.

Стіна, на яку натрапляють довгі документи

Кожен, хто створював системи типу RAG для матеріалів, довших за пост у блогу, знає про проблеми, які це приносить. Користувач запитує систему, засновану на 200-сторінковому щорічному звіті, як загальний чистий дохід у фінансовому році 2024 порівнюється з фінансовим роком 2023. Стандартна процедура виглядає так:

  1. Розділити документ на фрагменти фіксованого розміру, зазвичай від 300 до 500 токенів.
  2. Вбудувати кожен фрагмент за допомогою моделі, наприклад text-embedding-3-large.
  3. Зберегти вектори у Pinecone, Weaviate, Milvus, Chroma, pgvector чи подібних сервісах.
  4. Під час запиту вбудувати запитання та отримати k найближчих векторів за косинусовою схожістю.
  5. Передати ці фрагменти до LLM для формування відповіді.

Для коротких, неструктурованих матеріалів, таких як пости у блогах, заявки на підтримку та документація до продуктів, це чудово. Однак звіти типу 10-K розбиваються на фрагменти прогнозованими способами.

Запит не схожий на відповідь. Метод пошуку векторів передбачає, що текст, найбільш схожий на запит, є найбільш релевантним. Але запит виражає намір, а не зміст. Запитання про тенденції доходів не відображає лише одну рядок таблиці на сторінці 47 — воно відображає кожен абзац, де йдеться про доходи.

Один термін, шістдесят місць. У довгому щорічному звіті термін „операційний дохід“ зустрічається десятки разів у різних контекстах. Методи порівняння схожості оцінюють їх приблизно однаково, тож уривок, який насправді відповідає на запитання, може ніколи не потрапити до трійки лідерів.

Розділення на частини псує таблиці. Заголовок таблиці опиняється у частині 14, а потрібна рядок — у частині 15. Кожна з цих частин сама по собі не має сенсу. Надкриття елементів, семантичне розділення та парсери, які розуміють структуру таблиць, можуть допомогти, але жоден з них не усуває основної проблеми.

Відсутня пам’ять між раундами. Якщо попереднє повідомлення стосувалося активів, а наступне — «а як щодо зобов’язань?», система сприймає це як абсолютно нове запитання. Ніщо не свідчить про те, що користувач, ймовірно, має на увазі ту саму частину того самого звіту.

Переліки посилань залишаються прихованими. Припустимо, сторінка 12 вказує читачам на Додаток G, а цей додаток знаходиться на сторінці 87. Текст додатку не має семантичного збігу з початковим запитом, тому система RAG ніколи не буде слідувати за цим посиланням. Введення метаданих переліку посилань у фрагменти тексту є тимчасовим рішенням, але воно нестабільне та не підходить для масштабування.

Це не рідкісні крайні випадки — вони випливають з самої архітектури. Як зазначив співзасновник VectifyAI Мінтянь Чжан у оголошенні про проект, для ефективного пошуку насправді потрібна релевантність, а релевантність вимагає міркувань.

Що таке PageIndex

PageIndex — це безвекторна, заснована на міркуваннях платформа типу RAG, випущена компанією VectifyAI у вересні 2025 року; її створили Чжан та Юй Танг. Вона ліцензована за ліцензією MIT, і кількість записів у її репозиторії швидко зростала — до кінця березня 2026 року її оцінили 23 000 разів, а кількість копій сягнула майже 2 000.

Її основна ідея походить від AlphaGo. Замість того, щоб повністю сканувати простір та порівнювати кожен фрагмент за допомогою векторної схожості, тут використовується навчена стратегія для інтелектуального орієнтування. PageIndex застосовує цей підхід до документів, маючи три значущі характеристики:

  • Відсутність векторної бази даних. Структура документа та процес міркувань великих мовних моделей замінюють пошук за схожістю, тому тут немає Pinecone, Chroma чи ембеддингів.
  • Відсутність розділення на фрагменти. Документи зберігаються у вигляді природних розділів, глав, абзаців та таблиць, а не у довільних вікнах по 500 токенів.
  • Навігація у стилі людини. Вона працює як людина, яка шукає інформацію в документі вручну: дивиться зміст, знаходить розділ, який, ймовірно, містить відповідь, та читає його.

Метод складається з двох кроків.

Крок 1: ієрархічний деревоподібний індекс

Під час обробки документа не створюються жодні ембеддинги. PageIndex аналізує структуру документа та створює ієрархічне дерево, яке по суті є змістом, придатним для роботи з LLM. Кожен вузол містить назву, короткий опис розділу, діапазон сторінок та дочірні вузли для підрозділів, які можуть бути розташовані на будь-якій глибині.

Документ SEC обсягом 50 сторінок може створити дерево з 30 до 50 вузлів, яке зберігається у вигляді одного JSON-об’єкта. Жодна інформація не зберігається у векторній базі даних — це лише JSON. Весь об’єкт поміщається в одне вікно контексту LLM, і ви можете відкрити будь-який вузол та прочитати його. Вузол виглядає так:

{
  "node_id": "0006",
  "title": "Financial Stability",
  "start_index": 21,
  "end_index": 22,
  "summary": "Covers the Federal Reserve's financial stability oversight...",
  "sub_nodes": [
    {
      "node_id": "0007",
      "title": "Monitoring Financial Vulnerabilities",
      "start_index": 22,
      "end_index": 28,
      "summary": "Describes the Fed's vulnerability monitoring framework..."
    },
    {
      "node_id": "0008",
      "title": "Domestic and International Cooperation",
      "start_index": 28,
      "end_index": 31,
      "summary": "Federal Reserve collaboration with international bodies..."
    }
  ]
}

Кожен node_id вказує на необроблений вміст відповідного розділу: текст, таблиці та зображення. Штучний інтелект може запитати вміст будь-якого вузла, коли це необхідно.

Ключовою особливістю архітектури є місце розташування цього індексу. База даних векторів зберігає окремий, статичний індекс ембеддингів поза межами моделі. Тут дерево JSON знаходиться всередині активного контексту ШІ під час виконання завдань. VectifyAI називає це індексом у контексті, і модель не просто запитує його, а аналізує його в реальному часі.

Крок 2: пошук у дереві, здійснюваний шляхом міркувань

Коли надходить запит, PageIndex передає ШІ дерево, яке містить лише назви та короткі описи, без самого тексту, і фактично запитує, де шукати відповідь за цією структурою та цим запитом. Модель читає назви та описи вузлів, визначає, які розділи, ймовірно, містять відповідь, та повертає список ідентифікаторів вузлів. Потім система завантажує справжній контент цих вузлів.

Процес пошуку є циклом:

  1. Проаналізуйте зміст, щоб зрозуміти структуру та визначити потенційні розділи.
  2. Виберіть розділ, який, ймовірно, містить потрібну інформацію.
  3. Прочитайте його, отримавши справжній текст з цього вузла.
  4. Перевірте, чи цього достатньо. Якщо так, напишіть відповідь. Якщо ні, поверніться до першого кроку та спробуйте інший розділ.

Це інша парадигма, ніж векторний пошук. База даних векторів обчислює косинусову схожість для кожного фрагмента паралельно, що робить процес швидким, але поверхневим. PageIndex натомість використовує ШИМ для аналізу того, де, ймовірно, знаходиться відповідь. Під час цього процесу модель може слідувати за переліками посилань, розпізнавати, що запит щодо даних додатку належить до відповідного вузла додатку, та усвідомлювати, що багатокомпонентний запит потребує інформації з двох розділів. Кожен крок також залишає слід міркувань, тож ви можете бачити, які вузли були відвідані та чому. Таким чином процес пошуку перестає бути „чорною скринькою“.

Приклад Додатку G

Демонстрація, створена на основі інтеграції MCP від VectifyAI, показує, чому це важливо. Хтось запитує про загальну суму відстрочених активів у щорічному звіті Федеральної резервної системи. Основна частина звіту на сторінках 75–82 розглядає зміни відстрочених активів, але так і не наводить їхню загальну суму. Однак на сторінці 77 є речення, у якому зазначено, що таблиця 5.3 узагальнює інформацію про доходи, витрати та розподіли, а додаток G містить більш детальні дані.

Vector RAG застрягає на цьому етапі. Додаток G — це таблиця чисел, яка не має семантичної схожості з запитом про відстрочені активи, тому база даних її ігнорує. PageIndex працює інакше. Штучний інтелект читає структуру даних, переходить до фінансового розділу, знаходить посилання на додаток G, слідує за ним у структурі, отримує потрібну таблицю та повертає точну цифру, при цьому слід відстеження кожного кроку.

Python, від початку до кінця

Наведений нижче код адаптований з офіційної книги PageIndex, з примітками, які пояснюють його функціонування. Почніть з встановлення:

pip install pageindex openai

Імпорт:

import os
import json
import asyncio
from pageindex import PageIndexClient
from openai import AsyncOpenAI

Далі — облікові дані та клієнти. Ключ API отримується з панелі керування PageIndex:

# Grab an API key from https://dash.pageindex.ai/api-keys
PAGEINDEX_API_KEY = os.environ["PAGEINDEX_API_KEY"]
OPENAI_API_KEY = os.environ["OPENAI_API_KEY"]pi_client = PageIndexClient(api_key=PAGEINDEX_API_KEY)
openai_client = AsyncOpenAI(api_key=OPENAI_API_KEY)

Створіть дерево

Використайте інструменти:

import pageindex.utils as utils

Завантажте PDF, і PageIndex створить дерево. Індексування займає деякий час, тому скрипт буде перевіряти статус до моменту готовності та виведе результат:

# Upload a PDF; PageIndex handles the tree generation
doc = pi_client.upload("annual_report_2024.pdf")
doc_id = doc["doc_id"]# Tree generation takes a bit, so we poll
while not pi_client.is_retrieval_ready(doc_id):
    print("Still indexing...")
    import time; time.sleep(5)# Grab the tree and take a look
tree = pi_client.get_tree(doc_id, node_summary=True)["result"]
print("Document Tree:")
utils.print_tree(tree)

У вихідному документі відображаються кожен розділ, підрозділ та діапазон сторінок, що дозволяє одразу зрозуміти, чи правильно дерево відтворило макет.

Нехай ШІ шукає в дереві

Це ключовий крок. Функція отримує дерево та запит:

async def find_relevant_nodes(tree: dict, query: str) -> list:
    """LLM reasons over tree structure to identify relevant nodes."""

Усередині первинний текст видаляється для економії токенів, адже моделі потрібні лише заголовки та короткі описи. У запиті модель представлена як експерт з пошуку документів, їй наказують слідувати перелікам посилань та просить надати JSON із обґрунтуванням та списком ID вузлів:

    # Strip raw text to save tokens; the LLM only needs titles and summaries
    tree_without_text = utils.remove_fields(
        tree.copy(), fields=["text"]
    )    search_prompt = f"""
    You are a document retrieval expert. Given a question and
    a hierarchical tree structure of a document, identify all
    nodes likely to contain the answer.    Each node has a node_id, title, and summary.
    Follow cross-references if a section mentions another.    Question: {query}    Document tree structure:
    {json.dumps(tree_without_text, indent=2)}    Reply in this JSON format only:
    {{
        "thinking": "<reasoning about which nodes are relevant>",
        "node_list": ["node_id_1", "node_id_2"]
    }}
    """    response = await openai_client.chat.completions.create(
        model="gpt-4.1",
        messages=[{"role": "user", "content": search_prompt}],
        temperature=0,
        response_format={"type": "json_object"},
    )    result = json.loads(response.choices[0].message.content)
    print(f"LLM reasoning: {result['thinking']}")
    return result["node_list"]

Отримати контент та відповідь

Наступна функція збирає необроблений текст для обраних вузлів:

def collect_node_content(tree: dict, node_ids: list) -> str:
    """Pull raw text from the nodes the LLM selected."""
    all_nodes = utils.flatten_tree(tree)
    context_parts = []

Потім тіло циклу формує контекст, причому кожен розділ позначається назвою та діапазоном сторінок, а весь процес охоплює вибір вузлів, отримання контенту, створення відповіді з посиланнями та повернення відповіді разом із використаними вузлами.

    for node in all_nodes:
        if node["node_id"] in node_ids:
            title = node.get("title", "Untitled")
            pages = f"pages {node.get('start_index', '?')}-{node.get('end_index', '?')}"
            text = node.get("text", "")
            context_parts.append(
                f"[{title} | {pages}]\n{text}"
            )
    return "\n\n---\n\n".join(context_parts)
async def answer_query(tree: dict, query: str) -> dict:
    """Full vectorless RAG pipeline: tree search + answer generation."""    # Step 1: LLM picks the nodes
    node_ids = await find_relevant_nodes(tree, query)    # Step 2: Fetch content from those nodes
    context = collect_node_content(tree, node_ids)    # Step 3: Generate answer with citations
    answer_prompt = f"""
    Answer the question using only the provided context.
    Cite specific pages and sections in your answer.    Context:
    {context}    Question: {query}
    """    response = await openai_client.chat.completions.create(
        model="gpt-4.1",
        messages=[{"role": "user", "content": answer_prompt}],
        temperature=0,
    )    return {
        "answer": response.choices[0].message.content,
        "retrieved_nodes": node_ids,
        "context_length": len(context),
    }
# Run it
query = "What was the total value of deferred assets in 2023?"
result = asyncio.run(answer_query(tree, query))
print(result["answer"])
print(f"Nodes used: {result['retrieved_nodes']}")

Підключення до MCP

Для Claude, Cursor чи будь-якого агента, сумісного з MCP, PageIndex підключається безпосередньо через хостований сервер:

{
  "mcpServers": {
    "pageindex": {
      "type": "http",
      "url": "https://api.pageindex.ai/mcp",
      "headers": {
        "Authorization": "Bearer your_api_key"
      }
    }
  }
}

Існує також локальний сервер MCP для завантаження файлів з диска, і для його роботи потрібен Node.js 18 або новіша версія:

{
  "mcpServers": {
    "pageindex": {
      "command": "npx",
      "args": ["-y", "@pageindex/mcp"]
    }
  }
}

Цифри в контексті

Основна цифра наведена FinanceBench, який використовує реальні звіти SEC, зокрема 10-K, 10-Q та 8-K, і приймає лише точні відповіді. Агент фінансового аналізу Mafin 2.5 від VectifyAI, створений на основі PageIndex, було протестовано на всій базі даних. Звітна точність:

Система Точність Охоплення бенчмарку
Mafin 2.5 (PageIndex) 98,7% 100%
Традиційний векторний RAG близько 50% змінюється
GPT-4o без RAG близько 31% 100%
Perplexity близько 45% змінюється

Різниця у близько 49 відсоткових пунктів порівняно з традиційним векторним RAG — це не незначне покращення. Це зовсім інший клас результатів. Три фактори постійно згадуються:

  • Наступні перелінковані посилання. Внутрішні посилання в документі, такі як „див. Додаток G“, обробляються через структуру дерева. Пошук за схожістю не розуміє їх.
  • Збереження структури. Таблиці мають заголовки, примітки та зв’язки між клітинками. PageIndex зберігає їх як вузли дерева, тоді як розділення на частини їх знищує.
  • Багатокрокове міркування. Запит, який порівнює фінансовий рік 2023 з фінансовим роком 2024 та наводиться в різних частинах документа, природним чином обробляється за допомогою ітеративного циклу.

Де він поступається

PageIndex є вражаючим для своєї ніші, але він не може замінити векторний RAG у всіх випадках.

  • Це повільніше. Кожне отримання даних вимагає кількох викликів LLM: читання індексу, аналізу вузлів, завантаження контенту, перевірки достатності інформації, можливо, повторного оброблення. Vector RAG відповідає за мілісекунди, а PageIndex — за секунди. Співзасновник зазначив, що оскільки отримання даних відбувається безпосередньо під час генерації, час отримання першого токена може бути порівнянним із звичайним викликом LLM, проте загальна затримка все одно є вищою.
  • Це дорожче. На кожен запит потрібно кілька викликів для обробки даних, на відміну від одного лише пошуку ембеддингів. При великому обсязі даних це призводить до значних витрат.
  • Це створено для глибини, а не для ширини. Він чудово підходить для глибокого аналізу одного довгого документа. Пошук 10 000 коротких документів одночасно — це сфера, де проявляють себе векторні бази даних, а створення та аналіз дерева для кожного з них є непрактичним.
  • Структура має значення. Добре сформатований документ, поданий до SEC, створює чудову ієрархічну структуру. Погано сформатований скан без логічних розділів призводить до поганої структури змісту, що впливає на все інше. Для таких випадків існує режим, заснований на візуалізації, але структуровані документи залишаються оптимальним варіантом.
  • Міркування ШІ також не є ідеальними. Метод Vector RAG дає помилки, оскільки ембеддинги лише наближено відтворюють значення. Система PageIndex також допускає помилки, коли міркування є неповними: модель може перейти до неправильного розділу, неправильно прочитати резюме чи пропустити важливий підрозділ. Ці помилки легше інтерпретувати та, можливо, простіше виправляти, але вони все одно трапляються.
  • Один показник ефективності. FinanceBench оцінює якість перевірки на добре структурованих документах, поданих до SEC. Результати для складних, багатодоменних, неоднозначних запитів у дуже різних типах документів не були незалежно перевірені в такій самій мірі. 98,7 відсотка — це реальна цифра, але важливо знати, що саме вона вимірює.
  • Коли його використовувати

    Використовуйте PageIndex, коли:

    • документи є довгими, структурованими та професійними: річні звіти, контракти, регуляторні звіти, технічні посібники
    • точність має більше значення, ніж швидкість, наприклад, коли неправильна відповідь коштує грошей або створює ризики для дотримання правил
    • запити вимагають багатоетапного міркування або переходу між розділами
    • вам потрібні записи аудиту, оскільки кожна відповідь супроводжується слідом міркувань та посиланнями на елементи, що цінується в регульованих галузях

    Залишайтеся з векторним RAG, коли:

    • вам потрібні відповіді за кілька секунд при високій кількості запитів
    • ви шукаєте у великому корпусі коротких документів
    • документи є неструктурованими або мають формат розмови
    • 90 відсотків точності є достатніми
    • основною обмеженням є вартість за запит

    Гібридний підхід, ймовірно, недостатньо досліджений. Використовуйте векторний пошук, щоб знайти важливий документ у великому корпусі, а потім передайте його до PageIndex для глибокої обробки.

    Що нового з моменту запуску

    Проєкт стрімко розвивається з вересня 2025 року.

    Агентний векторний RAG. Найновіша версія дозволяє під’єднати самостійно розгорнутий PageIndex до SDK OpenAI Agents. Агент отримує три інструменти (get_document(), get_document_structure() та get_page_content()) та самостійно аналізує структуру, викликаючи відповідні інструменти та отримуючи контент без необхідності ручного вибору елементів. Це приклад, з якого варто починати розробку агентних робочих процесів:

    pip install openai-agents
    python3 examples/agentic_vectorless_rag_demo.py
    

    Режим, заснований на візуалізації без OCR. Тепер потік обробки даних, орієнтований на візуальний аналіз, оминає етап OCR. Зображення сторінок потрапляють безпосередньо до моделі, яка розуміє візуальну інформацію, а структура дерева формується на основі того, що бачить ця модель на сторінці. Це має велике значення для фінансових документів, де формат балансу має таке ж значення, як і текст. Графіки, таблиці з об’єднаними клітинками та примітки залишаються недоторканими, оскільки ніщо спочатку не перетворюється на текст.

    SDK на TypeScript, pageindex-js-sdk, з’явився на початку 2026 року для інтеграції з Node.js та веб-інструментів для обробки документів. Розширення для робочого столу .mcpb за один клік дозволяє встановити PageIndex у Claude Desktop з автоматичним обробленням процедури OAuth. ChatIndex, окремий репозиторій з січня 2026 року, застосовує ту саму ідею індексування деревом до довгих історій чатів.

    Ширші зміни

    PageIndex є частиною ширшого переходу від пасивного пошуку, який знаходить все, що виглядає схожим, до активного, агентного пошуку, який обмірковує, де шукати. Це вже помітно у інструментах для програмування. Claude Code та Cursor відійшли від векторно-орієнтованих технологій RAG для коду на користь активного дослідження кодової бази, і команда PageIndex очікує, що документи також підуть цим шляхом. Для структурованих документів таке прогнозування здається правдоподібним.

    Векторні бази даних не зникнуть. Вони швидкі, дешеві та достатньо ефективні для вирішення величезної кількості проблем. Але там, де точність, прозорість та глибоке розуміння документів є обов’язковими, як у фінансових аудитах, юридичних дослідженнях, медичних записах та дотриманні регуляцій, пошук, заснований на логічних міркуваннях, є значно кращим рішенням.

    Код та документація знаходяться за адресою https://github.com/VectifyAI/PageIndex, а документація — за адресою https://docs.pageindex.ai/.