Phase 19 - Lesson 02

Capstone 02 — RAG over Codebase (Cross-Repo Semantic Search)

En 2026, cualquier organización de ingeniería seria ejecuta una búsqueda de código interna que comprende el significado, no solo las cadenas de texto. Sourcegraph Amp, las respuestas sobre base de código de Cursor, el grafo empresarial de Augment, el repomap de Aider, el MCP interno de Pinterest — todos tienen la misma forma. Ingerir múltiples repositorios, analizar con tree-sitter, incrustar (embed) fragmentos a nivel de función y de clase, búsqueda híbrida, reordenar (rerank) y responder con citas. Este proyecto final (capstone) te desafía a construir una herramienta que maneje 2 millones de líneas de código en 10 repositorios y sobreviva a la reindexación incremental en cada git push.

Type: Capstone Languages: Python (ingestion), TypeScript (API + UI) Prerequisites: Phase 5 (NLP foundations), Phase 7 (transformers), Phase 11 (LLM engineering), Phase 13 (tools), Phase 17 (infrastructure) Phases exercised: P5 · P7 · P11 · P13 · P17 Time: 30 hours

Problem

Para 2026, cada agente de programación de frontera se distribuye con una capa de recuperación de base de código porque las ventanas de contexto por sí solas no resuelven las preguntas multirrepositorio. El contexto de 1 millón de tokens de Claude ayuda; no elimina la necesidad de una recuperación clasificada y relevante. La búsqueda de coseno ingenua sobre fragmentos crudos envenena los resultados en código generado automáticamente, en duplicaciones de monorrepositorios y en la cola larga de símbolos raramente importados. La respuesta en producción es una búsqueda híbrida (densa + BM25) sobre fragmentos conscientes de AST (árbol de sintaxis abstracta) con un reordenador (reranker), respaldada por un grafo de referencias de símbolos.

Aprenderás esto indexando una flota real — no un repositorio de tutorial — y midiendo MRR@10, fidelidad de las citas y frescura incremental. Los modos de fallo son de infraestructura: un monorrepositorio de 100k archivos, un push que modifica la mitad de los archivos, una consulta que necesita cruzar cuatro repositorios para responder correctamente.

Concept

Una pipeline de ingesta consciente de AST analiza cada archivo con tree-sitter, extrae nodos de funciones y clases, y divide en fragmentos (chunks) en los límites de los nodos en lugar de ventanas fijas de tokens. Cada fragmento obtiene tres representaciones: una incrustación (embedding) densa (Voyage-code-3 o nomic-embed-code), términos esparsos BM25 y un resumen breve en lenguaje natural. El resumen añade una tercera modalidad recuperable — los usuarios preguntan "cómo se autoriza X" y el resumen menciona "authz", incluso si el código solo tiene check_permission.

La recuperación es híbrida. Una consulta dispara búsquedas densas y BM25 en paralelo, fusiona el top-k y entrega la unión a un reordenador de codificación cruzada (cross-encoder reranker, como Cohere rerank-3 o bge-reranker-v2-gemma-2b). La lista reordenada va a un sintetizador de contexto largo (Claude Sonnet 4.7 con caché de prompt, o Llama 3.3 70B autohospedado) con instrucciones de citar cada afirmación por archivo e intervalo de líneas. Las respuestas sin citas son rechazadas por un posfiltro.

La frescura incremental es el problema de infraestructura. Un git push dispara un webhook de diff indicando qué archivos y símbolos cambiaron. Solo los fragmentos afectados se vuelven a incrustar. Las aristas de símbolos entre archivos (importaciones, llamadas a métodos) se recalculan. El índice se mantiene consistente sin reprocesar 2 millones de líneas en cada commit.

Architecture

git push --> webhook --> ingest worker (LlamaIndex Workflow)
                           |
                           v
             tree-sitter parse + AST chunk
                           |
            +--------------+----------------+
            v              v                v
          dense        BM25 index       summary (LLM)
        (Voyage / bge)  (Tantivy)        (Haiku 4.5)
            |              |                |
            +------> Qdrant / pgvector <----+
                            |
                            v
                      symbol graph (Neo4j / kuzu)
                            |
   query --> LangGraph agent (retrieve -> rerank -> synth)
                            |
                            v
                 Claude Sonnet 4.7 1M context
                            |
                            v
                 answer + file:line citations

Stack

  • Análisis: tree-sitter con gramáticas para 17 lenguajes (Python, TS, Rust, Go, Java, C++, etc.)
  • Embeddings densos: Voyage-code-3 (alojado) o nomic-embed-code-v1.5 (autohospedado), fallback para bge-code-v1
  • Índice esparso: Tantivy (Rust) con BM25F, con pesos diferenciados por campos (nombre del símbolo vs cuerpo)
  • Base de datos vectorial: Qdrant 1.12 con búsqueda híbrida, o pgvector + pgvectorscale para equipos con menos de 50 millones de vectores
  • Modelo de resumen de fragmentos: Claude Haiku 4.5 o Gemini 2.5 Flash, con caché de prompt
  • Reordenador (reranker): Cohere rerank-3 o bge-reranker-v2-gemma-2b autohospedado
  • Orquestación: LlamaIndex Workflows para ingesta, LangGraph para el agente de consulta
  • Sintetizador: Claude Sonnet 4.7 (contexto de 1M) con caché de prompt
  • Grafo de símbolos: Neo4j (gestionado) o kuzu (embebido) para aristas de importación y llamada
  • Observabilidad: spans de Langfuse por etapa de recuperación + síntesis

Build It

  1. Varredor de ingesta. Recorre el historial de git en cada webhook de push. Colecta los archivos cambiados. Para cada archivo, analiza con tree-sitter, extrayendo nodos de funciones y clases con sus respectivos intervalos de líneas de origen. Emite registros de fragmentos {repo, path, start_line, end_line, symbol, body}.

  2. Resumidor de fragmentos. Agrupa fragmentos en llamadas a Haiku 4.5 con caché de prompt en el preámbulo del sistema. Prompt: "Resume esta función en una frase, nombrando su contrato público y efectos secundarios". Almacena el resumen junto al fragmento.

  3. Pool de embeddings. Dos colas paralelas: densa (lotes de 128 con Voyage-code-3) y resumen (mismo modelo, pero sobre la cadena del resumen). Escribe los vectores en Qdrant con los metadatos {repo, path, start_line, end_line, symbol, kind}.

  4. Índice BM25. Índice Tantivy ponderado por campos: peso 4 para el nombre del símbolo, peso 1 para el cuerpo del símbolo, peso 2 para el resumen. Permite consultas como "encuentra la función llamada X" junto con "encuentra la función que hace X".

  5. Grafo de símbolos. Para cada fragmento, registra las aristas: importaciones (este archivo usa el símbolo Y del repositorio Z), llamadas (esta función llama al método M en la clase C), herencia. Almacena en kuzu. Utilizado en el momento de la consulta para expandir la búsqueda más allá de las fronteras de los repositorios.

  6. Agente de consulta. Estructura LangGraph con tres nodos. retrieve dispara búsquedas densas y BM25 en paralelo, deduplicando por (repo, path, symbol). rerank ejecuta el codificador cruzado (cross-encoder) en el top-50 y mantiene el top-10. synth llama a Claude Sonnet 4.7 con los fragmentos reordenados en el contexto, utiliza caché de prompt y exige citas en formato archivo:línea.

  7. Validación de citas. Analiza la salida del modelo; cualquier afirmación sin una ancla en formato (repo/path:start-end) es marcada para reintento o descartada. Retorna solo respuestas debidamente citadas al usuario.

  8. Reindexación incremental. En cada webhook, calcula la diferencia (diff) a nivel de símbolo. Solo genera nuevos embeddings para los fragmentos cuyo texto cambió. Recalcula las aristas de símbolos para fragmentos cuyas importaciones cambiaron. Métrica: un push de 50 archivos debe reindexarse en menos de 60 segundos para una flota de 2M de líneas de código.

  9. Evaluación. Etiqueta 100 preguntas multirrepositorio con sus respectivas respuestas patrón en formato archivo:línea. Mide MRR@10, nDCG@10, fidelidad de las citas (proporción de afirmaciones con anclas verificables) y latencia p50/p99.

Use It

$ code-rag ask "how is S3 multipart abort wired into our retry budget?"
[retrieve]  12 chunks dense + 7 chunks bm25, 16 unique after dedup
[rerank]    top-5 kept (cohere rerank-3)
[synth]     claude-sonnet-4.7, cache hit rate 68%, 2.1s
answer:
  Multipart aborts are triggered by `AbortMultipartOnFail` in
  services/uploader/retry.go:122-148, which decrements the per-bucket
  retry budget defined in config/budgets.yaml:34-51 ...
  citations: [services/uploader/retry.go:122-148, config/budgets.yaml:34-51,
              libs/s3client/multipart.ts:44-61]

Ship It

La habilidad entregable se detalla en outputs/skill-codebase-rag.md. Dado un conjunto de repositorios, monta la pipeline de ingesta, el índice híbrido y el agente de consulta, y retorna respuestas citadas para cualquier pregunta multirrepositorio. Rúbrica:

Weight Criterion How it is measured
25 Retrieval quality MRR@10 y nDCG@10 en un conjunto de prueba de 100 preguntas
20 Citation faithfulness Proporción de afirmaciones en las respuestas que contienen anclas archivo:línea válidas
20 Latency and scale Latencia de consulta p95 en 10k QPS en el tamaño del corpus indexado
20 Incremental indexing correctness Tiempo desde el git push hasta que las modificaciones sean localizables en un commit de 50 archivos
15 UX and answer formatting Citas cliqueables, vista previa de fragmentos de código y facilidad de preguntas de seguimiento

Exercises

  1. Reemplaza Voyage-code-3 por nomic-embed-code autohospedado. Mide la diferencia en el MRR@10. Reporta si la brecha disminuye cuando la reordenación (reranking) está activada.

  2. Inyecta un 20% de código generado por IA (código repetitivo generado por LLM) en el corpus de código y vuelve a evaluar. Observa el envenenamiento de la recuperación. Añade una etiqueta "generado" en los metadatos y reduce el peso de esos resultados en la búsqueda.

  3. Haz un benchmark de la búsqueda híbrida de Qdrant frente a pgvector + pgvectorscale en el tamaño real de tu base de código. Reporta la latencia p99 para un tamaño de lote igual a 1.

  4. Añade una verificación semanal contra desvíos de rendimiento: reejecuta semanalmente la evaluación de las 100 preguntas. Dispara alertas ante caídas del MRR@10 > 5%.

  5. Extiende el sistema para resolución de símbolos multilenguaje: una función en Python que llama a un servicio en Go vía gRPC. Utiliza el grafo de símbolos para mapear esta conexión.

Key Terms

Term What people say What it actually means
AST-aware chunking "División a nivel de función" División del código en los límites de los nodos estructurales de tree-sitter en lugar de límites de ventanas de tokens fijas
Hybrid search "Densa + esparsa" Ejecución paralela de búsquedas vectoriales y BM25, fusionando y reordenando los mejores resultados
Cross-encoder rerank "Clasificación de segunda etapa" Modelo que evalúa conjuntamente el par (consulta, candidato), ofreciendo mayor precisión que la similitud de coseno simple
Prompt caching "Caché de prompt del sistema" Funcionalidad de Claude / OpenAI que descuenta hasta un 90% el costo de tokens repetidos al inicio del prompt
Symbol graph "Grafo de código" Mapeo de conexiones de importaciones, llamadas de funciones y herencias entre archivos y repositorios
Citation faithfulness "Tasa de fundamentación de la respuesta" Proporción de afirmaciones que el usuario puede verificar directamente haciendo clic en el enlace y leyendo el fragmento referenciado
Incremental re-index "Tiempo de disponibilidad en la búsqueda" Tiempo transcurrido desde el git push hasta que las modificaciones en los símbolos estén disponibles para consulta

Further Reading

0 lifetime access. Curriculum based on AI Engineering from Scratch by Rohit Ghumare (MIT, used under attribution).