Gymnasia: arquitectura de un agente de IA que corre en el móvil

Gymnasia: arquitectura de un agente de IA que corre en el móvil

Qué es Gymnasialink image

Gymnasia es una aplicación móvil de entrenamiento personal hecha con React Native y Expo. Dentro lleva dos agentes de IA: un coach conversacional y un subagente de visión que estima calorías y macronutrientes a partir de fotos de comida.

Lo interesante, y de lo que va este post, es que los dos se ejecutan enteros en el dispositivo. No hay backend propio: la orquestación, las herramientas y el almacenamiento viven en App.tsx y en AsyncStorage, y las claves de API las pone el usuario. Los datos del usuario nunca salen del móvil salvo por una única excepción que se explica más abajo.

Este es el post principal de la serie. Debajo están los dos grafos de arquitectura, y al final el índice de la serie con los posts que van saliendo sobre cada parte del desarrollo.

El agente general: Gymnasia Coachlink image

Nodos = pasos y decisiones, líneas = paso de datos. El ciclo discontinuo claro es el bucle agentic de uso de herramientas; la línea roja discontinua es la única llamada que sale a un servicio externo distinto del proveedor LLM.
no create_feature_issue reintenta con tool_result
Usuario (chat)
System promptGitHub AGENTS.md + caché local
Proveedor activoBYOK · 1 de 3 (is_active)
Llamada al modelostreaming SSE/XHR
¿Pide usar una herramienta?
handleToolCall()12 herramientas, 100% local
Respuesta finalal usuario, en streaming
AsyncStoragedieta · rutinas · medidas · memoria
GitHub Issues APIúnico destino externo

Desliza el grafo en horizontal para verlo entero.

Flujo normal Bucle agentic (tool result → modelo) Llamada externa (no LLM)

Gymnasia Coach es el agente conversacional general de la app, el de la pestaña Chat IA. Es un agente BYOK (Bring Your Own Key): el usuario configura su propia clave de API de OpenAI, Anthropic o Google, y la app llama directamente al proveedor elegido desde el dispositivo. No existe backend propio: toda la lógica de orquestación, las herramientas y el almacenamiento viven en App.tsx y en AsyncStorage local.

Configuración y proveedor activolink image

Ajustes BYOK

El usuario guarda su API key

Una de las 3 claves (OpenAI / Anthropic / Google) se marca como is_active. Solo esa se usa para el chat general.

SecureStore / AsyncStorage
System prompt remoto

prompts/AGENTS.md en GitHub

Se descarga desde raw.githubusercontent.com/maximofn/gymnasia, se cachea localmente y cae a un prompt por defecto embebido si falla la red.

Editable sin publicar app

Llamada al proveedorlink image

OpenAIResponses API · streaming SSE/XHR
AnthropicMessages API · thinking habilitado
GoogleGemini generateContent

Cada proveedor tiene su propio adaptador de request/response, porque los formatos de tools, de streaming y de bloques de contenido son distintos en los tres. Pero comparten el mismo bucle agentic que viene ahora.

Bucle agentic (tool use)link image

Paso 1

Modelo responde

Texto en streaming y/o bloques tool_use / function_call.

Paso 2

handleToolCall()

Se ejecuta 100% en local contra el estado de la app (AsyncStorage, repos JSON).

Paso 3

Resultado → modelo

Se reinyecta como tool_result / function_call_output. Se repite hasta que no pide más herramientas.

Las 12 herramientaslink image

🧠 Memoria personal
list_personal_data_keysLista las claves de datos personales guardadas del usuario.
read_field_descriptionLee la descripción de un campo antes de interpretarlo.
read_field_valueLee el valor de un campo concreto (por ejemplo nombre u objetivo).
save_personal_dataGuarda o actualiza un array de campos {key, description, value}.
🍽️ Dieta
search_foodsBusca en el repositorio JSON de alimentos por nombre, categoría o rango de macros.
read_meal_foodsLee los alimentos ya registrados en una comida y fecha.
add_meal_foodAñade un alimento (gramos + macros) a una comida y fecha concretas.
🏋️ Entrenamiento
search_exercisesBusca ejercicios por músculo, equipamiento o dificultad en el repo local.
read_routinesLee las rutinas (templates) ya creadas por el usuario.
create_routineCrea una rutina nueva y vincula imágenes de ejercicio por nombre exacto del repo.
📏 Medidas y meta
read_measurement / write_measurementLee o guarda medidas corporales (peso, % de grasa, perímetros) por fecha.
create_feature_issueLa única herramienta que sale del dispositivo: crea un issue en GitHub (maximofn/gymnasia) cuando detecta una petición de mejora.

Almacenamiento y salidalink image

Sin base de datos

LocalStore (AsyncStorage)

Dieta, rutinas, medidas y datos personales viven en el dispositivo. Los repos de alimentos y ejercicios son JSON estáticos empaquetados con la app.

Excepción

GitHub Issues API

Único punto de salida a un servicio externo distinto del proveedor LLM: create_feature_issue.

Externo
Caveat de plataforma: en navegador, Anthropic necesita un proxy CORS local (apps/anthropic_proxy/cors-proxy.py) porque el navegador bloquea la llamada directa a api.anthropic.com. OpenAI y Google funcionan directo desde el navegador.

El subagente de visión: Food Estimatorlink image

Nodos = pasos y decisiones, líneas = paso de datos. El ciclo discontinuo claro es el bucle agentic de uso de herramientas; la línea discontinua oscura es la ruta alternativa sin foto.
no reintenta con tool_result alternativa sin foto
Usuario1–6 fotos + texto opcional
System promptFood Estimator (visión)
Selección por prioridadGoogle → OpenAI → Anthropic
Modelo analiza imágenes
¿Detecta código de barras?
scan_barcode()→ OpenFoodFacts API
Estimacióntexto libre o JSON si se pide
Confirmación usuariorequestStructuredNutritionJSON
add_meal_food()→ Dieta local (AsyncStorage)
MiniChat manualFOOD_AI_SYSTEM_PROMPT, sin fotos

Desliza el grafo en horizontal para verlo entero.

Flujo normal Bucle agentic (tool result → modelo) Ruta alternativa

El Food Estimator es el subagente de visión que estima calorías y macros a partir de fotos de comida (pestaña Dieta → Estimación IA). Es independiente del chat general: tiene su propio system prompt, su propia herramienta y su propia política de selección de proveedor.

Entradalink image

Input del usuario

1–6 fotos de la comida

Cámara o galería. También admite texto (preguntas de seguimiento sobre la estimación) reutilizando el contexto de la conversación.

Selección de proveedor por prioridadlink image

A diferencia del chat general, aquí no se usa el proveedor activo: se prueba en orden hasta encontrar el primero con clave de API configurada.

1GoogleGemini · visión
2OpenAIResponses API · visión
3AnthropicNo admite imágenes en web

System prompt especializadolink image

Nutricionista visual

Estima siempre kcal, proteína (g), carbohidratos (g), grasa (g) y peso total (g). Da rangos si hay incertidumbre.

Clasificación

Determina si es producto_comercial, receta o alimento base genérico.

Salida estructurada

Si el usuario pide "Devuelve json", responde solo con JSON: dish_name, calories_kcal, protein_g, carbs_g, fat_g.

Bucle agentic con el código de barraslink image

Detección

¿Hay un código de barras en la foto?

El prompt obliga al modelo a usar la herramienta si detecta un EAN/UPC en cualquiera de las imágenes.

Única tool

scan_barcode(barcode)

Llama a OpenFoodFacts (API pública) con el código leído y devuelve datos nutricionales exactos del producto.

Externo · world.openfoodfacts.org

Producto comercial confirmado

Si se usó scan_barcode, la clasificación es siempre producto_comercial, con datos exactos en vez de estimados.

Igual que en el agente general, el resultado de la herramienta se reinyecta al modelo y el bucle se repite, con un máximo de 5 rondas, hasta obtener una respuesta final.

Persistencia del resultadolink image

requestStructuredNutritionJSON

Confirmación del usuario

Cuando el usuario acepta la estimación, se pide el bloque JSON final y se parsea.

add_meal_food

Se añade a la dieta local del día y comida seleccionados, en el mismo store que usa el agente general.

Variante: estimación manuallink image

MiniChat · FOOD_AI_SYSTEM_PROMPT

El usuario describe un alimento por texto

Flujo conversacional: el usuario nombra el alimento, el modelo pregunta ingredientes y cantidades si faltan, calcula valores por 100 g o unidad, el usuario confirma y devuelve el JSON para guardar en el repo de alimentos.

Sin herramientas · sin imágenes
Diseño clave: el Food Estimator prioriza el proveedor con mejor relación coste/calidad en visión (Google primero) en lugar de usar el proveedor activo del usuario, porque la estimación de fotos es la operación más frecuente y más sensible a coste de la app.

La serielink image

Este post es la portada. Cada parte del desarrollo del agente se cuenta en un post propio, y todos se van enlazando aquí.

  • Todavía no hay posts publicados en la serie. Los primeros serán el de planificación y el de tool calling.

Mientras tanto, la ficha del proyecto está en maximofn.com/gymnasia y el código es abierto, en GitHub.

Seguir leyendo

Últimos posts -->

¿Has visto estos proyectos?

Gymnasia

Gymnasia Gymnasia
Expo
React Native
TypeScript
OpenAI
Anthropic

App de fitness con dos agentes que se ejecutan íntegramente en el dispositivo, sin backend, de forma que los datos del usuario nunca salen del móvil. Un coach conversacional BYOK con adaptadores para OpenAI, Anthropic y Google, 12 tools locales y system prompt remoto con fallback offline, y un subagente de visión que estima macronutrientes a partir de fotos de comida, con lectura de códigos de barras contra OpenFoodFacts.

LangGraph Deep Researcher

LangGraph Deep Researcher LangGraph Deep Researcher
Python
LangGraph
FastAPI
React
TypeScript
Docker

Sistema multiagente de investigación construido con LangGraph. Un supervisor descompone tu pregunta en temas y lanza subagentes de búsqueda en paralelo; cada uno comprime sus hallazgos antes de pasarlos a un agente redactor que escribe el informe final en markdown con sus fuentes. Streaming en vivo por WebSockets, modelo configurable por rol y claves de API propias que nunca se guardan en el servidor.

Tau

Tau Tau
Python
LangChain

Sistema multiagente de tutoría para estudiantes de secundaria, con un agente por asignatura y material de curso elaborado y validado por un equipo de profesores. Llegó a usarse con alumnos reales en un colegio privado en España y en un instituto en Colombia.

Ver todos los proyectos -->
>_ Disponible para proyectos

¿Tienes un proyecto con IA?

Hablemos.

maximofn@gmail.com

Especialista en Machine Learning e Inteligencia Artificial. Desarrollo soluciones con IA generativa, agentes inteligentes y modelos personalizados.

¿Quieres ver alguna charla?

Últimas charlas -->

¿Quieres mejorar con estos tips?

Últimos tips -->

Usa esto en local

Los espacios de Hugging Face nos permite ejecutar modelos con demos muy sencillas, pero ¿qué pasa si la demo se rompe? O si el usuario la elimina? Por ello he creado contenedores docker con algunos espacios interesantes, para poder usarlos de manera local, pase lo que pase. De hecho, es posible que si pinchas en alún botón de ver proyecto te lleve a un espacio que no funciona.

Flow edit

Flow edit Flow edit

Edita imágenes con este modelo de Flow. Basándose en SD3 o FLUX puedes editar cualquier imagen y generar nuevas

FLUX.1-RealismLora

FLUX.1-RealismLora FLUX.1-RealismLora
Ver todos los contenedores -->
>_ Disponible para proyectos

¿Tienes un proyecto con IA?

Hablemos.

maximofn@gmail.com

Especialista en Machine Learning e Inteligencia Artificial. Desarrollo soluciones con IA generativa, agentes inteligentes y modelos personalizados.

¿Quieres entrenar tu modelo con estos datasets?

short-jokes-dataset

HuggingFace

Dataset de chistes en inglés

Uso: Fine-tuning de modelos de generación de texto humorístico

231K filas 2 columnas 45 MB
Ver en HuggingFace →

opus100

HuggingFace

Dataset con traducciones de inglés a español

Uso: Entrenamiento de modelos de traducción inglés-español

1M filas 2 columnas 210 MB
Ver en HuggingFace →

netflix_titles

HuggingFace

Dataset con películas y series de Netflix

Uso: Análisis de catálogo de Netflix y sistemas de recomendación

8.8K filas 12 columnas 3.5 MB
Ver en HuggingFace →
Ver más datasets -->