Wenn Sie Entwicklungsumgebungen (IDEs) in Kombination mit Codierungsagenten verwenden, haben Sie wahrscheinlich Codevorschläge und -änderungen gesehen, die überraschend genau und related sind.

Dieses Maß an Qualität und Präzision ist darauf zurückzuführen, dass die Agenten auf einem tiefen Verständnis Ihrer Codebasis basieren.

Nehmen Sie als Beispiel Cursor. Im Index & Dokumente Auf der Registerkarte können Sie einen Abschnitt sehen, der zeigt, dass Cursor die Codebasis Ihres Projekts bereits „aufgenommen“ und indiziert hat:

Abschnitt „Indizierung und Dokumente“ auf der Registerkarte „Cursoreinstellungen“ | Bild vom Autor

Wie können wir additionally überhaupt ein umfassendes Verständnis einer Codebasis aufbauen?

Im Kern lautet die Antwort Retrieval-Augmented Technology (RAG)ein Konzept, mit dem viele Leser möglicherweise bereits vertraut sind. Wie die meisten RAG-basierten Systeme basieren diese Instruments auf semantische Suche als Schlüsselfähigkeit.

Anstatt Wissen rein nach Rohtext zu organisieren, wird die Codebasis indiziert und basierend auf der Bedeutung abgerufen.

Dadurch können Abfragen in natürlicher Sprache die relevantesten Codes abrufen, die Programmierer dann nutzen können, um effektiver zu argumentieren, zu modifizieren und Antworten zu generieren.

In diesem Artikel untersuchen wir das RAG-Pipeline in Cursor, der es Codieragenten ermöglicht, ihre Arbeit unter Verwendung des Kontextbewusstseins der Codebasis zu erledigen.

Inhalt

(1) Erkundung der Codebase RAG Pipeline
(2) Den Codebase-Index auf dem neuesten Stand halten
(3) Zum Abschluss


(1) Erkundung der Codebase RAG Pipeline

Sehen wir uns die Schritte in der RAG-Pipeline von Cursor zum Indizieren und Kontextualisieren von Codebasen an:

Schritt 1 – Chunking

In den meisten RAG-Pipelines müssen wir zunächst das Laden von Daten, die Textvorverarbeitung und das Parsen von Dokumenten aus mehreren Quellen verwalten.

Bei der Arbeit mit einer Codebasis kann jedoch ein Großteil dieses Aufwands vermieden werden. Der Quellcode ist innerhalb eines Projekt-Repositorys bereits intestine strukturiert und sauber organisiert, sodass wir das übliche Parsen von Dokumenten überspringen und direkt mit dem Chunking beginnen können.

In diesem Zusammenhang besteht das Ziel des Chunking darin, Code aufzuteilen sinnvolle, semantisch kohärente Einheiten (z. B. Funktionen, Klassen und logische Codeblöcke), anstatt den Codetext willkürlich aufzuteilen.

Durch semantisches Code-Chunking wird sichergestellt, dass jeder Block die Essenz eines bestimmten Codeabschnitts erfasst, was zu einem genaueren Abruf und einer nützlichen Generierung im weiteren Verlauf führt.

Um dies konkreter zu machen, schauen wir uns an, wie Code-Chunking funktioniert. Betrachten Sie das folgende Python-Beispielskript (machen Sie sich keine Gedanken darüber, was der Code tut; der Fokus liegt hier auf seiner Struktur):

Nach der Anwendung von Code-Chunking ist das Skript sauber in vier strukturell sinnvolle und zusammenhängende Blöcke unterteilt:

Wie Sie sehen, sind die Blöcke aussagekräftig und kontextrelevant, da sie die Codesemantik berücksichtigen. Mit anderen Worten: Beim Chunking wird die Aufteilung des Codes in der Mitte eines logischen Blocks vermieden, es sei denn, dies ist aufgrund von Größenbeschränkungen erforderlich.

In der Praxis bedeutet dies, dass Chunk-Splits eher zwischen Funktionen als innerhalb von Funktionen und zwischen Anweisungen und nicht in der Mittelzeile erstellt werden.

Für das obige Beispiel habe ich verwendet Chonkieein leichtes Open-Supply-Framework, das speziell für das Code-Chunking entwickelt wurde. Es bietet neben vielen anderen verfügbaren Chunking-Techniken eine einfache und praktische Möglichkeit, Code-Chunking zu implementieren.


(Optionale Lektüre) Unter der Haube des Code Chunking

Das obige Code-Chunking ist kein Zufall und wird auch nicht durch naives Aufteilen des Codes mithilfe von Zeichenanzahlen oder regulären Ausdrücken erreicht.

Es beginnt mit einem Verständnis der Syntax des Codes. Der Prozess beginnt normalerweise mit der Verwendung eines Quellcode-Parsers (z. B Baumsitter), um den Rohcode in einen umzuwandeln abstrakter Syntaxbaum (AST).

Ein abstrakter Syntaxbaum ist im Wesentlichen eine baumförmige Darstellung des Codes, der dessen Struktur und nicht den eigentlichen Textual content erfasst. Anstatt Code als Zeichenfolge zu betrachten, sieht das System ihn jetzt als logische Codeeinheiten wie Funktionen, Klassen, Methoden und Blöcke.

Betrachten Sie die folgende Python-Codezeile:

x = a + b

Anstatt als reiner Textual content behandelt zu werden, wird der Code in eine konzeptionelle Struktur wie diese umgewandelt:

Task
├── Variable(x)
└── BinaryExpression(+)
├── Variable(a)
└── Variable(b)

Dieses strukturelle Verständnis ermöglicht ein effektives Code-Chunking.

Jedes sinnvolle Codekonstrukt, z. B. eine Funktion, ein Block oder eine Anweisung, wird dargestellt als ein Knoten im Syntaxbaum.

Beispielillustration eines einfachen abstrakten Syntaxbaums | Bild vom Autor

Anstatt Rohtext zu bearbeiten, wirkt sich das Chunking direkt auf den Syntaxbaum aus.

Der Chunker durchläuft diese Knoten und gruppiert benachbarte Knoten, bis ein Token-Restrict erreicht ist, wodurch semantisch kohärente und größenbegrenzte Chunks entstehen.

Hier ist ein Beispiel für einen etwas komplizierteren Code und den entsprechenden abstrakten Syntaxbaum:

whereas b != 0:
    if a > b:
        a := a - b
    else:
        b := b - a
return 
Beispiel für abstrakte Syntax kostenlos | Bild unten verwendet Inventive Commons

Schritt 2 – Einbettungen und Metadaten generieren

Sobald die Blöcke vorbereitet sind, wird ein Einbettungsmodell angewendet, um eine Vektordarstellung (auch Einbettungen genannt) für jeden Codeblock zu generieren.

Diese Einbettungen erfassen die semantische Bedeutung des Codes und ermöglichen den Abruf für Benutzerabfragen und Generierungsaufforderungen, die mit semantisch verwandtem Code abgeglichen werden, selbst wenn sich genaue Schlüsselwörter nicht überschneiden.

Dies verbessert die Abrufqualität für Aufgaben wie Codeverständnis, Refactoring und Debugging erheblich.

Über die Generierung von Einbettungen hinaus ist ein weiterer wichtiger Schritt Anreicherung jedes Chunks mit relevanten Metadaten.

Zum Beispiel Metadaten wie die Dateipfad und den entsprechenden Codezeilenbereich denn jeder Block wird zusammen mit seinem Einbettungsvektor gespeichert.

Diese Metadaten liefern nicht nur wichtigen Kontext darüber, woher ein Block stammt, sondern ermöglichen auch eine metadatenbasierte Schlüsselwortfilterung während des Abrufs.


Schritt 3 – Verbesserung des Datenschutzes

Wie bei jedem RAG-basierten System ist der Datenschutz ein vorrangiges Anliegen. Dies wirft natürlich die Frage auf ob Dateipfade selbst vertrauliche Informationen enthalten können.

In der Praxis verraten Datei- und Verzeichnisnamen oft mehr als erwartet, beispielsweise interne Projektstrukturen, Produktcodenamen, Shopper-IDs oder Eigentumsgrenzen innerhalb einer Codebasis.

Daher werden Dateipfade als vertrauliche Metadaten behandelt und erfordern eine sorgfältige Handhabung.

Um dieses Drawback zu beheben, gilt Cursor Verschleierung des Dateipfads (auch bekannt als Pfadmaskierung) auf der Clientseite, bevor Daten übertragen werden. Jede Komponente des Pfades, geteilt durch / Und .wird mit einem geheimen Schlüssel und einer kleinen festen Nonce maskiert.

Dieser Ansatz verbirgt die tatsächlichen Datei- und Ordnernamen und behält gleichzeitig genügend Verzeichnisstruktur bei, um effektives Abrufen und Filtern zu unterstützen.

Zum Beispiel, src/funds/invoice_processor.py kann umgewandelt werden in a9f3/x72k/qp1m8d.f4.

Hinweis: Benutzer können steuern, welche Teile ihrer Codebasis mit Cursor geteilt werden, indem sie a verwenden .cursorignore Datei. Cursor bemüht sich nach besten Kräften, zu verhindern, dass der aufgelistete Inhalt in LLM-Anfragen übertragen oder darauf verwiesen wird.


Schritt 4 – Einbettungen speichern

Nach der Generierung werden die Chunk-Einbettungen (mit den entsprechenden Metadaten) in einer Vektordatenbank gespeichert Turbopufferdas für die schnelle semantische Suche in Millionen von Codeblöcken optimiert ist.

Turbopuffer ist eine serverlose Hochleistungssuchmaschine, die Vektor- und Volltextsuche kombiniert und durch kostengünstigen Objektspeicher unterstützt wird.

Um die Neuindizierung zu beschleunigen, werden Einbettungen auch in AWS zwischengespeichert und durch den Hash jedes Blocks verschlüsselt, sodass unveränderter Code bei der nachfolgenden Indizierungsausführung wiederverwendet werden kann.

Aus datenschutzrechtlicher Sicht ist dies wichtig zu beachten Lediglich Einbettungen und Metadaten werden in der Cloud gespeichert. Das bedeutet, dass unser ursprünglicher Quellcode auf unserem lokalen Laptop verbleibt und ist nie gespeichert auf Cursor-Servern oder in Turbopuffer.


Schritt 5 – Semantische Suche ausführen

Wenn wir eine Abfrage in Cursor senden, wird diese zunächst in einen Vektor umgewandelt, wobei dasselbe Einbettungsmodell für die Generierung von Chunk-Einbettungen verwendet wird. Es stellt sicher, dass sowohl Abfragen als auch Codeblöcke im selben semantischen Raum leben.

Aus Sicht der semantischen Suche läuft der Prozess wie folgt ab:

  1. Cursor vergleicht die Abfrageeinbettung mit Codeeinbettungen in der Vektordatenbank, um die semantisch ähnlichsten Codeblöcke zu identifizieren.
  2. Diese Kandidatenblöcke werden von Turbopuffer in der Rangfolge basierend auf ihren Ähnlichkeitswerten zurückgegeben.
  3. Da der Rohquellcode niemals in der Cloud oder der Vektordatenbank gespeichert wird, bestehen die Suchergebnisse nur aus Metadaten, insbesondere die maskierten Dateipfade und entsprechenden Codezeilenbereiche.
  4. Durch das Auflösen der Metadaten entschlüsselter Dateipfade und Zeilenbereiche ist der lokale Shopper dann in der Lage, die tatsächlichen Codeblöcke aus der lokalen Codebasis abzurufen.
  5. Die abgerufenen Codeblöcke werden dann in ihrer ursprünglichen Textform als Kontext zusammen mit der Abfrage an das LLM bereitgestellt, um eine kontextbezogene Antwort zu generieren.

Im Rahmen einer hybriden Suchstrategie (Semantik + Schlüsselwort) kann der Codierungsagent auch Instruments wie verwenden grep Und ripgrep um Codeausschnitte basierend auf genauen Zeichenfolgenübereinstimmungen zu finden.

OpenCode ist ein beliebtes Open-Supply-Coding-Agent-Framework, das in Terminal-, IDEs- und Desktop-Umgebungen verfügbar ist.

Im Gegensatz zu Cursor arbeitet es direkt auf der Codebasis und verwendet Textsuche, Dateiabgleich und LSP-basierte Navigation anstelle einer einbettungsbasierten semantischen Suche.

Infolgedessen bietet OpenCode ein starkes Strukturbewusstsein, es fehlen jedoch die tieferen semantischen Abruffunktionen von Cursor.

Zur Erinnerung: Unser Authentic-Quellcode ist nicht auf Cursor-Servern oder im Turbopuffer gespeichert.

Bei der Beantwortung einer Anfrage muss Cursor jedoch immer noch vorübergehend die relevanten ursprünglichen Codeblöcke an den Codierungsagenten übergeben, damit er eine genaue Antwort erzeugen kann.

Dies liegt daran, dass die Chunk-Einbettungen nicht zur direkten Rekonstruktion des Originalcodes verwendet werden können.

Klartextcode wird nur zum Zeitpunkt der Inferenz und nur für die spezifischen benötigten Dateien und Zeilen abgerufen. Außerhalb dieser kurzlebigen Inferenzlaufzeit wird die Codebasis nicht distant gespeichert oder beibehalten.


(2) Den Codebase-Index auf dem neuesten Stand halten

Überblick

Unsere Codebasis entwickelt sich schnell weiter, da wir entweder die vom Agenten generierten Änderungen akzeptieren oder manuelle Codeänderungen vornehmen.

Um die Genauigkeit des semantischen Abrufs zu gewährleisten, synchronisiert Cursor den Codeindex automatisch durch regelmäßige Überprüfungen, normalerweise alle fünf Minuten.

Bei jeder Synchronisierung erkennt das System sicher Änderungen und aktualisiert nur die betroffenen Dateien, indem es veraltete Einbettungen entfernt und neue generiert.

Darüber hinaus werden Dateien stapelweise verarbeitet, um die Leistung zu optimieren und Störungen unseres Entwicklungsworkflows zu minimieren.

Verwendung von Merkle-Bäumen

Wie sorgt Cursor dafür, dass das so reibungslos funktioniert? Es scannt den geöffneten Ordner und berechnet ein Merkle-Baum von Datei-Hasheswodurch das System Änderungen in der gesamten Codebasis effizient erkennen und verfolgen kann.

Additionally intestine, was ist ein Merkle-Baum?

Es handelt sich um eine Datenstruktur, die wie ein System digitaler kryptografischer Fingerabdrücke funktioniert und es ermöglicht, Änderungen in einer großen Menge von Dateien effizient zu verfolgen.

Jede Codedatei wird in einen kurzen Fingerabdruck umgewandelt, und diese Fingerabdrücke werden hierarchisch zu einem einzigen Fingerabdruck der obersten Ebene kombiniert, der den gesamten Ordner darstellt.

Wenn sich eine Datei ändert, müssen nur ihr Fingerabdruck und eine kleine Anzahl zugehöriger Fingerabdrücke aktualisiert werden.

Illustration eines Merkle-Baums | Bild unten verwendet Inventive Commons

Der Merkle-Baum der Codebasis wird mit dem Cursor-Server synchronisiert, der regelmäßig nach Fingerabdruck-Nichtübereinstimmungen sucht, um festzustellen, was sich geändert hat.

Dadurch kann festgestellt werden, welche Dateien geändert wurden, und bei der Indexsynchronisierung nur diese Dateien aktualisiert werden, wodurch der Prozess schnell und effizient bleibt.

Umgang mit verschiedenen Dateitypen

So verarbeitet Cursor im Rahmen des Indizierungsprozesses effizient verschiedene Dateitypen:

  • Neue Dateien: Automatisch zum Index hinzugefügt
  • Geänderte Dateien: Alte Einbettungen entfernt, neue geschaffen
  • Gelöschte Dateien: Sofort aus dem Index entfernt
  • Große/komplexe Dateien: Kann aus Leistungsgründen übersprungen werden

Hinweis: Die Codebasis-Indizierung von Cursor beginnt automatisch, wenn Sie einen Arbeitsbereich öffnen.


(3) Zusammenfassung

In diesem Artikel haben wir einen Blick über die LLM-Generierung hinaus geworfen und die Pipeline hinter Instruments wie Cursor untersucht, die über RAG den richtigen Kontext aufbaut.

Durch die Aufteilung des Codes entlang sinnvoller Grenzen, seine effiziente Indizierung und die kontinuierliche Aktualisierung dieses Kontexts im Zuge der Weiterentwicklung der Codebasis können Codierungsagenten weitaus relevantere und zuverlässigere Vorschläge liefern.

Von admin

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert