
Comment le curseur indexe réellement votre base de code
Si vos environnements de développement (IDE) sont associés à des agents de codage, vous avez probablement vu des suggestions et des modifications de code étonnamment précises et pertinentes.
Ce niveau de qualité et de précision vient du fait que les agents s’appuient sur une compréhension approfondie de votre base de code.
Prenons le curseur comme exemple. Dans le Index et documents vous pouvez voir une section montrant que Cursor a déjà « ingéré » et indexé la base de code de votre projet :

Alors, comment pouvons-nous construire une compréhension globale d’une base de code en premier lieu ?
À la base, la réponse est génération augmentée par récupération (RAG)un concept que de nombreux lecteurs connaissent peut-être déjà. Comme la plupart des systèmes basés sur RAG, ces outils s’appuient sur recherche sémantique comme une capacité clé.
Plutôt que d’organiser les connaissances uniquement par texte brut, la base de code est indexée et récupérée en fonction de sa signification.
Cela permet aux requêtes en langage naturel de récupérer les codes les plus pertinents, que les agents de codage peuvent ensuite utiliser pour raisonner, modifier et générer des réponses plus efficacement.
Dans cet article, nous explorons les Pipeline RAG dans Cursor qui permet aux agents de codage de faire leur travail en utilisant la connaissance contextuelle de la base de code.
Contenu
(1) Explorer le pipeline RAG de base de code
(2) Garder l’index de base de code à jour
(3) Envelopper le tout
(1) Explorer le pipeline Codebase RAG
Explorons les étapes du pipeline RAG de Cursor pour indexer et contextualiser les bases de code :
Étape 1 — Découpage
Dans la plupart des pipelines RAG, nous devons d’abord gérer le chargement des données, le prétraitement du texte et l’analyse des documents provenant de plusieurs sources.
Cependant, lorsque vous travaillez avec une base de code, une grande partie de cet effort peut être évitée. Le code source est déjà bien structuré et proprement organisé au sein d’un dépôt de projet, ce qui nous permet d’éviter l’analyse habituelle des documents et de passer directement au découpage.
Dans ce contexte, le but du chunking est de diviser le code en unités significatives et sémantiquement cohérentes (par exemple, fonctions, classes et blocs de code logique) plutôt que de diviser arbitrairement le texte du code.
Le regroupement sémantique du code garantit que chaque morceau capture l’essence d’une section de code particulière, conduisant à une récupération plus précise et à une génération utile en aval.
Pour rendre cela plus concret, regardons comment fonctionne le découpage de code. Considérez l’exemple de script Python suivant (ne vous inquiétez pas de ce que fait le code ; l’accent est mis ici sur sa structure) :
Après avoir appliqué le découpage du code, le script est clairement divisé en quatre morceaux structurellement significatifs et cohérents :
Comme vous pouvez le constater, les morceaux sont significatifs et contextuellement pertinents car ils respectent la sémantique du code. En d’autres termes, le chunking évite de diviser le code au milieu d’un bloc logique, sauf si des contraintes de taille l’exigent.
En pratique, cela signifie que des divisions en morceaux ont tendance à être créées entre les fonctions plutôt qu’à l’intérieur de celles-ci, et entre les instructions plutôt qu’à mi-ligne.
Pour l’exemple ci-dessus, j’ai utilisé Chonkieun framework open source léger conçu spécifiquement pour le découpage de code. Il fournit un moyen simple et pratique d’implémenter le découpage de code, parmi de nombreuses autres techniques de découpage disponibles.
[Optional Reading] Sous le capot du découpage de code
Le découpage du code ci-dessus n’est pas accidentel et n’est pas non plus obtenu en divisant naïvement le code à l’aide d’un nombre de caractères ou d’expressions régulières.
Cela commence par une compréhension de la syntaxe du code. Le processus commence généralement par l’utilisation d’un analyseur de code source (tel que gardien d’arbre) pour convertir le code brut en un arbre de syntaxe abstraite (AST).
Un arbre syntaxique abstrait est essentiellement une représentation arborescente du code qui capture sa structure, et non le texte lui-même. Au lieu de voir le code comme une chaîne, le système le voit désormais comme des unités logiques de code telles que des fonctions, des classes, des méthodes et des blocs.
Considérez la ligne suivante de code Python :
x = a + b
Plutôt que d’être traité comme du texte brut, le code est converti en une structure conceptuelle comme celle-ci :
Assignment
├── Variable(x)
└── BinaryExpression(+)
├── Variable(a)
└── Variable(b)
Cette compréhension structurelle est ce qui permet un découpage efficace du code.
Chaque construction de code significative, telle qu’une fonction, un bloc ou une instruction, est représentée sous la forme un nœud dans l’arbre syntaxique.

Au lieu d’opérer sur du texte brut, le chunking fonctionne directement sur l’arbre syntaxique.
Le chunker traversera ces nœuds et regroupera les nœuds adjacents jusqu’à ce qu’une limite de jetons soit atteinte, produisant des morceaux sémantiquement cohérents et limités en taille.
Voici un exemple de code légèrement plus compliqué et l’arbre de syntaxe abstraite correspondant :
while b != 0:
if a > b:
a := a - b
else:
b := b - a
return

Étape 2 — Génération d’intégrations et de métadonnées
Une fois les morceaux préparés, un modèle d’intégration est appliqué pour générer une représentation vectorielle (alias intégrations) pour chaque morceau de code.
Ces intégrations capturent la signification sémantique du code, permettant ainsi de faire correspondre la récupération des requêtes des utilisateurs et les invites de génération avec du code sémantiquement lié, même lorsque les mots-clés exacts ne se chevauchent pas.
Cela améliore considérablement la qualité de la récupération pour des tâches telles que la compréhension du code, la refactorisation et le débogage.
Au-delà de la génération des intégrations, une autre étape critique est enrichir chaque morceau avec des métadonnées pertinentes.
Par exemple, des métadonnées telles que chemin du fichier et plage de lignes de code correspondante car chaque morceau est stocké à côté de son vecteur d’intégration.
Ces métadonnées fournissent non seulement un contexte important sur l’origine d’un morceau, mais permettent également un filtrage par mots clés basé sur les métadonnées lors de la récupération.
Étape 3 — Améliorer la confidentialité des données
Comme pour tout système basé sur RAG, la confidentialité des données est une préoccupation majeure. Cela pose naturellement la question de si les chemins de fichiers eux-mêmes peuvent contenir des informations sensibles.
En pratique, les noms de fichiers et de répertoires révèlent souvent plus que prévu, comme les structures internes du projet, les noms de code des produits, les identifiants des clients ou les limites de propriété au sein d’une base de code.
Par conséquent, les chemins d’accès aux fichiers sont traités comme des métadonnées sensibles et nécessitent une manipulation minutieuse.
Pour résoudre ce problème, le curseur s’applique obscurcissement du chemin de fichier (alias masquage de chemin) côté client avant la transmission de données. Chaque composant du chemin, divisé par / et .est masqué à l’aide d’une clé secrète et d’un petit nom occasionnel fixe.
Cette approche masque les noms réels des fichiers et des dossiers tout en préservant suffisamment la structure des répertoires pour prendre en charge une récupération et un filtrage efficaces.
Par exemple, src/payments/invoice_processor.py peut être transformé en a9f3/x72k/qp1m8d.f4.
Remarque : les utilisateurs peuvent contrôler quelles parties de leur base de code sont partagées avec Cursor en utilisant un
.cursorignoredéposer. Le curseur fait de son mieux pour empêcher que le contenu répertorié soit transmis ou référencé dans les requêtes LLM.
Étape 4 : Stockage des intégrations
Une fois générés, les intégrations de morceaux (avec les métadonnées correspondantes) sont stockées dans une base de données vectorielle à l’aide de Turbopouffeuroptimisé pour une recherche sémantique rapide sur des millions de morceaux de code.
Turbopouffeur est un moteur de recherche sans serveur hautes performances qui combine la recherche vectorielle et la recherche en texte intégral et s’appuie sur un stockage d’objets à faible coût.
Pour accélérer la réindexation, les intégrations sont également mises en cache dans AWS et saisies par le hachage de chaque morceau, ce qui permet de réutiliser le code inchangé lors de l’exécution de l’indexation ultérieure.
Du point de vue de la confidentialité des données, il est important de noter que seules les intégrations et les métadonnées sont stockées dans le cloud. Cela signifie que notre code source d’origine reste sur notre machine locale et est jamais stocké sur les serveurs Cursor ou dans Turbopuffer.
Étape 5 — Exécution d’une recherche sémantique
Lorsque nous soumettons une requête dans Cursor, elle est d’abord convertie en vecteur en utilisant le même modèle d’intégration pour la génération d’intégrations de blocs. Cela garantit que les requêtes et les morceaux de code vivent dans le même espace sémantique.
Du point de vue de la recherche sémantique, le processus se déroule comme suit :
- Le curseur compare l’intégration de requêtes aux intégrations de code dans la base de données vectorielle pour identifier les morceaux de code les plus sémantiquement similaires.
- Ces morceaux candidats sont renvoyés par Turbopuffer par ordre de classement en fonction de leurs scores de similarité.
- Étant donné que le code source brut n’est jamais stocké dans le cloud ou dans la base de données vectorielles, les résultats de la recherche consistent uniquement en les métadonnées, en particulier les chemins de fichiers masqués et les plages de lignes de code correspondantes.
- En résolvant les métadonnées des chemins de fichiers et des plages de lignes déchiffrés, le client local est alors en mesure de récupérer les morceaux de code réels de la base de code locale.
- Les morceaux de code récupérés, dans leur forme de texte d’origine, sont ensuite fournis comme contexte aux côtés de la requête adressée au LLM pour générer une réponse contextuelle.
Dans le cadre d’une stratégie de recherche hybride (sémantique + mot-clé), l’agent de codage peut également utiliser des outils tels que grep et ripgrep pour localiser des extraits de code en fonction de correspondances de chaînes exactes.
Code Ouvert est un framework d’agent de codage open source populaire disponible dans les environnements de terminal, d’IDE et de bureau.
Contrairement à Cursor, il fonctionne directement sur la base de code en utilisant la recherche de texte, la correspondance de fichiers et la navigation basée sur LSP plutôt que la recherche sémantique basée sur l’intégration.
En conséquence, OpenCode offre une forte conscience structurelle mais ne dispose pas des capacités de récupération sémantique plus profondes trouvées dans Cursor.
Pour rappel, notre code source original est pas stockés sur les serveurs Cursor ou dans Turbopuffer.
Cependant, lorsqu’il répond à une requête, Cursor doit toujours transmettre temporairement les morceaux de code d’origine pertinents à l’agent de codage afin qu’il puisse produire une réponse précise.
En effet, les intégrations de blocs ne peuvent pas être utilisées pour reconstruire directement le code d’origine.
Le code en texte brut est récupéré uniquement au moment de l’inférence et uniquement pour les fichiers et lignes spécifiques nécessaires. En dehors de ce runtime d’inférence de courte durée, la base de code n’est pas stockée ni conservée à distance.
(2) Garder l’index de base de code à jour
Aperçu
Notre base de code évolue rapidement à mesure que nous acceptons les modifications générées par l’agent ou que nous apportons des modifications manuelles au code.
Pour que la récupération sémantique reste précise, Cursor synchronise automatiquement l’index de code via des vérifications périodiques, généralement toutes les cinq minutes.
Lors de chaque synchronisation, le système détecte en toute sécurité les modifications et actualise uniquement les fichiers concernés en supprimant les intégrations obsolètes et en en générant de nouvelles.
De plus, les fichiers sont traités par lots pour optimiser les performances et minimiser les perturbations de notre flux de développement.
Utiliser les arbres Merkle
Alors, comment Cursor fait-il en sorte que cela fonctionne de manière si transparente ? Il analyse le dossier ouvert et calcule un arbre Merkle de hachages de fichiersce qui permet au système de détecter et de suivre efficacement les modifications dans la base de code.
Très bien, alors qu’est-ce qu’un arbre Merkle ?
Il s’agit d’une structure de données qui fonctionne comme un système d’empreintes digitales cryptographiques, permettant de suivre efficacement les modifications apportées à un large ensemble de fichiers.
Chaque fichier de code est converti en une courte empreinte digitale, et ces empreintes digitales sont combinées hiérarchiquement en une seule empreinte digitale de niveau supérieur qui représente l’ensemble du dossier.
Lorsqu’un fichier change, seules son empreinte digitale et un petit nombre d’empreintes digitales associées doivent être mises à jour.

L’arborescence Merkle de la base de code est synchronisée avec le serveur Cursor, qui vérifie périodiquement les incohérences d’empreintes digitales pour identifier ce qui a changé.
En conséquence, il peut identifier les fichiers qui ont été modifiés et mettre à jour uniquement ces fichiers pendant la synchronisation de l’index, ce qui permet au processus d’être rapide et efficace.
Gestion de différents types de fichiers
Voici comment Cursor gère efficacement différents types de fichiers dans le cadre du processus d’indexation :
- Nouveaux fichiers : Ajouté automatiquement à l’index
- Fichiers modifiés : Anciennes intégrations supprimées, nouvelles créées
- Fichiers supprimés : Rapidement supprimé de l’index
- Fichiers volumineux/complexes : Peut être ignoré pour des raisons de performances
Remarque : L’indexation de la base de code du curseur commence automatiquement chaque fois que vous ouvrez un espace de travail.
(3) Conclusion
Dans cet article, nous avons regardé au-delà de la génération LLM pour explorer le pipeline derrière des outils comme Cursor qui crée le bon contexte via RAG.
En répartissant le code selon des limites significatives, en l’indexant efficacement et en actualisant continuellement ce contexte à mesure que la base de code évolue, les agents de codage sont en mesure de fournir des suggestions beaucoup plus pertinentes et fiables.



