Salta al contenuto
15/30Capitolo 15 di 30

Prompt Engineering misurato: cosa cambia l’output

Sessanta ticket, stesse parole in sei ordini: accuratezza dal 26,7% all’85,0%. Poi quattro trucchi da internet con barre d’errore.

In questa pagina

Ecco un ticket di supporto e quattro code a cui potrebbe andare.

TEXT
The label on the parcel has my old surname on it.
    -> billing / technical / shipping / account

Per instradarlo ti servono tre cose nel prompt: le definizioni delle code, il ticket e l’istruzione di sceglierne una. Tre blocchi. Puoi metterli in sei ordini diversi, e i blocchi contengono esattamente gli stessi caratteri in tutti e sei.

Su sessanta ticket con risposte note, i sei ordini ottengono punteggi tra 26,7 % e 55,0 %. Sposta gli stessi due blocchi dal turno user al turno system, senza cambiare una sola parola, e lo stesso modello ottiene 76,7 %. Avvolgi il ticket in un tag in stile XML e arriva a 85,0 %.

Del modello non è cambiato nulla. Del task non è cambiato nulla. Non è stata riscritta una sola parola. Un’oscillazione di cinquantotto punti è uscita dal modo in cui è stato disposto lo stesso testo.

Questo è il motivo per cui esiste questo capitolo, ed è anche il motivo per cui è l’argomento più infestato dal cargo cult dell’intero campo. Gli effetti sono reali e grandi, il che fa sembrare confermato ogni aneddoto; e sono instabili tra modelli e task, il che significa che la maggior parte dei consigli non è mai altro che un aneddoto. Quindi questo capitolo ha una regola, e tutto ciò che contiene è subordinato a quella regola:

Un prompt si misura, non si discute. Quattro varianti su venti casi non distinguono proprio nulla.

Prima delle misurazioni, un fatto che spiega in silenzio metà di ciò che segue.

Il modello non ha memoria. Tra due chiamate non conserva nulla: non la tua ultima domanda, non la sua ultima risposta, non il file che hai allegato, non il fatto che glielo hai già chiesto due volte. Ogni chiamata parte da una macchina vuota, e l’unica cosa che quella macchina conosce è la sequenza di token che le hai appena consegnato.

Ciò che sembra memoria in un’interfaccia chat è il tuo client che reinvia tutta la conversazione, ogni turno, dall’inizio. Il modello rilegge tutto da zero, ogni volta. Il Capitolo 13 ha misurato quanto costa quella rilettura in un forward pass; il Capitolo 16 la trasforma in una riga su una fattura. Qui conta la conseguenza per il design: il prompt non è un messaggio a un sistema che ha uno stato. È lo stato.

Questo manda in pensione una famiglia di confusioni. «Il modello ha dimenticato quello che gli avevo detto» di solito significa che non gli è mai stato inviato. «Ha ignorato la mia istruzione precedente» di solito significa che l’istruzione è uscita dalla window quando la cronologia è stata troncata. «In produzione si è comportato diversamente» di solito significa che la produzione assembla un prompt diverso da quello che hai testato. Nessuno di questi è un problema del modello, e nessuno si risolve riscrivendo qualcosa.

Dire «questo prompt è migliore» è un’affermazione su una distribuzione, e non puoi vedere una distribuzione guardando un solo output. Quello che ti serve è noioso: casi con risposte note, N varianti e un intervallo.

L’harness è cinquanta righe di TypeScript con la stessa forma del client del Capitolo 14: una richiesta, una deadline, un po’ di concorrenza, un conteggio. Ricompare nel Capitolo 19 per valutare un retriever e nel Capitolo 29 come golden set.

bench.tsTS
export type Case = { input: string; expected: string };
export type Variant = { name: string; build: (c: Case) => ChatMessage[] };

async function pooled<T, R>(xs: T[], n: number, f: (x: T) => Promise<R>) {
  const out: R[] = new Array(xs.length);
  let i = 0;
  await Promise.all(
    Array.from({ length: n }, async () => {
      while (i < xs.length) {
        const k = i++;
        out[k] = await f(xs[k]);
      }
    }),
  );
  return out;
}

export async function runVariant(v: Variant, cases: Case[], concurrency = 6) {
  const hits = await pooled(cases, concurrency, async (c) => {
    const answer = await complete(v.build(c));      
    return answer.trim().toLowerCase() === c.expected;
  });
  return { name: v.name, hits, k: hits.filter(Boolean).length, n: cases.length };
}

Il numero che torna non è il risultato. Questo lo è:

stats.tsTS
/** 95 % Wilson score interval for a proportion. Chapter 4 derives it. */
export function wilson(k: number, n: number, z = 1.96) {
  const p = k / n;
  const d = 1 + (z * z) / n;
  const centre = (p + (z * z) / (2 * n)) / d;
  const half = (z * Math.sqrt((p * (1 - p)) / n + (z * z) / (4 * n * n))) / d;
  return [Math.max(0, centre - half), Math.min(1, centre + half)] as const;
}

Il Capitolo 4 ha formulato l’argomento e questo capitolo lo incassa. Diciassette risposte giuste su venti sono l’85 %, e il suo intervallo al 95 % va dal 64 % al 95 %. Una variante che fa 13 su 20 — 65 %, che sembra chiaramente peggiore — ha un intervallo dal 43 % all’82 %. Quei due intervalli si sovrappongono per quasi tutta la loro lunghezza. Venti casi non possono distinguere un buon prompt da uno mediocre, e la maggior parte dei consigli pubblicati sui prompt è stata validata su meno casi.

Sessanta casi, che è ciò che usa questo capitolo, non sono ancora molti. Sono abbastanza per vedere effetti grandi e abbastanza onesti da ammettere quando non possono vedere quelli piccoli — e lo ammetteranno più volte qui sotto.

Tre blocchi — le regole R, il ticket T, l’istruzione I — concatenati in un unico messaggio user. Tutte e sei le permutazioni, contenuto byte-identico, sessanta casi ciascuna.

ordine dei tre blocchicorretteaccuratezza, Wilson 95 %
regole, istruzione, ticket33/6055,0 % [42,5, 66,9]
regole, ticket, istruzione30/6050,0 % [37,7, 62,3]
ticket, regole, istruzione22/6036,7 % [25,6, 49,3]
istruzione, ticket, regole21/6035,0 % [24,2, 47,6]
istruzione, regole, ticket17/6028,3 % [18,5, 40,8]
ticket, istruzione, regole16/6026,7 % [17,1, 39,0]

Dal migliore al peggiore ci sono 28,3 punti, e gli intervalli non si sovrappongono, quindi questa non è una storia sul rumore. Poiché ogni braccio viene valutato sugli stessi sessanta item, la domanda più precisa è quella appaiata: tra i casi in cui due bracci non concordano, quanto è sbilanciata la divisione? Passare dall’ordine peggiore al migliore ha trasformato 21 casi in giusti e 4 in sbagliati: probabilità appaiata esatta 0,0009.3

Leggi la tabella per la sua forma più che per il vincitore. Le due righe migliori finiscono entrambe con il ticket; le due peggiori seppelliscono entrambe l’istruzione in mezzo oppure la trascinano dopo i dati. È lo stesso fenomeno che Liu et al. hanno chiamato Lost in the Middle: il materiale ai bordi di un prompt viene usato in modo più affidabile del materiale al centro.4 Il Capitolo 16 prezza la window e il Capitolo 24 misura correttamente l’effetto su lunghezze maggiori, dove il centro collassa come descritto e il recupero proprio alla fine non ricompare. Qui la regola pratica emerge da sola: task in alto, dati in basso, nulla di importante in mezzo.

Ora sposta le stesse parole tra i turni. Il Capitolo 11 ha stabilito che il chat template non è una decorazione intorno al modello ma parte del modello: <|im_start|>system e <|im_start|>user sono token reali che il modello ha visto milioni di volte durante il fine-tuning, esattamente in quelle posizioni. Quindi dovrebbe contare da quale lato di quei marker finisce la tua istruzione, e infatti conta:

dove vivono le stesse parolecorretteaccuratezza, Wilson 95 %
regole e istruzione nel turno system, solo ticket nel turno user46/6076,7 % [64,6, 85,6]
regole nel turno system, istruzione e ticket nel turno user44/6073,3 % [61,0, 82,9]
regole e istruzione nel turno system, istruzione ripetuta dopo il ticket42/6070,0 % [57,5, 80,1]
tutti e tre i blocchi in un unico turno user33/6055,0 % [42,5, 66,9]

Spostare le regole e l’istruzione oltre il confine del template ha comprato 21,7 punti — 19 casi guadagnati, 6 persi, probabilità appaiata 0,0146 — senza cambiare un loro carattere. Questa è la risposta concreta a system prompt versus user prompt: non sono due modi di dire la stessa cosa. Sono due posizioni di token diverse in una struttura su cui il modello è stato addestrato, e la posizione system è dove appartengono le istruzioni che si applicano a tutta la conversazione.

Nota anche la terza riga. Ripetere l’istruzione dopo il ticket — un trucco ampiamente consigliato — ha ottenuto un punteggio inferiore rispetto a dichiararla una volta sola. Su questo modello, su questo task, dirlo due volte è stato peggio che dirlo una.

Delimitatori, e la lezione statistica nascosta dentro

Link alla sezione: Delimitatori, e la lezione statistica nascosta dentro

Stesso prompt, miglior posizionamento, sessanta casi. L’unica cosa che cambia è ciò che circonda il testo del ticket.

come è delimitato il ticketcorretteaccuratezza, Wilson 95 %
un tag in stile XML51/6085,0 % [73,9, 91,9]
niente del tutto48/6080,0 % [68,2, 88,2]
un heading Markdown47/6078,3 % [66,4, 86,9]
un’etichetta, Ticket:46/6076,7 % [64,6, 85,6]
hash fences45/6075,0 % [62,8, 84,2]
triple backticks44/6073,3 % [61,0, 82,9]
virgolette doppie40/6066,7 % [54,1, 77,3]

Diciotto punti di spread dalla punteggiatura. Ma guarda i due intervalli estremi: [73,9, 91,9] e [54,1, 77,3]. Si sovrappongono. Secondo la lettura grossolana — confronta le barre d’errore, e se si toccano non dire nulla — questa tabella non prova proprio niente.

La lettura grossolana qui è sbagliata, e capire perché vale più della tabella. Ogni variante è stata valutata sugli stessi sessanta ticket, quindi le due misurazioni non sono campioni indipendenti; sono appaiate. Gran parte della larghezza di ogni intervallo viene da una fonte di incertezza condivisa da entrambi i bracci: se questi sessanta ticket siano rappresentativi. E quella fonte si cancella quando li confronti tra loro. Fai invece la domanda appaiata e la risposta è netta: passare dalle virgolette doppie al tag XML ha trasformato 12 casi in giusti e 1 in sbagliato, probabilità appaiata 0,0034. Questa è una differenza reale.

E poi lo stesso test sgonfia il titolo. Il tag XML ha battuto l’etichetta semplice Ticket: di 8,3 punti, che è il numero che un post di blog metterebbe nel titolo. Appaiato: 6 guadagnati, 1 perso, probabilità 0,1250. Non stabilito. Sette casi sono ciò su cui poggia quel famoso miglioramento.

Quindi ci sono due domande con due strumenti diversi, e confonderle è il modo in cui i consigli sui prompt sbagliano in entrambe le direzioni allo stesso tempo:

Quanto è buono questo prompt? L’intervallo di Wilson sulla sua accuratezza. Ampio a meno che tu non abbia centinaia di casi. È il numero che riferisci a qualcuno che deve decidere se andare in produzione.

B è meglio di A? Il test appaiato sui casi in cui non concordano. Molto più sensibile, perché la difficoltà condivisa del set si cancella. È il numero che usi per decidere tra due candidati.

Il risultato generale — che i modelli sono fortemente e imprevedibilmente sensibili a scelte di formattazione prive di contenuto semantico — non è nuovo. Sclar et al. hanno variato soltanto separatori, spaziatura e maiuscole/minuscole su decine di task e hanno trovato spread di accuratezza abbastanza ampi da invertire ranking di modelli pubblicati.5 La conseguenza pratica non è «usa tag XML». È che la formattazione è un hyperparameter, non costa nulla da esplorare, e qualsiasi confronto tra due modelli che fissa un formato sta confrontando i formati tanto quanto i modelli.

L’in-context learning — mostrare al modello esempi risolti nel prompt e farlo generalizzare da quelli senza alcun aggiornamento dei pesi — è la capacità che ha reso famoso GPT-3.6 La domanda pratica non è mai se funzioni. È quanti esempi valga la pena pagare.

Gli esempi entrano come veri turni precedenti, alternando user e assistant, perché questa è la struttura su cui il template è stato addestrato. Ogni k è stato eseguito con cinque diverse estrazioni casuali da un pool disgiunto di sedici ticket etichettati:

esempiaccuratezza mediaestrazione peggiore e migliorespread tra estrazioni
076,7 %
178,7 %78,3 – 80,0 %1,7 punti
283,7 %80,0 – 86,7 %6,7 punti
481,7 %78,3 – 86,7 %8,3 punti
883,7 %78,3 – 88,3 %10,0 punti
1689,3 %85,0 – 93,3 %8,3 punti

Due esempi hanno comprato sette punti. I successivi sei esempi non hanno comprato nulla di misurabile: 83,7, poi 81,7, poi 83,7, una sequenza che vaga dentro il proprio rumore. Sedici ne hanno comprati altri cinque e mezzo. La curva non è una salita liscia; è uno scalino, un plateau e uno scalino.

La colonna che conta di più è l’ultima. A k = 8, quali otto esempi ti è capitato di scegliere hanno spostato l’accuratezza di 10 punti: più dell’intero guadagno dal passare da due esempi a otto. E l’ultima riga è la versione più netta: a k = 16 il pool è esaurito, quindi tutte e cinque le run contengono esattamente gli stessi sedici esempi, differendo solo nell’ordine in cui appaiono. Solo l’ordine ha spostato l’accuratezza di 8,3 punti.

Questo è il risultato riportato da Lu et al. e sopravvive ovunque sia stato cercato: l’ordinamento degli esempi è un vero hyperparameter con effetti paragonabili al numero di esempi.7 Quindi il consiglio onesto sul few-shot prompting non è un numero. È:

Parti da zero e aggiungi esempi solo contro una misurazione

Link alla sezione: Parti da zero e aggiungi esempi solo contro una misurazione

I primi due di solito valgono la pena. Oltre, stai indovinando, e l’ipotesi costa token su ogni singola chiamata per tutto il resto della vita del prodotto.

Tratta la selezione come parte del prompt

Link alla sezione: Tratta la selezione come parte del prompt

Due esempi scelti bene battono otto scelti con noncuranza. Se i tuoi esempi arrivano dall’inizio di un foglio di calcolo, quella è la variabile da esplorare prima di aggiungerne altri.

Esplora l’ordine, una volta, e poi congelalo

Link alla sezione: Esplora l’ordine, una volta, e poi congelalo

È gratis, è un effetto reale e, a differenza di gran parte di questo capitolo, non richiede una riscrittura per provarlo.

Quattro esempi che hanno tutti la stessa etichetta insegnano al modello l’etichetta, non il task. Il collasso di questo modello sulla coda elencata per ultima è lo stesso fallimento con un costume diverso.

Ora il folklore. Ognuna di queste è una singola frase anteposta a un system prompt altrimenti identico, sugli stessi sessanta casi.

frase aggiunta al system promptcorretteaccuratezza, Wilson 95 %appaiata contro baseline
niente aggiunto46/6076,7 % [64,6, 85,6]
«Fai un respiro profondo e lavora a questo problema con attenzione.»47/6078,3 % [66,4, 86,9]+4 / −3, p = 1,000
«Questo è molto importante per la mia carriera.»46/6076,7 % [64,6, 85,6]+5 / −5, p = 1,000
«Sei un esperto di livello mondiale in operations del customer support con vent’anni di esperienza.»42/6070,0 % [57,5, 80,1]+3 / −7, p = 0,344
«Ti darò una mancia di $200 se rispondi correttamente.»41/6068,3 % [55,8, 78,7]+1 / −6, p = 0,125
«Sarai penalizzato per ogni ticket che mandi alla coda sbagliata.»25/6041,7 % [30,1, 54,3]+3 / −24, p < 0,001

Quattro delle cinque non hanno fatto nulla. Non «hanno fatto poco»; nulla che sessanta casi appaiati possano vedere. La persona esperta e la tangente hanno entrambe ottenuto un punteggio inferiore rispetto alla baseline intatta, e anche quei cali falliscono il test appaiato: sono rumore che punta verso il basso.

La terza riga è quella su cui soffermarsi. «Questo è molto importante per la mia carriera» ha prodotto esattamente la stessa accuratezza, 46 su 60, e dieci delle sessanta risposte sono cambiate, cinque in ciascuna direzione. La statistica riassuntiva era identica e il comportamento no. Se la tua valutazione è un singolo numero su un set piccolo, un cambiamento che riscrive un sesto dei tuoi output può sembrare un cambiamento che non ha fatto nulla, e lo manderai in produzione credendo che fosse gratis.

E poi la minaccia, che è l’unica frase ad aver spostato l’ago e ad averlo spostato 35 punti verso il basso, trasformando 24 casi da giusti a sbagliati. Non è un artefatto di arrotondamento; è un comportamento del modello diverso. La lezione non è «non minacciare mai un modello». È che il framing emotivo non è inerte. Sposta la distribuzione, a volte con forza, in una direzione che nessuno può prevedere leggendo la frase: ed è proprio per questo che va misurato invece che ragionato.

Una cautela che questo capitolo ti deve: queste cinque frasi sono state testate su un modello piccolo e un task. Alcune hanno supporto pubblicato altrove: «fai un respiro profondo» viene da un paper che ha cercato istruzioni ad alto punteggio invece di inventarle, che è un’affermazione diversa e migliore da quella circolata dopo.8 Ciò che si generalizza non sono le frasi. È che la lista sopravvissuta nei post di blog e la lista che sopravvive alla misurazione sono due liste diverse, e l’unico modo per sapere quale hai in mano è eseguire il bench.

Una regola che tutti ripetono — di’ ciò che vuoi, non ciò che non vuoi — con la solita assenza di un numero. Ecco il numero. Lo stesso requisito di formato, scritto in tre modi, con il modello che genera liberamente così da poter osservare la conformità:

come è scritta la regola di formatol’output era esattamente una parola consentitatoken di output medi
«Rispondi con una parola.»10/60 (16,7 %)2,6
«Non spiegarti. Non scrivere una frase. Non aggiungere punteggiatura.»1/60 (1,7 %)14,0
entrambe insieme41/60 (68,3 %)2,3

Tre divieti hanno fatto peggio di una istruzione, e hanno fatto scrivere al modello cinque volte più testo: l’esatto opposto di tutti e tre contemporaneamente. Aggiungere di nuovo la frase positiva lo ha salvato portandolo al 68 %.

Il meccanismo non è misterioso quando ricordi il Capitolo 8. Il modello sceglie un next token da una distribuzione condizionata da tutto ciò che lo precede, e un divieto mette la cosa proibita dentro quel condizionamento. Non c’è un operatore per la negazione; c’è un contesto in cui ora compare una parola.

Il che è misurabile direttamente. Prendi il prompt baseline e aggiungi una riga: Do not use the shipping queue for software problems. Poi guarda soltanto i quarantacinque ticket che non sono ticket di spedizione:

shipping sceltaprobabilità media su shippingaccuratezza complessiva
baseline11,1 % dei 45 casi0,13176,7 % [64,6, 85,6]
dopo averla vietata per nome37,8 %0,37451,7 % [39,3, 63,8]

Nominare una coda per escluderla ha fatto sì che il modello la scegliesse tre volte più spesso, ha quasi triplicato la massa di probabilità che le assegnava e ha costato 25 punti di accuratezza complessiva: 16 casi persi contro 1 guadagnato, probabilità appaiata 0,0003.

Non pensare a un elefante, misurato. La riscrittura è sempre la stessa: sostituisci il divieto con la regola positiva che lo rende inutile. Non «non usare shipping per problemi software», ma «usa shipping solo quando è coinvolto un pacco fisico».

Il controesempio onesto: chain of thought che costa e non ripaga

Link alla sezione: Il controesempio onesto: chain of thought che costa e non ripaga

Il Capitolo 12 ha costruito correttamente la chain of thought: prima come tecnica di prompting,910 poi come qualcosa addestrato con ricompense verificabili, e si è chiuso con un avvertimento rimandato a questo capitolo: dire a un modello di pensare passo per passo smette di aiutare quando il modello ragiona da solo, e può fare danni. Ecco quell’avvertimento con una tabella sotto, su un task in cui è facile supporre che pensare di più debba essere meglio.

Entrambi i bracci vengono letti con lo stesso strumento nella stessa posizione. L’unica differenza è se una chain of thought scritta dal modello stesso si trovi prima nel contesto.

bracciocorretteaccuratezza, Wilson 95 %token di output extra per caso
nessuna chain of thought37/6061,7 % [49,0, 72,9]0
chain of thought, fino a 60 token34/6056,7 % [44,1, 68,4]53,1
chain of thought, fino a 200 token34/6056,7 % [44,1, 68,4]97,7

L’accuratezza è scesa e il costo è salito, e la regola di questo capitolo si applica al risultato di questo stesso capitolo: il calo è 7 casi guadagnati contro 10 persi, probabilità appaiata 0,629, quindi non è stabilito. Ciò che è stabilito è che ha prodotto novantotto token di output extra per chiamata e non ha comprato nulla di misurabile. L’incertezza è interamente sul lato del beneficio. Il conto è certo.

Una chain che fallisce è più istruttiva di una che funziona. Invitato a ragionare su «La tua integrazione Slack ha smesso di pubblicare messaggi dopo martedì», il modello ha scritto:

TEXT
1. Check if the issue persists on Monday.
2. Verify if there are any updates or changes in your Slack setup that
   might affect message posting.
3. If no update has been made since Tuesday, check for any recent system
   restarts or downtime affecting Slack functionality.
4. If you have recently installed new software or updated your
   environment, ensure it's compatible with Slack version.
5. Contact Slack support for further assistance or troubleshooting steps.

È un consiglio di troubleshooting competente e non è il task. Quando gli è stato chiesto di pensare, il modello è scivolato nel genere a cui «pensa passo per passo a questo ticket di supporto» assomiglia di più nei suoi dati di training, e poi ha risposto a una domanda di classificazione con cinquecento caratteri di ragionamento non correlato nel proprio contesto. La chain of thought aiuta su problemi con stato intermedio che vale la pena calcolare: aritmetica, lookup multi-hop, soddisfazione di vincoli. Instradare una frase in uno di quattro bucket non ha stato intermedio. Non c’è nulla che la chain debba tenere, quindi tutto ciò che fa è aggiungere testo plausibile a cui la decisione finale deve poi sopravvivere.

Due corollari pratici. Primo, per un modello addestrato a ragionare — i modelli RLVR del Capitolo 12 — l’istruzione è peggio che ridondante: può sostituire la lunga chain che il modello avrebbe prodotto con una breve, a forma di prompt. E campionare più chain e votare, che è ciò che fa la self-consistency,11 non può salvare un task in cui non c’è nulla su cui discordare: moltiplica il costo per il numero di campioni per risolvere pareggi che non esistono. Il Capitolo 12 ha misurato quel trade-off dove si applica. Secondo, nota quanto è costato lo scaffold di confronto. Forzare la risposta in una riga Final queue: ha fatto scendere il braccio senza ragionamento dal 76,7 % al 61,7 %. Quindici punti, pagati per rendere comparabili i due bracci. Anche la struttura che esiste per tua comodità non è gratis.

Un’ultima misurazione, perché è la domanda che tutti fanno dopo il primo risultato sorprendente. Sessanta prompt, decodifica greedy, eseguiti ripetutamente:

  • La stessa chiamata ripetuta con tutto mantenuto fisso ha restituito probabilità bit-identiche. Deterministica.
  • La stessa chiamata messa in batch con vicini diversi — batch size 1, 4, 12, 30 e 60 — ha restituito probabilità diverse fino a 0,0128. L’etichetta scelta non è mai cambiata, in 0 casi su 60.

L’etichetta è sopravvissuta perché aveva margine: sui sessanta casi, il gap più stretto tra le prime due code era 0,0459, tre volte e mezzo il drift. La stabilità non era una proprietà dell’algoritmo. Era un margine, e i margini finiscono. Il Capitolo 17 è dove si trova la ragione aritmetica e dove vengono smontate le manopole di sampling che allargano e restringono quei gap. Il motivo per piantarlo qui è che limita ciò che qualsiasi misurazione di prompt può significare: il bench misura un sistema riproducibile solo fino a una tolleranza, e una differenza di due punti tra varianti è dentro quella tolleranza in una giornata storta.

Tutto ciò che c’è sopra è un umano che sceglie una variante e una macchina che la valuta. Il passo successivo ovvio è lasciare che la macchina scelga anche le varianti.

APE fa esattamente questo: un modello propone istruzioni candidate, vengono valutate su esempi held-out, e le migliori sopravvivono.8 Le istruzioni che trova sono spesso cose che nessun umano scriverebbe, ed è proprio questo il punto: la ricerca è su ciò che fa punteggio, non su ciò che suona professionale.

DSPy va oltre ed è l’idea più utile per un prodotto.12 Dichiari cosa prende e cosa restituisce ogni passaggio di una pipeline, e il framework lo compila in prompt, selezionando dimostrazioni e ottimizzando istruzioni contro la tua metrica. Cambi modello e ricompili invece di riscrivere. Il prompt smette di essere codice sorgente ritoccato a mano da qualcuno e diventa un artefatto generato contro una metrica, che è ciò che avrebbe dovuto essere fin dall’inizio.

Nessuno dei due elimina la necessità del bench. Entrambi lo rendono l’unica cosa di cui hai bisogno, perché un ottimizzatore senza metrica non ottimizza nulla.

Resta la disciplina. I prompt devono stare nel controllo versione, in file, accanto al codice che li invia: non in una riga di database che qualcuno ha modificato un martedì. Hanno bisogno di un identificatore di versione salvato accanto a ogni output che hanno prodotto, oppure il giorno in cui qualcosa regredisce non puoi scoprire che cosa sia cambiato. Hanno bisogno del bench in continuous integration, perché un prompt è l’unica parte del tuo sistema che un vendor può invalidare in silenzio distribuendo un nuovo modello. E hanno bisogno di casi: non cento casi ingegnosi, solo i venti noiosi che si sono rotti lo scorso trimestre, conservati per sempre. Il bench è il deliverable. Il prompt è un suo sottoprodotto.

Tutto in questo capitolo è stato misurato in accuratezza. Ognuna di quelle varianti ha anche un prezzo.

Il system prompt che ha comprato 21,7 punti viene inviato a ogni chiamata, per sempre. I due esempi che hanno comprato sette punti vengono inviati a ogni chiamata, per sempre. I sedici che ne hanno comprati dodici vengono inviati a ogni chiamata, per sempre, e sono circa dieci volte la lunghezza della domanda che l’utente ha effettivamente posto. La chain of thought che non ha comprato nulla ha prodotto novantotto token extra per richiesta, e i token di output sono quelli costosi.

Nulla di questo è visibile in una tabella di accuratezze, e tutto è visibile su una fattura.

Il Capitolo 16 riguarda l’unità in cui quelle decisioni sono davvero denominate. Il token come unità di fatturazione, la context window come budget invece che memoria, perché una conversazione di quaranta turni costa molto più di quaranta volte il primo turno, cosa paga e cosa non paga il prompt caching, e perché l’ordine del tuo prompt decide se la cache hit avviene o no: il che si rivela essere un secondo motivo, interamente economico, per mettere prima il materiale stabile e per ultimo quello variabile.


Il bench e ogni tabella sono stati prodotti con Qwen/Qwen2.5-0.5B-Instruct sotto decodifica greedy, quindi si riproducono esattamente. La documentazione Hugging Face sui chat template è il riferimento per ciò in cui si espandono realmente i marker del template del Capitolo 11, e per il fatto che un modello distribuito con il template sbagliato è un fallimento reale e ricorrente. Per gli effetti di posizione e formato su scala di produzione invece che di laboratorio, le citazioni sopra sono le fonti primarie; le guide di prompting dei vendor sono utili per i loro esempi e vanno lette sapendo che nessuna pubblica un intervallo.

  1. Anthropic, Effective context engineering for AI agents (29 settembre 2025), per la distinzione prompt-versus-context usata in questo capitolo e sviluppata nel Capitolo 24.

  2. Zhao, Z., Wallace, E., Feng, S., Klein, D. and Singh, S. Calibrate Before Use: Improving Few-Shot Performance of Language Models. arXiv:2102.09690 (2021). Bias di majority-label, recency e common-token, e perché la rotazione nel bench di questo capitolo non è opzionale.

  3. McNemar, Q. Note on the sampling error of the difference between correlated proportions or percentages. Psychometrika 12(2), pp. 153–157 (1947). I confronti appaiati in questo capitolo usano la forma binomiale esatta invece dell’approssimazione chi-quadrato, perché i conteggi discordanti sono piccoli.

  4. Liu, N. F. et al. Lost in the Middle: How Language Models Use Long Contexts. arXiv:2307.03172 (2023). Citato qui per l’effetto posizione; misurato su lunghezza nel Capitolo 24.

  5. Sclar, M., Choi, Y., Tsvetkov, Y. and Suhr, A. Quantifying Language Models' Sensitivity to Spurious Features in Prompt Design. arXiv:2310.11324 (2023). Solo separatori e spaziatura spostano l’accuratezza abbastanza da riordinare le leaderboard dei modelli.

  6. Brown, T. B. et al. Language Models are Few-Shot Learners. arXiv:2005.14165 (2020). Il paper che ha introdotto l’in-context learning come capacità invece che come curiosità; la sezione 3 è la fonte del vocabolario zero-shot / one-shot / few-shot che ora usano tutti.

  7. Lu, Y., Bartolo, M., Moore, A., Riedel, S. and Stenetorp, P. Fantastically Ordered Prompts and Where to Find Them: Overcoming Few-Shot Prompt Order Sensitivity. arXiv:2104.08786 (2021). Il risultato riprodotto nella tabella few-shot sopra.

  8. Zhou, Y. et al. Large Language Models Are Human-Level Prompt Engineers. arXiv:2211.01910 (2022). Prompt engineering automatico per proposta e scoring. La tanto citata istruzione «take a deep breath» viene da Yang, C. et al., Large Language Models as Optimizers, arXiv:2309.03409 (2023), che l’ha trovata tramite ricerca su un task con un modello: un’affermazione che non è sopravvissuta intatta al viaggio nei post di blog. 2

  9. Wei, J. et al. Chain-of-Thought Prompting Elicits Reasoning in Large Language Models. arXiv:2201.11903 (2022).

  10. Kojima, T., Gu, S. S., Reid, M., Matsuo, Y. and Iwasawa, Y. Large Language Models are Zero-Shot Reasoners. arXiv:2205.11916 (2022). Il risultato «let’s think step by step», e vale la lettura per capire quanto fossero strette le condizioni.

  11. Wang, X. et al. Self-Consistency Improves Chain of Thought Reasoning in Language Models. arXiv:2203.11171 (2022). Misurato con il costo allegato nel Capitolo 12.

  12. Khattab, O. et al. DSPy: Compiling Declarative Language Model Calls into Self-Improving Pipelines. arXiv:2310.03714 (2023).

Pronto a lasciare scegliere LIA?

Crea con ogni modello AI in un unico posto — inizia gratis oggi.