O modelo já pode devolver um lote
OpenAI, Anthropic e Google podem devolver várias chamadas a ferramentas na mesma rodada. São pedidos: o provedor não executa o código do seu aplicativo. Se você espera cada chamada com await dentro de um loop, cada uma aguarda a anterior. Três leituras que dependem da rede podem pagar três esperas seguidas mesmo quando seus argumentos já estão completos.
O lote do modelo também não comprova independência. O app precisa definir quais tools leem, quais modificam dados e quais exigem confirmação de um efeito externo. Essa decisão pertence ao código responsável pelos dados.
Escritas são barreiras
O Gymnasia já declara o efeito de cada tool em um catálogo compartilhado. Oito são leituras: descobrir campos pessoais, ler suas descrições ou valores, consultar medições, refeições e rotinas, e buscar alimentos ou exercícios. Leituras consecutivas podem se sobrepor. Salvar dados pessoais, registrar uma medição, adicionar um alimento e criar uma rotina são escritas locais; propor uma issue é uma escrita externa.
O escalonador permite até quatro leituras ativas. Toda escrita espera as chamadas anteriores terminarem e roda sozinha. Uma tool desconhecida também é uma barreira. A sequência leitura, leitura, escrita, leitura aguarda as duas primeiras, espera a escrita e então inicia a última. Não antecipa uma leitura através de uma escrita.
Ordem de conclusão não é ordem de resultados
Identidades e ocorrências são atribuídas na ordem original antes do início da execução. O resultado da chamada zero volta à posição zero mesmo se terminar por último. Os adaptadores preservam IDs do provedor, histórico, assinaturas de raciocínio e o diário que impede efeitos repetidos durante uma nova tentativa.
Um erro recuperável marca apenas seu próprio resultado e as outras leituras completam. Um erro fatal interrompe o início de novas leituras, aguarda as que já estão ativas e encerra a rodada antes de uma escrita posterior. Se várias falham de forma fatal, retorna a primeira pela posição original. Uma escrita de resultado incerto continua sem nova tentativa.
OpenAI Responses, Anthropic, Google Interactions e o adaptador compatível com OpenAI compartilham o escalonador. Mantemos a possibilidade de o provedor emitir várias chamadas: desativá-la exigiria mais rodadas do modelo para obter as leituras e não substituiria as barreiras do cliente.
Um celular pode sobrepor esperas
Essa mudança não cria threads. JavaScript executa o trabalho síncrono em sua thread e pode iniciar várias operações assíncronas enquanto espera os resultados. Buscar alimentos na memória não distribui o cálculo entre núcleos; esperar fragmentos do catálogo de exercícios oferece uma oportunidade de sobreposição. A documentação de desempenho do React Native explica os limites da thread JavaScript.
Quatro é um limite conservador de tools ativas, ainda sujeito a perfilamento nativo. Não é um limite global de requisições HTTP: uma tool pode baixar várias páginas. Também não comprova um custo específico de memória ou bateria. O benefício depende do cache, do aparelho e da conexão.
A comparação percorre o Coach
Exportamos o mesmo app web Expo antes e depois da mudança. O Coach usa o executor e o catálogo reais. Um provedor falso devolve três buscas: press, curl e sentadilla. Cada download de um fragmento de busca espera 200 ms controlados. O cronômetro começa na entrega da rodada com as chamadas e termina quando o app pede a continuação com os três resultados. Não inclui o tempo de geração de um modelo real.
Depois de um par de aquecimento descartado, medimos 30 pares com cache frio/quente no Chromium 145.0.7632.6, viewport 390 × 844. Frio apaga apenas os fragmentos de busca: o manifesto e as páginas de exercícios permanecem em cache. Quente mantém tudo. A mediana calcula a média dos dois valores centrais; p95 usa a posição superior mais próxima.
| Variante | Frio: mediana / p95 | Quente: mediana / p95 | Downloads frios simultâneos |
|---|---|---|---|
| Em série | 696.00 / 859.86 ms | 57.94 / 160.83 ms | 1 |
| Paralelo | 263.59 / 278.73 ms | 46.72 / 62.91 ms | 3 |
A mediana fria cai aproximadamente 62% nesse cenário. O teste também verifica exercícios não vazios, IDs na ordem correta, resposta visível e ausência de exceções de página. Diferenças com cache quente incluem trabalho local e ruído de execução, então não justificam uma promessa geral de melhoria. São medições controladas na web, não tempos do APK nem da resposta completa de um modelo.
Medir antes de prometer uma melhoria
Testes deterministas verificam limite, barreiras e ordem com promises que terminam fora de ordem. Um relógio falso transforma esperas de 100, 200 e 300 ms em 300 ms concorrentes contra 600 ms em série. Os quatro dialetos cobrem erros recuperáveis e fatais, além de duas escritas reais na mesma data seguidas de uma leitura. Cem sequências geradas procuram violações do limite ou das barreiras.
A referência anterior está compilada como Staging 1.50.4. A implementação foi integrada e a versão web 1.51.0 passou no QA com chamadas reais à OpenAI: três buscas no mesmo turno preservaram a ordem; um erro de leitura não cancelou as outras leituras e o último valor fictício salvo permaneceu após recarregar. O APK de Produção 1.51.0 está compilado, verificado e publicado. A comparação nativa continua pendente. No celular, alterne ambos os APKs no mesmo aparelho com dados e consultas equivalentes. Separe cache frio e quente, registre mediana, p95 e erros, e use traces Android para CPU, memória e frames. Bateria exige sessões longas repetidas, com brilho e conectividade equivalentes.
O método reproduzível e as amostras estão documentados junto ao código. Não adicionamos telemetria de desempenho ao app nem exportamos dados de uma sessão pessoal para obter esses números.
Implementação revisável e testes na PR do Gymnasia. Método reproduzível, amostras e limites da comparação.
Continue com a série
Índice da série sobre como construir um agente. Esta parte completa a discussão do loop: receber um lote, conferir os efeitos e devolver cada resultado com sua identidade.