AI & MCP
Valutare un agente con un benchmark: harness, pass^k e varianza
Un benchmark per agenti è task fissi, ambiente pulito, grader automatico e k ripetizioni. Senza il quarto numero il risultato è un aneddoto.
In sintesi
- Un harness ha quattro parti: task, ambiente che si resetta, grader e ripetizioni.
- pass@k misura la capacità, pass^k misura l'affidabilità: sono due domande diverse.
- Con 20 task il margine d'errore è largo: riporta l'intervallo, non il punto.
Un benchmark per agenti è fatto di quattro parti: task fissi, un ambiente che si resetta, un grader automatico e k ripetizioni per task. Se manca il quarto numero, il risultato è un aneddoto.
Le quattro parti di un harness
- Task: una richiesta con un risultato atteso e un contesto di partenza identico per ogni modello.
- Ambiente: una copia pulita del repository, senza
node_modulesodistavanzati da un giro precedente. - Grader: un processo che decide pass o fail senza chiedere al modello come è andata.
- Ripetizioni: almeno tre giri per task, perché un agente non dà due volte la stessa risposta.
Il grader è la parte che si sbaglia più spesso. Un test che l'agente può modificare non è un grader, è un suggerimento: va eseguito da fuori, su file che l'agente non può toccare. Lo stesso vale per il tempo limite e per il numero massimo di file modificati, che vanno decisi prima e non dopo aver visto il risultato.
pass@k e pass^k
pass@k risponde a «almeno uno dei k tentativi riesce», ed è la metrica giusta per la capacità: un agente che non ci arriva mai è fuori. pass^k risponde a «tutti i k tentativi riescono», ed è la metrica giusta per l'affidabilità: un agente che riesce una volta su tre non è pronto per un flusso automatico.
node -e "const p=0.8;for(const k of [1,3,5])console.log('pass^'+k,(100*p**k).toFixed(0)+'%')"
Con una probabilità di successo dell'80%, il comando stampa pass^1 80%, pass^3 51% e pass^5 33%. Lo stesso agente sembra ottimo con un tentativo e mediocre con cinque: la differenza non è nel modello, è nella domanda che il benchmark pone.
La varianza con 20 task
node -e "const n=20,p=0.8,se=Math.sqrt(p*(1-p)/n);console.log((100*p).toFixed(0)+'% +/- '+(196*se).toFixed(0)+' pt')"
Il comando stampa 80% +/- 18 pt: con venti task un 80% misurato vale fra il 62% e il 98%. Due modelli che differiscono di dieci punti su venti task sono indistinguibili. Per scendere a un margine di cinque punti servono centinaia di task, oppure meno task ma con un grader più fine di pass o fail.
Tre grader, tre verità
| Grader | Cosa misura | Costo | Come fallisce |
|---|---|---|---|
| Exit code | build, tipi, test | basso | un test debole passa per motivi sbagliati |
| Diff atteso | la modifica richiesta | medio | un fix equivalente ma diverso viene bocciato |
| Rubrica o giudice | qualità, tono, completezza | alto | premia le risposte lunghe e ignora i dettagli |
Per un agente di codice il primo è obbligatorio, il secondo è utile sui task piccoli, il terzo serve solo dove non esiste un oracolo. Un giudice automatico va validato contro un campione giudicato a mano: se non concorda su venti casi, il numero che produce non è una misura.
Contaminazione e rumore
Un task pubblico è quasi sempre già visto in addestramento, quindi il punteggio misura la memoria del modello e non la capacità. Anche l'harness introduce rumore: prompt diversi da quelli reali, cache calda al primo giro, tool che rispondono con latenze diverse. La difesa è noiosa: task presi da bug chiusi del tuo repository, ambiente ricreato a ogni giro, stesso prompt per tutti, ripetizioni dichiarate.
Cosa riportare
- Numero di task e ripetizioni, perché senza quei due numeri il resto non è interpretabile.
- Successo con intervallo, non il punto: 80% su venti task è 80% ± 18 punti.
- Costo per task riuscito, che unisce token e fallimenti in un numero solo.
- Passi mediani e file toccati, che anticipano la manutenzione futura meglio del punteggio.
La limitazione
Il nostro harness esegue due task: serve a verificare che un modello produca JSON valido e JSX coerente, non a classificare modelli. Anche un harness grande resta cieco su ciò che non misura: un agente può passare tutti i task e restare inutilizzabile perché lento, caro o insicuro sul disco. Il punteggio è un filtro, la decisione resta umana.
Dove continuare
La costruzione della fixture e il costo per attività riuscita sono in valutare un modello per il codice, l'ordine dei passi in orchestrare agenti multi-step, i rischi di scrittura su disco in sicurezza di un agente di coding. Il catalogo che alimenta i task è in 111.985 componenti e 1.556 blocchi; un record citabile è agent dock di dqnamo.
Domande frequenti
Quanti task servono per un benchmark credibile?
Venti task distinguono solo differenze grandi. Con venti task e un successo dell'80% l'intervallo è di circa 18 punti, quindi due modelli a dieci punti di distanza non sono separabili. Per differenze fini servono centinaia di task o un grader più fine.
Che differenza c'è fra pass@k e pass^k?
pass@k chiede se almeno un tentativo su k riesce, e misura la capacità. pass^k chiede se tutti i k tentativi riescono, e misura l'affidabilità. Un agente con pass^1 all'80% scende al 51% con pass^3 e al 33% con pass^5.
Posso usare un LLM come giudice?
Solo dove non esiste un oracolo, e solo dopo averlo validato su un campione giudicato a mano. Un giudice senza accordo misurato premia le risposte lunghe, ignora i dettagli e produce un numero che sembra una misura.
Come evito la contaminazione?
Usando task che non esistono in pubblico: bug chiusi del tuo repository, con il diff atteso già disponibile. Ruota i task quando il repository cambia forma e non riusare lo stesso set per anni.
Mi fido dei punteggi dei benchmark pubblici?
Come primo filtro sì, per escludere chi non sa scrivere codice. Non per scegliere fra i primi cinque, perché i task sono noti e il formato del prompt è diverso dal tuo. La fixture decide, il benchmark orienta.