Passa al contenuto principale

Compaction della conversazione — riassumi la cronologia lato server e vai avanti

Avanzato
What you'll learn
  • Distinguere i quattro modi di ridurre il contesto — context editing, compaction on-demand, keep-tail, in background e a soglia — e scegliere quello giusto
  • Inviare una richiesta di compaction e leggere il blocco di compaction firmato che torna al posto di una risposta
  • Fare lo scambio correttamente: blocco per primo, messaggi riassunti rimossi, header beta su ogni richiesta successiva
  • Riconoscere i due errori di scambio che restituiscono HTTP 200 e corrompono la conversazione in silenzio
  • Gestire il caso in cui non torna nessun riassunto — cinque stop reason, ognuna con una correzione diversa
  • Contare quanto costa davvero la compaction (usage.iterations, non i campi token di primo livello)
  • Sapere cosa un riassunto non può mai portarsi dietro: immagini, documenti, upload, URL recuperati e istruzioni di sistema precedenti

Ogni agente a lunga esecuzione prima o poi sbatte contro lo stesso muro: la conversazione cresce fino al punto in cui la richiesta successiva non ci sta più. Hai due opzioni oneste — buttare via il contesto vecchio, oppure comprimerlo. La compaction è l'opzione della compressione, fatta dal modello, lato server: invii la conversazione con un parametro in più e, invece di una risposta, ottieni un blocco di riassunto firmato che rimandi indietro al posto dei messaggi che sostituisce.

La firma è la parte interessante. Il blocco non è una stringa che hai scritto e iniettato tu — è contenuto prodotto dall'API, che l'API verificherà. Modifica un solo carattere del testo del riassunto e la richiesta successiva fallisce con compaction_signature_invalid.

Quale strumento di riduzione è quale​

Qui si sovrappongono quattro funzionalità, e i team le confondono continuamente. Fanno lavori diversi:

FunzionalitàChi decideCosa faDove finisce il blocco
Context editing (clear_tool_uses_20250919)Tu, in modo dichiarativoCancella del tutto i vecchi risultati degli strumenti — nessun riassuntoNessun blocco; vedi Memoria e context editing
Compaction on-demand (compaction)Tu, per singola richiestaRiassume i messaggi che invii, in una chiamata separata che non restituisce nessuna rispostaMetti il blocco per primo e cancelli ciò che ha sostituito
Compaction keep-tailTu, scegliendo un punto di taglioCome l'on-demand, ma gli ultimi turni restano parola per parolaBlocco per primo, i turni conservati dopo di esso
Compaction in backgroundTu, in modo asincronoCome l'on-demand, ma la conversazione continua mentre il riassunto viene scrittoIl blocco sostituisce esattamente i messaggi che hai inviato; i turni successivi restano
Compaction a soglia (compact_20260112)L'API, a un trigger di tokenRiassume il contesto più vecchio a metà di una richiesta ordinariaIl blocco segue i messaggi riassunti e l'API li elimina per te

Due regole che vale la pena memorizzare prima di scrivere qualsiasi riga di codice:

  • Il context editing cancella, la compaction riassume. Per esecuzioni molto lunghe puoi usarli a strati — ma non nella stessa richiesta che contiene compaction (vedi Limiti).
  • La compaction on-demand e quella a soglia rimandano indietro il loro blocco in direzioni opposte. On-demand: il blocco va per primo, i vecchi messaggi li rimuovi tu. A soglia: il blocco arriva per ultimo, li rimuove l'API. Invertire queste due cose è l'errore più comune in assoluto.

Come funziona lo scambio​

La richiesta di compaction è fuori dal percorso della conversazione: trasporta la tua cronologia, restituisce un riassunto e non genera nessun turno dell'assistente. La tua richiesta reale successiva è quella che fa continuare la conversazione.

Verifica il supporto a runtime​

Il supporto dei modelli alla compaction è un flag di capability, non qualcosa da scrivere a mano nel codice. Chiama la Models API con l'header beta e leggi capabilities.compaction su ciascun modello. Questo sopravvive ai lanci e alle deprecazioni dei modelli come una lista copiata non farà mai.

Lancia la tua prima richiesta di compaction​

Guided walkthrough1 of 6
  1. Invia anthropic-beta: compact-2026-09-04 sulla richiesta di compaction E su ogni richiesta successiva che trasporta il blocco restituito. Dimenticalo su una richiesta successiva e ottieni un errore di validazione confuso che dice che compaction non è un tipo di content block previsto — il messaggio non menziona mai l'header.

curl — chiedi un riassunto della conversazione finora

curl https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: compact-2026-09-04" \
-H "content-type: application/json" \
-d '{
  "model": "claude-opus-5-5",
  "max_tokens": 4096,
  "messages": [
    {"role": "user", "content": "I am building a recipe app. Name the main entities."},
    {"role": "assistant", "content": "Recipe, Ingredient, Step, and a RecipeIngredient entry holding quantity and unit."},
    {"role": "user", "content": "Good. Now suggest field names for Recipe."}
  ],
  "compaction": {"type": "summarize"}
}'

Ciò che torna è un unico content block e una stop reason inconfondibile:

{
"content": [
{
"type": "compaction",
"content": "Summary of the conversation: the user is designing the data model for a recipe app...",
"signature": "EuYBCkQY..."
}
],
"stop_reason": "compaction",
"usage": {
"input_tokens": 0,
"output_tokens": 0,
"iterations": [{"type": "compaction", "input_tokens": 144, "output_tokens": 276}]
}
}

Nota gli zeri. I campi token di primo livello sono a zero perché non è stata generata nessuna risposta — il costo reale sta in usage.iterations. Se la tua dashboard dei costi somma solo i campi di primo livello, la compaction sembrerà gratis e la tua fattura non sarà d'accordo.

Lo streaming funziona, ma non c'è nulla da trasmettere: ricevi un solo content_block_start che trasporta il blocco completo, poi content_block_stop, senza eventi delta in mezzo (eventi ping possono arrivare intorno a essi).

Continua dal riassunto​

Sostituisci i messaggi che hai inviato con il messaggio dell'assistente restituito, mantenendo il blocco byte per byte come l'API l'ha restituito — content, signature, e niente di aggiunto.

La richiesta successiva — blocco per primo, vecchi messaggi spariti

{
"model": "claude-opus-5-5",
"max_tokens": 2048,
"messages": [
  {
    "role": "assistant",
    "content": [
      {
        "type": "compaction",
        "content": "Summary of the conversation: the user is designing the data model...",
        "signature": "EuYBCkQY..."
      }
    ]
  },
  {"role": "user", "content": "Now do the same for Ingredient."}
]
}

Lo scambio è governato da tre regole:

  1. Il blocco va per primo in messages — sia come messaggio assistant a sé stante, sia come primo content block del primo messaggio (user o assistant, va bene in entrambi i casi).
  2. I messaggi riassunti vengono rimossi. Se qualcuno di essi resta davanti al blocco, ottieni un 400 con compaction_block_misplaced.
  3. Esattamente un blocco compaction per richiesta, su ogni richiesta successiva, con l'header beta.

Due messaggi assistant di fila qui sono legittimi — se una risposta è arrivata mentre il riassunto veniva scritto, semplicemente segue il blocco.

I due errori che non sollevano nessun errore​

Watch out
  • Messaggi riassunti lasciati DOPO il blocco: nessun errore. L'API li rimanda tranquillamente a Claude, quindi paghi il riassunto E la cronologia che avrebbe dovuto sostituire.
  • Blocco omesso da una richiesta successiva: nessun errore. Claude semplicemente non riceve nessun riassunto, e la conversazione perde in silenzio tutto ciò che precedeva lo scambio.
  • Entrambi i fallimenti restituiscono HTTP 200. Niente nella risposta dice che hai sbagliato — fai asserzioni sulla forma della tua cronologia nei test, non sui codici di stato.

Compattare di nuovo​

Per compattare una conversazione che già inizia con un blocco, invia di nuovo compaction. Il nuovo blocco riassume il vecchio riassunto e tutto ciò che viene dopo. Da quel momento in poi, invia solo il blocco più recente.

Compaction in un loop​

La decisione su quando è tua. La stima economica di «quanto sarà grande la mia prossima richiesta» è input_tokens + output_tokens dall'ultima risposta — la richiesta successiva rimanda quella risposta, quindi conta. Con il prompt caching attivo, input_tokens copre solo i token dopo l'ultimo cache breakpoint, quindi aggiungi anche cache_read_input_tokens e cache_creation_input_tokens. Oppure invia gli stessi messaggi all'endpoint di conteggio dei token.

Python — compatta quando la richiesta successiva diventerebbe troppo grande

from anthropic.types.beta import BetaMessageParam

client = anthropic.Anthropic()
COMPACT_AT_TOKENS = 120_000   # set this near your real input budget
SYSTEM = "You help design a recipe app's data model. Keep answers short."
QUESTIONS = [...]             # your stream of user turns

history: list[BetaMessageParam] = []
for turn, question in enumerate(QUESTIONS, start=1):
  history.append({"role": "user", "content": question})
  response = client.beta.messages.create(
      model="claude-opus-5-5",
      max_tokens=8192,
      system=SYSTEM,
      betas=["compact-2026-09-04"],
      messages=history,
  )
  history.append({"role": "assistant", "content": response.content})

  # The next request resends this reply too, so count it.
  conversation_tokens = response.usage.input_tokens + response.usage.output_tokens
  if conversation_tokens > COMPACT_AT_TOKENS and turn < len(QUESTIONS):
      summary = client.beta.messages.create(
          model="claude-opus-5-5",
          max_tokens=4096,
          system=SYSTEM,                      # same system prompt and tools
          betas=["compact-2026-09-04"],
          messages=history,
          compaction={"type": "summarize"},
      )
      if summary.stop_reason == "compaction":  # check BEFORE reading content
          history = [{"role": "assistant", "content": summary.content}]

Nota la forma dello scambio: la cronologia viene sostituita, non ampliata. E il controllo di stop_reason viene per primo — quando non torna nessun riassunto, questo loop conserva la cronologia completa e riprova dopo il turno successivo, che è esattamente il fallback giusto.

In Python, usa client.beta.messages. Se chiami client.messages e serializzi i blocchi da solo, usa to_dict() o model_dump(exclude_none=True) — un model_dump() semplice aggiunge citations: null e text: null al blocco, e l'API rifiuta i campi che non ha restituito.

Il tool runner dell'SDK può farlo per te: chiama compact_before_next_turn() (compactBeforeNextTurn() in TypeScript, Java e PHP; CompactBeforeNextTurn() in C# e Go) e invierà la richiesta di compaction appena il turno corrente e le sue chiamate agli strumenti sono finiti. Crea il runner con la beta compact-2026-09-04 — il runner non la aggiunge.

Conserva gli ultimi turni parola per parola​

Non esiste un parametro per questo, cosa che sorprende molti. La compaction keep-tail è una decisione su cosa metti nella richiesta: scegli un punto di taglio, invii per il riassunto solo i messaggi più vecchi e metti il blocco restituito davanti ai turni che hai conservato.

Guided walkthrough1 of 4
  1. I messaggi precedenti al taglio entrano nella richiesta di compaction; quelli dal taglio in avanti vengono conservati per intero. Più ne conservi, meno spazio liberi.

Compaction in background​

La compaction in background (o asincrona) cambia due cose nel loop: la richiesta di compaction gira mentre la conversazione continua sulla sua cronologia completa, e lo scambio attende l'arrivo del blocco.

Guided walkthrough1 of 5
  1. Invia la richiesta di compaction su una copia della cronologia e ricorda il conteggio — è esattamente ciò che il blocco sostituirà.

Mentre quella richiesta gira hai due richieste aperte contemporaneamente, e la richiesta di compaction conta sui tuoi rate limit come qualsiasi altra. Quindi avviala mentre la finestra ha ancora spazio per i turni che arriveranno nel frattempo.

Compaction a soglia: lascia decidere all'API​

La compaction a soglia è la variante automatica. La configuri una volta come edit di context_management sulle tue richieste ordinarie, e l'API riassume il contesto più vecchio a metà di una richiesta quando i token di input superano il tuo trigger.

CampoSignificato
type"compact_20260112"
trigger{"type": "input_tokens", "value": N} — input_tokens è l'unico tipo di trigger; value deve essere almeno 50.000 (default 150.000)
pause_after_compactionfalse per default — impostalo per mettere in pausa dopo la generazione del riassunto invece di continuare la risposta
instructionsPrompt di riassunto personalizzato, opzionale; quando è presente sostituisce integralmente il prompt predefinito

Rimandare indietro il blocco è la parte facile, ed è l'opposto dell'on-demand: aggiungi in coda tutto il content della risposta, blocco incluso, e l'API elimina i blocchi che lo precedono nelle richieste successive. L'header beta è compact-2026-01-12.

Scegli la compaction a soglia quando vuoi il comportamento «non ci penso più». Scegli l'on-demand quando vuoi che la compaction avvenga a un confine semantico — fine di un sotto-task, dopo che un piano è stato concordato, prima di aprire un nuovo file — invece che a un conteggio di token. E scegline una per richiesta: compaction e context_management non possono essere inviati insieme, la compaction a soglia non può girare su una richiesta che trasporta un blocco on-demand firmato, e il tool runner rifiuta di compattare mentre il suo context_management contiene un edit di compaction.

Scrivi il tuo prompt di riassunto​

Senza instructions, l'API usa il proprio prompt di riassunto. Una stringa instructions non vuota (fino a 16.384 caratteri) sostituisce integralmente quel prompt — non viene aggiunta in coda.

instructions — di' cosa deve sopravvivere, e vieta le chiamate agli strumenti

{
"compaction": {
  "type": "summarize",
  "instructions": "Summarize this design conversation. Preserve every entity and field name agreed so far, every decision marked FINAL, and the user's latest open request. Do not call tools; respond with the summary text only."
}
}

Due cose si meritano un posto in ogni instructions personalizzata: una lista esplicita di ciò che va conservato, e «non chiamare strumenti». Un riassuntore che chiama uno strumento non produce nessun riassunto (vedi la sezione successiva).

Quando non torna nessun riassunto​

Un riassunto viene prodotto solo quando la chiamata di riassunto termina normalmente, con testo e senza chiamate agli strumenti. Altrimenti ottieni un 200 con content vuoto — ed è per questo che stop_reason è la cosa su cui ramificare. La chiamata viene fatturata in entrambi i casi e riportata in usage.iterations (consumo a zero se nessuna chiamata è stata possibile). In ogni caso puoi continuare senza riassunto e compattare più tardi.

stop_reasonCausaCorrezione
"max_tokens"Il riassunto è stato troncatoRimanda con un max_tokens più grande
"model_context_window_exceeded"Nessuno spazio per il prompt di riassuntoRimanda con instructions più corte o meno messaggi
"tool_use"Il modello ha chiamato uno strumento invece di riassumereRimanda con instructions che vietano le chiamate agli strumenti
"refusal"La richiesta è stata rifiutata — stop_details indica la categoria di policyContinua senza riassunto
"end_turn"La chiamata non ha restituito testoContinua senza riassunto

Errori​

La maggior parte dei 400 porta un messaggio che dice cosa rimuovere o rimandare; alcuni portano anche un error.details.error_code che inizia con compaction_.

ErroreCausaCorrezione
529 overloaded_error + compaction_unavailableProblema transitorio del server nel produrre o leggere un bloccoRiprova
400 compaction_block_misplacedMessaggi riassunti rimasti davanti al bloccoRimuovili così che il blocco venga per primo
400 compaction_signature_invalid / compaction_content_mismatchLa signature o il content del blocco è stato modificato dopo la restituzioneInvia il blocco esattamente come restituito
400 compaction_nothing_to_summarizemessages non contiene contenuto user o assistantInvia almeno un messaggio
400 (solo messaggio)Più di un blocco compaction nella richiestaInviane esattamente uno — il più recente
400 (solo messaggio)L'ultimo turno assistant termina con una chiamata a uno strumento senza risultatoInvia i risultati degli strumenti di quel turno, poi compatta
400 (solo messaggio)stop_sequences, output_config.format per l'output strutturato, o tool_choice di tipo any/tool inviati su una richiesta di compactionRimuovili — su una chiamata di riassunto non farebbero nulla
400 errore di validazione che nomina un campo in eccesso, es. compaction.citations: Extra inputs are not permittedIl blocco è stato ri-serializzato con campi che l'API non ha mai restituitoSerializza con to_dict() / model_dump(exclude_none=True)
400 "compaction requires anthropic-beta: compact-2026-09-04"Header beta mancante sulla richiesta di compactionAggiungi l'header
400 errore di validazione che dice che compaction non è un tipo di content block previstoHeader beta mancante su una richiesta successiva che trasporta il blocco — il messaggio non menziona l'headerAggiungi l'header a ogni richiesta che trasporta il blocco

Limiti e interazioni​

  • Un solo meccanismo di riduzione per richiesta. compaction e context_management non possono essere combinati.
  • Budget dei task. Non inviare output_config.task_budget.remaining con compaction né su richieste che trasportano il blocco — restituisce un 400. Vedi Budget dei task.
  • Prompt caching. cache_control sul blocco piazza un breakpoint subito dopo il riassunto — di solito esattamente dove lo vuoi, dato che il riassunto è ora un prefisso stabile. Rimandare indietro il blocco nelle richieste successive non aggiunge nessun costo di compaction.
  • Il conteggio dei token ignora completamente il parametro compaction.
  • I messaggi di sistema precedenti si dissolvono. Un messaggio role: "system" a metà conversazione che cade nell'intervallo riassunto viene riassunto come qualsiasi altra cosa, quindi le sue istruzioni smettono di applicarsi appena il blocco le sostituisce. Se un'istruzione conta ancora, ridichiarala in un nuovo messaggio role: "system" subito dopo il tuo prossimo nuovo turno user, e tienila nella cronologia da lì in avanti.
  • Alcuni contenuti non possono sopravvivere a un riassunto. Immagini, documenti, blocchi container_upload e URL recuperati all'interno dei messaggi riassunti sono persi — il riassunto è testo. Ridichiara o ricarica tutto ciò che serve ancora a un turno successivo.
Pro tip
  • Tratta il confine della compaction come un checkpoint su cui puoi ragionare: logga il testo del riassunto insieme al numero di messaggi che ha sostituito. Quando più tardi un agente si comporta come se avesse dimenticato qualcosa, quel log ti dice in un colpo d'occhio se il fatto è stato riassunto via o non è mai stato catturato.
  • La compaction è uno strumento per la finestra di contesto, non per i costi. Riduce i token che rimandi a ogni turno successivo, ma la chiamata di riassunto in sé viene fatturata — su una conversazione breve puoi facilmente spendere più di quanto risparmi.
  • Se fai anche trimming lato client, decidi quale strato possiede quale contenuto: context editing per i risultati degli strumenti obsoleti, compaction per il filo del discorso. Due meccanismi che si litigano gli stessi messaggi sono il modo in cui i riassunti finiscono per descrivere cose che il modello non può più vedere.

Errori comuni​

  • Leggere content[0] prima di controllare stop_reason. Cinque stop reason diverse restituiscono un content vuoto con un 200. È la prima fonte di IndexError nel codice che usa la compaction.
  • Sommare i campi token di primo livello. Sono a zero in una risposta di compaction. Il costo sta in usage.iterations.
  • Ri-serializzare il blocco. Qualsiasi campo che aggiungi e che l'API non ha restituito (citations: null è il classico) viene rifiutato.
  • Compattare troppo tardi. La richiesta di compaction deve stare essa stessa nella finestra di contesto. «Compatto quando la richiesta fallisce» non è una strategia.
  • Conservare una coda che separa una chiamata a uno strumento dal suo risultato. Rifiutato senza appello — scegli i tagli ai confini dei messaggi utente.
  • Dare per scontato che le immagini sopravvivano. Non sopravvivono. E nemmeno gli upload o le pagine recuperate.

Mettiti alla prova​

Check yourself

0/3
  1. La tua richiesta di compaction restituisce HTTP 200, ma response.content è una lista vuota. Cos'è successo?
  2. Dopo una compaction on-demand, dove va il blocco in messages — e cosa succede ai messaggi che ha riassunto?
  3. Una conversazione includeva uno screenshot a cui l'agente continua a fare riferimento. Fai la compaction. Cosa deve fare il tuo codice?

Glossario​

Glossario della compaction
Premi Invio o Spazio per girare la carta. Usa le frecce sinistra e destra per spostarti tra le carte.Termine mostrato.
1 / 8

Punti chiave​

Key takeaways
  • La compaction scambia una chiamata di riassunto con una cronologia più corta — l'API riassume i messaggi che invii e restituisce un unico blocco FIRMATO da usare al loro posto
  • On-demand: blocco per primo, cancelli tu ciò che ha sostituito. A soglia: blocco per ultimo, cancella l'API. Confondere le due è il bug più comune
  • Rami su stop_reason, mai sullo stato HTTP — un riassunto fallito è un 200 con content vuoto, e lo sono anche entrambi gli errori di scambio
  • Il costo vive in usage.iterations; i campi token di primo livello sono a zero. La compaction risparmia token di re-invio, non necessariamente soldi
  • Compatta PRIMA che la finestra sia piena — anche la richiesta di compaction deve starci dentro
  • Immagini, documenti, upload, URL recuperati e istruzioni di sistema precedenti non sopravvivono a un riassunto. Ridichiara ciò che conta ancora
  • Un meccanismo per richiesta: compaction e context_management non possono essere combinati, e un blocco firmato blocca la compaction a soglia

Avanti​

  • Memoria e context editing — il fratello basato sulla cancellazione, e come usarlo a strati con la compaction
  • Budget dei task — il tetto indicativo di token che copre un intero loop agentico, anche attraverso la compaction
  • Prompt caching — perché un blocco di compaction è un cache breakpoint insolitamente buono
  • Context engineering — compaction, presa di appunti e retrieval just-in-time come un'unica disciplina