Бязвектарны RAG з PageIndex: пошук праз разважанне замест вектараў
PageIndex замяняе эмбедынгі і разбіўку на LLM, якая ідзе па дрэве дакумента. Кароткі эскіз на Python і выпадкі, калі вектарны RAG усё ж лепшы.
Крыніцы: **I Stopped Using Vector Databases for RAG : PageIndex Vectorless RAG**
Кожны, хто ужо дзеяе з сістэмамі RAG якіясь час, знае гэты раздражэнне. Вы вітрачаеце гадзіны на стварэнне эмбедынгаў, налаштовванне розмера фрагментаў, выбір базы вектарных дадзеных, а сістэма все равно дае некарэктны адказ. Яна знаходзіць штось падобнае, пры тым што „падобнае“ і „рэлевантнае“ — гэта не адно і тое ж.
Фінансавыя звясткі яскрава паказваюць гэта. Возьмімо 120-сторонній документ 10-K. Сістэма на адной вектарнай базе RAG продовжае браць фрагменты з канцэптуальнага падсумку, тады калі справжні адказ знаходзіцца ў прымечанні на 87-й стороне. Оба фрагменты маюць спакойныя словы і аднаковы значэнне, але толькі ў аднам з іх ёсць неабходная цыфра.
PageIndex была створана самэ для борьбы з гэтым.
Што такое PageIndex
PageIndex — гэта фрэймворк RAG адаптаванага ад адкрытага кода ад команды VectifyAI. Яго цэнтральная ідэя простая і адрозніцца ад большай часткі іншых раўняючыхся раўняў. У працоўцы замест таго, каб ператварыць дакумент у вектары і адрабатваць пошук найбліжэйшага суседа, ён стварае іерархічны дрэва з дакумента, і LLM сам вырашае, як дасягнуць адпаведнай адпаведзі.
Ён абоўсумова прыменшвае вікорыстоўванне эмбедынгаў, хранення вектараў і ўдзелення дакументаў на часткі.
Праект называе гэта «vectorless RAG», і гэтыя апісанні досыць точныя. Ён з’явіўся у верасні 2025 года і быстра прывернуў увагу, галоўная прычына — адзін паказаны показнік: 98,7% точнасці на FinanceBench, проты прыблізна 50% для традыцыянальнага vector RAG на тым жа тэсце. Ставіцеся з асторогай да показнікаў, паказаных віробнікамі, і тэставаце на своіх сабскайных дакументах, але калі гэтыя разлікі застаюцца, хоць часткова, то гэта зусім іншы ранг.
Дзе выконваецца vector RAG
Асэнцыйныя падазрыцці вектарнага RAG заключаюцца у тым, што тэкст, які мае найбольшую сэмантычную супараднасць з запытам, ў той жа час ёсць і найболей рэлевантным. У практыцы гэтыя падазрыцці ўсё часы не падтрымваюцца.
Якшо запытаць: «Калькуюцься якія загальныя боргі компаніі у 2023 годзе?», то фрагменты з найвышым балам можу быць такімі:
- ліст генеральнага дырэктара, у яком обгаваруецца стратэгія кантролю боргаў,
- раздзіл пра рызыкі, прысвечаны правілам боргавых зобавэсцяў,
- ступенек у гласарыі, які дае вялікія боргі.
Усе тры фрагменты маюць блізкі значэнне да запытання, але ніхто з іх не ёсць адказам — адказам являецца конкрэтная цыфра ў балансе на сторанцы 64.
Да гэтага дадаюцца ўсё большыя проблемы:
- Разбіўка на часткі разрушае кантэкст. Разліквідаванне 100-сторанковага дакумента на фрагменты по 500 токенав ведае да втраты яго структуры. Рэчэнца, якая гаворыць "як згадана ў Тэбліце 3.2", стае аддзеўнанай ад Тэбліцы 3.2, калі яны опінуюцца ў разных частках, і логіка ўзаемных звязкаў дакумента ігнаруецца.
- Вектары не можу следаваць за перакіднымі ссылкамі. У дакументах пішацца "дывіцеся Дадатак G" або "зверніцеся да паказнікаў пакананнага года ў Раздзеле 4", але алгорытмы пошуку на адпоўненасці не маюць можлівасці следаваць за гэтымі паказаннямі. Гэта проста фрагменты тэксту без жадных звязкаў з ўсімі цілямі, на якія воны натыкаюцца.
- Запіты выражаюць намер, а не змест. Пользоватэль опісвае, што ён хочаць дазнацца, а не як будзе выглядаць адпаведны адказ. Адказ можа вжываць іншы слоўнік, і алгорытмы на адпоўненасці сталквуюцца з труднасцямі, таму што яны паўставляюць тэкст запіту з тэкстам адказу.
Пошук па логіцы
Змена падходу заключаецца ў наступным. Уместо таго, каб спытацца, якія часткі дакладна падобнаеся да запіту, PageIndex спытаецца, дзе чалавек-эксперт стаў бы шукаць у гэтым докуменце, каб даў адпаведны адказ. Гэта іншая стратэгія адналёгчэння, і яна набліжэй падходзіць да структураваных, складных документав.
Як гэта працюе
Існуе два этапы.
Этап 1: стварэнне дрэва індэкса. Пры отрыманні документа PageIndex не дзельніць яго на часткі. Ён чытае яго і стварае іерархію, якая відпраўляеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаеястаея
Annual Report 2023
├── Business Overview
│ ├── Products and Services
│ └── Market Position
├── Risk Factors
│ ├── Financial Risks
│ └── Operational Risks
├── Financial Statements
│ ├── Balance Sheet
│ │ ├── Assets
│ │ └── Liabilities
│ └── Income Statement
└── Notes to Financial Statements
├── Note 1: Accounting Policies
└── Note 12: Long-term Debt
Дакумент большэй не ў форме плоскага аркуша. У ёму ёсць структура, і LLM можа пераходзіць праз яе так, як чалавек перагледвае кнігу.
Этап 2: пошук у дрэве на аднойчыннай аснове. Калі прыходзіць запіт, нічога не падае ў базу дадзеных у вектарнай форме. LLM прыме запіт і верхні ўрадзень дрэва, і яму прасяць аднойчынна вырашыць, дзе можа знаходзіцца адпаведна адказ. Ён можа выйсці да вывару, што запіт пра давялегалегкі борг належыць у роздзеле Фінансовых выкладнікаў, верагодна ў прымітках, і вернуць ID вузла.
PageIndex пасля чаго розгледвае гэты вузел, паказваючы наступны ўрадзень дэталяў, а LLM продовжае глыбокае дзеянне, пакуль не дасягне тыяўніка, якая адпавядае на запіт.
Чалавечы аналітык працуе так сама. Ніхто не чытае 200-сторонній атлас з першай да апошней старонкі. Вы пераглядаеце змест, знаходзіце правы роздзел і ідзеце туды без перапытак. PageIndex робіць гэта з шыроцай швалёй машыны.
За выключэннем апошнага запрашоўвання кантэнту, кожны этап являецца процэсам разумовага аналізу модэлю. Нічога не падходзіць за дапамою вектараў; LLM сама выбірае, дзе шукаць.
Маленькае рэалізаванне
У наступным практычным прыкладзе прадстаўлена спрасцаваная версія гэй ідэі на Python, дзе як LLM выступае Gemini.
Крок 1: аналіз дакумента
Спачатку неабходна выкарыстоўваць функцыі для выкарыстоўвання неапранутага тэксту сторынка за сторынкай. PyMuPDF эфектыва караце з PDF-файламі.
import fitz # pip install pymupdf
def parse_pdf(pdf_path: str) -> list[dict]:
doc = fitz.open(pdf_path)
pages = []
for i, page in enumerate(doc):
text = page.get_text().strip()
if text:
pages.append({"page_num": i + 1, "text": text})
doc.close()
return pages
Потым групавайце суседзяючыя сторанкі ў раздэлы. На адзінку з дэтальным разбіяннем, гэты спосаб даглядае за межамі сторанак і зберагае супаўзяны контэнт разам.
def group_pages_into_sections(pages, per_section=3):
sections = []
for i in range(0, len(pages), per_section):
batch = pages[i : i + per_section]
section_id = f"S{str(i // per_section + 1).zfill(3)}"
combined_text = "\n\n".join(p["text"] for p in batch)
sections.append({
"section_id": section_id,
"start_page": batch[0]["page_num"],
"end_page": batch[-1]["page_num"],
"text": combined_text,
})
return sections
Шаг 2: стварэнне індэкса дрэва за дапамою LLM
У гэтым і знаходзіцца сэрцо методу. Эмбеддынгі не выкарыстоўваюцца, а мадэль чытае адглед кожнага раздзела і піша структураваны падсумак. Рэзультатам яе працы стае вялікое дрэва у фармаце JSON, у кожнай вёсцы якога ёсць заглавле, короткі падсумак і спіс ключоўых тэм.
from google import genai
from google.genai import types
client = genai.Client(api_key="YOUR_GEMINI_API_KEY")
def index_section(section: dict) -> dict:
preview = section["text"][:1500]
prompt = f"""Read this section from a document and summarize it.
Section pages: {section['start_page']} to {section['end_page']}
Text:
{preview}
Respond with ONLY valid JSON:
{{
"title": "short descriptive title (5-8 words)",
"summary": "2-3 sentence summary of what this section covers",
"key_topics": ["topic1", "topic2", "topic3"]
}}"""
response = client.models.generate_content(
model="gemini-2.0-flash",
contents=prompt,
config=types.GenerateContentConfig(temperature=0.0),
)
parsed = json.loads(response.text.strip())
return {
"node_id": section["section_id"],
"title": parsed["title"],
"pages": f"{section['start_page']}-{section['end_page']}",
"summary": parsed["summary"],
"key_topics": parsed["key_topics"],
}
Заўёршыце процэс для всіх раздзелаў, і цэлы дрэва будзе запісаны як файл JSON.
def build_tree_index(sections):
nodes = [index_section(s) for s in sections]
return {
"title": "Your Document Title",
"total_sections": len(nodes),
"nodes": nodes,
}
# Save for reuse
with open("tree.json", "w") as f:
json.dump(tree, f, indent=2)
Шаг 3: пошук у дрэве, керованы выважванням
У гэты момент гэты падход абсалютна адзінаковы ад вектарнага RAG. Прыходзіць запіт, і замест таго, каб шукаць у базе дадзеных, вы даеце LLM усе дрэва і запытваеце, якія ўзелкі з наібольшай верагатыбайцю маюць адпаведны адказ. Ён паводзіцца як аналітык, які працуе з зместам кнігі.
def retrieve_sections(tree: dict, query: str) -> dict:
# Build a compact text representation of the tree
tree_text = f"Document: {tree['title']}\n\n"
for node in tree["nodes"]:
tree_text += f"[{node['node_id']}] Pages {node['pages']} | {node['title']}\n"
tree_text += f" Summary: {node['summary']}\n"
tree_text += f" Topics: {', '.join(node['key_topics'])}\n\n"
prompt = f"""You are a document retrieval expert.
Given this document tree, identify which sections most likely answer the question.
Think step by step about where a human expert would look.
{tree_text}
QUESTION: {query}
Respond with ONLY valid JSON:
{{
"reasoning": "your step-by-step reasoning about where to look",
"selected_ids": ["S001", "S004"],
"confidence": "high/medium/low"
}}"""
response = client.models.generate_content(
model="gemini-2.0-flash",
contents=prompt,
config=types.GenerateContentConfig(temperature=0.0),
)
return json.loads(response.text.strip())
Поле аргументаў у адпаведзі ўжо не проста дэкоратыў. Яно фіксуе, чаму мадэль выбрала тыя часткі, і гэта і ёсць значэнне пояснюемасці тут. Вы можэце пераглядзець рашэнне пра выбіранне інфармацыі, чаго не можа запроста зробіць вектарны пошук.
Крок 4: запрашаць контэнт
Калі у вас ўжо ёсць ID выбранных частак, запрашайце неапдытаваны тэкст тых частак. Не трэба вектарных пошукоў і вычыслення эмбедынга — достатнья толькі перагляд у словніку частак, якія вы вялікі час аналізавалі.
def retrieve_content(selected_ids: list, sections: list) -> str:
section_map = {s["section_id"]: s for s in sections}
context_parts = []
for sid in selected_ids:
if sid in section_map:
sec = section_map[sid]
context_parts.append(
f"--- Pages {sec['start_page']}-{sec['end_page']} ---\n"
+ sec["text"][:3000]
)
return "\n\n".join(context_parts)
Шаг 5: стварэнне адказу
Зявлены тэкст і первасны запыт прыходзяць да Gemini, і мадэль складае точны адказ з цітатамі на рэвэлі.
def generate_answer(query: str, context: str) -> str:
prompt = f"""Answer the question using only the provided context.
Be specific. Include exact numbers, technical terms, and cite page numbers.CONTEXT:
{context}
QUESTION: {query}
ANSWER:"""
response = client.models.generate_content(
model="gemini-2.0-flash",
contents=prompt,
config=types.GenerateContentConfig(temperature=0.1),
)
return response.text.strip()
PageIndex протыў vector RAG
Этыя два падходы разлічаюцца за некалькома аспектамі. Vector RAG ўзлётны па швальнасці, можа працаваць з вялікімі колекцыямі дакументаў і выкарыстоўвае эмбеддынгі і індэксы. PageIndex не патрабуе нічога з гэтага, але выконвае калькі последовальных вызоў LLM за кожны запіт; у якім выніку ён дае вышэю точнасць у працэ са структураванымі дакументамі і адпаведныя паследованасці рашэнняў.
Калі выбіраць PageIndex
Ён не ўніверсальны заменнік для vector RAG. Гэта аднае інструмент для адной задачы.
Выбірайце PageIndex, калі:
- Вы працуеце з аднім довгім, структураваным дакументам, дзе точнасць важнейшая за швальнасць: фінансавыя звясткі, прававыя контракты, норматыўныя дакументы, тэхнічныя інструкцыі, акадэмічныя працы. Будзь-што з значымія іерархіямі, дзе «дастатнька блізкасць» недастатня.
- Патрэбна можлівасць адказаў з поясненням, таму што корыстувальнікі аб заінтэрэсаваныя стороны павінны точна бачыць, звядзе гэта адказ. Вы отрымваеце след аб разумовых кроках з апісанням раздзелаў і номераў сторанак.
- Вы ствараеце рашэнні для рэгулююцыхся сектароў, такіх як фінансы, права і охорона здароў’я, дзе некарэктны адказ мае рэальныя наследкі, і важна точнасць адпаведных даных.
Выбірайце vector RAG, калі:
- Вы шукаеце сярод вялікага калекцыі дакументаў і павінны выявіць, якія з іх ёсць рэлевантнымі. Vector RAG чырпае ў гэтым, тады як PageIndex сфокусаваны на адзінам дакуменце і не прызначаны для выбору правільнага з тыячы.
- Для вас важны праўоцэннасць і час адпаведзення. Колькісныя запыты да LLM для кожнага запіту стаюць дорогімі і повольнымі, калі паспрабоўкі зростаюць.
- Матэрыял ёсць короткім, слабка арганізаваным або напісаным у фармаце дыялогу. Сіла методу заключаецца ў навігацыі па іерархіі, а без іерархіі яго прынадзы знікаюць.
Спяюце іх разам. Іспользуйце вектарны пошук, каб знайсці правільны дакумент у вашай колекцыі, а пасля іспользуйце PageIndex, каб выявіць точны адказ усерэдзіні яго. Такі гібрыд выкарыстоўвае адзіныя з лепшых якостэй обох падходаў.
Большая ідэя
PageIndex на самай справе спрыяе пошуку як проблеме разумавання, а не проблеме сэмабільнасці. Гэта змена мае значэнне не толькі для дакументаў. Аналізы паказваюць, што Claude Code перэстаў павяржацца на пошук на адной основе эмбедынгаў для пошуку коду і цяпер апіраецца на пошук, керованы разумаванням. Шаблон стае яшчэ ясней: калі задача выкалікае патрэбу разумець структуру і следаваць логіцы, разумаванне перамагае сэмабільнасць.
Базы дадзэнайў вектарных даных не знікнуць. Яны застаюцца шырокаіснымі, магчымаеі пашырэнне і ідэальнымі для сэмантычнага пошуку ў вялікіх калекцыях. Але для складнай задачы пошуку абоўсцю правільных дазвялей у сложаным дакуменце падход, які спачатку аналізуе логіку, выглядае як крашчая альтэрнатыва.
PageIndex ўзнаходзіцца сярод найлепшых рэалізацый гэтай ідэі сёння. Ён ў фармаце адкрытага кода, мае чысты API, а праект публікуе хмарную службу і сервер MCP, якія дазволяюць інтэграцыю з Claude або іншымі агентскімі рабочымі практыкамі без неабяжнага напісання коду для пошуку. Прыклады з офіцыяльнага кулінарнага кнігі ўсё ж такі ёсць разумным пачатком і дазволяюць запусціць працюючую сістэму за апоўну паловы час. Калі вашы корыстувальнікі ставяць точныя запытанні пра 200-сторонніяя звярненні, гэта варта спробаваць.
Супаўзвязаныя матэрыялы
- Як працюе RAG: эмбеддынгі, базы вектарных дадзеных і чанкаванне — Адступіце за адным фрагментам тэксту ад токенізацыі праз агрэгатаванне да пошуку за дапамою косай косынкі, і пазнайце, чаму чанкаванне і мадэлі эмбеддынга вялі за якасцю RAG.
- PageIndex: RAG без вектарных баз дадзеных, адаснованы на логічных выводах з дрэва індэксаў — PageIndex заменяе базы вектарных дадзеных дрэва індэксаў, кія керуецца LLM. Пазнайце, як гэта працюе, прыклады коду на Python, рэзультаты тэставання і калі яго варта выкарыстоўваць.