Tenia cinco repos privados en mi org de GitHub. Cada uno con descripcion bonita: “RAG production”, “multi-tenant LLM gateway”, “agent orchestration con LangGraph”. Cuando los abri por dentro, tres tenian un README de una linea y nada mas.
Hoy hice publico uno de los dos que si tenian codigo. Se llama DocuQuery.
Que es
Una plataforma RAG: subis PDFs, se trocean, se vectorizan, y respondes preguntas en lenguaje natural con citas a la fuente.
Stack:
- FastAPI + Pydantic v2
- PostgreSQL 16 con SQLAlchemy 2 async + Alembic
- Qdrant para los vectores
- OpenAI:
text-embedding-3-smallygpt-4o-mini - structlog en JSON, Docker Compose, ruff, mypy strict
Repo: github.com/GeosData/llm-rag-platform
La decision tecnica que escribi como ADR
El call menos obvio fue Qdrant en vez de pgvector. Ya tenia Postgres en el stack. pgvector hubiera sido cero infra nueva.
Termine eligiendo Qdrant por dos razones:
- Aislamiento del blast radius. Los queries RAG corren a tasa mucho mayor que el CRUD. Una recompactacion del indice o un burst de busquedas no deberia tumbar el resto de la API. Engine separado mantiene el daño contenido.
- Headroom de latencia. A 1M de vectores, p95 ~30ms en Qdrant me deja espacio para reranking, hybrid search, multi-vector. pgvector funciona, pero cada optimizacion gasta presupuesto de Postgres que otras tablas tambien necesitan.
Acepte el costo operativo: un container mas, otro storyboard de backup, otro health check.
El ADR completo esta en el repo: docs/adr/0001-vector-store-qdrant-vs-pgvector.md. Incluye la condicion para revisar la decision si el proyecto crece.
Lo que aprendi escribiendolo
Tenia el trade-off “intuitivo” en la cabeza desde hace meses. Cuando me sente a redactar el ADR, dos cosas pasaron:
- Algunas razones se cayeron. Habia cosas que pensaba importantes y al ponerlas en frio no defendian la decision. Las saque.
- Una razon que no tenia clara aparecio. El argumento del failure isolation no era el que tenia en la cabeza al principio. Salio escribiendo. Es el mas fuerte de los dos finales.
Naval lo dice mejor que yo: si no podes escribirlo, no lo entendes. Lo confirme una vez mas.
Lo que no esta perfecto y deje publico igual
Antes de hoy el repo tenia el stack y el codigo, pero:
tests/vacio (solo__init__.py).- Sin ADRs.
- Coverage = 0%.
Tenia dos opciones: pulir tres semanas en privado y subir cuando estuviera “listo”, o publicar con lo justo y mejorar en commits visibles.
Eleji la segunda. Lo que agregue antes de flipear publico:
- 6 tests para
chunker(cubre casos de input corto, largo, whitespace, overlap). - 2 tests para el pipeline RAG (mockean OpenAI y la busqueda vectorial, verifican estructura de respuesta y caso de retrieval vacio).
- ADR-001.
- Seccion “Architecture decisions” en el README linkeando al ADR.
Coverage subio de 0% a 25% global, con services/chunker.py y services/rag.py en 100%. Hay services/ingest.py, services/embeddings.py y los handlers de API todavia sin cubrir. Esos vienen en commits proximos.
[B-ROLL: terminal con output pytest -v mostrando 8 passed]
No es buen ejemplo de “ship perfecto”. Es buen ejemplo de ship visible, mejorar visible. Un recruiter o un peer puede ver el repo hoy, ver los proximos commits, y leer la trayectoria. No puede leer una trayectoria que existe solo en mi maquina.
La leccion meta
La razon real por la que tenia repos privados con descripciones aspiracionales era simple: scaffold sin ship me hacia sentir que estaba avanzando sin exponerme al juicio de que el repo estuviera “incompleto”.
Es self-deception barato. Te genera la sensacion de portfolio sin el costo del feedback.
La regla que me puse para los proximos 90 dias:
Repo privado con descripcion ambiciosa pero sin codigo = falsa pieza de portfolio. O lo construyo y lo abro, o le pongo “Roadmap-only, no code yet” en la descripcion, o lo borro.
Tres de mis cinco repos privados caen en esa categoria. Decision pendiente sobre que hacer con cada uno.
Que sigue
DocuQuery hoy es alpha funcional. Los proximos commits que tengo apuntados:
- Tests para
services/ingest.py(pipeline de subida + chunk + embed + upsert). - Tests para handlers API.
- ADR-002 sobre por que
gpt-4o-miniy no Claude/Gemini. - ADR-003 sobre estrategia de chunking (512 tokens / 64 overlap, por que no semantic chunking).
- Hybrid search (vector + BM25) detras de feature flag.
Todo eso publico desde el primer commit.
Si te interesa el codigo o tenes feedback, el repo esta en github.com/GeosData/llm-rag-platform. Issues abiertos.