Claude Opus 5.5: l'effort come unica manopola
Sintesi della lezione basata sulla pagina di lancio "Claude Opus 5.5" e sulla documentazione ufficiale della piattaforma.
Il concetto in una frase
Claude Opus 5.5 (id di modello claude-opus-5-5, uscito il 22 settembre 2026) non è interessante per i benchmark: è interessante perché chiude una porta e ne apre una sola. Il ragionamento interno del modello non si può più spegnere, e di conseguenza l'unico volante rimasto per decidere quanto il modello pensa — e quindi quanto costa e quanto ci mette — è un singolo parametro: effort.
Concetto
Tre termini da fissare prima di andare avanti.
- Token
- l'unità in cui il modello legge e scrive (circa 0,55 parole sul tokenizer attuale). Si paga a milione di token: Opus 5.5 costa 4 $ per milione in input e 20 $ per milione in output, contro i 5 $/25 $ di Opus 5. Finestra di contesto 1M token, output massimo 128K.
- Thinking block (blocco di ragionamento)
- un blocco di contenuto che il modello produce prima della risposta visibile, in cui ragiona. Sono token generati, quindi si pagano come output, anche quando non li vedi.
- Adaptive thinking (ragionamento adattivo)
- la modalità in cui è il modello a decidere turno per turno se e quanto pensare. Sostituisce il vecchio schema manuale
thinking: {type: "enabled", budget_tokens: N}, dove era l'utente a fissare un tetto rigido di token di ragionamento. Su Opus 5.5 l'adaptive thinking è sempre attivo e non disattivabile: siathinking: {"type": "disabled"}sia{"type": "enabled", "budget_tokens": N}restituiscono un errore 400.
Perché esiste questo assetto. Il modo vecchio di controllare il costo era binario e grezzo: o si spegne il ragionamento, o gli si dà un budget fisso di token che non ha idea di quanto sia difficile il problema davanti. Entrambi sbagliati per motivi diversi — spegnere il ragionamento su Opus 5 produceva persino fallimenti silenziosi, con il modello che scriveva una chiamata a tool nel testo visibile invece che in un blocco tool_use, e la chiamata non partiva mai. effort risolve il problema spostando il controllo da "quanti token di pensiero" a "quanto ti impegni": è un segnale comportamentale, non un tetto. Sui problemi facili il modello smette da solo, su quelli difficili continua.
Il secondo pezzo del concetto è la contropartita di questa scelta. Se il ragionamento è sempre presente e viene rimandato indietro a ogni turno della conversazione, diventa un bene estraibile: chi ha accesso all'API potrebbe raccogliere catene di ragionamento di un modello di frontiera e usarle per addestrarne uno più economico (distillazione). Per impedirlo, Opus 5.5 introduce la preserved thinking: ogni blocco di ragionamento viene firmato crittograficamente e legato al prefisso esatto della conversazione in cui è nato. Se si modifica qualcosa che stava prima di quel blocco, la firma non torna e la richiesta viene rifiutata. Il risultato pratico, per chi scrive codice, è un vincolo architetturale netto: la conversazione diventa append-only.
Schema / Flusso
Le due manopole e i due vincoli, nel ciclo di una richiesta agentica:
La macchina di validazione della preserved thinking, sul giro successivo:
Approfondimento tecnico
I cinque livelli di effort, e la trappola del default
I valori ammessi sono low, medium, high, xhigh, max, si passano dentro output_config (non al primo livello della richiesta) e non richiedono header beta. Il punto che fa inciampare tutti in migrazione: su Opus 5.5 il default è medium, mentre su ogni altro modello che supporta effort è high. Una richiesta che oggi omette effort girava a high su Opus 5 e girerà a medium su Opus 5.5 — un gradino più in basso, a parità di codice. Se si cambia solo la stringa del modello e la qualità cala, è quasi sempre questo, non il modello.
Cosa scegliere: low per i sotto-agenti e i compiti meccanici (meno chiamate a tool, più consolidate, zero preamboli); medium per il lavoro agentico ordinario; high per il ragionamento difficile; xhigh per le sessioni lunghe oltre la mezz'ora con budget di token nell'ordine dei milioni; max quando la correttezza vale più del costo. La documentazione insiste su un punto controintuitivo: non riportare i livelli da un modello precedente, va rifatto lo sweep sulle proprie eval. La stessa etichetta significa cose diverse su modelli diversi.
Effort non è un budget
È l'errore concettuale più comune. Ci sono tre grandezze distinte che si confondono facilmente:
| Parametro | Natura | Chi lo vede | Cosa succede se si esaurisce |
|---|---|---|---|
output_config.effort | segnale comportamentale, nessun numero | il modello lo "sente" | niente: non si esaurisce |
max_tokens | tetto duro per risposta (thinking + testo) | il modello non lo sa | troncamento a metà frase |
output_config.task_budget | budget indicativo in token per l'intero loop agentico | il modello vede un contatore alla rovescia | il modello chiude con grazia |
Corollario operativo: ai livelli alti di effort va alzato max_tokens, altrimenti il modello si tronca mentre ragiona. 64K è un punto di partenza ragionevole; oltre i valori grandi serve lo streaming, perché una risposta non-streaming lunga sbatte contro i timeout HTTP dell'SDK.
Cambiare effort a metà conversazione senza bruciare la cache
Il prompt caching (memorizzazione del prefisso della richiesta per non ripagarlo a ogni turno) funziona per corrispondenza di prefisso: cambia un byte all'inizio e cade tutto quello che viene dopo. Siccome effort al primo livello della richiesta influenza il rendering del prompt, cambiarlo tra una richiesta e l'altra azzera la cache. Opus 5.5 offre la via d'uscita: un messaggio role: "system" con content vuoto e il nuovo output_config.effort dentro l'array messages, sotto header beta mid-conversation-output-config-2026-07-01. Il nuovo livello vale dal turno utente successivo e il prefisso resta intatto. Su Opus 5.5 la cache in lettura costa il 5% del prezzo base di input (0,20 $/MTok), quindi non è un dettaglio: una sessione agentica lunga paga o non paga a seconda di questo.
Cosa invalida la firma dei blocchi di ragionamento
Il prefisso controllato è: il prompt system di primo livello, l'array tools, e tutti i messaggi precedenti al blocco.
Invalidano: modificare/riordinare/cancellare un messaggio passato, cambiare il system prompt, aggiungere o togliere un tool, accorciare il contenuto di un vecchio tool_result, ri-codificare un'immagine, rimettere a posto un blocco di thinking tolto prima, togliere un blocco di thinking in mezzo tenendo quelli dopo.
Non invalidano: appendere messaggi nuovi, cambiare effort/max_tokens/tool_choice/metadata, aggiungere o togliere marcatori cache_control, togliere i blocchi di thinking dall'inizio, dalla fine, o tutti.
Le vie legittime per fare le cose che prima si facevano editando il prefisso:
Cambiare istruzioni
messaggio system a metà conversazione, invece di riscrivere il system di primo livello.
Cambiare i tool
blocchi tool_addition / tool_removal (beta inline-tools-2026-09-15).
Aggiornare il contesto
va messo nell'ultimo messaggio utente, non rigenerando il primo.
Tagliare la storia vecchia
compaction lato server, non cancellazione lato client.
Da notare: se un account è nato prima del 31 agosto 2026 il controllo non scatta per default, ma scrivere append-only comunque è l'unica scelta che fa funzionare il codice su entrambi i regimi senza doverlo toccare dopo.
Le altre tre rotture rispetto a Opus 5
- Il forced tool use non esiste più.
tool_choicedi tipoanyetooldanno 400 (tool_choice: type "tool" and "any" are not supported for this model.), anche sull'endpoint di conteggio token. Il sostituto ètool_choice: {"type": "auto"}piùstrict: truesulla definizione del tool (garantisce che l'input validi contro lo schema) oppure gli structured outputs, dicendo nel prompt quando il tool si applica. - Computer use vecchio stile fuori uso.
computer_20251124dà 400 su Claude API e Google Cloud — va dichiarato il toolsetcomputer_toolset_20260801, senza header beta, senzanamee senza dimensioni dello schermo (su Amazon Bedrock il vecchio continua invece a funzionare). - Il testo "di progresso" cambia contenitore. Il testo che il modello scriveva tra una chiamata a tool e l'altra ora torna dentro blocchi
thinkingdi progress-update, non più come blocchitext. Siccome il default dithinking.displayè"omitted", quel campo arriva vuoto: nessuna richiesta fallisce, ma l'interfaccia smette di mostrare avanzamenti e sembra impiantata. Per riaverli:display: "updates"(betathinking-display-updates-2026-08-18, mostra solo gli avanzamenti tenendo nascosto il ragionamento) o"summarized"(mostra entrambi, mescolati). Questa è la rottura più insidiosa perché non dà errore.
Rifiuti
Opus 5.5 può restituire HTTP 200 con stop_reason: "refusal" e un stop_details.category — le categorie coperte sono più ampie che su Opus 5 ("bio", "cyber", "reasoning_extraction"). Va sempre controllato stop_reason prima di leggere content. Il fallback lato server non ritenta i rifiuti di categoria "reasoning_extraction": quello torna al chiamante.
Come si colloca rispetto ai vicini
Non va confuso con Fable 5.1: Fable resta il modello per il ragionamento più esigente e le sessioni lunghissime, costa 10 $/50 $ per milione e ha default high; la regola pratica della documentazione è partire da Opus 5.5 e salire a Fable solo quando le eval su Opus 5.5 ad alto effort restano insufficienti.
E non va confuso effort con la compaction o il context editing: effort governa quanto il modello produce, la compaction riassume la storia quando ci si avvicina al limite di contesto, il context editing cancella i vecchi risultati dei tool. Tre leve su tre problemi diversi — costo di generazione, saturazione del contesto, rumore nel contesto.
Esempio pratico
Scenario. Un servizio interno che smista ticket di supporto gira oggi su claude-opus-5 con tre tool (search_kb, get_customer, create_jira), con thinking: {"type": "disabled"} perché qualcuno l'aveva messo per risparmiare, e con tool_choice forzato su create_jira nella fase finale. Va portato su Opus 5.5 per il taglio di prezzo del 20%, senza perdere qualità e senza bruciare la cache.
Passi.
- Cambia l'id del modello da
claude-opus-5aclaude-opus-5-5. - Togli il blocco
thinkinge sostituiscilo con un livello di effort esplicito. Dove si spegneva il ragionamento per risparmiare, il rimpiazzo èlowomedium, non l'assenza del parametro. - Sostituisci il forced tool use con
auto+strict: truee un'istruzione esplicita nel prompt. - Rendi il loop append-only: appendi
response.contentper intero, non solo il testo. È l'errore che rompe tutto, perché estrarre la stringa e riappendere quella butta via i blocchi di thinking firmati.
pythonimport anthropic, json
client = anthropic.Anthropic()
TOOLS = [
{
"name": "create_jira",
"description": "Apre un ticket Jira. Usalo solo dopo aver identificato cliente e categoria.",
"strict": True, # sostituisce il tool_choice forzato
"input_schema": {
"type": "object",
"properties": {"summary": {"type": "string"},
"customer_id": {"type": "string"}},
"required": ["summary", "customer_id"],
"additionalProperties": False, # obbligatorio con strict
},
},
# ... search_kb, get_customer
]
SYSTEM = "Sei il triage dei ticket. Quando cliente e categoria sono noti, apri il ticket con create_jira."
messages = [{"role": "user", "content": "Il cliente ACME non riesce ad accedere dal 3 marzo."}]
while True:
resp = client.messages.create(
model="claude-opus-5-5",
max_tokens=16000,
system=SYSTEM, # fisso per tutta la sessione: è nel prefisso firmato
tools=TOOLS, # idem: non toccarlo a metà conversazione
tool_choice={"type": "auto"}, # "tool"/"any" darebbero 400
output_config={"effort": "medium"}, # default esplicito; niente campo `thinking`
messages=messages,
)
if resp.stop_reason == "refusal":
raise RuntimeError(f"rifiuto: {resp.stop_details.category}")
# APPEND-ONLY: l'intero content, blocchi thinking firmati compresi
messages.append({"role": "assistant", "content": resp.content})
if resp.stop_reason != "tool_use":
break
results = []
for block in resp.content:
if block.type == "tool_use":
out = dispatch(block.name, block.input) # esecuzione lato applicazione
results.append({"type": "tool_result", "tool_use_id": block.id,
"content": json.dumps(out)})
# tutti i tool_result in UN SOLO messaggio utente
messages.append({"role": "user", "content": results})
print(resp.usage.cache_read_input_tokens) # se resta 0, qualcosa sta invalidando il prefisso
- Chiudi in
lowsenza perdere la cache. Il riassunto finale per l'operatore non meritamedium: va inserito un messaggio system a solo effort prima dell'ultimo turno utente.
pythonresp = client.beta.messages.create(
model="claude-opus-5-5",
max_tokens=4096,
system=SYSTEM,
output_config={"effort": "medium"},
messages=messages + [
{"role": "system", "content": [], "output_config": {"effort": "low"}},
{"role": "user", "content": "Riassumi in tre righe per l'operatore."},
],
betas=["mid-conversation-output-config-2026-07-01"],
)
- Fai lo sweep di effort sulle eval di regressione: gira il set di ticket a
low,medium,highe confronta accuratezza contro token spesi. Non ereditare il livello dal codice Opus 5.
Risultato atteso. Il loop gira senza 400. usage.cache_read_input_tokens è diverso da zero dal secondo turno in poi, perché system e tools non cambiano mai e i messaggi si limitano a crescere in coda — se resta a zero, c'è un invalidatore silenzioso in giro (un timestamp nel system prompt, un ordine di chiavi JSON che varia, un tool aggiunto a runtime). Il costo per ticket scende di circa il 20% per il solo cambio di listino, più quanto si guadagna scendendo di effort dove le eval reggono. Se l'interfaccia mostrava all'operatore il testo tra una chiamata e l'altra, quella parte è ora muta: per riaverla va aggiunto thinking: {"type": "adaptive", "display": "updates"} con il beta thinking-display-updates-2026-08-18, renderizzando i blocchi thinking non vuoti prima del tool_use che precedono.
Riferimenti
- Pagina di lancio: https://www.anthropic.com/claude-opus-5-5
- Panoramica modelli (id, prezzi, contesto, default effort): https://platform.claude.com/docs/en/about-claude/models/overview
- Parametro effort: https://platform.claude.com/docs/en/build-with-claude/effort
- Guida di migrazione a Opus 5.5 (le quattro rotture): https://platform.claude.com/docs/en/models/opus-5-5/migration-guide
- Preserved thinking (firma, prefisso,
drop_block): https://platform.claude.com/docs/en/build-with-claude/preserved-thinking - Prompt caching: https://platform.claude.com/docs/en/build-with-claude/prompt-caching