Vectorloses RAG mit PageIndex: Abruf durch Reasoning anstelle von Vektoren
PageIndex tauscht Embeddings und Chunking bei einem LLM aus, das einen Dokumentbaum durchläuft. Ein kurzer Python-Beispielcode sowie die Fälle, in denen vectorbasiertes RAG weiterhin die bessere Wahl ist.
Quellen: **I Stopped Using Vector Databases for RAG : PageIndex Vectorless RAG**
Jeder, der bereits seit einiger Zeit RAG-Systeme entwickelt hat, kennt diese Frustrierung. Man verbringt Stunden mit der Erstellung von Embeddings, justiert die Größe der Abschnitte, wählt eine Vektordatenbank aus – und das System gibt trotzdem die falsche Antwort. Es findet zwar etwas Ähnliches, doch „ähnlich“ und „relevant“ sind nicht dasselbe.
Finanzberichte zeigen das eindrucksvoll. Nehmen wir eine 120-seitige 10-K-Meldung: Eine Vektor-RAG-Einrichtung zieht ständig Abschnitte aus dem Zusammenfassungsteil, während die eigentliche Antwort in einem Fußnotenvermerk auf Seite 87 zu finden ist. Beide Abschnitte weisen Schlüsselwörter und Bedeutungen auf, doch nur einer enthält die benötigte Zahl.
PageIndex wurde genau dazu entwickelt, dieses Problem anzugehen.
Was PageIndex ist
PageIndex ist ein Open-Source-RAG-Framework vom VectifyAI-Team. Die zentrale Idee ist einfach und unterscheidet sich von den meisten anderen Lösungen. Anstatt ein Dokument in Vektoren umzuwandeln und eine Suche nach dem nächsten Nachbarn durchzuführen, erstellt es einen hierarchischen Baum aus dem Dokument und lässt ein LLM den Weg zur Antwort finden.
Es überspringt vollständig Embeddings, Vektorlagern und das Aufteilen in Blöcke.
Das Projekt bezeichnet dies als „vectorless RAG“, und diese Bezeichnung ist ziemlich präzise. Es erschien im September 2025 und erregte schnell Aufmerksamkeit – hauptsächlich aufgrund einer angegebenen Zahl: 98,7 % Genauigkeit auf FinanceBench, im Vergleich zu etwa 50 % für herkömmliches vectorbasiertes RAG am selben Benchmark. Behandeln Sie die von Anbietern gemeldeten Benchmark-Zahlen mit Vorsicht und testen Sie die Lösung an Ihren eigenen Dokumenten. Wenn der Unterschied zumindest teilweise bestehen bleibt, handelt es sich um eine völlig andere Kategorie.
Wo vectorbasiertes RAG versagt
Die zentrale Annahme von Vector RAG ist, dass der text, der semantisch am ähnlichsten zur Anfrage ist, auch der relevanteste ist. In der Praxis trifft diese Annahme ständig nicht zu.
Fragen Sie „Wie hoch war die Gesamtverschuldung des Unternehmens im Jahr 2023?“ – die am höchsten bewerteten Abschnitte könnten sein:
- ein Brief des CEOs, in dem es um eine Verschuldungsmanagementstrategie geht,
- eine Risikosektion zu Schuldenklauseln,
- ein Glossarartikel, der langfristige Schulden definiert.
Alle drei sind inhaltlich der Anfrage nahe, doch keiner liefert die Antwort, nämlich eine konkrete Zahl aus der Bilanz auf Seite 64.
Zusätzlich treten weitere Probleme auf:
- Chunking zerstört den Kontext. Das Aufteilen eines 100-seitigen Dokuments in 500-Token-Stücke führt zum Verlust seiner Struktur. Ein Satz wie „wie in Tabelle 3.2 erwähnt“ wird von Tabelle 3.2 getrennt, sobald beide in unterschiedlichen Blöcken landen, wodurch die Logik und der Ablauf des Dokuments ignoriert werden.
- Vektoren können Querverweise nicht verfolgen. In Dokumenten steht „siehe Anhang G“ oder „beziehen Sie sich auf die Zahlen des Vorjahres in Abschnitt 4“, doch Suchalgorithmen nach Ähnlichkeit haben keine Möglichkeit, diesen Hinweisen zu folgen. Es handelt sich dabei lediglich um Textfragmente ohne Verbindung zu ihren Zielen.
- Abfragen drücken Absichten aus, nicht den Inhalt. Der Benutzer beschreibt, was er wissen möchte, und nicht, wie die Antwort aussehen soll. Die Antwort kann unterschiedliche Vokabeln verwenden, wodurch die vektorbasierte Suche Schwierigkeiten hat, da sie den Frage-Text mit dem Antwort-Text abgleicht.
Retrieval durch Reasoning
Der Denkansatz ändert sich wie folgt: Anstatt zu fragen, welche Abschnitte der Abfrage ähneln, fragt PageIndex, wo ein menschlicher Experte in diesem Dokument nach einer Antwort suchen würde. Das ist eine andere Suchstrategie, die für strukturierte, komplexe Dokumente weitaus besser geeignet ist.
Wie es funktioniert
Es gibt zwei Phasen.
Phase 1: Erstellung des Baumindex. Bei einem Dokument teilt PageIndex es nicht in Abschnitte auf. Stattdessen liest es das Dokument und erstellt eine Hierarchie, die die natürliche Struktur des Dokuments widerspiegelt – ähnlich einem intelligenten, tief verschachtelten Inhaltsverzeichnis. Jeder Knoten steht für einen Abschnitt oder Unterabschnitt und enthält einen Titel sowie eine Zusammenfassung seines Inhalts. Das LLM schreibt diese Zusammenfassungen während der Indexierung und sie dienen später als Suchsignal.
Bei einer 10-K-Meldung könnte der Baum wie folgt aussehen:
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
Das Dokument ist nicht mehr flach. Es hat eine Struktur, und das LLM kann darin wie eine Person in einem Buch blättern.
Phase 2: Baumsuche durch Abwägung. Wenn eine Anfrage eingeht, wird nichts in eine Vektordatenbank gesendet. Das LLM erhält die Anfrage sowie die oberste Ebene des Baums und soll abwägen, wo sich die Antwort befinden könnte. Es könnte zu dem Schluss kommen, dass eine Frage zum Langfristverschuldung in dem Abschnitt zu den Finanzberichten, vermutlich in den Anmerkungen, zu finden ist, und gibt anschließend eine Knoten-ID zurück.
PageIndex erweitert dann diesen Knoten, um die nächste Ebene an Details anzuzeigen, und das LLM forscht weiter, bis es den Inhalt findet, der die Frage beantwortet.
Auch ein menschlicher Analyst arbeitet auf dieselbe Weise. Niemand liest einen 200-seitigen Bericht von Anfang bis Ende. Man prüft den Inhalt, findet den richtigen Abschnitt und geht direkt dorthin. PageIndex tut das mit maschineller Geschwindigkeit.
Ausgenommen der letzten Inhaltssuche ist jede Phase das Modell-Reasoning. Es wird nichts über Vektoren abgeglichen; das LLM bestimmt selbst, wo gesucht werden soll.
Eine einfache Implementierung
Die folgende Schritt-für-Schritt-Anleitung erstellt eine vereinfachte Version dieser Idee in Python, wobei Gemini als LLM verwendet wird.
Schritt 1: Dokument analysieren
Beginnen Sie damit, den Rohtext Seite für Seite zu extrahieren. PyMuPDF handhabt PDF-Dateien zuverlässig.
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
Gruppieren Sie anschließend aufeinanderfolgende Seiten zu Abschnitten. Im Gegensatz zu willkürlicher Aufteilung wird dabei die Grenze zwischen den Seiten respektiert und zusammenhängender Inhalt zusammengehalten.
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
Schritt 2: Erstellung des Baumindex mit einem LLM
Hier liegt das Herzstück der Methode. Embeddings werden übersprungen, und das Modell liest einen Auszug jeder Abschnitt und erstellt eine strukturierte Zusammenfassung. Das Ergebnis ist ein verschachtelter JSON-Baum, bei dem jeder Knoten einen Titel, eine kurze Zusammenfassung sowie eine Liste der Schlüsselthemen enthält.
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"],
}
Führen Sie dies in allen Abschnitten durch, und der gesamte Baum wird als JSON-Datei gespeichert.
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)
Schritt 3: durch Logik gesteuerte Baumsuche
Ab diesem Punkt weicht der Ansatz vollständig von vector RAG ab. Es kommt eine Frage, und anstelle einer Datenbanksuche gibt man dem LLM den gesamten Baum und fragt, welche Abschnitte am ehesten die Antwort enthalten. Es verhält sich wie ein Analyst, der ein Inhaltsverzeichnis durchsieht.
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())
Das Feld mit der Begründung in der Antwort dient nicht nur der Dekoration. Es dokumentiert, warum das Modell genau diese Abschnitte ausgewählt hat – das ist hier das, was Erklärbarkeit bedeutet. Sie können die Entscheidung zur Auswahl überprüfen, etwas, was eine Vektorsuche nicht bieten kann.
Schritt 4: Inhalt abrufen
Sobald Sie die ausgewählten Abschnitts-IDs haben, holen Sie den Rohtext dieser Abschnitte ab. Es erfolgt weder eine Vektorabfrage noch eine Embedding-Berechnung, sondern lediglich eine Suche im Wörterbuch der bereits analysierten Abschnitte.
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)
Schritt 5: Antwort erzeugen
Der abgerufte Text wird zusammen mit der ursprünglichen Frage an Gemini gesendet, und das Modell erstellt eine präzise Antwort mit Quellenangaben auf Seiteebene.
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 gegenüber Vector RAG
Die beiden Ansätze unterscheiden sich in mehreren Punkten. Vector RAG ist schnell, skaliert auf große Datensammlungen und benötigt Embeddings sowie einen Index. PageIndex benötigt nichts davon, führt aber pro Abfrage mehrere sequenzielle Aufrufe von LLMs durch und bietet im Gegenzug höhere Genauigkeit bei strukturierten Dokumenten sowie einen vollständigen Nachvollziehungsverlauf.
Wann sollte man PageIndex wählen?
Es handelt sich nicht um einen universellen Ersatz für Vector RAG – es ist ein anderes Werkzeug für eine andere Aufgabe.
Wählen Sie PageIndex, wenn:
- Sie mit einem einzigen, langen, strukturierten Dokument arbeiten, bei dem Genauigkeit wichtiger ist als Geschwindigkeit: Finanzberichte, Verträge, regulatorische Dokumente, technische Handbücher, wissenschaftliche Artikel. Alles, was eine sinnvolle Hierarchie aufweist und bei dem „gut genug“ nicht ausreicht.
- Es ist Erklärbarkeit erforderlich, denn Nutzer oder Interessengruppen müssen genau sehen, woher eine Antwort stammt. Sie erhalten einen Nachvollziehungsverlauf mit Abschnittsreferenzen und Seitenzahlen.
- Es wird für regulierte Branchen wie Finanzen, Recht und Gesundheitswesen entwickelt, in denen falsche Antworten echte Folgen haben und die gemeldete Genauigkeit von Bedeutung ist.
Bleiben Sie bei vector RAG, wenn:
- Sie in einer großen Sammlung suchen und herausfinden müssen, welche Dokumente relevant sind. Vector RAG ist dabei hervorragend, während PageIndex sich nur auf ein einzelnes Dokument konzentriert und nicht dazu geeignet ist, das richtige aus tausend herauszusuchen.
- Förderleistung und Latenzzeit für Sie wichtig sind. Mehrere aufeinanderfolgende Aufrufe von LLMs pro Abfrage werden bei steigendem Traffic teuer und langsam.
- Das Material kurz, locker strukturiert oder gesprächsartig ist. Die Stärke liegt im Navigieren in Hierarchien, und ohne Hierarchie verschwindet der Vorteil.
Kombinieren Sie sie. Verwenden Sie die Vektorsuche, um das richtige Dokument in Ihrer Sammlung zu finden, und nutzen Sie anschließend PageIndex, um die genaue Antwort daraus abzurufen. Ein solcher Hybrid nutzt das Beste aus beidem.
Die größere Idee
Was PageIndex tatsächlich tut, ist, die Suche als ein Reasoning-Problem statt als ein Ähnlichkeitsproblem zu betrachten. Dieser Wandel ist nicht nur für Dokumente relevant. Berichten zufolge verlässt sich Claude Code nicht mehr auf Embedding-Suche zur Suche nach Code, sondern setzt nun auf reasoningbasierte Abfragen. Das Muster wird immer klarer: Wenn eine Aufgabe das Verständnis von Strukturen und das Nachvollziehen von Logik erfordert, ist Reasoning besser als Ähnlichkeitsvergleiche.
Vektordatenbanken werden nicht verschwinden. Sie bleiben schnell, skalierbar und ideal für die semantische Suche in großen Datensammlungen. Doch bei der schwierigen Aufgabe, die genau richtigen Informationen in einem komplexen Dokument zu finden, scheint der auf logisches Denken ausgerichtete Ansatz die bessere Option zu sein.
PageIndex gehört heute zu den besseren Implementierungen dieser Idee. Er ist open source, verfügt über eine saubere API, und das Projekt stellt einen Cloud-Service sowie einen MCP-Server bereit, um ihn ohne das Schreiben von Retrieval-Code von Grund auf in Claude oder andere agierende Workflows einzubinden. Die offiziellen Beispiele aus dem Handbuch bilden einen sinnvollen Ausgangspunkt und ermöglichen es, ein funktionsfähiges System in etwa einer halben Stunde zum Laufen zu bringen. Wenn Ihre Nutzer präzise Fragen zu 200-seitigen Berichten stellen, lohnt es sich, es auszuprobieren.
Verwandte Literatur
- Wie RAG funktioniert: Embeddings, Vektordatenbanken und Chunking — Verfolgen Sie einen Textblock von der Tokenisierung über das Pooling bis zur Suche nach kosinusähnlicher Ähnlichkeit und erfahren Sie, warum Chunking sowie die Verwendung von Embedding-Modellen die Qualität von RAG bestimmen.
- PageIndex: RAG ohne Vektoren, basierend auf Reasoning mit einem Baumindex — PageIndex ersetzt Vektordatenbanken durch einen von einem LLM gesteuerten Baumindex. Erfahren Sie, wie es funktioniert, sehen Sie sich funktionierenden Python-Code, Vergleichsergebnisse sowie Anwendungsszenarien an.