Cinquemila contratti da classificare entro domani mattina e cinquemila contratti da classificare entro cinque secondi sono due problemi diversi, e la differenza non è la scala: è che nel primo caso puoi comprare metà del prezzo rinunciando alla sincronia. Sopra questa scelta si innesta la seconda: quante volte guardare lo stesso documento, e con quante istanze.

Che cosa compri con il Batch e a che cosa rinunci

La Message Batches API applica lo sconto in modo secco — «All usage is charged at 50% of the standard API prices» — su lotti fino a «either 100,000 Message requests or 256 MB in size, whichever is reached first». La finestra temporale ha due numeri diversi che vengono confusi spesso: la maggior parte dei lotti chiude entro un’ora, ma il contratto è «You can access batch results when all messages have completed or after 24 hours, whichever comes first», e «Batches expire if processing does not complete within 24 hours». I risultati restano scaricabili 29 giorni.

Quasi tutto quello che mandi alla Messages API entra in un lotto, incluse vision, tool use, server tool, thinking e conversazioni multi-turno. Le esclusioni sono poche e dicono molto sul modello di esecuzione:

stream: true                       i risultati sono un file, non uno stream
speed  (fast mode)                 regola la latenza sincrona, qui non si applica
store / previous_thread_event_id   i thread sono stateful, il batch no
cache_hint / context_hint          hint di scheduling sincrono
max_tokens: 0                      niente pre-warming della cache dentro un lotto

Niente stream significa niente barra di avanzamento per singolo elemento e niente timeout per elemento: l’unica unità osservabile è il lotto. Chi progetta un’ingestione seria mette una tabella di job accanto, non un array in memoria.

Il custom_id non è un dettaglio di comodo

La documentazione è esplicita: «Batch results can be returned in any order, and may not match the ordering of requests when the batch was created». L’unico aggancio è il campo custom_id, da 1 a 64 caratteri sul pattern seguente.

^[a-zA-Z0-9_-]{1,64}$

Fallo derivare da una chiave stabile del documento, non da un indice di riga: i quattro esiti possibili sono succeeded, errored, canceled ed expired, e per gli ultimi tre «You will not be billed for these requests». Ne segue che rimandare il solo sottoinsieme scaduto è gratis rispetto al tentativo perso, ma è idempotente soltanto se il custom_id è deterministico. E la scadenza non è teorica: «processing may be slowed down based on current demand and your request volume. In that case, you may see more requests expiring after 24 hours».

La cache è la leva vera, ma è best-effort

Quando cinquemila richieste condividono la stessa rubrica da trentamila token, il costo non lo domina il modello: lo domina il prefisso ripetuto. Una lettura dalla cache costa 0,1 volte l’input di base, una scrittura 1,25 volte con TTL di cinque minuti e 2 volte con TTL di un’ora. Da qui il consiglio esplicito: «Because batches can take longer than 5 minutes to process, consider using the 1-hour cache duration with prompt caching for better cache hit rates when processing batches with shared context».

Attenzione a non metterlo a budget come una certezza: «because asynchronous batch requests can be processed concurrently and in any order, cache hits are provided on a best-effort basis». La soglia minima di caching varia per modello, da 512 a 4.096 token, e la gerarchia del prefisso è tools, poi system, poi messages; la regola operativa è mettere il breakpoint «on the last block whose prefix is identical across the requests you want to share a cache». Un timestamp nel prefisso azzera il beneficio senza dare errore.

Multi-istanza e multi-passaggio sono due assi diversi

Il vocabolario di Anthropic separa due varianti della parallelizzazione: «Sectioning: Breaking a task into independent subtasks run in parallel» e «Voting: Running the same task multiple times to get diverse outputs». Il sectioning taglia la latenza e non aggiunge ridondanza; il voting non tocca la latenza, moltiplica il costo per N e in cambio ti dà un segnale di disaccordo. Il multi-passaggio vero e proprio è invece seriale, l’evaluator-optimizer, e compra precisione pagandola in latenza. Sopra c’è l’orchestrator-workers, dove «a central LLM dynamically breaks down tasks, delegates them to worker LLMs, and synthesizes their results».

Il conto va fatto prima: «Multi-agent implementations typically use 3-10x more tokens than single-agent approaches for equivalent tasks». Lo sconto del batch abbassa il prezzo unitario, non il numero di token: le due leve agiscono su assi diversi e vanno stimate separatamente, altrimenti il 50% risparmiato lo rimetti in coordinamento.

L’implementazione di riferimento pubblicata da Anthropic è l’ultrareview di Claude Code, che «launches a fleet of reviewer agents in a remote sandbox». Vale la pena leggere quale delle due proprietà porta il segnale: «every reported finding is independently reproduced and verified, so the results focus on real bugs rather than style suggestions», mentre la flotta più larga serve alla copertura, non alla precisione. Aggiungere revisori senza uno stadio di verifica indipendente produce più falsi positivi, non meno bug. Il prezzo dichiarato — da 5 a 25 dollari a revisione, da 5 a 10 minuti — è il metro con cui giudicare se una revisione a più istanze ha senso sul tuo flusso, e il limite di default (500 file e 8.000 righe modificate) ricorda che anche l’architettura di revisione ha un tetto di scope da rispettare a monte.