Saltar al contenido
Blog
23 de junio de 20265 min de lectura

Por qué el chunking rompe a tu agente de código (y casi nadie lo nota)

Llevo meses peleándome con RAG sobre código y siempre acabo en el mismo sitio: el problema no es el modelo, es cómo partimos el código. Aquí va lo que aprendí rompiéndolo.

RAGAgentesLLMAST
Por qué el chunking rompe a tu agente de código (y casi nadie lo nota)

Llevo un tiempo metido en cómo darle contexto a los agentes de código, y hay una cosa que me tenía obsesionado: por qué a veces el modelo responde como si le faltara medio archivo. Pensaba que era cosa del modelo, que necesitaba uno más grande o más caro. Estaba equivocado.

El problema casi nunca es el modelo. Es el chunking — cómo partimos el código antes de pasárselo.

El error de base: tratar el código como si fuera texto

El RAG clásico nació para documentos, y la receta es siempre la misma: partes el texto en trozos de un tamaño fijo —unos 500 tokens—, sacas un embedding de cada trozo y los guardas en un vector store. Cuando preguntas algo, recuperas los trozos más "parecidos" a tu pregunta. Punto.

La primera vez que lo monté para código, me pareció lógico. Tardé en darme cuenta de que con prosa funciona, pero con código falla en silencio — y eso es lo peor, porque no te avisa.

El motivo, cuando lo entiendes, es de cajón: el código no es texto plano. Una novela la puedes cortar casi por cualquier sitio y las dos mitades siguen teniendo sentido. El código no. Tiene una estructura sintáctica —lo que se llama un AST, el árbol de sintaxis— y un grafo de dependencias entre sus piezas. Si cortas contando tokens, te cargas justo eso.

Síntoma 1: funciones partidas por la mitad

Un chunk de 500 tokens corta donde le toca, no donde la función acaba. Así que el modelo recibe la primera mitad de verifyUser() y nada más. Y aquí está lo curioso: no sabe que le falta código, así que se inventa el resto. Lo llamamos "alucinación", pero la culpa no es suya — le diste una función a medias.

Síntoma 2: dependencias que se quedan fuera

Pongamos que verifyUser() llama a hashPassword(), y el bug de verdad está en hashPassword. La búsqueda te trae verifyUser porque encaja con tu pregunta, pero hashPassword está en otro trozo que no se parece en nada a lo que escribiste. Resultado: nunca lo ves, y el modelo tampoco.

Esto es lo que el chunking por tamaño no puede ver: la relación de "quién llama a quién". Trata cada trozo como una isla. Pero para entender una función casi siempre necesitas sus dependencias directas —lo que llama y quién la llama—, y eso vive en el grafo, no en lo parecidas que sean dos cadenas de texto.

Síntoma 3: la similitud semántica te la juega

Buscas verify user y el vector store, todo contento, te devuelve verifyEmail, verifyToken, validateUser. Nombres parecidos, embeddings cercanos, código que no te sirve para nada. Que dos nombres se parezcan no significa que el código sea relevante — y en programación los nombres parecidos están por todas partes.

Lo que saqué en claro

El chunking por tamaño destruye precisamente lo que hace que el código signifique algo: su estructura y sus relaciones. Optimiza por "trozos de tamaño cómodo" cuando debería optimizar por piezas con sentido y sus dependencias.

Recuperar "trozos parecidos" es, sencillamente, la pregunta equivocada. La buena —qué habría que recuperar en su lugar, y cómo usar el AST y el grafo para conseguirlo— es justo donde estoy metido ahora. Pero eso da para otro artículo.

Escrito porIsmael Manzano LeónFull Stack Developer · leo/ · leosoftware.dev
Hablar con Ismael