Zum Inhalt springen
RAGRAF

Open Source · MIT · v0.1.0

Code-Fakten statt blindem grep

RAGRAF indexiert das Repository und antwortet dem LLM-Agenten mit festen Operationen: wo etwas definiert ist, wer es aufruft, was bei einer Änderung bricht. Jeder Fakt — mit überprüfbarem Beleg file:line.

Python 3.13+SQLiteMCP · 23 Toolstree-sitter · 16 SprachenMIT

92%

Zieltreffer beim ersten Query

12 von 13, Messung

100%

Fakten mit Beleg file:line

Messung M6

1,0

Tool-Aufrufe pro Aufgabe

vorher 2,0–3,1

0

komplett gelesene Dateien

vorher 14

Gemessen an einem realen PHP-Portal (14 Navigationsaufgaben): bench/results/ und README des Projekts. Aufruf- und Trefferzahlen sind Messungen; „realistisch 43“ ist eine modellierte Korrektur, die im Artefakt selbst steht.

Problem

Der Agent arbeitet aus dem Gedächtnis über Code, den er nie gesehen hat

Drei Symptome, die jeder kennt, der einem LLM schon einmal eine Änderung in einem bestehenden Projekt übergeben hat. Alle drei sind gemessen, nicht ausgedacht.

Findet die Stelle nicht

grep nach einem Namen liefert Dutzende Kandidaten — der richtige ist nicht der erste. VulnGym: bei Ketten länger als 9 Schritte fand der beste Agent in 56,5% der Fehlfälle die Datei nicht (26,8%) oder die Datei, aber nicht die Zeile (29,7%). Über die Hälfte der Fehlschläge betrifft Navigation, nicht Analyse.

Quelle: Messung

Kennt das Datenbankschema nicht

Die Spalte wurde „wie die Variable im Nachbar-Modul“ benannt — und das fällt erst zur Laufzeit auf: SQLSTATE[42S22] Unknown column beim allerersten Speichern. Das Schema aus den Migrationen muss VOR dem Ausführen geprüft werden.

Quelle: realer Defekt

Weiß nicht, dass er veraltet ist

Der Index wurde gebaut, danach der Code geändert — der Agent erhält Fakten über einen Zustand, den es nicht mehr gibt. „Funktion existiert nicht“ und „Funktion entstand nach der Indexierung“ sind in der Antwort nicht unterscheidbar. Die Entscheidung fällt auf Basis eines falschen Fakts.

Quelle: Kernrisiko

Abfragesprachen helfen nicht

Dem Agenten Cypher/CPGQL geben? LLMxCPG (USENIX Security \u{27}25): Basismodelle erzeugen in 19–132 von 1278 Fällen eine gültige Abfrage an den Code-Graphen. Eine freie Abfragesprache wird zur Fehlerquelle.

Quelle: Literatur

Die Schlussfolgerung, aus der RAGRAF entstand: Mehr als die Hälfte der Agent-Fehlfälle betrifft nicht die Analyse, sondern die Navigation. Genau dieser Teil gehört aus dem Modell heraus — in einen externen Index, der mit Fakten antwortet und die Grenzen seines Wissens kennt.

Funktionen

Was RAGRAF leistet

Ein fester Vertrag statt freier Suche: 22 Fassaden-Operationen, 23 MCP-Tools, drei SQLite-Speicher. Nichts Überflüssiges — und nichts, das unbemerkt kaputtgeht.

22 feste Operationen

symbol, callers, impact, slice, schema, write_check, policy_trace … Der Agent wählt „was fragen“, nicht „wie fragen“: eine Graph-Abfragesprache gibt es nicht — aufgrund von Messungen, nicht aus Willkür.

Fassade: 22 op_* · MCP: 23 Tools

Jeder Fakt mit Beweis

path:line, Provenienz (stated — vom Analyzer bewiesen, derived — Heuristik), Konfidenz, index_rev. Eine Kante ohne Beweis lebt nicht als Fakt im Graphen.

Link-Verifizierbarkeit: 100% (Messung M6)

Der Index weiß, dass er veraltet ist

Die Revision ist unveränderlich; Änderungen sammeln sich in einem Append-only-Journal; betroffene Fakten werden in jeder Antwort als stale markiert; der Wächter der Prüfsummen reindexiert automatisch.

Drift-Journal · Wächter · Auto-Reindex

Hybride Suche, vollständig lokal

BM25 (FTS5) + Vektoren mit 384 Dimensionen (paraphrase-multilingual-MiniLM-L12-v2 via fastembed/ONNX). Ohne torch, ohne Cloud, ohne Abos. Das Modell wird einmalig geladen (~240 MB).

SQLite: Graph + FTS5 + vec0 in drei Dateien

DB-Schema aus Migrationen + write_check

Tabellen und Spalten werden aus migrations/*.sql gelesen — ohne DBMS-Zugriff. write_check gleicht den Schreibplan vor dem Ausführen mit dem Schema ab und schlägt die nächstliegenden existierenden Spaltennamen vor.

44 Tabellen / 453 Spalten im Piloten

MCP-Server und ehrliche Degradation

23 ragraf_*-Tools über stdio (JSON-RPC). Fehlt die Joern-Schicht, gilt capability=degraded: slice verweigert mit Begründung, alle übrigen Operationen funktionieren. Stille Degradation gibt es nicht.

Optionale CPG-Schicht: Joern + JDK 21

16 Sprachen werden als Baum geparst (PHP, Python, JS, TS, Java, C#, C, C++, Ruby, Lua, Go, Perl, bash, PowerShell, CSS, HTML), 10 weitere als Text erfasst (Markdown, SQL, YAML, JSON, TOML, INI, XML, RST, batch, Text).

Funktionsweise

Zwei Kreisläufe: Indexierung und Antwort

Die Indexierung erzeugt eine Snapshot-Revision. Antworten lesen den Snapshot und prüfen seine Frische. Code-Änderungen während der Arbeit gehen ins Journal, nicht in den Index.

Indexierung (ein Befehl)

  1. Schritt 1scanDurchlauf des Korpus, sha256 jeder Datei, Prüfsumme des Eingangs
  2. Schritt 2asttree-sitter: Symbole, Aufrufe, SQL-Literale, Zeilengrenzen
  3. Schritt 3resolveNamensauflösung: exakte Übereinstimmung → Name+Datei → ehrliche Verweigerung
  4. Schritt 4schemaTabellen und Spalten aus Migrationen — ohne Verbindung zur DB
  5. Schritt 5cpgJoern (optional): prozedurübergreifende Aufrufe; fehlt er — degraded
  6. Schritt 6lexvecFTS5-Tokens und Vektoren; unveränderte Chunks werden per Hash wiederverwendet

Antwort an den Agenten (jede Anfrage)

  1. Schritt 1Operationder Agent ruft eine feste Operation über MCP oder CLI auf
  2. Schritt 2Frischeder Wächter gleicht corpus_hash ab; bei Abweichung — stale-Marken oder Auto-Reindex
  3. Schritt 3FaktenErgebnis mit Belegen path:line, Provenienz und Konfidenz
  4. Schritt 4Umschlagindex_rev, capability, drift, limits, stale_warnings — in jeder Antwort

Drei Prinzipien, abgeleitet aus gemessenen Defekten

  • Der Index ist innerhalb einer Revision unveränderlich — Fakten aus verschiedenen Zeitpunkten vermischen sich nicht.
  • Veraltung wird angezeigt, nicht versteckt: ein stale-Fakt kann nicht für aktuell gehalten werden.
  • Degradation schweigt nie: capability und Grund stehen in jeder Antwort.

Architektur

Drei SQLite-Dateien und keinerlei Infrastruktur

Der gesamte Index-Zustand lebt im Verzeichnis .ragraf/ innerhalb des Projekts. Keine Dienste, keine Daemons, keine Cloud. Verzeichnis gelöscht — Index gelöscht.

graph.sqlite

Knoten (Klasse, Funktion, Methode), Kanten (Aufrufe, Vererbung, Importe) mit Beweisen, DB-Schema, Routen. Alles mit rev_id markiert.

lexvec.sqlite

FTS5-Lexik (BM25) und vec0-Vektoren (384) in einer Datei; Rohtext der Chunks — Fragmente ohne Festplattenzugriff.

control.sqlite

Revisionen, Append-only-Journal der Abweichungen, Reindex-Archive, Aufruf-Telemetrie. Wird niemals gelöscht.

Korpus-Konfiguration
# ragraf init <projektverzeichnis> — erzeugt die Konfiguration selbst.
# Der Index landet in .ragraf/ im Projekt (in die .gitignore aufnehmen).
project: myproject
root: C:/path/to/your/project
languages: [python, javascript, markdown]
include: ["**/*.py", "**/*.js", "**/*.md"]
exclude: ["**/node_modules/**", "**/build/**"]
secrets_exclude:            # wird nicht indexiert und nicht gelesen
  - "**/.env"
  - "**/*.pem"
embedding:
  model: sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2
  dim: 384
index:
  retain_revs: 3
  joern_enabled: true       # kein Joern — capability=degraded

Demo

So sieht es in der Praxis aus

Die Szenarien basieren auf realen Ausgaben des Produkts (Messungen und Projektartikel). Es sind Mockups, keine Screenshots: CLI und MCP haben bewusst keine Grafik — das ist Teil der Philosophie.

Anfrage des Agenten

ragraf -p config/portal.yaml query symbol --name z_agr_acl_audit

function · config/acl.php:112–148 · Signatur: function z_agr_acl_audit(array $data): voidconfig/acl.php:112
Parametervertrag (aus dem Docblock): array{action:string, user_id?:int, field?:string, to:?string, actor_id?:int} — Pflicht ist nur actionconfig/acl.php:104

Zuvor bauten Agenten den Aufruf anhand der Signatur „array $data“ und erhielten TypeError: der Typ array verrät die Zusammensetzung der Schlüssel nicht.

Mockup. Die Daten sind reale Produktausgaben aus dem Artikel und dem Aufrufjournal.

Schnellstart

Drei Schritte bis zum ersten Fakt

Administratorrechte werden nicht benötigt: Alles wird im Benutzerverzeichnis installiert. Verifizierte Plattform — Windows + PowerShell 7.6; der allgemeine pip-Weg funktioniert überall mit Python 3.13+.

  1. Installation

    Das Skript erzeugt eine venv in ~/.ragraf/venv, installiert die Abhängigkeiten und prüft jeden Schritt mit einer Abschlusstabelle. Protokoll: ~/.ragraf/logs/.

    PowerShell
    git clone https://github.com/paulmann/RAGRAF.git
    cd RAGRAF
    pwsh -NoLogo -NoProfile -File scripts/install.ps1
  2. Korpus

    ensure findet den Korpus, legt ihn für ein unbekanntes Verzeichnis an und baut einen veralteten Index neu — mit einem Befehl.

    PowerShell
    ragraf ensure C:\work\myproject
  3. Erste Abfrage

    Globale Flags -p und --json stehen VOR dem Unterbefehl. selftest — Bericht über 11 Komponenten.

    PowerShell
    ragraf -p <konfig> query search "порог переиндексации"
    ragraf -p <konfig> query symbol --name corpus_hash
    ragraf -p <konfig> selftest

Flag-Reihenfolge

Globale Flags --project/-p und --json stehen VOR dem Unterbefehl (ragraf -p cfg query search …). Ausnahme: ensure hat ein eigenes --json, es steht dahinter (ragraf ensure <dir> --json).

Erste Indexierung mit Vektoren

Lädt das Embedding-Modell (~240 MB) einmalig nach ~/.ragraf/models. Ohne Internet oder mit --no-vectors funktioniert die lexikalische BM25-Suche.

selftest ist kein Gate

Der Befehl liefert immer Exit-Code 0: Er ist ein Bericht, keine Prüfung. Den Bericht lesen: 9 Pflichtkomponenten + joern/java (optional).

TLS-Inspektion im Firmennetz

Fällt der Modell-Download mit certificate verify failed — truststore installieren: install.ps1 -WithTruststore oder pip install truststore.

Vollständige Befehlsliste — ragraf --help. Dokumentation und Messprotokolle — im Repository.

FAQ

Häufige Fragen

Kurze Antworten auf die häufigsten Fragen. Details — in der Dokumentation des Repositorys.

MIT — freie Nutzung, einschließlich kommerzieller. Externe Abhängigkeiten behalten ihre Lizenzen (tree-sitter — MIT, sqlite-vec — MIT/Apache-2.0, fastembed — Apache-2.0, PyYAML und tiktoken — MIT, ijson — BSD). Java und Joern werden separat installiert und stehen unter Apache-2.0; sie sind nicht Teil der Auslieferung.

Python 3.13 oder neuer (verifiziert mit 3.14), Administratorrechte werden nicht benötigt — alles wird im Benutzerverzeichnis installiert. Festplatte: ~2,5 GB für die Umgebung (ONNX Runtime, tree-sitter), der Index ~1 MB pro 1–2 Quelldateien. Verifizierte Plattform — Windows + PowerShell 7.6; der allgemeine pip-Weg auf Linux und macOS wurde nicht verifiziert. Die optionale Joern-Schicht (prozedurübergreifender Graph) ergänzt JDK 21 und ~5–6 GB.

Das lokale Modell sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2 (384 Dimensionen) via fastembed und ONNX Runtime — ohne torch und ohne Cloud. Das Modell wird einmalig (~240 MB) bei der ersten Indexierung mit Vektoren nach ~/.ragraf/models geladen; das Cache-Verzeichnis ist über RAGRAF_MODEL_CACHE umdefinierbar. Leseoperationen laden das Modell niemals. Indexierung ohne Vektoren (--no-vectors) arbeitet mit der lexikalischen BM25-Suche.

Alles bleibt auf Ihrem Rechner: drei SQLite-Dateien im Verzeichnis .ragraf/ des Projekts, das Aufrufjournal ebenfalls dort. Dateien aus der secrets_exclude-Liste werden gar nicht erst geöffnet — nicht indexiert, nicht gelesen, nicht protokolliert. Die Telemetrie speichert einen Hash des Arguments und einen kurzen Digest statt des vollen Abfragetexts. Der einzige Netzwerkverkehr: PyPI bei der Installation und der einmalige Modell-Download; weder API-Schlüssel noch eine Datenübertragung nach außen existieren im Produkt.

Sechzehn Sprachen werden als Baum geparst (Graph: Knoten und Kanten): PHP, Python, JavaScript, TypeScript, Java, C#, C, C++, Ruby, Lua, Go, Perl, bash, PowerShell, CSS, HTML. Zehn weitere werden als Text erfasst (Dateien sind per Suche auffindbar, ergeben aber keine Knoten): Markdown, SQL, YAML, JSON, TOML, INI, XML, RST, batch, Klartext. Am tiefsten verifiziert ist PHP — darauf wurden der Pilot und die CPG-Schicht (Joern php2cpg) gebaut.

In drei Verpflichtungen. Erstens: Der Index ist innerhalb einer Revision unveränderlich, Abweichungen zum Code sammeln sich in einem Append-only-Journal — ein veralteter Fakt wird in jeder Antwort als stale markiert und nicht stillschweigend als aktuell ausgegeben. Zweitens: Jeder Fakt trägt einen Beweis — path:line, Provenienz (stated/derived) und Konfidenz; Heuristik wird nie mit Bewiesenem vermischt. Drittens: statt freier Retrieval-Antworten — feste Operationen mit expliziten Grenzen (limits) und Fähigkeiten-Status (capability).

Eine Graph-Abfragesprache gibt es nicht und wird es nicht geben — das ist die Architektur-Entscheidung P4, gestützt auf Messungen (LLMxCPG: Basismodelle liefern 19–132 gültige CPGQL-Abfragen von 1278). Die 22 Operationen sind ein fester Satz; Erweiterungen werden in den Issues des Repositorys diskutiert. Das Projekt wird für konkrete Aufgaben entwickelt, deshalb werden Architekturvorschläge lieber gesehen als große Patches ohne vorheriges Gespräch.

RAGRAF ist selbst ein lokales Werkzeug: Für Index und Antworten wird kein Server benötigt. Der MCP-Server ist ein stdio-Prozess, den Ihr Agent startet. Das Admin-Panel (optional) hört nur auf 127.0.0.1:8777 und ist von außen nicht erreichbar. Die einzige externe Komponente — Joern mit einem persistenten Server auf localhost:9500 während der CPG-Indexierung; er wird automatisch gestoppt.

Geben Sie dem Agenten überprüfbare Fakten

Ein Code-Index ist keine „schnellere Suche“. Es ist die Verlagerung eines Teils der Arbeit aus dem Modell in eine externe Struktur, die weiß, was sie nicht weiß. Die wertvollste Eigenschaft von RAGRAF: Es verweigert eine selbstsichere Antwort, wenn es dazu kein Recht hat.

Die Signatur array $data ist ehrlich und nutzlos. Ein Docblock mit der Schlüssel-Zusammensetzung ist nützlich. Die Marke stale: true neben einem Fakt ist Pflicht.