Vai al contenuto principale

Tutorial API Claude Fable 5.1: crea un agente developer a lunga esecuzione in Python

Impara a usare il nuovo modello di punta di Anthropic per costruire un agente Python che legge una repository Flask prima di pianificare una modifica. Aggiungi aggiornamenti di avanzamento, tool di file in sola lettura e controlli dei costi.
Aggiornato 3 set 2026  · 15 min leggi

Esplora con l'AI

ChatGPTClaudePerplexity

Quando provo un nuovo modello tramite una chiamata API, la prima risposta mi dice ben poco. La mia prima esecuzione con Fable 5.1 ha restituito una struttura valida e un piano generico. Volevo sapere cosa succede quando la conversazione cresce: l’applicazione riesce a mantenere intatta la cronologia, a ispezionare i file senza leggere al di fuori del progetto, a segnalare l’avanzamento e a mostrare da dove arrivano i costi?

La nostra panoramica su Claude Fable 5.1 copre il lancio, i benchmark e i confronti tra modelli. Qui partiremo da una piccola chiamata Python e costruiremo attorno ad essa l’agent loop. L’agente finale riceve una richiesta di funzionalità, legge un progetto Flask e restituisce un piano collegato ai file che ha effettivamente ispezionato.

Vedremo come:

  • Effettuare una chiamata API a Claude Fable 5.1 e leggere in sicurezza i blocchi di contenuto
  • Impostare l’effort di ragionamento e cambiarlo a conversazione in corso (beta)
  • Limitare un’istruzione di sistema a un singolo turno (beta)
  • Restituire un piano strutturato con Pydantic
  • Aggiungere strumenti di repository in sola lettura con un perimetro alla radice del progetto
  • Eseguire un tool loop multi-turno
  • Leggere gli aggiornamenti di avanzamento dell’agente tra le chiamate ai tool (beta)
  • Mantenere validi i blocchi di thinking con una cronologia append-only
  • Mettere in cache il contesto ripetuto e stimare il costo della richiesta alle tariffe pubblicate
  • Gestire i rifiuti ed esporre l’agente tramite FastAPI

Le funzionalità beta usano header datati: confrontale con la documentazione di Anthropic prima di andare in produzione.

Quanto costa eseguire Claude Fable 5.1 in un agent loop?

Un agente reinvia lo stesso system prompt, le definizioni dei tool e il contesto del repository a ogni turno, quindi la tariffa che incide sulla spesa è quella di cache read, non quella di input.

Fable 5.1 costa $10 per milione di token in input e $50 per milione di token in output, invariato rispetto a Fable 5. Le cache read costano $0,25 per milione, in calo da $1, e le cache write di cinque minuti restano a $12,50 per milione. La nostra guida a Claude Fable 5.1 riporta la tabella completa delle tariffe e le stime di risparmio di Anthropic.

Leggere un prefisso in cache costa poco. Scriverlo no, dato che costa 50 volte la lettura, quindi il loop conviene solo quando un prefisso viene riletto più volte. Più avanti trovi la ripartizione dei costi su un’esecuzione reale e quale categoria ha pesato di più.

Il tetto di token dipende dal modello, non dal budget. Fable 5.1 offre una finestra di contesto da 1M di token con fino a 128K token di output per risposta, e max_tokens è un limite rigido su thinking più testo di risposta insieme. Con effort alto ti serve spazio per entrambi, motivo per cui il loop sotto imposta 16.000 invece di un numero più tondo.

Conservazione dei dati, Priority Tier e watermarking

Prima di scrivere codice, ci sono alcuni dettagli di accesso importanti. Due di questi bloccheranno del tutto le richieste:

  • Fable 5.1 richiede la conservazione dei dati per 30 giorni e non è disponibile con conservazione zero dati a meno che Anthropic non autorizzi l’accesso. Una richiesta da uno workspace incompatibile restituisce un 400 invalid_request_error senza altri indizi.

  • Il modello non è supportato su Priority Tier. Fable 5 sì, quindi questo inganna chi migra.

  • L’output testuale di Fable 5.1 porta il watermark testuale di Anthropic. Non aggiunge token e non richiede modifiche alla richiesta.

Usa Claude Fable 5.1 via API per costruire un agente developer consapevole del repository

Il nostro workflow ha due fasi:

  1. Un loop di ispezione con limiti legge i file consentiti del progetto.
  2. Una richiesta finale usando output strutturati trasforma tale contesto in un piano. 

Il progetto di esempio è una piccola API JSON in Flask per salvare e cercare segnalibri, con una factory dell’app, tre blueprint, un modulo di config, i modelli e una suite pytest. Uso il rate limiting come task di prova perché l’agente deve ispezionare setup dell’app, route, config e test prima di poter identificare i file e i test richiesti. Il codice completo e il progetto di esempio sono disponibili nella repository GitHub.

Diagramma di una richiesta di funzionalità che attraversa un agente Claude Fable 5.1, una allowlist di percorsi e un progetto di esempio, prima di restituire un piano strutturato

Le richieste raggiungono i file attraverso un unico perimetro. Immagine dell’autore.

L’agente può usare solo tre tool: list_project_files, read_project_file e get_project_metadata. Claude non accede mai direttamente al filesystem. Chiede un percorso, e il tuo codice decide se quel percorso è consentito.

Configurare l’API Claude Fable 5.1 in Python

Inizia con un ambiente Python separato e tieni la chiave API sul server.

Prerequisiti

Ti serve Python 3.10 o più recente e una chiave API Anthropic con accesso a claude-fable-5-1

Per creare una chiave API, accedi alla Claude Console, apri la pagina delle chiavi API, fai clic su Create key, quindi copia la chiave. È buona pratica darle un nome che ti aiuti a ricordarne l’uso, scegliere una data di scadenza e conservarla in modo sicuro.

Installa l’SDK e aggiungi la chiave API

Crea un ambiente virtuale e installa i pacchetti:

python -m venv .venv
source .venv/bin/activate          # macOS o Linux
.venv\Scripts\Activate.ps1         # Windows PowerShell
pip install anthropic==1.3.0 pydantic fastapi uvicorn python-dotenv

Mantieni l’SDK bloccato perché le funzionalità beta cambiano spesso. Gli aggiornamenti di avanzamento richiedono almeno la 1.1.0, e gli esempi usano la 1.3.0.

Metti la chiave in un .env file e aggiungi .env a .gitignore prima del primo commit. Deve stare su un server sotto il tuo controllo, mai in un browser o in una repository accessibile. Esporla può consentire uso non autorizzato dell’API e addebiti su input, output e cache.

ANTHROPIC_API_KEY=sk-ant-your-key-here

Fatto questo, il client trova la chiave da solo.

Effettua la tua prima chiamata API a Claude Fable 5.1 in Python

Invia la richiesta API più piccola possibile prima di costruirci sopra qualsiasi cosa.

Invia la prima richiesta API

Inizializza il client, invia un messaggio utente e stampa i metadati della risposta:

from anthropic import Anthropic
from dotenv import load_dotenv

load_dotenv()

client = Anthropic()
MODEL = "claude-fable-5-1"

response = client.messages.create(
    model=MODEL,
    max_tokens=512,
    messages=[{"role": "user", "content": "Reply in one sentence to confirm the API connection is working."}],
)

text = next((b.text for b in response.content if b.type == "text"), None)
print(text if text is not None else f"No text returned ({response.stop_reason})")
print(f"Model: {response.model}")
print(f"Stop reason: {response.stop_reason}")
print(f"Input tokens: {response.usage.input_tokens}")
print(f"Output tokens: {response.usage.output_tokens}")
print(f"Request ID: {response._request_id}")

Terminale che mostra una risposta API di Claude Fable 5.1 con ID modello, motivo di stop, conteggio token e ID richiesta

La prima chiamata restituisce testo più metadati. Immagine dell’autore.

La chiamata next(...) seleziona il primo blocco di testo. Il thinking adattivo è sempre attivo e non può essere disabilitato, quindi una risposta può iniziare con un blocco di thinking; inviare thinking: {"type": "disabled"} restituisce un 400 invece di disattivarlo. Quando un blocco di thinking viene per primo, response.content[0].text genera un’eccezione.

La soluzione è filtrare per tipo di blocco invece di assumere una posizione fissa. Registra anche response._request_id, dato che l’assistenza di Anthropic lo usa per tracciare una richiesta.

Ecco la richiesta usata negli esempi su planning ed effort. Richiede che l’agente ispezioni diversi file:

feature_request = (
    "Add rate limiting to the public API endpoints so one client cannot exhaust "
    "the search endpoint or brute force the token endpoint."
)

Mantieni invariato quel testo mentre confronti i livelli di effort e i conteggi dei token. I risultati descriveranno così le impostazioni dell’API e non un prompt diverso.

Imposta l’effort di ragionamento con output_config

Imposta l’effort tramite output_config. Accetta low, medium, high, xhigh e max. Il default dell’API è high.

response = client.messages.create(
    model=MODEL,
    max_tokens=8192,
    output_config={"effort": "high"},
    messages=[{"role": "user", "content": feature_request}],
)

L’effort può influire sull’uso dei token, sul comportamento dei tool e sulla latenza. Ho eseguito la stessa richiesta di funzionalità tre volte per ciascuno di quattro livelli di effort; la tabella mostra le medie:

Effort

Secondi

Token di thinking

Token output totali

Costo

low

7.7

111

173

$0.0093

medium

8.1

129

186

$0.0099

high

7.9

136

199

$0.0106

xhigh

20.0

151

1.764

$0.0888

I token di thinking sono inclusi nei token di output totali, quindi non sommare le due colonne. In queste esecuzioni, low, medium e high sono rimasti vicini per latenza e costo.

xhigh ha impiegato due volte e mezzo il tempo, ha prodotto quasi nove volte i token di output e ha costato otto volte tanto. 

La sintesi: parti da high, scendi a medium per i passaggi di routine e usa livelli più alti solo quando i tuoi test mostrano un miglioramento misurabile. A low effort, il modello può rispondere a memoria invece di chiamare uno strumento di retrieval. Se un turno ha bisogno di informazioni fresche, dillo o alza il livello.

Restringi l’ambito dell’agente con un system prompt

Il system prompt definisce il comportamento dell’agente:

SYSTEM_PROMPT = """You are a senior engineer who turns feature requests into implementation plans for an existing codebase.

Stay inside the requested feature. Do not propose unrelated refactors, dependency upgrades, or style changes.

If a file or dependency you need does not exist, say so plainly instead of inventing it.

Write in plain sentences and do not use em dashes.

Finish with concrete guidance: what changes, where, in what order, what could break, and which tests to add."""

La guida al prompting di Anthropic segnala che il modello può ampliare il task o fermarsi troppo presto. Il prompt gli dice di restare nell’ambito e di chiudere con indicazioni concrete. Uno schema gestirà il formato dell’output più avanti.

Restituisci un piano strutturato con Pydantic

Definisci il piano con Pydantic così che la tua applicazione possa validarlo e passarlo ad altro codice:

from pydantic import BaseModel, Field

class FeaturePlan(BaseModel):
    summary: str = Field(description="One or two sentences on what will be built.")
    implementation_steps: list[str]
    files_to_modify: list[str]
    risks: list[str]
    tests: list[str]

response = client.messages.parse(
    model=MODEL,
    max_tokens=8192,
    system=SYSTEM_PROMPT,
    messages=[{"role": "user", "content": feature_request}],
    output_format=FeaturePlan,
)

if response.stop_reason == "refusal":
    category = (
        response.stop_details.category
        if response.stop_details and response.stop_details.category
        else "unspecified"
    )
    print(f"Declined: {category}")
elif response.parsed_output is None:
    print(f"No plan. Stop reason: {response.stop_reason}")
else:
    print(response.parsed_output.summary)

messages.parse() converte il modello Pydantic in uno schema JSON, lo invia, valida la risposta e restituisce un oggetto tipizzato in parsed_output. Gli output strutturati sono generalmente disponibili, quindi non serve un header beta. Controlla prima stop_reason perché un rifiuto, trattato più avanti, salta lo schema e non lascia nulla da analizzare.

Quel risultato generico dell’introduzione ha fatto una cosa giusta: non ha nominato file che non poteva vedere. Uno schema valida la struttura, non l’ancoraggio fattuale.

Claude Fable 5.1 vs. Fable 5: modifiche di migrazione API

Prima di aggiungere i tool, considera i vincoli sui tool forzati, la compatibilità dei blocchi di thinking e la cronologia append-only.

  • Fable 5.1 rifiuta la selezione forzata dei tool. La sezione sul loop dei tool qui sotto mostra l’errore e la configurazione auto usata al suo posto.

  • I blocchi di thinking sono compatibili in una sola direzione. Fable 5.1 legge i blocchi dei modelli Claude precedenti, ma nessun modello precedente può leggere i suoi blocchi. 

Quando un router o un fallback sposta la conversazione su un modello più vecchio, l’API rimuove i blocchi incompatibili prima che il modello di destinazione li veda. La cronologia restante resta al suo posto, ma il modello più vecchio deve pianificare senza quei blocchi.

Modificare i turni precedenti invalida i blocchi di thinking successivi. Questo può rompere il trimming della cronologia e la sintesi lato client.

La guida alla migrazione copre l’insieme completo dei cambiamenti.

Aggiungi tool di repository in sola lettura

Ora fornisci al modello il contesto del repository tramite tool in sola lettura.

Definisci i tool in sola lettura

Il layer dei tool ha due parti: le funzioni Python che applicano le regole di accesso e gli schemi che Claude può chiamare.

Limita i percorsi alla radice del progetto

Sola lettura non significa sicuro. Un modello può chiedere ../../.env con la stessa facilità di config.py, quindi il guardrail appartiene al tuo codice, non al prompt:

def _resolve(self, relative_path: str) -> Path:
    relative = Path(relative_path)
    if relative.is_absolute() or relative.drive:
        raise ToolError(f"path is outside the project root: {relative_path}")

    cursor = self.root
    for part in relative.parts:
        cursor /= part
        if cursor.is_symlink():
            raise ToolError(f"symlinks are not followed: {relative_path}")

    candidate = (self.root / relative).resolve()

    # After resolving "..", the path still has to sit under the allowed root.
    if candidate != self.root and self.root not in candidate.parents:
        raise ToolError(f"path is outside the project root: {relative_path}")
    if candidate.name in DENY_NAMES:
        raise ToolError(f"reading {candidate.name} is not allowed")

    return candidate

Rifiuta percorsi assoluti e componenti symlink, poi risolvi il percorso e conferma che resti sotto la radice del progetto. Chiedere ../.env restituisce "path is outside the project root". L’errore del tool restituito permette all’agente di proseguire con file consentiti.

Definisci schemi di tool rigorosi

La classe reader controlla cosa può aprire Python. A Claude servono anche schemi JSON che descrivano le tre azioni che può richiedere:

EMPTY_SCHEMA = {
    "type": "object",
    "properties": {},
    "additionalProperties": False,
}

TOOLS = [
    {
        "name": "list_project_files",
        "description": "List readable text files in the project.",
        "input_schema": EMPTY_SCHEMA,
        "strict": True,
    },
    {
        "name": "read_project_file",
        "description": "Read one text file relative to the project root.",
        "input_schema": {
            "type": "object",
            "properties": {"path": {"type": "string"}},
            "required": ["path"],
            "additionalProperties": False,
        },
        "strict": True,
    },
    {
        "name": "get_project_metadata",
        "description": "Read project metadata and dependency manifests.",
        "input_schema": EMPTY_SCHEMA,
        "strict": True,
    },
]

strict controlla gli argomenti quando il modello sceglie un tool. Non forza una chiamata al tool, cosa che in Fable 5.1 è rilevante.

Esegui il tool loop multi-turno

Inizia con il loop base: invia i tool, ispeziona stop_reason, esegui quanto richiesto, aggiungi i risultati e ripeti.

MAX_AGENT_TURNS = 8
reader = ProjectReader("sample_project")
messages = [{"role": "user", "content": feature_request}]

for turn in range(1, MAX_AGENT_TURNS + 1):
    response = client.messages.create(
        model=MODEL,
        max_tokens=16000,
        system=SYSTEM_PROMPT,
        tools=TOOLS,
        messages=messages,
    )

    if response.stop_reason == "refusal":
        return declined(response.stop_details.category)
    if response.stop_reason == "max_tokens":
        return cutoff()
    if response.stop_reason != "tool_use":
        messages.append({"role": "assistant", "content": response.content})
        break

    messages.append({"role": "assistant", "content": response.content})
    results = []
    for block in response.content:
        if block.type != "tool_use":
            continue
        output, is_error = reader.run(block.name, block.input)
        results.append({
            "type": "tool_result",
            "tool_use_id": block.id,
            "content": output,
            "is_error": is_error,
        })

    messages.append({"role": "user", "content": results})
else:
    return turn_limit()

MAX_AGENT_TURNS limita le richieste del modello, non la spesa, quindi applica un limite separato sui costi se necessario. Il loop gestisce direttamente refusal, max_tokens e tool_use ; altri motivi di stop chiudono la fase di ispezione. Il campo is_error segnala al modello che un percorso è stato rifiutato, così può scegliere un’altra azione.

Perché la scelta forzata dei tool restituisce un 400

In Fable 5, potevi forzare la prima chiamata con tool_choice: {"type": "any"}. Fable 5.1 restituisce questo errore prima di eseguire la richiesta:

tool_choice: type "tool" and "any" are not supported for this model.

Le chiamate forzate salterebbero il thinking sempre attivo. Lascia tool_choice su auto, usa gli schemi rigorosi definiti sopra e nomina i tool nel prompt quando un passaggio ne richiede uno.

Fable 5.1 a volte emette una chiamata tool per turno, mentre Fable 5 le raggruppava. Questo aggiunge round-trip. Aggiungi questa riga al prompt: “Richiedi file indipendenti nello stesso turno invece di uno per turno.” In un’esecuzione di esempio sono stati raggruppati nove file indipendenti, anche se il conteggio varia.

Esegui in streaming le risposte e gli aggiornamenti di avanzamento di Claude Fable 5.1

Lo streaming di testo emette il contenuto della risposta man mano che viene generato; gli aggiornamenti di avanzamento coprono le pause tra le chiamate ai tool.

Esegui lo streaming delle risposte testuali

Il progetto completo usa context_system() per combinare SYSTEM_PROMPT con un riepilogo del progetto prima di avviare lo stream:

with client.messages.stream(
    model=MODEL,
    max_tokens=8192,
    system=context_system(),
    messages=[{"role": "user", "content": feature_request}],
) as stream:
    for chunk in stream.text_stream:
        print(chunk, end="", flush=True)
    final = stream.get_final_message()

print(f"\nOutput tokens: {final.usage.output_tokens}")

get_final_message() ti restituisce il messaggio assemblato con usage e motivo di stop una volta drenato lo stream. I chunk in streaming non garantiscono JSON completo, quindi attendi il messaggio finale prima di analizzare.

Mostra l’avanzamento tra le chiamate ai tool

Lo streaming di testo non copre i ritardi durante le chiamate ai tool. Fable 5.1 può scrivere brevi aggiornamenti di avanzamento prima delle chiamate ai tool. Con il thinking.display predefinito "omitted", i blocchi di thinking specifici per l’avanzamento sono vuoti, anche se il modello può produrre comunque un’introduzione testuale.

Con display: "updates" e l’header beta thinking-display-updates-2026-08-18 la documentazione API definisce un aggiornamento di avanzamento leggibile come un blocco thinking non vuoto mentre il ragionamento resta nascosto. Nelle esecuzioni live di questo progetto, il campo thinking è rimasto vuoto e lo stato leggibile è arrivato come un normale blocco text immediatamente prima di tool_use. L’helper quindi controlla entrambi i tipi di blocco, e il loop lo chiama solo sui turni che finiscono con tool_use:

PROGRESS_BETA = "thinking-display-updates-2026-08-18"

response = client.beta.messages.create(
    model=MODEL,
    max_tokens=16000,
    betas=[PROGRESS_BETA],
    thinking={"type": "adaptive", "display": "updates"},
    system=SYSTEM_PROMPT,
    tools=TOOLS,
    messages=messages,
)

def status_lines(response) -> list[str]:
    lines = []
    for block in response.content:
        if block.type == "thinking":
            text = (block.thinking or "").strip()
        elif block.type == "text":
            text = (block.text or "").strip()
        else:
            continue
        if text:
            lines.append(text)
    return lines

I messaggi di avanzamento descrivono i file che il modello prevede di leggere: "Leggerò il wiring dell’app, la config, le estensioni, le route pubbliche e auth, e i test esistenti, perché è lì che si aggancerebbe il rate limiting." Mostra questi messaggi e ignora i blocchi vuoti.

Terminale che mostra un agent loop di Claude Fable 5.1 con uso di token per turno, messaggi di avanzamento e letture di file in batch

L’agente legge i file mentre riporta i progressi. Immagine dell’autore.

Fable 5.1 ne scrive meno rispetto a Fable 5, soprattutto con effort più alto. Se la tua interfaccia richiede aggiornamenti regolari, chiedi una riga di apertura, messaggi di avanzamento e un riepilogo finale.

Cambia l’effort di Claude Fable 5.1 a conversazione in corso

La prossima funzione è davvero comoda. Come sappiamo, l’agente di repository non ha bisogno dello stesso livello di ragionamento a ogni turno.

Cambia l’effort tra i turni

In un agent loop, abbassa l’effort per i turni di retrieval di routine e rialzalo per il turno di pianificazione finale.

Con l’header beta mid-conversation-output-config-2026-07-01 puoi aggiungere un messaggio di sistema che cambia solo il livello di effort:

EFFORT_BETA = "mid-conversation-output-config-2026-07-01"

messages.append({"role": "system", "content": [], "output_config": {"effort": "low"}})
messages.append({"role": "user", "content": "Summarize the repository evidence in five words."})

response = client.beta.messages.create(
    model=MODEL,
    max_tokens=4096,
    betas=[EFFORT_BETA],
    output_config={"effort": "high"},
    messages=messages,
)

Il nuovo livello si applica dal turno utente successivo, non a metà dell’attuale, e non invalida la cache del prompt. Cambiare output_config.effort a livello di richiesta tra chiamate invece la invalida. 

L’agente mantiene l’impostazione di alto livello su high, aggiunge una direttiva medium per-messaggio prima del retrieval di routine e una high prima del piano finale. Un test abbinato ha usato 18 token di output a effort più basso contro 76 con l’impostazione precedente. Considera quel risultato come esempio, non come riduzione attesa.

Applica un’istruzione di sistema a un solo turno

Usa un’istruzione con ambito di turno per bloccare ulteriori letture di file durante la pianificazione finale.

Imposta clear_at: "next_user_message" su un messaggio di sistema con l’header beta mid-conversation-system-clear-at-2026-08-21 . L’API tratta il suo testo come istruzione di sistema per il turno corrente, poi smette di renderlo dopo il prossimo messaggio utente. Resta in messages, quindi la cronologia precedente non cambia, la cache continua a combaciare e il messaggio cancellato non costa token di input.

SCOPED_SYSTEM_BETA = "mid-conversation-system-clear-at-2026-08-21"

messages.append({"role": "system", "content": [], "output_config": {"effort": "high"}})
messages.append({"role": "user", "content": "Write the implementation plan now."})
messages.append({
    "role": "system",
    "content": (
        "For this turn only: do not request more files. Base the plan on what "
        "you have already read, and name only paths you actually opened."
    ),
    "clear_at": "next_user_message",
})

response = client.beta.messages.create(
    model=MODEL,
    max_tokens=16000,
    betas=[EFFORT_BETA, SCOPED_SYSTEM_BETA],
    tool_choice={"type": "none"},
    output_config={"format": {"type": "json_schema", "schema": plan_schema()}},
    system=agent_system(),
    tools=TOOLS,
    messages=messages,
)

tool_choice={"type": "none"} impedisce alla richiesta finale di chiamare un altro tool. L’istruzione con ambito limita il piano ai file che l’agente ha già ispezionato. Non aggiungere un promemoria e poi eliminarlo alla richiesta successiva. Quella modifica invalida i blocchi di thinking successivi.

Correggi gli errori 400 dei blocchi di thinking in Claude Fable 5.1

Un errore The block is bound to a different conversation significa che la cronologia precedente a un blocco di thinking è cambiata. Ogni blocco di thinking di Fable 5.1 è legato all’esatto system prompt, alle definizioni dei tool e ai messaggi che lo precedono.

Il risultato dipende da quando è stato creato il tuo account. 

  • Gli account creati il 31 agosto 2026 o dopo ricevono un 400 che dice che il blocco è legato a una conversazione diversa. 

  • Per gli account creati prima, l’API registra il disallineamento ma agisce solo quando la richiesta imposta thinking.block_binding.prefix_mismatch_behavior

Puoi rilevarlo con l’header beta thinking-binding-controls-2026-08-01, thinking.block_binding.prefix_mismatch_behavior impostato su "drop_block" e l’array input_transformations. Una cronologia modificata appare come reason: "prefix_binding_mismatch". Esegui questo controllo una volta sulla tua integrazione.

Le seguenti operazioni scatenano il disallineamento:

  • Modificare, riordinare o rimuovere un turno precedente mantenendo i successivi

  • Inserire testo per richiesta in un turno precedente e rimuoverlo alla richiesta successiva

  • Cambiare contenuto o ordine del system di alto livello o dell’array tools a conversazione in corso

  • Servire byte diversi da un URL di immagine o documento in una richiesta successiva

Ognuna di queste ha un sostituto che mantiene intatti i binding:

  • Aggiungi istruzioni con messaggi di sistema mid-conversation invece di modificare system

  • Cambia i tool con modifiche mid-conversation dei tool invece di cambiare l’array di alto livello. 

  • Riduci la cronologia con context editing o compaction lato server, che non contano come modifiche. 

  •  Rimanda indietro i blocchi di thinking invariati.

Spostare i marker di cache_control e cambiare l’effort a livello di richiesta sono entrambi sicuri e non invalidano i binding dei blocchi di thinking. Tuttavia, cambiare l’effort di alto livello riavvia la cache del prompt, quindi usa l’effort per messaggio quando il prefisso in cache deve restare invariato.

Cache dei prompt e costo dell’API Claude Fable 5.1

L’esecuzione seguente separa i costi di input nuovo, scritture in cache, letture in cache e output.

Aggiungi la cache automatica dei prompt

La cache dei prompt riduce il costo del contesto che si ripete tra i turni. La cronologia in crescita cambia dove dovrebbe cadere il breakpoint, quindi la cache automatica qui si adatta meglio.

Un campo cache_control di alto livello sposta il breakpoint all’ultimo blocco memorizzabile in cache a ogni richiesta:

response = client.beta.messages.create(
    model=MODEL,
    cache_control={"type": "ephemeral"},
    system=system,
    tools=TOOLS,
    messages=messages,
    # Other request fields...
)

Un prefisso memorizzabile in cache più corto di 512 token non viene messo in cache su Fable 5.1, anche se marcato con cache_control. L’API lo elabora normalmente e restituisce zero per entrambi i contatori della cache. Scrivere un prefisso da 583 token è costato $0,0073; leggerlo al turno successivo è costato $0,00015. Il secondo turno ha comunque dovuto scrivere la sua parte nuova nella cache, quindi un hit di cache non ha eliminato ogni costo di input.

Stima del costo API con cache

response.usage riporta separatamente input nuovo, creazione cache, letture cache e output. Prezza tutti e quattro i contatori a parte; sommare solo input e output nasconde il costo di scrittura in cache e sovrastima il prezzo delle letture da cache.

Ecco la ripartizione dei costi di un’esecuzione completa che ha letto 12 file in tre turni e prodotto un piano finale:

Voce

Token

Costo stimato

Quota

Output

5.713

$0.2857

59,4%

Scritture in cache

15.426

$0.1928

40,1%

Input nuovo

50

$0.0005

0,1%

Letture da cache

6.549

$0.0016

0,3%

Totale

27.738

$0.4806

100%

Le letture da cache hanno rappresentato una frazione inferiore a mezzo punto percentuale di questa stima. Alle vecchie tariffe di Fable 5, l’esecuzione sarebbe costata circa $0,4855 invece di $0,4806. I risparmi crescono quando ogni turno riutilizza molto più contesto.

In questa esecuzione, l’output ha prodotto quasi il 60% della stima, e le scritture in cache circa il 40%. Alla tariffa di cinque minuti usata qui, un token di scrittura in cache costa 50 volte un token di lettura in cache. Una scrittura in cache di un’ora costa 80 volte tanto.

Gestisci i rifiuti e i fallback di Claude Fable 5.1

Un rifiuto e una richiesta fallita richiedono comportamenti applicativi diversi.

Rileva i rifiuti prima di analizzare l’output

Un rifiuto prima dell’output arriva come HTTP 200 con stop_reason: "refusal", contenuto vuoto e stop_details. La categoria può essere nulla. Un rifiuto più tardi in uno stream può seguire output parziale, che l’applicazione dovrebbe scartare. Un try/except attorno alla chiamata non intercetterà nessuno dei due casi.

response = client.messages.create(model=MODEL, max_tokens=8192, messages=messages)

if response.stop_reason == "refusal":
    category = (
        response.stop_details.category
        if response.stop_details and response.stop_details.category
        else "unspecified"
    )
    return f"This request was declined ({category})."

Gestiscilo come stato applicativo. Se una richiesta consentita è poco chiara, riscrivila in modo più preciso. Non costruire logiche di retry con lo scopo di aggirare il classificatore.

Un rifiuto arriva come HTTP 200. Immagine dell’autore.

Configura il fallback lato server

Il fallback lato server può riprovare una richiesta rifiutata su un altro modello, usando fallbacks: "default" con l’header beta server-side-fallback-2026-07-01 . I target consentiti per Fable 5.1 sono Opus 4.8 e Opus 5

Il fallback predefinito si attiva solo quando la categoria di rifiuto ha un target consigliato. Un rifiuto reasoning_extraction testato non ha attivato il fallback; ispeziona usage.iterations invece di assumere che ogni rifiuto venga ritentato. Come detto prima, passare a un modello più vecchio elimina anche i blocchi di thinking di Fable 5.1.

Servi l’agente Claude Fable 5.1 con FastAPI

L’agente locale ora può esporre lo stesso workflow tramite un’API HTTP.

Crea l’endpoint del piano

Se ti serve solo uno script locale, salta questa sezione. Per un servizio web, usa FastAPI con AsyncAnthropic. Crea un client per il processo in un gestore di lifespan. Importa schema e prompt dal modulo dell’agente esistente.

@asynccontextmanager
async def lifespan(_: FastAPI):
    global client
    client = AsyncAnthropic()
    try:
        yield
    finally:
        await client.close()


@app.post("/plan", response_model=PlanResponse)
async def create_plan(body: PlanRequest):
    reader = resolve_project(body.project)
    messages, totals, turns, tool_calls = await inspect(reader, body.feature_request)
    plan, final_usage = await write_plan(messages)
    totals.add(final_usage)
    return PlanResponse(plan=plan, turns=turns, tool_calls=tool_calls, usage=as_usage(totals))

Nota che il chiamante invia un nome di progetto, non un percorso. resolve_project() lo mappa a uno dei pochi root consentiti, così una richiesta non può chiedere al server di leggere in posizioni arbitrarie. Questo servizio mappa i rifiuti a 422 come scelta applicativa. L’API di Claude li restituisce come HTTP 200.

Eseguilo con uvicorn app:app --reload. La documentazione interattiva è disponibile su http://localhost:8000/docs.

L’endpoint restituisce un piano con un costo stimato. Video dell’autore.

L’endpoint /plan/stream esegue l’ispezione in un task in background, inserisce eventi di avanzamento e tool in una asyncio.Queue e li emette tramite StreamingResponse. Quando lo stream si chiude, il generatore annulla il task in background. L’ interfaccia Streamlit nel repository rende lo stesso flusso di eventi.

Streamlit mostra l’avanzamento live dell’agente. Video dell’autore.

Checklist di deployment per l’agente Claude Fable 5.1

I limiti e i controlli costruiti prima restano parte del servizio. Prima del deployment, aggiungi gli elementi operativi che non si vedono in locale.

  • Rivedi i due retry di default dell’SDK per risposte 429 e 5xx, poi imposta max_retries e timeout in linea con il budget di latenza del servizio

  • Imposta un timeout di richiesta e verifica che l’attuale cancellazione del task SSE interrompa il lavoro in sospeso quando un client si disconnette

  • Registra ID modello, versione SDK, ID richiesta, motivo di stop e quattro categorie di token per ogni esecuzione

  • Allerta su incremento di scritture in cache, token di output, rifiuti ed esecuzioni che raggiungono il limite di turni

  • Conferma che l’impostazione di retention dell’account corrisponda ai requisiti del modello

  • Blocca la versione dell’SDK e ricontrolla gli header beta prima di ogni release

Quando usare Claude Fable 5.1 invece di Opus 5 o Sonnet 5

  • Anthropic consiglia Opus 5 come default ragionevole.
  • Prova Fable 5.1 quando Opus 5 non basta per analisi di repository lunghe, debugging complesso o task agentici con grande contesto.
  • Per lavoro su repository e task quotidiani, confronta Sonnet 5 e Opus 5 su qualità, latenza e costo.
  • Per classificazione, estrazione, risposte brevi e richieste semplici, Sonnet 5 è un buon default; per i task più semplici, anche Haiku 4.5 potrebbe bastare.

Non scegliere Fable 5.1 solo perché è più recente. Una singola richiesta può comunque usare effort e output strutturati; lo streaming funziona anche. Non trae beneficio dal loop o dalla cache di prefissi ripetuti usati qui.

Considerazioni finali

Il piano generico della mia prima chiamata è diventato utile solo dopo che l’agente ha letto il repository. Nell’esecuzione completa ha ispezionato 12 file in tre turni, mentre output e scritture in cache hanno rappresentato il 99,5% del costo stimato. Terrei il perimetro sui percorsi e la cronologia append-only, poi testerei se un effort più basso riduce i costi senza far saltare i tool sul repository al modello.

Se una risposta può risolvere il task, fermati agli output strutturati. Usa il tool loop quando la risposta deve dipendere dai file del repository o quando serve riportare l’avanzamento tra le chiamate.

Per dettagli sulla selezione dei modelli, ti consiglio il nostro corso Introduzione ai modelli Claude. Per prompting e workflow degli agenti, vedi il corso Sviluppo software con Cursor.

FAQ

Claude Fable 5.1 può leggere immagini oltre al codice?

Sì. Accetta input di immagini e può leggere grafici e PDF. Ho lasciato la vision fuori dall’esempio principale perché il piano sul repository non ne ha bisogno. Se estendessi questo agente per pianificare una modifica alla UI, invierei lo screenshot attuale insieme alla richiesta di funzionalità. Ridimensionalo prima se i dettagli visivi piccoli non influiscono sul task.

Perché il mio agente è diventato più lento dopo il passaggio da Fable 5?

Controlla i risultati dei tool prima di dare la colpa al modello. Se l’istruzione di batching vista prima è già presente, confronta sia il numero che le dimensioni. Il reader attuale limita ciascun file a 40.000 byte. Se è ancora troppo grande, aggiungi argomenti per intervalli di righe o ricerca in modo che il tool possa restituire solo le sezioni rilevanti.

Perché Claude Fable 5.1 restituisce un 400 invalid_request_error?

Non fare retry come prima cosa. Un invalid_request_error di solito indica una forma della richiesta o un’impostazione dell’account da cambiare. In questo progetto, le cause probabili sono tool_choice forzato, un’impostazione di retention incompatibile, un prefisso modificato con thinking preservato o un campo beta inviato senza il relativo header. Risolvi la causa indicata e poi invia di nuovo la richiesta.

Dovrei mettere in cache i file sorgente o un riepilogo?

Io uso questa regola: metti in cache i file sorgente quando il codice esatto conta per più turni. Se i passaggi successivi hanno bisogno solo dell’architettura o della mappa dei file, metti in cache un riepilogo. Il riepilogo costa meno token, ma potrebbe omettere la riga che serve al piano finale.

La Batch API può eseguire questo agente?

Non di per sé. La Batch API invia singole richieste Messages; non esegue questo tool loop lato client. La userei per revisioni di repository autoconclusive quando non servono progressi live. Eseguire il loop completo in batch richiede il tuo codice per elaborare le richieste dei tool di un batch prima di inviare il successivo.


Khalid Abdelaty's photo
Author
Khalid Abdelaty
LinkedIn

Sono un data engineer e community builder: lavoro su pipeline dati, cloud e strumenti di AI, e scrivo tutorial pratici e ad alto impatto per DataCamp e per sviluppatori alle prime armi.

Argomenti

Impara l’IA con DataCamp!

Programma

Ingegnere AI associato per sviluppatori

26 h
Scopri come integrare l'intelligenza artificiale nelle applicazioni software utilizzando API e librerie open-source. Inizia oggi il tuo percorso per diventare un ingegnere AI!
Vedi dettagliRight Arrow
Inizia Il Corso
Mostra altroRight Arrow
Correlato

blog

I 15 migliori server MCP remoti che ogni AI builder dovrebbe conoscere nel 2026

Scopri i 15 migliori server MCP remoti che stanno trasformando lo sviluppo AI nel 2026. Scopri come migliorano automazione, ragionamento, sicurezza e velocità dei workflow.
Abid Ali Awan's photo

Abid Ali Awan

15 min

blog

Tokenizzazione nel NLP: come funziona, sfide e casi d'uso

Guida al preprocessing NLP nel machine learning. Copriamo spaCy, i transformer di Hugging Face e come funziona la tokenizzazione in casi d'uso reali.
Abid Ali Awan's photo

Abid Ali Awan

10 min

blog

Che cos'è Snowflake? Guida per principianti alla piattaforma dati cloud

Esplora le basi di Snowflake, la piattaforma dati cloud. Scopri la sua architettura, le sue funzionalità e come integrarla nelle tue pipeline di dati.
Tim Lu's photo

Tim Lu

12 min

Mostra AltroMostra Altro