Lo zero silenzioso: quando un punteggio mancante di giudizio diventa una misurazione
Nell'ablazione del 2026-07-17 sulle strategie di generazione di documenti GRC (GLM-5.2 e Claude Opus 4.8 come scrittori e giudici su una scala 1-10), nel passaggio 2, 79 delle 446 righe di giudizio registrate non avevano un punteggio complessivo. L'aggregatore le ha contate come 0. Lo stesso riempimento con zeri aveva prodotto un effetto di 3,25 punti nel passaggio 1, e quando è scomparso abbiamo elaborato una teoria sul bias del giudice per spiegarlo. Lo scarto appaiato tra i due giudici sullo stesso documento era di 0,12.

Il numero più pericoloso in una valutazione giudicata da LLM non è un punteggio distorto. È un punteggio mancante, perché un aggregatore permissivo lo trasforma in uno zero senza avvertirti, e poi costruisci una teoria per spiegare lo zero. Lo sappiamo perché l'abbiamo fatto due volte nella stessa valutazione. In un'ablazione offline conclusa il 2026-07-17, abbiamo confrontato dieci strategie di generazione per deliverable GRC (pacchetti di policy, un RoPA del processore, un'analisi delle lacune SOC 2, una policy AI ISO/IEC 42001, una classificazione AI Act UE), valutati in cieco da due modelli giudici su una scala 1-10. Il passaggio 1 riportava che la generazione di un deliverable intero in un'unica passata superava la generazione passo-passo di 3,25 punti. Il passaggio 2 ha visto questo effetto collassare a 0,66, e la nostra stessa nota metodologica diagnosticava il collasso come un artefatto del giudice: un modello giudice era "1,5-2 punti più indulgente" dell'altro. Entrambi i risultati erano lo stesso bug. Settantanove delle 446 righe di giudizio nel passaggio 2 non avevano un campo overall, il riduttore eseguiva Number(r[d]) || 0, e ogni media complessiva nella scheda di valutazione del report, così come entrambe le cifre principali pesanti di framework, è riprodotta esattamente mediando quelle 79 righe come zeri. Chiamiamo questo lo zero silenzioso, e questo post racconta cosa ci è costato e cosa controlliamo ora.
La valutazione e ciò che ha riportato
La configurazione era ordinaria per questo tipo di lavoro. Nove scenari congelati, tutti sintetici e costruiti intorno a una fittizia società SaaS berlinese di 20 persone (un decimo, una nota di due diligence su un fornitore, era presente nel file di fixture ma non è stato renderizzato nei passaggi analizzati qui), tratti dalle forme più complesse di scrittura per la compliance che incontriamo: una relazione di audit interno multi-sezione, un pacchetto di policy multi-documento, un registro dei processori GDPR Art. 30, compiti pesanti di framework su SOC 2, ISO/IEC 42001 e AI Act UE, una policy basata su spazio di lavoro, una serie di dodici documenti, una brief con ambito ambiguo. Dieci varianti variavano la strategia di generazione: renderizzare una brief approvata sezione per sezione passo-passo, renderizzare la stessa brief in un'unica passata, rigenerare la brief con un planner più rigoroso inizialmente, scrivere liberamente dal task del cliente senza brief, aggiungere un ciclo di riflessione critica-revisione, e scambiare il modello scrittore. I due modelli scrittori erano z-ai/glm-5.2 e anthropic/claude-opus-4.8, entrambi chiamati tramite OpenRouter a luglio 2026. Fino a tre prove per scenario e variante nel passaggio 2; due varianti erano solo parzialmente popolate (la variante di brief rigenerata a 18 render e la riesecuzione offline passo-passo a 17, contro 27 per le altre nuove varianti renderizzate), e il baseline catturato è un set di riferimento una tantum di nove documenti.
Il giudizio era in cieco e assoluto: ogni giudice vedeva il task del cliente e un documento, mai la variante o il modello scrittore, e veniva istruito a restituire un JSON rigoroso con cinque sub-punteggi (correttezza, completezza, profondità , citazioni, onestà ), un overall, una lista di flag di fabbricazione e un verdetto in una riga. Due modelli giudici, gli stessi dei modelli scrittori. Il passaggio 1 utilizzava un'assegnazione anti-autopreferenza, il giudice GLM per le varianti scritte da Claude e il giudice Claude per quelle scritte da GLM, perché l'autopreferenza negli evaluatori LLM è documentata: Panickssery, Bowman e Feng hanno mostrato che gli evaluatori LLM riconoscono e favoriscono le proprie generazioni (arXiv:2404.13076, 2024-04-15), e Zheng et al. hanno catalogato bias di autopotenziamento, posizione e verbosità nei set di valutazione LLM-as-a-judge (arXiv:2306.05685, 2023-06-09). Il passaggio 2 applicava entrambi i giudici a ogni variante e ne faceva la media, che è un design migliore.
Il titolo del passaggio 1, dalla scheda di valutazione: la renderizzazione focalizzata in un'unica passata aveva un punteggio complessivo di 7,56 contro 4,31 per la renderizzazione passo-passo della stessa brief sullo stesso modello. Un effetto topologico di 3,25 punti. Il titolo del passaggio 2: quell'effetto era 0,66, la scrittura libera dal task era la variante migliore, e il design passo-passo era vicino al fondo per qualità e il peggiore per onestà e fabbricazione. La nota metodologica del report spiegava il divario tra i passaggi: giudicare varianti diverse con giudici diversi aveva reso i punteggi assoluti non comparabili, perché il giudice GLM era approssimativamente 1,5-2 punti più indulgente del giudice Claude. "Non confrontare mai i punteggi assoluti dei giudici LLM tra varianti giudicate da modelli diversi" è stato appuntato come la lezione. Sembrava corretto. Non era ciò che dicevano i dati.
Cosa contenevano effettivamente le righe
Il 2026-08-19 abbiamo riesaminato il file raw dei giudizi, riga per riga, invece della scheda di valutazione. Su 446 righe uniche (scenario, variante, prova, giudice) registrate nel passaggio 2 (su 466 pianificate; 20 giudizi pianificati non hanno alcuna riga, che l'harness ha registrato come fallimenti ed escluso correttamente da ogni media), 79 non avevano una chiave overall, il 18%. Si dividevano in due categorie. Sessantacinque righe erano giudizi completi meno un campo: tutti e cinque i sub-punteggi presenti, flag di fabbricazione presenti, il verdetto in una riga presente, e nessun overall. Il giudice aveva restituito il JSON richiesto con una chiave mancante, la chiave che per caso si trovava tra honesty e fabrication_flags nello schema richiesto. Quattordici righe erano oggetti JSON senza alcun campo del punteggio, che il parser accettava perché accettava qualsiasi oggetto JSON. La mancanza non era distribuita uniformemente tra i giudici: nel nostro test, 56 delle 229 righe del giudice Claude (24%) non avevano overall, tutte del tipo parziale, e 23 delle 217 righe del giudice GLM (11%) non ne avevano, 9 parziali e i 14 oggetti vuoti. Non abbiamo indagato sul perché; il nostro prompt del giudice, la gestione della modalità JSON sul percorso utilizzato e il limite di output di 1.200 token sono tutti candidati, e il tasso è una proprietà della nostra chiamata, non una rivendicazione su alcun vendor.
L'aggregatore poi faceva la cosa naturale in TypeScript:
for (const d of dims) out[d] = Number(mean(js.map((r) => Number(r[d]) || 0)).toFixed(2));
Una riga senza overall diventava uno 0 in una colonna in cui ogni valore reale si trovava tra 1 e 10, e la distribuzione valida era fortemente raggruppata in alto (su 367 punteggi overall validi, 299 erano 8 o superiori). Uno zero in una cella di sedici sposta la media di circa mezzo punto ai livelli di punteggio che abbiamo osservato. Otto zeri in una cella di sedici la dimezzano.
Ecco la scheda di valutazione del passaggio 2 come pubblicata, accanto alle stesse righe con i punteggi mancanti trattati come mancanti. Entrambi i giudici, tutti gli scenari, overall su 1-10:
| variante | overall pubblicato | overall righe valide | n giudici validi / righe giudici |
|---|---|---|---|
| renderizzazione passo-passo della brief, output baseline catturati | 6,56 | 7,50 | 14 / 16 |
| renderizzazione passo-passo della brief, riesecuzione offline (GLM-5.2) | 6,12 | 8,08 | 25 / 33 |
| renderizzazione in un'unica passata della brief (GLM-5.2) | 6,78 | 8,07 | 42 / 50 |
| renderizzazione in un'unica passata della brief + riflessione (GLM-5.2) | 7,02 | 8,14 | 44 / 51 |
| brief rigenerata + renderizzazione in un'unica passata (GLM-5.2) | 6,32 | 7,68 | 28 / 34 |
| scrittura libera dal task (GLM-5.2) | 6,38 | 8,30 | 40 / 52 |
| scrittura libera + riflessione (GLM-5.2) | 6,08 | 7,71 | 41 / 52 |
| renderizzazione in un'unica passata della brief (Claude Opus 4.8) | 7,45 | 8,59 | 46 / 53 |
| renderizzazione in un'unica passata della brief + riflessione (Claude Opus 4.8) | 6,56 | 8,53 | 40 / 52 |
| scrittura libera dal task (Claude Opus 4.8) | 7,53 | 8,49 | 47 / 53 |
Ogni overall pubblicato in questa tabella è la media riempita con zeri arrotondata a due decimali. Le dieci varianti che sembravano spaziare da 6,08 a 7,53 in realtà vanno da 7,50 a 8,59, una fascia di circa un punto, e le prime quattro varianti si trovano entro 0,3 l'una dall'altra. Il risultato pesante di framework pubblicato, quello su cui il report si è più appoggiato ("Claude free 8,06 versus brief 6,59; GLM free 7,19 versus brief 6,40"), diventa 9,06 versus 8,62 (16 e 13 righe valide) e 8,85 versus 8,73 (13 e 11). Un effetto di 1,47 punti diventa 0,44; un effetto di 0,79 punti diventa 0,12.
Il passaggio 1 è il caso più eclatante. I 4,31 della variante passo-passo provenivano da sedici righe del giudice Claude di cui otto non avevano overall; le otto che lo avevano avevano una media di 8,62. I 7,56 della variante focalizzata provenivano da diciotto righe con due mancanti; le sedici valide avevano una media di 8,50. L'effetto topologico di 3,25 punti che ha avviato l'intera indagine era, sulle righe che portavano un punteggio, di -0,12.
La teoria che abbiamo costruito sopra il bug
Questa è la parte da interiorizzare, perché il bug stesso è banale. Quando il passaggio 2 non è riuscito a riprodurre il passaggio 1, abbiamo fatto ciò che fanno le persone attente: abbiamo cercato un meccanismo. Il design anti-autopreferenza significava che le varianti scritte da Claude e da GLM nel passaggio 1 erano state valutate da giudici diversi, i bias documentati dei giudici LLM rendevano plausibile che "i giudici differissero in severità ", e una scheda di valutazione in cui le varianti giudicate da Claude erano basse e quelle giudicate da GLM erano alte sembrava misurata. Quindi abbiamo scritto uno scarto di indulgente di 1,5-2 punti e una regola di non confrontare mai i punteggi assoluti tra giudici diversi.
Le righe raw ci hanno permesso di testare direttamente quella teoria, perché il passaggio 2 aveva entrambi i giudici che valutavano lo stesso documento. Su 148 coppie (scenario, variante, prova) in cui entrambi i giudici avevano restituito un overall, la differenza media GLM-meno-Claude era di +0,12. Settantatré coppie erano identiche, 41 avevano GLM un punto più alto, 17 avevano Claude un punto più alto, e 17 differivano di due o più punti. Nei dati del passaggio 1 lo stesso scarto appaiato era di -0,06 su 72 coppie. Tra le 148 coppie complete non c'è alcun segno di uno scarto di indulgente di 1,5 punti. Quello che c'era: mancanza di dati, su due livelli. Tra i giudici, il 24% delle righe di un giudice non aveva overall contro l'11% dell'altro, e l'assegnazione anti-autopreferenza metteva il giudice con il tasso più alto su ogni variante scritta da GLM, quindi come famiglia quelle varianti raccoglievano più zeri, che spingevano i loro punteggi verso il basso nei confronti tra famiglie. All'interno di un giudice, la mancanza variava per variante: le due varianti nel titolo del passaggio 1 erano entrambe scritte da GLM e entrambe giudicate da Claude, e avevano otto contro due righe senza overall. L'"effetto topologico" di 3,25 punti non è mai stato un confronto tra giudici. Avevamo letto la mancanza come severità , e la spiegazione era abbastanza soddisfacente che nessuno ha aperto il file.
Questa è la forma generale dello zero silenzioso. Un valore mancante costretto a un numero non sembra un errore; sembra un punteggio basso, e i punteggi bassi ricevono spiegazioni. Uno scarto di severità costante di un giudice è un fallimento molto più lieve: lo scarto si annulla in qualsiasi confronto all'interno dello stesso giudice e appare immediatamente in un controllo appaiato. Una cella riempita con zeri non è coerente. Scala con la frequenza con cui il parsing è fallito in quella cella, e nei nostri dati quel tasso era correlato alla variante.
Cosa è sopravvissuto
Non tutto nel report era sbagliato, e la divisione è istruttiva. Le sessantacinque righe parziali portavano comunque honesty e fabrication_flags, quindi quelle due colonne hanno perso solo le quattordici righe vuote. Ricalcolate sulle righe con un valore valido di honesty, il baseline catturato della variante passo-passo ha ancora l'onestà più bassa (5,81 su 16 giudizi) e flag di fabbricazione su 16 su 16, mentre la variante libera di Claude ha l'onestà più alta (9,75 su 52) e flag su 1 su 52. Quel divario è reale. La classifica della qualità si è per lo più dissolta in una fascia di un punto, e la rivendicazione che "la scrittura libera batte la renderizzazione con brief" è passata da titolo a +0,23 complessivo per GLM-5.2 (8,30 contro 8,07) e -0,10 per Claude Opus 4.8 (8,49 contro 8,59). La rivendicazione che "la riflessione danneggia" è rimasta per la scrittura libera GLM (7,71 con riflessione contro 8,30 senza) ed è stata pari per la renderizzazione con brief di Claude (8,53 contro 8,59). Non abbiamo rieseguito l'ablazione; le conclusioni tratte dal report vengono ricalcolate sulle righe valide, e le lezioni metodologiche che abbiamo effettivamente imparato sono sotto.
C'è anche una lettura di compliance di questo, e non è decorativa. La Clausola 9.1 b) di ISO/IEC 27001:2022 richiede a un'organizzazione di determinare i metodi per il monitoraggio, la misurazione, l'analisi e la valutazione in modo che garantiscano risultati validi, e afferma che i metodi selezionati devono produrre risultati comparabili e riproducibili per essere considerati validi (ISO/IEC 27001:2022, Clausola 9.1, edizione 2022-10). Un revisore interno che trovasse che una dashboard delle prestazioni di un ISMS fosse stata mediata con celle vuote come zero potrebbe sollevare una non conformità contro quella clausola. Una scheda di valutazione che guida una decisione architetturale è, per analogia, un metodo di misurazione nello stesso senso, e l'abbiamo sottoposta a uno standard inferiore rispetto a quello che applicheremmo a un cliente.
Limiti
Questa è una rielaborazione dei giudizi che già avevamo, non una nuova esecuzione, e i numeri delle righe valide ereditano ogni limitazione dell'originale: nove scenari, fino a tre prove, punteggi interi da due giudici LLM raggruppati a 8-9, nessun test di significatività , e un giudice che è esso stesso un modello. Le celle delle righe valide vanno da 14 a 47 giudizi per variante su tutti gli scenari e da 4 a 16 nel sottoinsieme pesante di framework; i punteggi validi hanno una deviazione standard di 1,36, quindi un errore standard ingenuo a livello di riga per una media di variante completa (1,36 sulla radice quadrata di n, ignorando che i giudici sono appaiati e le prove si raggruppano per scenario) è approssimativamente 0,2 a 0,4, e leggiamo differenze sotto circa mezzo punto tra varianti come pari. I tassi di mancata compilazione di overall sono ciò che abbiamo osservato sul nostro prompt attraverso un percorso a luglio 2026, con un limite di token di 1.200 e modalità JSON richiesta; non abbiamo isolato la causa e non sappiamo se sia stabile. Lo scarto appaiato di 0,12 del giudice è specifico di questo punteggio e di questi documenti. Nessuna di queste considerazioni cambia il fatto centrale, che non dipende da alcuna di queste precisazioni: la colonna overall pubblicata è stata calcolata con l'18% delle sue righe costretto a zero, e i due risultati costruiti su di essa non sopravvivono alla loro rimozione.
Lo zero silenzioso, e una checklist
L'idea da portare avanti: in una valutazione giudicata da LLM, una chiamata a un giudice che non restituisce un punteggio non è un dato, e qualsiasi aggregatore che possa trasformarlo in uno lo farà prima o poi nella cella in cui conta di più. Le mitigazioni dei bias dei giudici valgono la pena di essere fatte, e sono inutili finché non hai contato i tuoi valori nulli. Se esegui una valutazione in cui un modello giudica l'output di un modello, applicala anche al tuo caso:
- Non forzare mai un punteggio mancante.
Number(x) || 0,x ?? 0,fillna(0)su una colonna di punteggio: tutti questi trasformano un giudizio assente in un punteggio inferiore al minimo della scala, peggiore di qualsiasi giudizio che la scala permetta. Tratta il mancante come mancante (pandas salta NaN di default in una media; il nostro riduttore fatto a mano non lo faceva) e fai fallire la scheda di valutazione rumorosamente se il conteggio valido di una cella è inferiore a quello che hai pre-registrato. - Stampa n valido accanto a ogni media. Una cella che legge "4,31" e una cella che legge "4,31 (8/16)" sono affermazioni diverse. La nostra stampava
ncome conteggio delle righe, che nascondeva che metà di una variante non aveva punteggio. - Valida l'output del giudice contro lo schema e riprova. Un oggetto JSON non è un giudizio. Richiedi ogni campo punteggiato, rifiuta e richiama in caso di errore, e registra il tasso di errore per giudice e per variante. Un tasso di errore che differisce per giudice o per variante è di per sé un risultato, e nel nostro caso lo era.
- Prima di teorizzare sul bias del giudice, calcola lo scarto appaiato. Se due giudici hanno valutato gli stessi documenti, la differenza su quelle coppie è lo scarto di punteggio tra loro su quel compito. Il nostro era 0,12 sulle coppie complete. Una storia di 1,5 punti che non hai ottenuto da righe appaiate è solo una storia.
- Tratta una dimensione dell'effetto implausibile come un errore nei dati prima di tutto. Uno scarto di 3,25 punti tra due render della stessa brief sullo stesso modello, con punteggi raggruppati a 8-9 e sedici-diciotto righe per variante, era implausibilmente grande rispetto alla distribuzione osservata e avrebbe dovuto inviarci alle righe prima di cercare un meccanismo.
- Se cambi giudici per variante, cambi anche i loro modi di fallimento. L'assegnazione anti-autopreferenza è un design difendibile, ma qualsiasi difetto specifico del giudice (fallimenti di parsing, troncamento, rifiuti) è allora confuso con il trattamento. Eseguire ogni giudice su ogni variante è più sicuro, e non ti salva comunque dagli zeri.
- Rielabora il titolo dalla riga raw prima che il report scriva una teoria. La scheda di valutazione è una vista. Le righe sono le prove. Il nostro report citava la scheda di valutazione, spiegava il proprio artefatto con un meccanismo e appuntava quel meccanismo come lezione. Il meccanismo era una chiave mancante.
Tutti i dati sopra sono nostre misurazioni su nostri scenari GRC sintetici (una società fittizia, nessun dato di cliente), scrittori e giudici z-ai/glm-5.2 e anthropic/claude-opus-4.8 come serviti tramite OpenRouter sia per la renderizzazione che per il giudizio, ablazione conclusa il 2026-07-17, rielaborata dai giudizi registrati il 2026-08-19.
Articoli correlati

Perché non deduplichiamo i fatti di conformità tramite similarità di embedding
Nella nostra valutazione di deduplicazione della memoria, le contraddizioni avevano una media di 0.938 cosine rispetto al fatto memorizzato più vicino, mentre i duplicati veri avevano una media di 0.940. Nessun singolo threshold separava chiaramente le coppie che non dovevano essere fuse da quelle che dovevano esserlo. Quindi, il cosine non viene mai utilizzato per decidere la fusione nel nostro pipeline.

Il test di simmetria: distribuire una correzione del prompt che non puoi riprodurre
Quando un bug di produzione non si riproduce offline, non puoi validare una correzione del prompt facendola avere successo. Puoi validarla verificando se la tua modifica sposta l'output in una direzione coerente o aggiunge semplicemente rumore variabile del modello. Ecco il test che abbiamo eseguito su 126 coppie A/B alla cieca.

La lotteria dell'ID del modello: stessa richiesta, estrazione diversa
Dietro una porta di accesso multi-provider, lo stesso ID del modello ha prodotto il suo primo token con una mediana di 312 ms senza output di reasoning un giorno, e a 3.073 ms con 2.627 caratteri di reasoning giorni dopo. La flag di instradamento che ci aspettavamo di prevenire questo non l'ha fatto.
