El lote ya puede venir del modelo
OpenAI, Anthropic y Google pueden devolver varias llamadas a herramientas en un mismo turno. Son solicitudes: el proveedor no ejecuta el código de tu aplicación. Si recorres las llamadas con un await dentro de un bucle, cada una espera a la anterior. Tres lecturas que dependen de la red pueden pagar tres esperas seguidas aunque sus argumentos ya estén completos.
El lote del modelo tampoco demuestra independencia. La app necesita sus propias reglas: qué tool lee, cuál modifica datos y cuál requiere confirmar un efecto externo. Esa decisión pertenece al código que administra los datos.
Las escrituras forman barreras
Gymnasia ya declara el efecto de cada tool en un catálogo común. Ocho son lecturas: descubrir campos personales, leer sus descripciones o valores, consultar mediciones, comidas y rutinas, y buscar alimentos o ejercicios. Las lecturas consecutivas pueden solaparse. Guardar datos personales, registrar una medición, añadir un alimento y crear una rutina son escrituras locales; proponer una issue es una escritura externa.
El planificador permite hasta cuatro lecturas activas. Toda escritura espera a que terminen las anteriores y se ejecuta sola. Una tool desconocida también forma una barrera. La secuencia lectura, lectura, escritura, lectura drena las dos primeras, espera la escritura y después comienza la última. No adelanta una lectura a través de una escritura.
Terminar tercero no significa devolver tercero
Antes de empezar se asignan las identidades y las ocurrencias de cada llamada en el orden original. El resultado de la llamada de índice cero vuelve a la posición cero aunque termine al final. Los adaptadores conservan los IDs del proveedor, el historial, las firmas de pensamiento y el diario que evita repetir efectos durante un reintento.
Un error recuperable se marca solo en su resultado y las otras lecturas completan. Un fallo fatal detiene el lanzamiento de nuevas lecturas, espera las que ya están activas y termina el turno antes de una escritura posterior. Si varias fallan fatalmente, se devuelve la primera por posición original. Una escritura de resultado incierto sigue sin reintentarse.
OpenAI Responses, Anthropic, Google Interactions y el adaptador compatible con OpenAI comparten este planificador. Conservamos la posibilidad de recibir varias llamadas del proveedor: prohibirla exigiría más rondas para obtener las lecturas y no sustituiría las barreras del cliente.
Un móvil puede solapar esperas
Este cambio no crea threads. JavaScript ejecuta el trabajo síncrono en su hilo y puede iniciar varias operaciones asíncronas mientras espera sus resultados. Buscar alimentos en memoria no reparte el cálculo entre núcleos; esperar fragmentos del catálogo de ejercicios sí ofrece una oportunidad de solapamiento. La documentación de rendimiento de React Native explica los límites del hilo JavaScript.
Cuatro es un límite conservador de tools activas, pendiente de perfilado nativo. No es un límite global de peticiones HTTP: una tool puede descargar varias páginas. Tampoco demuestra un consumo concreto de memoria o batería. La ganancia dependerá de la caché, el teléfono y la conexión.
La comparación se hizo sobre Coach
Exportamos la misma app web de Expo antes y después del cambio. Coach usa su ejecutor y el catálogo real. Un proveedor falso entrega tres búsquedas: press, curl y sentadilla. Cada descarga de un fragmento de búsqueda espera 200 ms controlados. El cronómetro empieza al entregar el turno con las llamadas y termina cuando la app pide la continuación con los tres resultados. No incluye tiempo de generación de un modelo real.
Tras una pareja de calentamiento descartada, medimos 30 parejas con caché fría y caliente en Chromium 145.0.7632.6, viewport 390 × 844. Fría significa borrar solo los fragmentos de búsqueda: el manifiesto y las páginas de ejercicios permanecen en caché. Caliente conserva todo. La mediana promedia los dos valores centrales; p95 usa el rango más próximo superior.
| Variante | Fría: mediana / p95 | Caliente: mediana / p95 | Descargas frías simultáneas |
|---|---|---|---|
| Serie | 696.00 / 859.86 ms | 57.94 / 160.83 ms | 1 |
| Paralelo | 263.59 / 278.73 ms | 46.72 / 62.91 ms | 3 |
La mediana fría baja aproximadamente un 62 % en este escenario. La prueba también comprueba ejercicios no vacíos, IDs en orden, respuesta visible y ausencia de excepciones de página. Las diferencias calientes contienen trabajo local y ruido: no permiten prometer esa mejora en otras condiciones. Estos resultados son una comparación controlada en web, no una medición del APK ni de la respuesta completa de un modelo.
Qué medir antes de prometer una mejora
Los tests deterministas verifican límite, barreras y orden con promesas que terminan fuera de orden. Un reloj falso convierte esperas de 100, 200 y 300 ms en 300 ms concurrentes frente a 600 ms en serie. Los cuatro dialectos se prueban con errores recuperables y fatales, y con dos escrituras reales sobre la misma fecha seguidas de una lectura. Cien secuencias generadas buscan violaciones del límite o de las barreras.
La referencia anterior está compilada como Staging 1.50.4. La implementación se fusionó y la web 1.51.0 pasó QA con OpenAI real: tres búsquedas en un turno conservaron su orden; un error de lectura no canceló las otras lecturas y el último dato ficticio guardado permaneció después de recargar. El APK de Producción 1.51.0 está compilado, verificado y publicado. La comparación nativa sigue pendiente. Para medirla en el teléfono, alterna ambos APK en el mismo dispositivo, con datos y consultas equivalentes. Separa frío y caliente, registra mediana, p95 y errores, y usa trazas de Android para CPU, memoria y frames. La batería requiere sesiones largas repetidas con brillo y conexión equivalentes.
El método reproducible y las muestras se documentan junto al código. No se añade telemetría de rendimiento a la app ni se exportan datos de una sesión personal para obtener estas cifras.
Implementación revisable y pruebas en la PR de Gymnasia. Método reproducible, muestras y límites de la comparación.
Continúa con la serie
Índice de la serie sobre cómo construir un agente. Esta entrega completa la explicación del bucle: recibir un lote, comprobar sus efectos y devolver cada resultado con su identidad.