Saltar al contenido
← Proyectos

Planfit

SaaS multi-tenant para entrenadores personales, con un agente de IA que lee y escribe en la base de datos de producción bajo supervisión.

Desarrollo activo 2023-05 → hoy
  • Angular 18 / Ionic
  • Django 4.2 / DRF
  • PostgreSQL + pgvector
  • DeepSeek
  • FastEmbed / ONNX
  • Raspberry Pi 5
Líneas de código
~31.000
Modelos de datos
25
Tools del agente IA
18
En desarrollo desde
mayo 2023

Qué es

Planfit es un SaaS multi-tenant para entrenadores personales: un panel de coach con gestión de cartera de clientes, rutinas, check-ins, nutrición y mensajería, más una app cliente (Ionic/Capacitor) para que cada alumno siga su plan. Corre en producción sobre una Raspberry Pi 5, con Render como entorno secundario.

Es mi proyecto personal más longevo: el primer commit de la app es de mayo de 2023. Durante dos años fue un CRUD de entrenamientos correcto pero convencional. En julio de 2026 se convirtió en otra cosa: un sistema con un agente de IA que puede leer y modificar la base de datos de producción — con las garantías que eso exige.

Arquitectura y decisiones técnicas

Multi-tenancy sin framework de tenancy. Todo el aislamiento entre coach y sus clientes se apoya en un único User.coach (self-FK) y un mixin de queryset. La invariante es dura y está testeada: acceder a un recurso fuera de tu scope siempre devuelve 404, nunca 403 — no se confirma ni la existencia del recurso ajeno.

El agente de IA tiene 18 tools de function-calling contra DeepSeek: lectura (perfil, historial, plan), escritura (crear/reprogramar/borrar entrenamientos) y analítica (resumen semanal, detección de estancamiento, adherencia). La frontera de seguridad no vive en el esquema de las tools — vive en el executor: ToolExecutor(user) resuelve la identidad del usuario en su constructor, nunca a partir de lo que decide el LLM, y cada escritura reverifica propiedad antes de tocar la base de datos.

Toda escritura del agente queda auditada. Cada acción genera un AgentAction que se ejecuta ya o queda pendiente de aprobación del coach, según el nivel de autonomía configurado por agente (no un interruptor global). El panel /panel/acciones/ muestra el historial completo, incluidas las ejecutadas en modo autónomo — la autonomía no significa perder la trazabilidad.

RAG híbrido corriendo en una Raspberry Pi. La memoria del agente combina búsqueda léxica y semántica fusionadas con Reciprocal Rank Fusion (RRF), con una capa de abstracción dual: SQLite FTS5 + sqlite-vec en desarrollo, PostgreSQL tsvector + pgvector en producción. Los embeddings se generan localmente con FastEmbed/ONNX (sin PyTorch, sin API externa) porque el proveedor de LLM (DeepSeek) no ofrece endpoint de embeddings y el hardware de producción es una Raspberry Pi.

Problemas con historia

La memoria que se recuperaba a sí misma. El system prompt del agente se construía después de guardar el mensaje del usuario en base de datos — y el post_save de ese mensaje lo indexaba al instante en la memoria RAG. El resultado: la búsqueda de contexto encontraba la propia pregunta que se estaba respondiendo, y memory_used salía en True casi siempre. Se arregló invirtiendo el orden: primero construir el contexto, luego persistir el mensaje.

El ¿? que rompía la búsqueda en silencio. La sintaxis MATCH de SQLite FTS5 no tolera signos de interrogación de apertura españoles y fallaba sin lanzar un error visible — la memoria del agente simplemente se quedaba muda para cualquier pregunta bien escrita en español. La solución no fue enumerar operadores especiales uno a uno, sino invertir la regla: todo lo que no sea un carácter de palabra se trata como separador.

Bugs de seguridad reales, encontrados y cerrados con test en rojo→verde, entre ellos: escalada de privilegios vía PATCH /users/<id>/ {"role":"admin"}, escrituras cross-tenant a través de claves foráneas editables, y una condición de carrera en la reserva de plazas de clases grupales resuelta con select_for_update.

Estado actual

En producción activa (Raspberry Pi + Render como respaldo), con desarrollo continuo. 616+ tests en el backend. El generador de rutinas por reglas (sin LLM) convive con el agente conversacional — son dos sistemas distintos y se presentan como tal.