Startseite / Artikel

Logisches Arbeiten an einem Dokumentbaum anstelle der Suche in Vektoren

PageIndex erstellt einen hierarchischen Index, der es einem LLM ermöglicht, ihn zu durchqueren, wobei das Aufteilen in Blöcke sowie die Verwendung von Embeddings übersprungen werden. Wie es funktioniert, das Ergebnis von FinanceBench sowie wann es eingesetzt werden sollte.

Verlag: REACTAPP.TOP10 Min. Lesezeit2394 Wörter

Quellen: I Threw Out My Vector Database. RAG Got Way Better With PageIndex

Es gibt kaum etwas Peinlicheres, als wenn ein Modell mitten in einer Live-Demo selbstbewusst den falschen Abschnitt eines 200-seitigen Jahresberichts zitiert. Es folgt dann das übliche Szenario: Das Team „behebt“ das Problem durch bessere Aufteilung der Daten, intelligentere Überlappungen sowie ein sorgfältiger abgestimmtes Embedding-Modell – der übliche Verbesserungskreislauf, den jeder RAG-Entwickler durchläuft, in der Überzeugung, dass die nächste Anpassung endlich funktionieren wird. Doch dann antwortet das System auf eine Frage zu den Umsatzzahlenprognosen für 2023 mit einem Absatz aus dem Kapitalausgabenplan von 2021. Da die beiden Abschnitte ähnliche finanzielle Begriffe verwendeten, betrachteten die Embeddings sie als verwandt – tatsächlich waren sie es jedoch nicht.

Ein solcher Fehler wirft die berechtigte Frage auf, ob der gesamte Ansatz von Grund auf fehlerhaft ist.

Warum Ähnlichkeit nicht ausreicht

Jedes RAG-Tutorial vermittelt dasselbe Verfahren: Das Dokument wird in 500-Wort-Abschnitte unterteilt, diese eingebettet, für jede Frage die nächstgelegenen Vektoren gefunden und diese Abschnitte an ein LLM übergeben. Es klingt elegant – semantische Suche in Kombination mit Sprachgenerierung.

Was dieses Verfahren außer Acht lässt, ist, dass Ähnlichkeit nicht gleichbedeutend mit Relevanz ist. Eine Vektordatenbank findet Texte, die semantisch der Frage nahekommen, aber nicht unbedingt solche, die sie beantworten. Ein Absatz über die Risikofaktoren eines Medikaments und ein anderer über seine Wirksamkeit liegen im Embedding-Raum nah beieinander, doch nur einer von ihnen beantwortet eine Frage zu den finanziellen Risiken des Unternehmens.

Das Aufteilen in Blöcke erzeugt ein weiteres Problem. Wenn ein 200-seitiges Rechtsdokument in Fenster mit 500 Tokenn aufgeteilt wird, verschwindet alles, was sein Verfasser sorgfältig aufgebaut hat: Abschnitte, Verweise, logischer Ablauf. Was übrig bleibt, ist ein Haufen von Fragmenten, bei denen unklar ist, woher sie stammen und wie sie miteinander verbunden sind. Eine bessere Aufteilung in Blöcke erleichtert die Sache etwas, löst aber das zugrundeliegende Problem nie, da eine weggeworfene Struktur nicht wiederhergestellt werden kann.

Ein menschlicher Analytiker würde niemals so lesen. Er würde den Inhaltstext überfliegen, direkt zum richtigen Abschnitt springen, diesen im Zusammenhang mit den umliegenden Kapiteln lesen und die Fußnoten prüfen. Er navigiert im Dokument als Ganzes.

Ein Baum anstelle von Vektoren

Das VectifyAI-Team fragte sich, wie ein RAG-System aussehen würde, wenn es auf diese Weise funktionieren würde, und entwickelte PageIndex. Sobald die Kernidee ausgesprochen wird, erscheint sie fast unangenehm offensichtlich: Man verzichtet darauf, Dokumente in Vektoren umzuwandeln und nach Ähnlichkeit zu suchen. Stattdessen wird ein hierarchischer Baumindex des Dokuments erstellt – im Grunde eine maschinenlesbare Inhaltsübersicht – und ein LLM wird dabei helfen, entlang dieses Baums zum relevanten Material vorzudringen.

Das bedeutet keinen Vektordatenspeicher, kein Chunking und keine Ähnlichkeitswerte – nur strukturiertes Indexieren und intelligente Navigation.

Schritt eins: Erstellung des Indexes

PageIndex zerlegt das ihm übergebene Dokument nicht in Fragmente. Er analysiert die natürliche Struktur – also Abschnitte, Unterabschnitte, Überschriften sowie logische Gruppierungen – und erstellt eine Hierarchie, die der Art entspricht, wie ein Experte den Inhalt organisieren würde. Stellen Sie sich ein automatisch generiertes Inhaltsverzeichnis vor, nur umfangreicher. Jeder Knoten enthält mehr als nur einen Titel: eine Zusammenfassung dessen, was seine Unterabteilung umfasst, die Fragen, die er plausibel beantworten kann, sowie seine Beziehung zu den Nachbar- und Überknoten.

Eine 100-seitige technische Spezifikation könnte eine drei- oder vierstellige Hierarchie erzeugen, wobei die wichtigsten Funktionsbereiche an der Spitze und die spezifischeren Themen in den darunterliegenden Unterknoten stehen. Die logische Hierarchie des Dokuments bleibt so, wie der Autor sie angeordnet hat. Der Vorgang wird einmal pro Dokument zu Beginn durchgeführt, und der Index wird gespeichert, während der ursprüngliche Inhalt unverändert und weiterhin zugänglich bleibt.

Zweiter Schritt: Durchquerung der Hierarchie

Wenn eine Anfrage eintrifft, fügt PageIndex sie nicht ein und führt auch keine Suche nach dem nächstgelegenen Knoten durch. Stattdessen navigiert ein LLM durch den Indexbaum. Es liest die Knoten der obersten Ebene und überlegt, welche Abzweigung am ehesten die Antwort enthält, folgt dieser Abzweigung, liest die nächste Ebene und überlegt erneut. Es geht so weiter nach unten, bis die relevanten Abschnitte ermittelt sind.

Das ist der Punkt, der alles verändert. Anschließend lädt es diese vollständigen Abschnitte ohne Fragmentierung aus dem ursprünglichen Dokument herunter und verwendet sie als Kontext für die Antwort.

Die Informationsabrufung ist von vornherein erklärbar. Es ist möglich, genau zu sehen, welche Knoten das LLM durchlaufen hat und auf welche Seiten des Originaldokuments es gelangt ist. Es gibt keinen „Black-Box“-Cosinus-Score, und die Überlegungsabfolge ist sichtbar.

Outfit ausprobieren

Die Einrichtung ist einfach. Beginnen Sie damit, das Repository zu klonen und seine Abhängigkeiten zu installieren:

git clone https://github.com/VectifyAI/PageIndex.git
cd PageIndex
pip3 install --upgrade -r requirements.txt

Erstellen Sie anschließend eine .env-Datei im Wurzelverzeichnis, die den API-Schlüssel enthält:

CHATGPT_API_KEY=your_openai_api_key_here

Um einen PDF zu indizieren, muss der Script darauf verweisen. Ein Finanzbericht ist ein gutes Beispiel, da dort das Tool besonders gut funktioniert:

python3 run_pageindex.py --pdf_path /path/to/annual_report.pdf

Markdown-Dokumente verwenden eine andere Flagge:

python3 run_pageindex.py --md_path /path/to/technical_spec.md

Dadurch ist die Einrichtung abgeschlossen. Es muss keine Vektordatenbank konfiguriert werden, kein Embedding-Modell ausgewählt und es gibt keine Chunking-Parameter, über die man sich den Kopf zerbrechen muss.

Die Verarbeitung eines Dokuments erzeugt eine Indexstruktur, die in etwa so aussieht – hier vereinfacht dargestellt:

{
  "document": "Apple Inc. Annual Report 2023",
  "index": {
    "title": "Apple Inc. Annual Report 2023",
    "summary": "Comprehensive financial and operational report covering revenue, product segments, risks, and strategic outlook",
    "children": [
      {
        "title": "Business Overview",
        "summary": "Company description, product lines, and market position",
        "pages": [1, 8],
        "children": [...]
      },
      {
        "title": "Financial Results",
        "summary": "Revenue, operating income, EPS, and segment performance for fiscal 2023",
        "pages": [45, 72],
        "children": [
          {
            "title": "Revenue by Product Category",
            "summary": "iPhone, Mac, iPad, Wearables, and Services revenue breakdown",
            "pages": [46, 52]
          },
          {
            "title": "Geographic Revenue Distribution",
            "summary": "Americas, Europe, Greater China, Japan, Rest of Asia Pacific",
            "pages": [53, 58]
          }
        ]
      },
      {
        "title": "Risk Factors",
        "summary": "Operational, market, regulatory, and competitive risks",
        "pages": [89, 110]
      }
    ]
  }
}

Stellen Sie sich vor, ein Analyst fragt nach den Einnahmen aus iPhones im Haushaltsjahr 2023. Anstatt nach ähnlichen Textfragmenten zu suchen, liest das LLM den Index, kommt zu dem Schluss, dass „Finanzielle Ergebnisse > Einnahmen nach Produktkategorie“ der zu durchsuchende Bereich ist, geht dorthin und holt die Seiten 46 bis 52 vollständig ab. Die Antwort enthält eine genaue Seitenangabe, und da das Modell den Abschnitt zu den Risikofaktoren nie gesehen hat, stört nichts Unwesentliches die Genauigkeit.

Eine vereinfachte Version der auf logischem Denken beruhenden Informationsabrufung sieht im Code so aus:

import json
from openai import OpenAI

client = OpenAI()
def navigate_index(query: str, index_node: dict, depth: int = 0) -> list[dict]:
    """
    Recursively navigate the document index using LLM reasoning.
    Returns list of relevant leaf nodes with page references.
    """
    children = index_node.get("children", [])
    if not children:
        # Leaf node: return this section as relevant
        return [index_node]
    # Ask the LLM which branches are relevant to the query
    children_summary = "\n".join([
        f"[{i}] {child['title']}: {child['summary']}"
        for i, child in enumerate(children)
    ])
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[
            {
                "role": "system",
                "content": (
                    "You are navigating a document index to find sections relevant "
                    "to a query. Select the index numbers of sections that are likely "
                    "to contain the answer. Return a JSON array of selected indices."
                )
            },
            {
                "role": "user",
                "content": (
                    f"Query: {query}\n\n"
                    f"Available sections:\n{children_summary}\n\n"
                    f"Which sections should I look into? Return JSON array of indices only."
                )
            }
        ],
        temperature=0,
        response_format={"type": "json_object"}
    )
    selected = json.loads(response.choices[0].message.content).get("indices", [])
    relevant_nodes = []
    for idx in selected:
        if idx             # Recurse into selected branches
            relevant_nodes.extend(
                navigate_index(query, children[idx], depth + 1)
            )
    return relevant_nodes

def answer_with_pageindex(query: str, index: dict, document_pages: dict) -> str:
    """
    Full PageIndex retrieval and answer generation.
    """
    # Navigate the index to find relevant sections
    relevant_nodes = navigate_index(query, index)
    # Retrieve full text from identified pages
    context_parts = []
    citations = []
    for node in relevant_nodes:
        pages = node.get("pages", [])
        if pages:
            page_start, page_end = pages[0], pages[1]
            for page_num in range(page_start, page_end + 1):
                if page_num in document_pages:
                    context_parts.append(document_pages[page_num])
                    citations.append(f"p.{page_num}")
    context = "\n\n".join(context_parts)
    # Generate answer with full, unchunked context
    answer_response = client.chat.completions.create(
        model="gpt-4o",
        messages=[
            {
                "role": "system",
                "content": (
                    "Answer the question based on the provided document sections. "
                    "Be precise. If the answer involves numbers or dates, quote them exactly."
                )
            },
            {
                "role": "user",
                "content": f"Document sections:\n{context}\n\nQuestion: {query}"
            }
        ],
        temperature=0
    )
    answer = answer_response.choices[0].message.content
    citation_str = ", ".join(set(citations))
    return f"{answer}\n\n**Source:** {citation_str}"

Betrachten Sie, was fehlt: Embeddings, Kosinusähnlichkeit, Vektordatenbanken sowie Logik zur Aufteilung in Abschnitte. Strukturiertes Reasoning über einen Dokumentindex ersetzt den gesamten Abrufprozess.

Beweise aus einem Finanz-Benchmark

FinanceBench sammelt Fragen zu echten Finanzdokumenten wie SEC-Unterlagen, Jahresberichten und Gewinnmitteilungen. Die Antworten müssen numerisch präzise sein, die Abschnitte müssen genau zugeordnet werden und Fußnoten sorgfältig gelesen werden. RAG-basierte Ansätze haben hier oft Schwierigkeiten, da Finanzdokumente voller ähnlich klingender Formulierungen zu unterschiedlichen Themen sind.

Mafin 2.5, der auf PageIndex basiert, erreichte auf FinanceBench eine Genauigkeit von 98,7 %. Typische Vektor-RAG-Systeme bei vergleichbaren Finanzbenchmarks erzielen je nach Konfiguration Werte zwischen 65 und 80 %. Dieser Unterschied ist keineswegs gering – er unterscheidet ein Werkzeug, dem ein Investmentanalyst tatsächlich vertrauen kann, von einem, das plausibel klingendes Unsinn erzeugt, das Zeile für Zeile überprüft werden muss.

Die Struktur erklärt das. In Finanzdokumenten ist gerade die Hierarchie von größter Bedeutung. Ein konsolidierter Bilanzplan, die Anhangangaben sowie die MD&A folgen alle einem klaren, durchdachten Aufbau, auf den Analysten ständig angewiesen sind. PageIndex bewahrt und nutzt diesen Aufbau, während das Vektor-Chunking ihn zerstört.

Wann welches wählen?

PageIndex ersetzt Vektor-RAG nicht universell. Die richtige Wahl hängt vom Projekt ab.

Er eignet sich besonders gut, wenn:

  • Die Dokumente weisen eine klare Hierarchie auf, wie bei Berichten, Handbüchern, Verträgen und Spezifikationen.
  • Genauigkeit und korrekte Zitierungen sind unverzichtbar.
  • Es handelt sich um ein spezialisiertes Gebiet, wie Finanzen, Recht, Medizin oder regulatorische Tätigkeiten.
  • Fragen erfordern Verweise auf mehrere Abschnitte desselben Dokuments.
  • Erklärbarkeit ist wichtig, und das System muss zeigen, warum es das getan hat, was es getan hat.

Traditionelle vektorbasierte RAG-Lösungen sind weiterhin sinnvoll, wenn:

  • Das Korpus aus Tausenden kurzer, unabhängiger Dokumente besteht, wie z. B. Support-Tickets oder Produktbewertungen.
  • Die Dokumente keine einheitliche Struktur aufweisen.
  • Das Ziel eine annähernde semantische Suche in einem umfangreichen Korpus ist.
  • Die Latenz die Hauptbeschwerde ist und das Chunking bereits optimiert wurde.

Die meisten Anwendungsfälle für Unternehmensdokumente, bei denen ein System wirklich nützlich sein muss und nicht nur in einer Demo beeindruckend wirken soll, ähneln viel mehr der ersten Liste als der zweiten.

Für den Produktivbetrieb ohne die Belastung durch Infrastruktur bietet PageIndex außerdem eine REST API sowie MCP-Integration. Die Einrichtung für Entwickler sieht wie folgt aus:

import requests

PAGEINDEX_API_KEY = "your_api_key"
BASE_URL = "https://api.pageindex.ai/v1"
def upload_document(file_path: str) -> str:
    """Upload a document and get back a document_id."""
    with open(file_path, "rb") as f:
        response = requests.post(
            f"{BASE_URL}/documents",
            headers={"Authorization": f"Bearer {PAGEINDEX_API_KEY}"},
            files={"file": f}
        )
    return response.json()["document_id"]

def query_document(document_id: str, question: str) -> dict:
    """Query an indexed document and get a cited answer."""
    response = requests.post(
        f"{BASE_URL}/query",
        headers={
            "Authorization": f"Bearer {PAGEINDEX_API_KEY}",
            "Content-Type": "application/json"
        },
        json={
            "document_id": document_id,
            "question": question
        }
    )
    return response.json()

# Example usage
doc_id = upload_document("q3_earnings_report.pdf")
result = query_document(doc_id, "What was total revenue in Q3?")
print(result["answer"])
print(f"Sources: {result['citations']}")

Beim Hochladen erstellt die API automatisch den Index, und bei einer Abfrage liefert sie strukturierte Antworten mit nachvollziehbaren Seitenverweisen.

Die größere Idee

Hinter den Benchmark-Werten geht etwas Tieferes vor sich. Herkömmliche Vektor-RAG-Systeme betrachten ein Dokument als einen Satz zusammenhängender Textfragmente und ignorieren die von dessen Autor geschaffene Struktur. Jeder Überschrift, Absatzbruch und Verweis ist ein Signal – die Art und Weise, wie der Autor den Lesern zeigt, wohin sie gehen sollen –, doch das Zerlegen in Blöcke vernichtet all das, bevor versucht wird, Relevanz aus dem Nichts wiederherzustellen. PageIndex behandelt ein Dokument genau so, wie es ist: als strukturiertes Werkzeug, das für die Navigation durch einen kompetenten Leser konzipiert ist.

Ein leitender Analyst, der einen Jahresbericht durchliest, beginnt nicht bei Seite eins. Er schaut sich den Inhaltstext an, springt direkt zu dem Teil, der ihn interessiert, überprüft eine Zahl anhand der Anmerkungen und kehrt anschließend zum MD&A zurück. Das LLM in PageIndex macht genau dasselbe.

Deshalb gilt das auch für spezialisierte Bereiche. Ein Bericht über eine pharmazeutische Studie hat eine andere Hierarchie als ein Rechtsvertrag oder eine technische Spezifikation – doch in jedem Fall folgt der Autor einer internen Logik, die erfahrene Leser nutzen können. PageIndex erfasst diese Logik und ermöglicht so die Suche entsprechend.

Eine andere Diagnose

Die ernüchternde Schlussfolgerung ist, dass jahrelange Bemühungen vielleicht das falsche Problem anvisiert haben. Der Fokus lag auf Chunking-Strategien, obwohl der eigentliche Fehler bereits zu Beginn die Struktur zerstörte. Embedding-Modelle wurden angepasst, obwohl semantische Ähnlichkeit niemals das richtige Signal für die Suche war. Reranker wurden hinzugefügt, um die Suchqualität zu retten, obwohl die Suchmethode selbst das Engpassproblem darstellte.

Das RAG-Ökosystem entwickelte komplexe Mechanismen, um den ursprünglichen Fehler des Chunkings auszugleichen. PageIndex schlägt eine Alternative vor: Keine Chunks, keine Embeddings, keine Ähnlichkeitssuche. Man sollte einen Index erstellen, der die tatsächliche Struktur des Dokuments widerspiegelt, und sich durch ihn mithilfe von Schlussfolgerungen bewegen.

Es beantwortet nicht alles. Bei semantischer Suche in Millionen kurzer, unstrukturierter Dokumente bleibt die Vektorsuche weiterhin überlegen. Für die breite Palette von Fragen zu langen, strukturierten Dokumenten, die den größten Teil der Unternehmensanforderungen abdeckt, verdient ein hierarchisches Baumstrukturmodell jedoch ernsthafte Aufmerksamkeit. Führen Sie die zweimal fehlgeschlagene Demo erneut durch, indem Sie nach Umsatzzahlenprognosen für 2023 fragen – das System weist Sie nun auf den richtigen Abschnitt hin, lädt die richtigen Seiten und antwortet mit genauen Quellenangaben. Kein falsches Jahr, kein falscher Abschnitt, keine fehlerhafte Zuversicht.

PageIndex ist Open Source und kann auf GitHub selbst gehostet werden. Zudem stehen eine gehostete Chat-Schnittstelle sowie API-Dokumentation mit Referenzen zu REST und MCP zur Verfügung, um es mit Ihren eigenen Dokumenten auszuprobieren.

Verwandte Artikel

  • Spring AI: Bessere RAG-Ergebnisse durch hybraten Suchansatz mit Lucene und RRF — Kombinieren Sie die Vektorsuche mit Lucene BM25 in einem Spring AI-Retriever und fügen Sie die Ranglisten mithilfe von Reciprocal Rank Fusion zusammen, damit RAG sowohl Bedeutung als auch genaue Begriffe berücksichtigt.
  • Wie OpenAI’s Agents API die Wirtschaftlichkeit der KI-Arbeit verändert — Der Agent steuert Modellaufrufe, Toolaufrufe, Sandbox-Umgebungen, Wiederholungsversuche sowie menschliche Überprüfungen, sodass die Token-Zählung nicht mehr den Kosten entspricht. Messen Sie stattdessen die angenommenen Aufgaben.
  • Hybride Suche für RAG: BM25, Rank Fusion und Reranking schlagen Vektoren — Warum reine Vektorsuche exakte Begriffe in RAG übersieht, und wie BM25 in Kombination mit semantischer Suche, Reciprocal Rank Fusion sowie einem Cross-Encoder zur Erhöhung der Suchgenauigkeit auf 91 % führte.
  • Pinecone, Weaviate, Milvus, Qdrant oder Chroma: Die Auswahl einer Vektordatenbank — Einfache Erklärung der Vorzüge jeder Vektordatenbank, von serverlosen und hybriden Suchmethoden über Indexierung mit Rust und GPU bis hin zu den jeweiligen Kompromissen und Einsatzmöglichkeiten.