Blog
Inteligencia Artificial2024-11-1812 min

RAG en producción: lo que aprendimos construyendo Tuki con pgvector y Claude

Implementar un sistema RAG en producción tiene complejidades que ningún tutorial menciona. Documentamos las decisiones reales que tomamos al construir Tuki.

El problema con los tutoriales de RAG

Cuando empezamos a construir Tuki, los recursos disponibles sobre RAG eran abundantes pero superficiales. Todos mostraban el mismo flujo: cargar un PDF, convertirlo en chunks, generar embeddings, guardarlos en un vector store, y hacer búsqueda por similitud coseno cuando llega una pregunta.

Lo que ningún tutorial mostraba era qué pasa cuando ese sistema entra en producción con un tenant real como AAETAV, con documentación oficial, reglamentos y preguntas de socios llegando en tiempo real.

Por qué elegimos pgvector sobre soluciones especializadas

La primera decisión fue la base de datos vectorial. Las opciones más visibles eran Pinecone, Weaviate y Qdrant. Sin embargo, para un sistema multi-tenant donde ya teníamos PostgreSQL corriendo en Coolify, agregar otro servicio gestionado no tenía sentido operativo.

pgvector es una extensión de PostgreSQL que agrega soporte nativo para vectores. La decisión implicaba cero infraestructura adicional, transacciones ACID en embeddings, y queries híbridas: filtrar por tenant_id y hacer búsqueda vectorial en la misma query SQL.

Voyage AI vs OpenAI Embeddings

OpenAI text-embedding-3-small fue barato con resultados aceptables en inglés, pero en español técnico los resultados eran notablemente peores. Voyage AI voyage-large-2-instruct cambió todo. La precisión en búsquedas semánticas en español subió considerablemente.

Chunking: el detalle que nadie documenta bien

Implementamos chunks por sección lógica con un overlap de 100 tokens entre chunks consecutivos. El overlap es clave: evita que una pregunta que cae exactamente en el límite entre dos chunks no encuentre respuesta.

El prompt de Claude que funcionó

Lo que funcionó fue un prompt que define explícitamente la jerarquía de fuentes: primero los documentos recuperados, después el conocimiento general solo para completar contexto, y nunca inventar información específica del dominio.

Multi-tenancy: el detalle de aislamiento

Implementamos un campo tenant_id en la tabla de embeddings y todas las queries de búsqueda vectorial incluyen un filtro por tenant. Agregamos un test automático que verifica que las búsquedas de un tenant nunca devuelven resultados de otro.