Passa al contenuto principale

Message Threads — Invia solo il nuovo turno

Avanzato
What you'll learn
  • Dire con precisione cosa fa risparmiare un thread — byte di richiesta e time to first token — e cosa no: i token, e quindi il denaro
  • Eseguire il ciclo di vita a tre stati: create, continue, replay — e costruire il percorso di replay prima del rilascio, non dopo
  • Classificare ogni parametro come create-only, fixed o free e riconoscere a colpo d'occhio un thread_fingerprint_mismatch
  • Mantenere attivo il prompt caching su un thread entro il budget di 4 breakpoint
  • Continuare in sicurezza un turno in streaming e capire perché continuare prima di message_stop restituisce un 404
  • Gestire i cinque errori dei thread, più i due casi in cui devi avviare un thread completamente nuovo invece di recuperare
  • Decidere se il tuo carico di lavoro è di quelli in cui i thread aiutano davvero

Un lungo agent loop paga una tassa silenziosa che con il modello non c'entra nulla. Al turno 40 la tua richiesta è di nuovo l'intera conversazione: ogni file letto, ogni screenshot, ogni risultato di tool. L'API deve riceverla tutta, validarla tutta e tokenizzarla tutta prima che il modello possa emettere anche un solo token. La conversazione ha smesso di essere l'input ed è diventata l'upload.

Un message thread elimina l'upload. Invii la conversazione completa una volta sola. Da quel momento ogni richiesta porta solo i nuovi messaggi più l'id della risposta precedente, e l'API ricostruisce la conversazione dal proprio lato prima di chiamare il modello.

Cosa fa risparmiare davvero un thread​

Leggi questa sezione prima di pianificare una migrazione, perché quasi ogni resoconto di seconda mano su questa funzione la riporta in modo sbagliato.

Watch out
  • I thread NON riducono il conto dei token. Anthropic è esplicita: Claude legge comunque l'intera conversazione a ogni turno, quindi l'uso dei token resta lo stesso. Prezzi e rate limit non cambiano.
  • I numeri spettacolari che potresti aver visto citati — da centinaia di kilobyte a un paio di kilobyte — sono dimensioni del payload della richiesta, non token. Un upload più piccolo è un vero guadagno di latenza. Non è una fattura più leggera.
  • La leva sui costi resta il prompt caching, e un thread non lo attiva al posto tuo. Vedi 'Prompt caching su un thread' più sotto.

Quello che ottieni è latenza e banda. Il dato misurato che Anthropic pubblica: per richieste nella fascia 500k–1M token, il time to first token mediano scende di circa il 35%.

Quindi la regola decisionale onesta:

Il tuo carico di lavoroI thread valgono la pena?
Coding agent lungo — ogni risultato di tool e modifica di file fa crescere la conversazioneSì. È il caso per cui sono stati progettati.
Computer-use agent che carica uno screenshot a ogni turnoSì. Gli screenshot restano nel thread invece di essere ricaricati.
Flusso multi-turno pesante su documenti o immaginiSì. La riduzione di payload maggiore.
Chat con pochi turni breviNo. Non c'è nulla di significativo da eliminare e ti prendi la complessità del replay.
Job batch, conteggio dei token, compaction on demandImpossibile. Vedi Funzioni che i thread non supportano.
Ti serve zero data retention su questo trafficoNo. I thread non sono idonei a ZDR. Vedi Conservazione dei dati.
Thread e sessioni sono funzioni diverse

I message threads vivono sulla Messages API. I session threads sono un concetto separato dentro Claude Managed Agents. Stessa parola, livello diverso — non lasciare che il risultato di una ricerca per l'uno ti porti all'altro.

Il ciclo di vita a tre stati​

Guided walkthrough1 of 3
  1. Invia la prima richiesta esattamente come fai oggi — conversazione completa, system, tools — e aggiungi il parametro thread con type 'create'. L'API memorizza la conversazione più la risposta di Claude sotto l'id della risposta. Invia l'header beta message-threads-2026-08-12 in questa e in ogni richiesta successiva del thread.

Create, poi continue​

curl — create a thread, then send only the new turn

# Turn 1 — full conversation, system, tools, and thread: create
curl https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: message-threads-2026-08-12" \
-H "content-type: application/json" \
-d '{
  "model": "claude-opus-5-5",
  "max_tokens": 1024,
  "cache_control": {"type": "ephemeral"},
  "system": "You are a support agent for Acme. Keep replies short.",
  "messages": [{"role": "user", "content": "Hi! I have a question about an order."}],
  "thread": {"type": "create"}
}'

# Turn 2 — only the new message. No system, no tools.
# message_id is the id from the turn-1 response.
curl https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: message-threads-2026-08-12" \
-H "content-type: application/json" \
-d '{
  "model": "claude-opus-5-5",
  "max_tokens": 1024,
  "cache_control": {"type": "ephemeral"},
  "messages": [{"role": "user", "content": "How long does standard shipping usually take?"}],
  "thread": {"type": "continue", "previous_message_id": "'"$message_id"'"}
}'

Gli SDK ufficiali includono un thread helper che costruisce queste richieste per te — aggiunge l'header beta, esegue create al primo turno, continue dopo, omette system e tools e riesegue il replay una volta su un 404. È la scelta predefinita giusta. La forma grezza qui sopra serve quando usi l'API da un linguaggio senza helper, da un gateway o da curl.

I tre gruppi di campi​

È la tabella da appendere sopra la scrivania. Ogni parametro di una richiesta thread appartiene esattamente a un gruppo, e il gruppo decide se puoi inviarlo, devi inviarlo o puoi cambiarlo.

GruppoCampiRegola
Create-onlysystem, toolsDa inviare solo nella richiesta create, con gli eventuali breakpoint cache_control su di essi. Inviarli in una richiesta continue — anche come null — restituisce un 400.
Fixedmodel, tool_choice, thinking, mcp_servers (tranne il token di autorizzazione di ciascun server), context_management, output_config, le skills dentro container, il cache_control di primo livello e l'insieme dei valori anthropic-betaDa inviare in ogni richiesta, con lo stesso valore della richiesta create. Un valore diverso restituisce 400 thread_fingerprint_mismatch, e il messaggio di errore indica i campi che differiscono.
Freemax_tokens, stop_sequences, stream, metadata, service_tier e tutto ciò che non è nominato sopraSi possono cambiare liberamente da un turno all'altro.

Due eccezioni da ricordare:

  • Gli URL dei server MCP, i token di autorizzazione MCP e l'id del container non vengono memorizzati. Invia mcp_servers e container in ogni richiesta che li usa. Il token di autorizzazione può cambiare tra una richiesta e l'altra.
  • fallbacks è tutto o niente. Se usi il server-side fallback, invia fallbacks in ogni richiesta del thread oppure in nessuna. Aggiungerlo o toglierlo a metà thread è un thread_fingerprint_mismatch. Il suo valore può cambiare.

Per cambiare un campo create-only o fixed non modifichi il thread — ne avvii uno nuovo con una richiesta create che porta i nuovi valori.

Cambiare system o tools può invalidare il thinking preservato

Su Claude Fable 5.1, Mythos 5.1, Opus 5.5, Sonnet 5.5 e Haiku 5.5, avviare un thread nuovo con un system o dei tools diversi rende non validi i blocchi thinking e redacted_thinking già presenti nella conversazione, e la richiesta create può restituire un 400. La soluzione: rimuovi ogni blocco thinking e redacted_thinking dalla conversazione che invii. Il modello risponde allora senza quel ragionamento precedente. Vedi Thinking e effort.

Il percorso di replay non è opzionale​

Watch out
  • La tua applicazione resta la fonte di verità. Tieni una tua copia di ogni messaggio.
  • Nessuna risposta — né create né continue — ti dice se l'API ha memorizzato quel turno. Lo scopri alla richiesta continue SUCCESSIVA, quando funziona oppure restituisce 404.
  • Il thread helper dell'SDK tiene la sua copia in memoria finché l'oggetto helper è in vita, e non la salva altrove. Se la conversazione deve sopravvivere al tuo processo, persistila tu.
  • Costruisci il replay dal primo giorno. Un thread che non è mai scaduto in sviluppo scadrà in produzione alla prima chiamata a un tool lenta.

Un 404 con error.details.error_code impostato a thread_not_found significa "non posso continuare questo", e la risposta deliberatamente non dice perché. Le cause documentate:

  • Il thread è scaduto (5 minuti di inattività).
  • L'API ha servito il turno precedente ma non lo ha memorizzato — per esempio una risposta che termina con una chiamata a un tool troncata da max_tokens.
  • Un modello di fallback ha scritto la risposta precedente.
  • La risposta precedente è ancora in generazione.
  • La richiesta proviene da un'organizzazione, un workspace, un utente o un service account diverso. Un thread creato con una chiave del workspace resta legato a quella chiave.
  • I nuovi messaggi aggiungono qualcosa che la conversazione memorizzata non può accettare — l'esempio classico è la prima immagine in una conversazione che non ne aveva.
Controlla il codice di errore prima di fare il replay

Anche la richiesta di un modello inesistente restituisce 404. Ramifica su error.details.error_code, mai sul solo status.

Per recuperare: invia la conversazione completa dalla tua copia in una nuova richiesta create, con system e tools, poi continua dall'id della nuova risposta. Prima rimuovi i breakpoint cache_control dai messaggi precedenti (vedi il budget di 4 breakpoint più sotto) — l'helper dell'SDK lo fa per te.

Se una create di replay riesce ma la continue immediatamente successiva restituisce di nuovo 404, l'API potrebbe non essere in grado di memorizzare quella conversazione — per esempio perché è molto grande. Smetti di usare thread per quella conversazione e invia richieste normali. L'helper dell'SDK automatizza esattamente questo se lo costruisci con unsupported_request_behavior impostato a continue_unthreaded; senza questa impostazione farà il replay a ogni singolo turno, il che è strettamente peggio che non usare un thread.

Append-only, e una sola continue per id​

Un thread cresce soltanto. Ogni id di risposta può essere continuato esattamente una volta, e continui sempre dall'id più recente. Ne derivano tre conseguenze:

  • Niente modifiche, niente ramificazioni. Per cambiare un messaggio precedente o esplorare un'alternativa, avvia un nuovo thread.
  • Niente riassunto sul posto. In un thread non si possono sostituire i primi turni con un riassunto. Per compattare, scrivi il riassunto nel tuo codice e invialo come normale messaggio in una nuova richiesta create. La compaction on demand non è disponibile con i thread.
  • Ritentare una continue non è idempotente. Se lanci due volte la stessa continue, la seconda riceve thread_already_continued o thread_continue_in_flight.

L'API non deduplica i messaggi che invii in una continue. Se reinvii un messaggio può comparire due volte nella conversazione, oppure dare errore. Una richiesta continue che inizia con la risposta precedente dà errore a priori.

Il caso particolare di pause_turn​

Quando una risposta si ferma con stop_reason: "pause_turn", riprendila con una richiesta continue dall'id di quella risposta e una lista messages vuota:

Resume a paused turn — empty messages, same thread

{
"model": "claude-opus-5-5",
"max_tokens": 1024,
"cache_control": {"type": "ephemeral"},
"messages": [],
"thread": {"type": "continue", "previous_message_id": "msg_01..."}
}

Non inviare un nuovo messaggio utente mentre il turno è in pausa — l'API lo rifiuta. Se la ripresa restituisce 404, rifai il replay dell'intera conversazione inclusa la risposta in pausa in una nuova richiesta create. Con l'helper dell'SDK, chiama send() senza messaggi.

Dopo un rifiuto, ricomincia​

Quando una risposta ha stop_reason: "refusal", non continuare il thread. L'API potrebbe già conservare il turno rifiutato, e una continue non può rimuoverlo. Avvia un nuovo thread con una richiesta create che ometta o riformuli il turno che ha causato il rifiuto. Vedi Rifiuti e sicurezza. L'helper dell'SDK se ne occupa: scarta il turno rifiutato e la sua chiamata successiva avvia un nuovo thread.

Prompt caching su un thread​

Un thread e una cache sono ortogonali. Un thread non mette in cache il tuo prompt al posto tuo — il prompt caching si comporta esattamente come senza thread, e sei tu a decidere dove mettere i breakpoint. Se lo salti, ottieni un thread più veloce da caricare e costoso come prima.

La ricetta semplice: invia un cache_control di primo livello in ogni richiesta del thread. L'API lo applica all'ultimo blocco cacheable dei messages di quella richiesta, così ogni turno legge dalla cache la conversazione precedente e vi scrive il nuovo turno. Ricorda che il cache_control di primo livello è un campo fixed — stesso valore in ogni richiesta, altrimenti ottieni un fingerprint mismatch.

Se posizioni tu i breakpoint sui blocchi, valgono quattro regole:

  1. I breakpoint inviati nella richiesta create continuano a funzionare nei turni successivi se stanno su tools, su system o su un blocco di messaggio che non è l'ultimo. Un breakpoint sull'ultimo blocco di una richiesta scrive la cache in quella richiesta — quindi per mettere in cache ogni nuovo turno, ne invii uno in ogni richiesta.
  2. Non puoi spostare né rimuovere un breakpoint dopo averlo inviato, perché una richiesta continue non può mai reinviare un messaggio precedente.
  3. Il budget è di 4 breakpoint per richiesta thread. Il cache_control di primo livello ne conta uno. I breakpoint su system e tools della richiesta create contano di nuovo a ogni richiesta continue. I breakpoint sui messaggi memorizzati precedenti non contano. Se lo superi ottieni 400 thread_unsupported_request con unsupported: ["cache_control"].
  4. Quando fai il replay, rimuovi i breakpoint dai messaggi precedenti e tienine uno sull'ultimo blocco — altrimenti è il replay stesso a sforare il budget.
Fai il conto una volta

cache_control di primo livello (1) + un breakpoint su system (2) + uno su tools (3) ti lascia esattamente uno spare in ogni richiesta continue. È un budget attorno a cui si può progettare, ma non da usare con leggerezza.

Per la versione cross-provider di questo compromesso, vedi Prompt caching tra provider.

Streaming​

I thread funzionano con lo streaming, con una regola di tempismo facile da sbagliare.

L'id della risposta arriva presto, nell'evento message_start. Non continuare da esso finché non hai ricevuto message_stop. Una continue inviata prima può restituire 404, perché la risposta precedente è ancora in generazione.

Se lo stream di una richiesta continue cade prima di message_stop, reinvia la stessa identica richiesta continue. Se quella restituisce thread_already_continued o thread_continue_in_flight, rifai il replay dell'intera conversazione in una nuova richiesta create. L'ambiguità è intrinseca: la tua connessione può essere caduta prima che il server salvasse la risposta, oppure dopo. Il replay è sempre sicuro; il retry a volte no.

L'helper dell'SDK non ritenta una continue dopo uno stream interrotto. Conserva i blocchi completati e il testo arrivato, e il turno successivo rifà il replay dell'intera conversazione. Per reinviare invece il turno interrotto, chiama send() senza messaggi. E leggi ogni stream fino alla fine — l'helper registra la risposta a message_stop, quindi abbandonare uno stream a metà lascia il thread occupato finché non lo chiudi (esci dal blocco with in Python, abort() in TypeScript, Close/close() altrove).

Errori​

Tutti e cinque vengono restituiti prima che il modello giri: la richiesta non consuma token, e né il thread né la sua scadenza cambiano.

Statuserror_codeSignificatoCosa fare
404thread_not_foundL'API non può continuare il thread.Replay: conversazione completa in una nuova richiesta create.
400thread_fingerprint_mismatchUn campo fixed differisce dal valore memorizzato. Il messaggio dice quale.Mantieni identici i campi fixed, oppure avvia un nuovo thread per cambiarne uno.
400thread_already_continuedQuell'id di risposta è già stato continuato.Continua dall'id più recente. Se il tuo client non ha mai visto quella risposta, fai il replay.
400thread_continue_in_flightUn'altra continue da questo id è ancora in corso — di solito un tuo stesso retry.Lasciala finire e continua dalla sua risposta, oppure fai il replay.
400thread_unsupported_requestLa richiesta usa una funzione o un'impostazione dell'organizzazione che i thread non supportano. error.details.unsupported la nomina quando presente.Rimuovi la funzione, oppure invia la richiesta senza thread.

error.type è invalid_request_error per i 400 e not_found_error per il 404. Alcuni 400 sulle richieste thread non portano alcun error_code — una continue senza previous_message_id, o una che include system o tools.

A thread error body — note where error_code actually lives

{
"type": "error",
"error": {
  "type": "invalid_request_error",
  "message": "thread: a maximum of 4 blocks with cache_control may be provided when `thread` is set. Found 5.",
  "details": {
    "error_code": "thread_unsupported_request",
    "unsupported": ["cache_control"]
  }
},
"request_id": "req_011CSHoEeqs5C35K2UUqR7Fy"
}

I valori documentati di error.details.unsupported sono context_management, cache_control, fallbacks, hipaa e inference_hooks. Anthropic può aggiungerne — quindi tratta un valore non riconosciuto come "invia questa richiesta senza thread" e non come un bug del tuo client. Una richiesta che incappa in più condizioni ne riceve segnalata una sola.

Funzioni che i thread non supportano​

  • Message Batches. Un elemento di batch che contiene thread fallisce con un invalid_request_error nei risultati.
  • Conteggio dei token. Una richiesta di conteggio dei token con thread è un 400. Conta invece la conversazione completa, con system e tools. Vedi Token e prezzi.
  • Compaction. La compaction on demand non è disponibile con i thread.
  • Context editing — con una sola eccezione: clear_thinking_20251015 con keep: "all".
  • Organizzazioni con la HIPAA readiness attiva.
  • Organizzazioni con gli Inference hooks attivi. Da segnalare a chi gestisce la tua organizzazione: un'impostazione a livello di piattaforma abilitata da qualcun altro può rendere i thread inutilizzabili per tutto il tuo team.

Altri due limiti rigidi: non esiste alcuna API per elencare, leggere o eliminare un thread, e i thread non sono disponibili su Amazon Bedrock.

Conservazione dei dati​

I message threads non sono idonei a ZDR. Usarli non influisce sull'uso delle altre funzioni idonee a ZDR — l'ambito è questo traffico, non il tuo account.

Cosa contiene il thread: la conversazione inviata al modello, system e tools della richiesta create e un fingerprint dei campi fixed. È tenuto in memoria, cifrato con una chiave gestita dal server, mai scritto su storage durevole, ed eliminato al massimo 1 ora dopo l'ultimo utilizzo del thread. I file sono tenuti per riferimento, quindi ogni file usato dalla conversazione deve restare disponibile finché usi il thread.

Se la tua postura di compliance richiede ZDR su un carico di lavoro, quel carico non può usare i thread. Vedi Privacy.

Errori comuni​

ErroreSintomoSoluzione
Aspettarsi una fattura più bassaFattura invariataI thread tagliano byte e TTFT, non token. Aggiungi il prompt caching per i costi.
Reinviare system in una continue400, senza error_codeÈ create-only. Omettilo — anche null fallisce.
Ritoccare thinking o tool_choice a metà threadthread_fingerprint_mismatchCampi fixed. Avvia un nuovo thread per cambiarne uno.
Considerare la risposta una prova di memorizzazione404 a sorpresa più tardiNulla conferma la memorizzazione. Costruisci il replay.
Continuare da message_start404 intermittenti sotto caricoAspetta message_stop.
Retry alla cieca di una continue interrottathread_already_continuedRiprova una volta, poi fai il replay. Il replay è sempre sicuro.
Continuare dopo un rifiutoConversazione compromessaAvvia un nuovo thread senza quel turno.
Breakpoint su system + tools + primo livello + per turnothread_unsupported_requestIl budget è 4, e i breakpoint di system/tools si ricontano a ogni turno.
Inviare un nuovo messaggio utente durante pause_turnRifiutatoContinua con "messages": [].
Helper che fa il replay a ogni singolo turnoLatenza peggiore che senza threadImposta unsupported_request_behavior a continue_unthreaded.

Check yourself

0/4
  1. Sposti un coding agent da 40 turni sui message threads e i payload delle richieste passano da ~600 KB a ~2 KB. Cosa succede alla tua fattura API mensile?
  2. Al turno 12 di un thread arriva un 404 con error_code thread_not_found. Qual è il recupero corretto?
  3. La tua richiesta create mette un breakpoint cache_control su system, uno su tools e invia un cache_control di primo livello. Quanti breakpoint restano disponibili in ogni richiesta continue?
  4. Quale di queste cose puoi fare su un thread esistente?
Key takeaways
  • Un thread scambia upload con stato: invii la conversazione una volta, poi solo i nuovi messaggi più previous_message_id. Header beta message-threads-2026-08-12 in ogni richiesta.
  • Compra byte e latenza — circa il 35% di TTFT mediano in meno su richieste da 500k–1M token — e nient'altro. Uso dei token, prezzi e rate limit non cambiano.
  • Classifica ogni parametro: system e tools sono create-only; model, thinking, tool_choice, context_management, output_config e il cache_control di primo livello sono fixed; max_tokens e simili sono free.
  • La tua app è la fonte di verità. Nessuna risposta conferma la memorizzazione, un thread scade dopo 5 minuti di inattività e l'unico recupero è rifare il replay dell'intera conversazione in una nuova richiesta create.
  • Append-only, una continue per id di risposta: niente modifiche, niente ramificazioni, niente compaction sul posto. Cambiare qualcosa di fixed significa un nuovo thread.
  • Il caching resta compito tuo. Il cache_control di primo livello in ogni richiesta è la ricetta semplice, dentro un budget rigido di 4 breakpoint in cui system e tools si ricontano a ogni turno.
  • Streaming: continua solo dopo message_stop. Ritenta una continue interrotta una volta; poi fai il replay — il replay è sempre sicuro.
  • Non per batch, conteggio dei token, compaction, la maggior parte del context editing, organizzazioni con HIPAA attivo, organizzazioni con inference hooks attivi, Amazon Bedrock, o qualunque carico di lavoro che richieda ZDR.

Fonti e approfondimenti​

Avanti​