# 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

---

## 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:

- un costo **economico** — ogni token si paga in input;
- un 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.

## 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"**.

## 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.

---

## I quattro strati del contesto

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

```
┌───────────────────────────────────────────────────────────────────┐
│ 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               │
└───────────────────────────────────────────────────────────────────┘
```

## Il flusso di decisione

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

```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]
```

---

## Le sei inversioni

### 1. Regole → 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.

### 2. Esempi → 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.

### 3. Tutto 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.

### 4. Ripetere → 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.

### 5. Memoria 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.

### 6. Specifiche 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".

---

## Dove la fiducia nel giudizio NON si applica

| Situazione | Perché 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 **sì** → è 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".

## 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.

---

## 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.
```

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

*Un ingegnere esperto che legge solo il repo lo saprebbe?*

| Righe | Verdetto | Azione |
|---|---|---|
| `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** |

### Passo 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.
```

### Passo 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.

### Passo 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**.

### Passo 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.

---

## Riferimenti

- **"The new rules of context engineering for Claude 5 generation models"** — https://claude.com/blog/the-new-rules-of-context-engineering-for-claude-5-generation-models
  Fonte delle sei inversioni, della struttura a quattro strati, del caso di studio sulla rimozione dell'80% del system prompt di Claude Code, e del comando `/doctor`.
- Note correlate: [Prompting Claude Opus 5](./prompting-opus-5.md) — il livello sotto: come formulare le richieste a quel modello, quali comportamenti nativi *sottrarre* dal prompt · [Scegliere il modello Claude giusto](./using-claude-models.md)
