Context engineering per i modelli di generazione 5

Sintesi della lezione basata sul blog Claude «The new rules of context engineering for Claude 5 generation models».

Fonte: https://claude.com/blog/the-new-rules-of-context-engineering-for-claude-5-generation-models

§ 01cosa entra
quando
in quale forma

Il concetto in una frase

Il context engineering è la disciplina che decide cosa entra nella finestra di contesto di un modello, quando entra e in quale forma. Non è prompt engineering: quello ottimizza la singola formulazione di una richiesta, questo progetta l'intero budget informativo di un agente — system prompt, file di memoria del progetto, descrizioni degli strumenti, skill caricate su richiesta, file allegati.

Esiste perché la finestra di contesto è una risorsa a somma zero con due costi distinti:

Costo economico

ogni token si paga in input.

Costo attenzionale

più istruzioni concorrenti ci sono, più il modello deve deliberare su quale prevalga, e più facilmente sbaglia priorità.

Un contesto gonfio non è solo caro: è confuso.

§ 0280% rimosso
zero perdita
misurabile

La tesi: la strategia si è invertita

Anthropic ha rimosso oltre l'80% del system prompt di Claude Code — le istruzioni di base iniettate a ogni sessione — senza perdita di prestazioni misurabile. Il motivo: quelle istruzioni non aggiungevano capacità, compensavano limiti dei modelli precedenti. Rimosso il limite, il compenso diventa un peso.

Il cambio di paradigma in una riga: da «prescrivere regole» a «progettare interfacce e caricare al momento giusto, fidandosi del giudizio del modello».

§ 03tre termini
da fissare

Tre termini da fissare subito

Progressive disclosure

(rivelazione progressiva) — tenere fuori dal contesto iniziale ciò che serve raramente, e renderlo raggiungibile su richiesta (una skill che il modello invoca, uno strumento la cui definizione completa viene recuperata solo quando serve).

Skill

una cartella con un file di istruzioni che il modello carica solo quando il compito lo richiede; nel contesto iniziale resta soltanto una riga di descrizione.

Deferred loading

(caricamento differito) — per gli strumenti, dichiararne l'esistenza senza iniettarne lo schema completo; il modello cerca e carica lo schema al bisogno.

§ 04quattro strati
per volatilità

I quattro strati del contesto

Ordinati per volatilità, da fisso a per-sessione, con il criterio di cosa mettere dove:

I quattro strati del contesto, ordinati per volatilità 1 SYSTEM PROMPT volatilità: ~mai «in quale prodotto sei e qual è la tua funzione» lo scrive chi costruisce l'agente, non l'utente 2 CLAUDE.md volatilità: per-repo SOLO le trappole non deducibili dal filesystem «usiamo TypeScript» ← lo vede da tsconfig.json «tutti i tipi vivono in types.ts, non accanto al modulo» 3 SKILL volatilità: per-compito procedure e opinioni del team, caricate SOLO se pertinenti nel contesto iniziale c'è solo la descrizione di una riga 4 REFERENCE (@mention) volatilità: per-richiesta la specifica del lavoro di adesso preferisci codice eseguibile a prosa descrittiva
Sorgente del diagramma (ASCII)
┌───────────────────────────────────────────────────────────────────┐
│ 1. SYSTEM PROMPT              volatilità: ~mai                    │
│    "in quale prodotto sei e qual è la tua funzione"               │
│    lo scrive chi costruisce l'agente, non l'utente                │
├───────────────────────────────────────────────────────────────────┤
│ 2. CLAUDE.md                  volatilità: per-repo                │
│    SOLO le trappole non deducibili dal filesystem                 │
│    ✗ "usiamo TypeScript"      ← lo vede da tsconfig.json          │
│    ✓ "tutti i tipi vivono in types.ts, non accanto al modulo"     │
├───────────────────────────────────────────────────────────────────┤
│ 3. SKILL                      volatilità: per-compito             │
│    procedure e opinioni del team, caricate SOLO se pertinenti     │
│    nel contesto iniziale c'è solo la descrizione di una riga      │
├───────────────────────────────────────────────────────────────────┤
│ 4. REFERENCE (@mention)       volatilità: per-richiesta           │
│    la specifica del lavoro di adesso                              │
│    preferisci codice eseguibile a prosa descrittiva               │
└───────────────────────────────────────────────────────────────────┘
§ 05albero
di decisione
dove metterla

Il flusso di decisione

Hai un'informazione in mano e non sai dove metterla:

Dove collocare un'informazione destinata all'agente Ho un'informazione da dare all'agente Il modello la scopre da solo dal codice o dai file? Non scriverla. È rumore. Serve in OGNI sessione? no È una procedura o una convenzione di processo? Riguarda l'uso di UNO strumento? Skill caricata su richiesta no, è la specifica di questo lavoro Esiste in forma di codice? Descrizione dello strumento e NON duplicata altrove no È un vincolo o un obiettivo? no Reference: test suite, signature, mockup HTML Reference: documento, ma scritto per essere eseguito vincolo: azione irreversibile, sicurezza, contratto obiettivo: stile, qualità, gusto System prompt / CLAUDE.md esplicito e imperativo Descrivi il RISULTATO, non la regola meccanica
Sorgente del diagramma (mermaid)
flowchart TD
    A[Ho un'informazione<br/>da dare all'agente] --> B{Il modello la scopre<br/>da solo dal codice<br/>o dai file?}
    B -->|Si'| Z[Non scriverla.<br/>E' rumore.]
    B -->|No| C{Serve in<br/>OGNI sessione?}

    C -->|No| D{E' una procedura<br/>o una convenzione<br/>di processo?}
    D -->|Si'| E[Skill<br/>caricata su richiesta]
    D -->|No, e' la specifica<br/>di questo lavoro| F{Esiste in forma<br/>di codice?}
    F -->|Si'| G[Reference: test suite,<br/>signature, mockup HTML]
    F -->|No| H[Reference: documento,<br/>ma scritto per essere eseguito]

    C -->|Si'| I{Riguarda l'uso<br/>di UNO strumento?}
    I -->|Si'| J[Descrizione dello strumento<br/>e NON duplicata altrove]
    I -->|No| K{E' un vincolo<br/>o un obiettivo?}
    K -->|Vincolo: azione irreversibile,<br/>sicurezza, contratto| L[System prompt / CLAUDE.md<br/>esplicito e imperativo]
    K -->|Obiettivo: stile,<br/>qualita', gusto| M[Descrivi il RISULTATO,<br/>non la regola meccanica]
§ 06sei inversioni
dal meccanismo
al criterio

Le sei inversioni

01Regole → giudizio

Il caso concreto. Il vecchio system prompt di Claude Code diceva:

«Default to writing no comments. Never write multi-paragraph docstrings or multi-line comment blocks — one short line max»

Il nuovo dice:

«Write code that reads like the surrounding code: match its comment density, naming, and idiom.»

La differenza strutturale non è la lunghezza, è il livello di astrazione. La prima versione specifica il meccanismo (zero commenti, una riga massimo); la seconda specifica il criterio di successo (coerenza col codice circostante). La prima fallisce in ogni caso limite non previsto — una funzione crittografica che ha davvero bisogno di tre paragrafi, un codebase in cui tutti i moduli hanno docstring complete. La seconda si adatta perché delega la valutazione a chi ha il codice davanti.

02Esempi → interfacce espressive

Contro-intuitivo. Gli esempi d'uso negli strumenti sembrano gratis: sono informazione, come può fare male? Fanno male perché ancorano. Un esempio che mostra come esplorare un file con grep suggerisce implicitamente che quello è il pattern giusto, e il modello lo replica anche dove un approccio diverso sarebbe migliore.

L'alternativa: rendere autoesplicativo lo schema dello strumento. Un enum di stato — pending, in_progress, completed — con la nota «tieni un solo elemento in in_progress» comunica il vincolo dalla forma dei parametri, non da una traccia da imitare.

03Tutto in anticipo → progressive disclosure

Caso reale: le procedure di code review e verifica sono uscite dal system prompt di Claude Code e sono diventate skill invocabili. Il guadagno è doppio — token liberati per il lavoro vero, e nessuna istruzione di verifica che interferisce quando il compito non è una verifica.

04Ripetere → dire una volta sola

I modelli più vecchi rispondevano meglio alle istruzioni ripetute e a quelle poste a fine contesto (il classico recency bias). Da qui la pratica di duplicare le regole d'uso di uno strumento sia nel system prompt sia nella sua descrizione. Sui modelli attuali la ripetizione non aggiunge aderenza — e ha un costo nascosto: due copie divergono, e due istruzioni in conflitto sullo stesso strumento sono peggio di una sola.

05Memoria manuale → auto-memoria

Il modello preserva da sé ciò che è rilevante per il lavoro in corso e per le preferenze dell'utente, riducendo il bisogno di annotare a mano nei file di progetto.

06Specifiche semplici → riferimenti ricchi

Il punto più operativamente utile: il codice è un linguaggio di specifica più preciso della prosa, e il modello lo parla nativamente.

  • Un mockup HTML del layout desiderato batte una descrizione del layout — e batte anche uno screenshot.
  • Una test suite che passa/fallisce batte un elenco di requisiti.
  • Una rubrica esplicita di valutazione batte «fai un buon lavoro».
§ 07quattro casi
dove il vincolo
esplicito resta

Dove la fiducia nel giudizio NON si applica

SituazionePerché il vincolo esplicito resta necessario
Azioni irreversibili (cancellazioni, push, invii, pagamenti) Il costo dell'errore è asimmetrico. Nessun guadagno di flessibilità compensa un rm -rf sbagliato. Anthropic cita la cancellazione involontaria di file come il caso che giustificava le regole rigide.
Contratti (API pubbliche, schemi DB, formati di serializzazione) Il «giudizio» del modello su cosa sia una firma sensata non ha accesso ai consumatori a valle.
Convenzioni arbitrarie del team Se la scelta non è deducibile dal codice né migliore di alternative (es. «gli ID sono sempre snake_case anche in TypeScript»), il modello non può inferirla. Va scritta.
Sicurezza e privacy Un vincolo che protegge terzi non è materia di gusto.

L'errore speculare: de-vincolare tutto

La rimozione riguarda le regole compensatorie (quelle che curavano un difetto del modello), non quelle informative (quelle che comunicano un fatto sul tuo mondo che il modello non può conoscere).

Il test per distinguerle:

Se un collega senior esperto leggesse solo il codice, saprebbe questa cosa?

Se → è compensatoria: cancellala.

Se no → è informativa: tienila.

Il costo nascosto della progressive disclosure

La progressive disclosure sposta il costo, non lo elimina. Una skill non caricata costa zero token ma costa una decisione: il modello deve accorgersi che gli serve. Se la descrizione di una riga è vaga, la skill non viene mai invocata e la conoscenza è di fatto persa.

La descrizione della skill è quindi il pezzo di context engineering più denso di tutto il sistema: è l'unica parte sempre presente, e determina se il resto esiste o no.

Corollario pratico: le condizioni di attivazione vanno nella descrizione, non nel corpo — «attivami quando l'utente chiede X, Y, Z» batte «questa skill riguarda X».

§ 08quattro
distinzioni

Da non confondere

  • Context engineering ≠ prompt engineering

    il secondo è un sottoinsieme: riguarda la formulazione di un turno. Il primo riguarda l'architettura informativa persistente.

  • Progressive disclosure ≠ compattazione (compaction)

    la rivelazione progressiva evita di far entrare informazione non necessaria; la compattazione riassume informazione già entrata quando lo spazio finisce. Sono complementari: buona progressive disclosure fa scattare la compattazione più tardi.

  • Progressive disclosure ≠ context editing

    il context editing rimuove blocchi vecchi (risultati di strumenti già consumati); la progressive disclosure li tiene fuori dall'inizio.

  • Progressive disclosure ≠ RAG

    nel RAG un sistema esterno recupera passaggi per similarità e li inietta. Nella progressive disclosure è il modello che decide di leggere una risorsa nominata, con controllo esplicito e tracciabile su cosa ha letto.

Strumento diagnostico: il comando /doctor di Claude Code analizza system prompt, skill e file di progetto per segnalare ridondanze e sovradimensionamenti.

§ 09180 righe

25 righe

Esempio pratico

Scenario

Un team di quattro persone mantiene fleet-api, un servizio TypeScript/Node che espone gli endpoint di gestione flotta. Hanno un CLAUDE.md cresciuto per accumulo in otto mesi: 180 righe, scritto quando usavano un modello di generazione precedente.

L'agente si comporta male in due modi opposti: a volte è paralizzato (chiede conferma per rinominare una variabile locale), a volte fa la cosa sbagliata con sicurezza (aggiunge migrazioni al database senza chiedere, perché nessuna regola lo vietava mentre venti regole vietavano cose innocue).

Il file attuale, abbreviato ai punti rappresentativi:

markdown
# fleet-api

Questo progetto è scritto in TypeScript e usa Node.js 20.
Il package manager è pnpm. I test girano con vitest.
La struttura è src/routes, src/services, src/db.

## Regole di codice
- NON aggiungere MAI commenti al codice.
- Non scrivere docstring multi-paragrafo. Una riga massimo.
- Usa SEMPRE async/await, MAI .then().
- IMPORTANTE: usa sempre const, mai let se puoi evitarlo.
- CRITICO: preferisci funzioni pure.
- Non usare `any`. MAI. In nessun caso.
- Chiedi conferma prima di modificare qualsiasi file.

## Come fare code review
1. Leggi il diff completo con git diff
2. Per ogni file modificato, controlla che i test coprano il caso
3. Verifica che non ci siano console.log residui
4. Controlla che gli errori siano gestiti con il nostro AppError
5. Verifica che le query non facciano N+1
6. Controlla i permessi sugli endpoint nuovi
   [...altre 14 righe di procedura...]

## Strumento search_fleet
Cerca veicoli. Esempio: search_fleet({query: "targa AB123CD"}).
Esempio: search_fleet({query: "depot Milano"}).
IMPORTANTE: usa sempre search_fleet quando l'utente chiede di un veicolo.
Ricorda: search_fleet va usato per cercare veicoli.

01Passo 1 — Classifica ogni riga con il test del collega senior

Un ingegnere esperto che legge solo il repo lo saprebbe?

RigheVerdettoAzione
TypeScript, Node 20, pnpm, vitest, struttura cartelle Sì — deducibile da package.json, tsconfig.json, pnpm-lock.yaml, un ls Cancellare. ~5 righe di puro rumore che consumano attenzione a ogni turno
async/await, const, funzioni pure, no any Sì — lo vede dallo stile del codice esistente. Regole compensatorie Sostituire con un criterio
Chiedi conferma prima di modificare qualsiasi file È la causa della paralisi: vincolo scritto troppo largo per proteggere un caso stretto Restringere all'irreversibile
Come fare code review (20 righe) Informativo (procedura del team) ma serve solo in una sessione su venti Spostare in skill
Strumento search_fleet Esempi che ancorano + stessa istruzione ripetuta 3 volte + posto sbagliato (file di progetto invece della descrizione dello strumento) Riprogettare l'interfaccia

02Passo 2 — Riscrivi il CLAUDE.md

Da 180 righe a queste:

markdown
# fleet-api

Servizio di gestione flotta: gli endpoint che i clienti usano per
tracciare veicoli e depositi.

## Trappole non ovvie

- **I tipi condivisi vivono tutti in `src/types.ts`**, non accanto al
  modulo che li usa. Aggiungere un tipo accanto al modulo rompe la
  generazione dell'SDK client (`pnpm gen:sdk` legge solo quel file).
- **Gli errori HTTP passano da `AppError`.** Un `throw` generico
  bypassa il middleware e finisce in 500 anonimi, invalidando la
  correlazione di trace.
- **`src/db/legacy/` è congelato.** È letto dal vecchio importer
  batch che non è nel repo. Modificarlo rompe un consumatore
  che non vedi.

## Vincoli

Chiedi conferma prima di: creare migrazioni del database, modificare
la firma di un endpoint pubblico, toccare i workflow CI. Per tutto il
resto — refactor, rinomine locali, test, fix — procedi.

## Stile

Scrivi codice che si legga come quello che lo circonda: eguaglia la
densità di commenti, i nomi e gli idiomi del modulo in cui stai
lavorando.

03Passo 3 — Estrai la procedura di review in una skill

File .claude/skills/code-review/SKILL.md, con la descrizione scritta per essere trovata:

markdown
---
name: code-review
description: Attivami quando l'utente chiede di rivedere un diff, una
  PR o "controlla le mie modifiche" su fleet-api. Esegue la checklist
  di review del team (copertura test, AppError, query N+1, permessi
  sugli endpoint nuovi).
---

# Review di fleet-api

[le 20 righe di procedura, ora fuori dal contesto di default]

Le 20 righe scendono a una riga di descrizione sempre presente. La condizione di attivazione — «rivedi un diff / una PR / le mie modifiche» — sta nella descrizione, che è l'unico pezzo che il modello vede sempre.

04Passo 4 — Riprogetta lo strumento invece di istruirlo

Le istruzioni ripetute e gli esempi spariscono; il vincolo passa nello schema:

typescript
{
  name: "search_fleet",
  description:
    "Cerca veicoli o depositi nella flotta. Chiamalo quando la " +
    "domanda dipende dallo stato attuale della flotta (posizione, " +
    "disponibilità, assegnazione) — non rispondere a memoria.",
  input_schema: {
    type: "object",
    properties: {
      scope: {
        type: "string",
        enum: ["vehicle", "depot"],
        description: "Cosa cercare."
      },
      match: {
        type: "string",
        description:
          "Targa (AB123CD), ID deposito, o nome città del deposito."
      },
      status: {
        type: "string",
        enum: ["active", "maintenance", "decommissioned"],
        description:
          "Filtra per stato. Omesso = solo 'active'."
      }
    },
    required: ["scope", "match"],
    additionalProperties: false
  }
}

L'enum su scope comunica il dominio meglio di due esempi, il campo match documenta i formati accettati nel punto in cui il modello li deve produrre, e il default implicito di status è dichiarato dove serve. La frase quando chiamarlo — la parte prescrittiva che vale ancora la pena scrivere — sta nella descrizione dello strumento e da nessun'altra parte.

05Passo 5 — Passa le specifiche in codice

Per il lavoro successivo (un nuovo endpoint di assegnazione veicolo), il team smette di scrivere un documento di requisiti e scrive invece la test suite che l'endpoint deve superare, allegandola con @:

typescript
// tests/routes/assign.spec.ts  — questa È la specifica
describe("POST /vehicles/:id/assign", () => {
  it("assegna un veicolo attivo a un deposito", async () => { /* ... */ });
  it("rifiuta con 409 se il veicolo è in maintenance", async () => { /* ... */ });
  it("rifiuta con 403 se il chiamante non possiede il deposito", async () => { /* ... */ });
  it("è idempotente: riassegnare allo stesso deposito è 200", async () => { /* ... */ });
});

Risultato atteso

Tre effetti misurabili e uno strutturale.

Il contesto di default scende da ~180 righe a ~25 più una riga di descrizione della skill: i token liberati vanno al codice vero, e il modello non deve più arbitrare tra venti imperativi in conflitto prima di ogni azione.

La paralisi sparisce perché il vincolo «chiedi conferma» è passato da qualsiasi file a tre categorie irreversibili nominate. E specularmente il buco si chiude: le migrazioni al database, che prima non erano coperte da nessuna regola, ora lo sono esplicitamente — perché l'esercizio di riscrittura ti costringe a chiederti quali sono davvero le azioni costose, invece di sperare che una rete a maglie larghe le catturi.

Lo stile diventa corretto nei casi limite: nel modulo crittografico, dove il codice circostante ha docstring complete, l'agente le scrive; nei route handler, dove non ce ne sono, non le scrive. Nessuna delle due era ottenibile con «mai commenti, una riga massimo».

E la specifica in test suite si verifica da sé: pnpm test è il criterio di completamento, non il giudizio del modello su un documento in prosa. Il caso di idempotenza — quello che in un elenco di requisiti a prosa si perde sempre — è un test che passa o rompe.

Il criterio di correttezza per giudicare il tuo CLAUDE.md dopo questo esercizio è netto:

Ogni riga superstite deve essere un fatto sul tuo mondo che il codice non racconta.

Se una riga insegna al modello a fare il suo lavoro, quel modello non ne ha più bisogno.

§ 10fonti
note correlate

Riferimenti