GimnasiaArquitectura del Agente IA — Documentación técnica
100% local-first BYOK · sin backend apps/mobile/App.tsx
Grafo de funcionamiento
Nodos = pasos/decisiones, líneas = paso de datos. El ciclo lima discontinuo 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.
Flujo normal Bucle agentic (tool result → modelo) Llamada externa (no LLM)
Gymnasia Coach es el agente conversacional general de la app (pestaña Chat IA). Es un
agente BYOK (Bring Your Own Key): el usuario configura su propia API key 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, herramientas y almacenamiento vive en App.tsx
y en AsyncStorage local.
1 · Configuración y proveedor activo
Ajustes BYOK
El usuario guarda su API key
Una de las 3 keys (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
↓
2 · Llamada al proveedor (uno activo)
OpenAI
Responses API · streaming SSE/XHR
Anthropic
Messages API · thinking habilitado
Google
Gemini generateContent
Cada proveedor tiene su propio adaptador de request/response (formatos de tools, streaming y bloques de contenido distintos), pero comparten el mismo bucle agentic descrito abajo.
↓
3 · Bucle agentic (tool use)
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 tools.
↓
4 · Herramientas disponibles (12)
🧠 Memoria personal
list_personal_data_keys
Lista las claves de datos personales guardadas del usuario.
read_field_description
Lee la descripción de un campo antes de interpretarlo.
read_field_value
Lee el valor de un campo concreto (ej. nombre, objetivo).
save_personal_data
Guarda/actualiza un array de campos {key, description, value}.
🍽️ Dieta
search_foods
Busca en el repositorio JSON de alimentos por nombre, categoría o rango de macros.
read_meal_foods
Lee los alimentos ya registrados en una comida/fecha.
add_meal_food
Añade un alimento (gramos + macros) a una comida y fecha concretas.
🏋️ Entrenamiento
search_exercises
Busca ejercicios por músculo, equipamiento o dificultad en el repo local.
read_routines
Lee las rutinas (templates) ya creadas por el usuario.
create_routine
Crea una rutina nueva; vincula imágenes de ejercicio por nombre exacto del repo.
📏 Medidas & meta
read_measurement / write_measurement
Lee o guarda medidas corporales (peso, % grasa, perímetros) por fecha.
create_feature_issue
Única herramienta que sale del dispositivo: crea un issue en GitHub (maximofn/gymnasia) cuando detecta una petición de mejora.
5 · Almacenamiento y salida
Sin base de datos
LocalStore (AsyncStorage)
Dieta, rutinas, medidas y datos personales viven en el dispositivo. Los repos de alimentos/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 (web), Anthropic requiere 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.
Grafo de funcionamiento
Nodos = pasos/decisiones, líneas = paso de datos. El ciclo lima discontinuo es el bucle
agentic de uso de herramientas; la línea gris discontinua es la ruta alternativa sin foto.
Flujo normal Bucle agentic (tool result → modelo) Ruta alternativa
El Food Estimator es el sub-agente 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.
1 · Entrada
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.
↓
2 · Selección de proveedor (por prioridad, no por proveedor activo)
A diferencia del chat general, aquí se prueba en orden hasta encontrar el primero con API key configurada:
1
Google
Gemini · visión
2
OpenAI
Responses API · visión
3
Anthropic
No admite imágenes en Web
↓
3 · System prompt especializado
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.
↓
4 · Bucle agentic con herramienta de código de barras
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 tool se reinyecta al modelo y el bucle se repite (máx. 5 rondas) hasta obtener una respuesta final.
↓
5 · Persistencia del resultado
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/comida seleccionados (mismo store que usa el agente general).
Variante: estimación manual (sin foto)
MiniChat · FOOD_AI_SYSTEM_PROMPT
El usuario describe un alimento/receta por texto
Flujo conversacional: 1) el usuario nombra el alimento, 2) el modelo pregunta ingredientes/cantidades si faltan, 3) calcula valores por 100g/unidad, 4) el usuario confirma, 5) devuelve 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 sensible a coste de la app.