itza-rag
Un pipeline RAG local sin framework — embeddings vía Ollama, almacén en disco de dos archivos y coseno por fuerza bruta en NumPy, para que un modelo chico corriendo en tu máquina "conozca" un repo.
Tengo un agente local corriendo sobre un modelo chico (qwen2.5-coder:7b) en mi
propia máquina: sin browsing, sin API de pago, sin ventana de contexto para
tragarse un repo entero. Para que sirviera de algo necesitaba que conociera la
carpeta sobre la que trabaja. itza-rag es lo mínimo que resuelve eso: un
pipeline RAG local en un solo archivo de Python, sin LangChain, sin base de datos
vectorial y sin servicio de nadie.
Qué hace
Dos comandos, y ya:
itza_rag.py index <carpeta> [--out DIR] [--exts .py,.ts,.md,...]
itza_rag.py query "<pregunta>" --index DIR [--k 5] [--model qwen2.5-coder:7b]
index recorre la carpeta, parte los archivos en chunks, los embebe y guarda el
resultado en disco. query embebe la pregunta, saca los k chunks más parecidos,
los pega como contexto y se los manda al modelo local con una instrucción
deliberadamente estricta: responde solo con el contexto de abajo, cita el
archivo:líneas de donde lo sacaste, y si el contexto no alcanza responde
NO_SE: con lo que falta. Un modelo chico obligado a citar y con permiso
explícito de decir “no sé” miente muchísimo menos.
Y trae escrita su propia regla de uso, que es la parte que me importa: para “¿dónde está definido X?” se usa ripgrep, no esto. Este pipeline es para preguntas conceptuales — “¿cómo funciona X?”, “¿por qué se hizo Y así?” — que son justo las que una búsqueda literal no contesta. Saber qué herramienta no usar es la mitad del valor.
Bajo el capó
Embeddings vía Ollama. Se llama a /api/embed con nomic-embed-text, en
lotes de 32, contra el Ollama que ya corre local. Sin key, sin cuota, sin que el
código salga de la máquina.
Chunking distinto según el archivo. Los .md se parten por encabezado
(#, ##, …), porque en un documento el encabezado es la unidad semántica. El
código se parte por bloques separados por línea en blanco y luego se fusionan
hasta un techo de 80 líneas, lo que evita a la vez los chunks de una sola línea
(inútiles para el coseno) y los chunks gigantes que diluyen la señal. Cada chunk
se guarda con su archivo y su rango de líneas — de ahí salen las citas.
El “almacén vectorial” son dos archivos. Una matriz embeddings.npy con los
vectores ya normalizados y un chunks.json con los textos y su procedencia. Nada
de Chroma, Pinecone ni FAISS.
La búsqueda es un producto punto. Como los vectores están normalizados al
indexar, la similitud coseno es literalmente matrix @ qvec, y el top-k sale de
un argsort. Es fuerza bruta y no me disculpo: para menos de ~100k chunks NumPy
lo resuelve en un parpadeo, y montar un índice ANN aquí sería pagar complejidad
por un problema que no tengo.
También ignora lo que hay que ignorar (.git, node_modules, dist, .venv,
__pycache__, .angular, …) y respeta una lista de extensiones configurable.
El stack
- Python 3 — solo librería estándar más NumPy. Cero frameworks de RAG.
- Ollama en local:
nomic-embed-textpara embeber,qwen2.5-coder:7b(o el que le pases con--model) para responder. - Almacén en disco:
.npy+.json, dos archivos por índice. - CLI con
argparse, unos 200 renglones en un solo archivo.
Vale la pena mencionar
Este proyecto está en la lista justamente por lo que no tiene. Un RAG “completo” hoy son cinco dependencias, un servidor de base de datos vectorial y una capa de abstracción que hay que aprender. La versión que de verdad necesitaba —indexar una carpeta y contestar preguntas conceptuales sobre ella— cabe en un archivo que puedo leer entero en cinco minutos, y por eso puedo depurarlo y cambiarle el chunking cuando una respuesta sale mal.
Aparte, es la pieza local de un arreglo más grande: el agente chico de la máquina puede contestar sobre un repo sin mandar el código a ninguna API ni gastar de la cuota de los modelos grandes. Ese reparto —lo barato y privado en local, lo caro y capaz en la nube, y una manera clara de decidir qué va dónde— es la misma que persigue Agent Fleet Console, el otro proyecto de esta lista.
¿Un café y platicamos?
¿Te gustó lo que leíste? Construyo productos así de punta a punta — y siempre estoy para una buena plática. Hablemos del tuyo, o nomás intercambiamos ideas con un café.