Note de recherche
Faut-il remplacer le RAG d'Anatoly par PageIndex ? Une question ouverte avec une réponse mesurable
Poser une question d'outillage plutôt que la trancher. Les deux systèmes ont des formes différentes : lookup vectoriel par fonction d'un côté, marche TOC pilotée par LLM de l'autre. Savoir si l'un doit remplacer l'autre dépend de l'économie de la charge de travail et de conditions d'essai non mesurées. Cette note pose la question honnêtement, expose notre prior, et décrit l'expérience bornée qui la trancherait.
Note par : Rémi Viau (mainteneur Anatoly), avec Claude (Anthropic Opus 4.7) comme partenaire d'analyse. Repos référencés : anatoly, anatoly-bench, PageIndex.
La question#
PageIndex de VectifyAI est un système de retrieval "vectorless" : un document est parsé une fois en arbre hiérarchique de type table des matières, et à la requête un LLM raisonne dans cet arbre pour choisir la section pertinente. Leur chiffre phare est 98.7% sur FinanceBench, un benchmark QA sur des filings financiers.
La question à laquelle cette note essaie de répondre : faut-il remplacer le RAG vectoriel fonction-à-fonction d'Anatoly par PageIndex, en partie ou en totalité, pour améliorer la détection de doublons et réduire les dépendances ?
Nous n'avons pas encore de mesures. Cette note décrit comment nous formulons la question, notre prior actuel, et l'expérience qui la trancherait réellement.
Ce que nous savons des deux formes#
Les deux systèmes sont structurellement différents, d'une manière qui compte plus qu'un chiffre de coût.
| Dimension | RAG Anatoly (actuel) | PageIndex |
|---|---|---|
| Unité indexée | Fonction (extrait AST) | Section de document (nœud TOC) |
| Primitive de requête | "k plus proches voisins de ce vecteur" | "LLM, choisis la section pertinente" |
| LLM dans la boucle | À l'indexation (Haiku, pour les résumés NLP qui pèsent 40% du score hybride) | À la requête (n'importe quel LLM, GPT-4o par défaut mais Haiku tient aussi) |
| Stockage | LanceDB local (768d code, 384d NLP) | Arbre JSON, pas de vecteurs |
| Stack | TypeScript / Node | Python |
La ligne granularité est celle qui compte le plus. L'axe duplication d'Anatoly émet une requête "trouve-moi les fonctions sémantiquement similaires à celle-ci", une par fonction, plusieurs centaines voire milliers de fois par audit. PageIndex n'expose pas nativement cette primitive : son idiome est "étant donné une question, navigue dans une TOC et cite une section". Reformuler la détection de doublons par fonction en marches TOC n'est pas seulement coûteux, c'est un changement de catégorie.
C'est la raison porteuse pour laquelle ce swap n'est pas un drop-in. Les arguments coût et stack viennent ensuite.
Le coût, honnêtement#
Une version précédente de cette note affirmait un écart de 100 à 1000×. Ce chiffre était gonflé et supposait GPT-4o côté PageIndex et "gratuit" côté Anatoly. Aucun des deux n'est exact.
- Anatoly appelle déjà un LLM dans son pipeline RAG. L'embedding NLP 384d est généré à partir d'un résumé Haiku de chaque fonction. Ces résumés ne sont pas optionnels : ils pèsent 40% du score de similarité hybride dans
searchByIdHybrid(). Le "retrieval gratuit" d'Anatoly est en réalité un "coût LLM payé à l'indexation, puis mis en cache agressivement". - PageIndex n'impose pas GPT-4o. Faire tourner la même boucle de retrieval avec Haiku diviserait le coût par requête d'environ un ordre de grandeur par rapport au chiffre des démos vendeurs.
La comparaison honnête est entre deux profils de coût :
- Anatoly : N appels Haiku à l'indexation, cache basé sur le contenu, coût par requête proche de zéro.
- PageIndex : environ 0 à l'indexation, M appels LLM par audit à la requête.
Qui gagne dépend de la charge de travail. Codebases stables ré-auditées souvent (taux de hit cache élevé, beaucoup de requêtes par fonction indexée) favorisent Anatoly largement. Codebases qui bougent vite (taux de hit cache faible, peu de requêtes répétées par fonction) réduisent l'écart. Nous n'avons pas mesuré le point de bascule. Le "100× à 1000×" était de la rhétorique, pas des données ; le facteur réaliste sur un audit typique est probablement 2× à 5×, toujours en faveur d'Anatoly pour les charges que nous shippons aujourd'hui.
Ce que nous ne savons pas#
Trois choses que cette note ne peut pas trancher sans mesures.
- Les cas de doublons durs. La cosinus vectorielle marche bien sur les cas faciles (surface lexicale proche, embeddings proches). Sur les cas durs (deux fonctions qui calculent la même chose via des chemins de code très différents et un naming différent), un LLM raisonnant sur les deux corps pourrait plausiblement battre la cosinus. C'est précisément la thèse "similarité ≠ pertinence" de PageIndex. Nous avons un prior que cela compte moins en code qu'en prose, mais nous ne l'avons pas testé.
- Le plafond F1. Le RAG d'Anatoly est passé de 40.7% à 65.5% F1 sur la fixture
slot-engineentre les v2 et v17 d'anatoly-bench. La trajectoire est positive, mais 65.5% reste modeste en absolu. Nous ne savons pas jusqu'où l'architecture actuelle peut aller. Une partie de l'écart restant peut être traitée dans la stack actuelle (rerankers, meilleurs embeddings) ; une autre partie peut-être pas. - Le coût d'intégration cross-stack. Appeler du Python depuis Node est un pattern connu (subprocess ou HTTP local). Ce n'est pas gratuit, mais ce n'est pas non plus un blocage structurel. Tant que nous n'essayons pas, nous devinons.
Notre prior, conditionnel#
Compte tenu de l'argument granularité et du profil de charge que nous shippons (codebases stables, audits répétés, taux de hit cache autour de 95%), nous penchons contre le remplacement du vector store fonction-à-fonction par PageIndex. Ce prior est modérément fort sur l'axe duplication, où la granularité est la contrainte dominante. Il est plus faible sur le retrieval de documentation longue, où la navigation TOC pourrait mieux convenir que l'approche par chunk de titre actuelle dans doc-indexer.ts.
Nous mettrions à jour vers PageIndex si l'un des points suivants s'avérait :
- Sur un set curé de doublons "durs", un LLM avec les deux corps de fonctions en contexte produit un F1 mesurablement meilleur que le retriever hybride actuel.
- Le taux de hit cache sur un projet représentatif tombe bien sous les ~95% que nous supposons, érodant l'avantage d'amortissement.
- Le coût d'intégration d'un sidecar Python s'avère inférieur au gain marginal sur le retrieval de doc longue.
L'expérience qui trancherait#
Le prochain pas utile n'est pas plus d'analyse, c'est une expérience bornée.
- Choisir 20 paires de doublons catalogués dans
anatoly-benchcouvrant les cas faciles, moyens et durs. - Faire tourner trois retrievers sur la même fixture : le retriever hybride actuel, l'actuel plus un reranker local (voir "Ce que nous ferions de toute façon" ci-dessous), et une marche LLM façon PageIndex avec Haiku.
- Scorer F1 et coût total end-to-end. Le chiffre de coût doit inclure la dépense à l'indexation côté Anatoly et la dépense par requête côté PageIndex.
L'expérience tient en un week-end et viendrait soit falsifier le prior conditionnel ci-dessus, soit le faire évoluer en vraie recommandation. Sans cela, "ne pas remplacer" reste de la rhétorique.
Ce que nous ferions de toute façon#
Trois choses méritent d'être faites quelle que soit l'issue de l'expérience, parce qu'elles prolongent la trajectoire F1 du retriever actuel et qu'elles ne coûtent rien. Nous les avions présentées comme des alternatives dans la première version de cette note ; à la réflexion ce sont du travail de base, pas des alternatives.
- Reranker léger local. Ajouter une étape après le top-k hybride qui re-score les voisins avec un petit reranker ONNX comme bge-reranker-v2-m3. Les rerankers améliorent systématiquement le F1 sur des top-k bruités, sans coût LLM. Cela appartient à la comparaison ci-dessus comme baseline équitable.
- Affiner seuils et poids hybrides. Le split 60/40 code/NLP de
searchByIdHybrida été posé à l'intuition. Cross-valider le poids, le top-k et le plancher de similarité suranatoly-benchest gratuit et pourrait déplacer le F1 de façon mesurable. La même démarche empirique qui a produit le résultat sur la discipline de concision s'applique ici. - Évaluer des embeddings code plus récents. Comparer le
jina-embeddings-v2-base-codeactuel à des options plus récentes comme Qodo-Embed-1, un run de fixture par modèle.
Fichiers clés pour référence#
Pour le lecteur qui veut lire le code dont cette note parle :
src/rag/vector-store.ts: wrapper LanceDB, cœur du retrieval.src/rag/embeddings.ts: orchestration des embeddings ONNX / SDK.src/rag/indexer.ts: extraction AST en records FunctionCard.src/rag/doc-indexer.ts: chunking Markdown ; le site le plus plausible pour une expérience PageIndex.src/core/file-evaluator.ts: le point d'entréepreResolveRag()dans le pipeline d'audit.src/core/axes/duplication.ts: consommateur RAG principal.
Le contexte marché plus large autour de l'audit et de la review de code est dans Audit IA vs Review IA de code en 2026.