ELAI S.r.l.

Un JSON valido può chiedere la cosa sbagliata: come controllare le proposte di un agente

Dieci millimetri o dieci centimetri? Un esperimento riproducibile distingue conformità allo schema, limiti di dominio e corrispondenza alla richiesta.

Un JSON valido può chiedere la cosa sbagliata: come controllare le proposte di un agente

La richiesta è piccola; la differenza non lo è

Immaginiamo che un assistente AI debba preparare una proposta di spostamento di 10 millimetri. Il programma che riceve la proposta si aspetta un oggetto con due campi: un valore numerico e un’unità di misura. L’assistente restituisce 10 e cm. Il documento è JSON leggibile, i campi sono quelli previsti e il valore potrebbe persino rientrare nell’intervallo ammesso. Eppure descrive 100 millimetri. Il problema non è una virgola mancante: è una richiesta diversa.

La domanda centrale è quindi: che cosa dimostra davvero il superamento di un controllo? Costruiremo tre predicati, cioè domande con risposta vero o falso: conformità a uno schema S, rispetto di un limite B e corrispondenza a un obiettivo I. Il lettore potrà seguire l’intero ragionamento con un righello mentale; formule e codice renderanno esplicito ciò che altrimenti rimane nascosto nelle parole “output valido”. Non eseguiremo movimenti, chiamate a macchine o azioni aziendali: tutto il caso è una simulazione di proposte.

Prima del modello: definire ciò che deve restare vero

Nel nostro esempio l’obiettivo non viene dedotto dal testo prodotto dal modello: è una quantità di riferimento già disponibile e ritenuta attendibile, pari a 1/100 di metro. Il limite di dominio, scelto soltanto per questo esempio didattico, è 3/20 di metro, cioè 150 millimetri. Sono due informazioni diverse: il limite dice fin dove una proposta può arrivare nel modello del problema; il riferimento dice quale spostamento è stato richiesto. Essere sotto il tetto non significa essere nel punto giusto.

Questo presupposto delimita il risultato. Dimostreremo che alcuni controlli non bastano a verificare una richiesta strutturata già nota; non dimostreremo di comprendere automaticamente qualsiasi istruzione in linguaggio naturale. Se l’utente scrive soltanto “spostalo un po’”, il bersaglio manca. Un confronto esatto con un numero non può ricostruire un’informazione mai fornita. Il sistema deve prima ottenere una specifica utilizzabile, gestendo l’ambiguità nel contesto applicativo.

Che cosa chiediamo allo schema

JSON è un formato per rappresentare dati; JSON Schema permette di descrivere vincoli che un documento deve soddisfare. Usiamo il dialetto Draft 2020-12. Lo schema di base richiede un oggetto, entrambi i campi value e unit, nessun campo aggiuntivo, un valore di tipo number compreso fra 0 e 150 inclusi e un’unità scelta fra mm e cm. Sono regole del nostro contratto, non caratteristiche universali delle misure o degli agenti. In particolare, il limite numerico 150 vale prima di interpretare l’unità.

S(x) = object(x) AND required(value, unit) AND no_extra_fields(x)\n AND number(x.value) AND 0 <= x.value <= 150\n AND x.unit IN {"mm", "cm"}

La lettera x indica la proposta. AND significa che tutte le condizioni devono essere vere. Il tipo conta: la stringa “10” non è il numero 10, mentre il valore booleano true non è una quantità numerica nel contratto. Anche un’unità non ammessa o un campo aggiuntivo causano il rifiuto. Questi controlli sono utili: impediscono che il programma interpreti dati fuori dal formato concordato. Tuttavia, due proposte 10 mm e 10 cm soddisfano entrambe esattamente queste regole.

Portare quantità diverse nella stessa unità

Per confrontare quantità fisiche serve una rappresentazione comune. Definiamo N, una funzione di normalizzazione che converte in metri le proposte conformi a S. Non cambia lo spostamento descritto: cambia il modo in cui lo scriviamo. Un centimetro diventa 1/100 di metro; dieci millimetri diventano 10/1000 di metro. Entrambi sono 1/100 di metro. Prima di normalizzare controlliamo tipo e unità: non diamo un significato arbitrario a un campo sconosciuto.

N(x) = x.value / 1000 m, if x.unit = "mm"\nN(x) = x.value / 100 m, if x.unit = "cm"\nB(x) = [0 m <= N(x) <= 3/20 m]\nI(x) = [N(x) = 1/100 m]

B chiede se la quantità normalizzata appartiene all’intervallo del nostro modello. I chiede se coincide con il riferimento. Le parentesi quadre indicano il risultato logico della comparazione, non una nuova unità. Per x uguale a 10 cm otteniamo N(x)=1/10 m: è sotto 3/20 m, ma non è 1/100 m. Abbiamo dunque una proposta per cui S è vero, B è vero e I è falso. Un solo controesempio basta a confutare l’implicazione generale secondo cui conformità e limite garantiscano l’obiettivo.

Il controesempio inverso aiuta a evitare un rimedio troppo rigido. Confrontare il JSON con una sola stringa attesa rifiuterebbe 1 cm se la stringa ammessa contenesse 10 mm, pur descrivendo lo stesso spostamento. La verifica dell’intento deve scegliere un’equivalenza pertinente al dominio. Nel nostro dominio ristretto essa è l’uguaglianza della quantità normalizzata. Questo non autorizza a considerare equivalenti documenti arbitrari: un identificativo testuale, per esempio, potrebbe dover rimanere identico carattere per carattere.

Uno schema più forte: le condizioni fra campi

Sarebbe scorretto concludere che JSON Schema verifichi soltanto la punteggiatura o sia incapace di rappresentare il limite B. Per le nostre due unità basta aggiungere una condizione: se unit è cm, value deve essere al massimo 15. Il limite di 150 per mm è già presente nello schema base. Con allOf combiniamo i vincoli; con if e then applichiamo quello condizionale. Lo schema rafforzato S_B rappresenta così anche il limite dopo conversione, entro il contratto definito.

S_B = S AND { if: {properties: {unit: {const: "cm"}}, required: ["unit"]}, then: {properties: {value: {maximum: 15}}} }

La formula precedente è una notazione leggibile per la composizione, non un documento JSON da incollare così com’è: lo schema completo valido si trova nell’archivio. Il codice controlla il suo accordo con S e B per tutti i 302 casi della griglia. Ma 10 cm continua a superare S_B e a mancare il bersaglio. Rendere più ricco il contratto di dominio risolve una classe di errori; non aggiunge da solo il riferimento a questa specifica richiesta.

Quando il bersaglio è già noto, si può anche generare uno schema specifico della richiesta che ammetta 10 mm oppure 1 cm tramite costanti e alternative. Non esiste una barriera magica fra “schema” e “significato”: conta quali informazioni e vincoli abbiamo effettivamente codificato. In una soluzione la conversione e il confronto stanno nel programma; nell’altra parte della stessa logica è incorporata nello schema. Entrambe dipendono dall’affidabilità del bersaglio e dalla correttezza della traduzione dei requisiti.

Un esperimento finito che possiamo controllare tutto

L’esperimento usa Python 3.14.0 e jsonschema 4.26.0 con Draft202012Validator. Consideriamo tutti i valori interi da 0 a 150 inclusi, associati a ciascuna delle due unità: 151 per 2, quindi 302 proposte. Non è un campione di un modello linguistico, non ci sono prompt o chiamate a un servizio AI, e non viene stimata alcuna probabilità. Enumeriamo un insieme costruito da noi, utile perché il risultato può essere verificato senza fidarsi di un grafico.

Tutte le 302 proposte soddisfano S per costruzione. Per B passano tutti i 151 valori in millimetri e soltanto i 16 valori da 0 a 15 in centimetri: in totale 167. Per I passano due sole coppie intere: 10 mm e 1 cm. Restano 165 proposte che rispettano il limite senza realizzare il bersaglio. Se cambiassimo griglia, intervallo o ammettessimo altri decimali, cambierebbero i conteggi. La conclusione logica del controesempio rimarrebbe valida; i numeri non diventano un benchmark generale degli agenti.

PropostaSBI
10 mmTTT
1 cmTTT
10 cmTTF
100 mmTTF
20 cmTFF
"10" mmF——
true mmF——

Nella tabella T significa vero, F falso. Il trattino significa non applicabile: dopo il fallimento dello schema non interpretiamo la proposta come una quantità ammessa e non calcoliamo B o I. Confondere un controllo non eseguito con un risultato negativo rende i registri più difficili da interpretare. La figura riprende gli stessi casi: guardiamo soprattutto le righe 10 cm e 100 mm, in cui le prime due colonne restano positive mentre l’ultima cambia.

Conteggi sulla griglia intera di 302 proposte e controlli dei casi esemplificativi. T/F indicano vero/falso; grigio e trattino indicano non applicabile. Il limite è un’ipotesi didattica, non una certificazione di sicurezza.
Conteggi sulla griglia intera di 302 proposte e controlli dei casi esemplificativi. T/F indicano vero/falso; grigio e trattino indicano non applicabile. Il limite è un’ipotesi didattica, non una certificazione di sicurezza.

Dal testo ai numeri: che cosa fa davvero il codice

L’archivio contiene experiment.py, gli schemi effettivi, le dipendenze fissate e tutti i risultati. Il parser legge i numeri decimali con Decimal e rifiuta esplicitamente NaN e Infinity: non assumiamo che ogni parser Python applichi automaticamente la stessa politica. Dopo la validazione, Fraction rappresenta esattamente i rapporti di conversione. In questo modo il confronto con 1/100 e 3/20 non dipende dall’approssimazione binaria di 0,01 o 0,15. L’esattezza riguarda questi numeri dichiarati, non la precisione di un sensore fisico.

La procedura assess separa i passaggi: parsing, schema, normalizzazione, limite, bersaglio. Conserva il testo originale nei risultati affinché sia chiaro quale proposta è stata controllata. I test aggiuntivi includono un numero scritto come stringa, un booleano, un’unità sconosciuta, un campo non previsto, un campo mancante, un valore negativo e un numero non finito. Il caso 1.0 cm verifica che una rappresentazione decimale ammessa continui a corrispondere al riferimento. Il codice breve in fondo isola il controesempio; quello nell’archivio esegue anche la validazione e l’enumerazione completa.

Il costo della nostra enumerazione è proporzionale al numero di proposte, mantenendo fisso questo piccolo schema. Non è una misura della complessità generale di JSON Schema, che può comprendere riferimenti e strutture molto più articolate. Non misuriamo latenza di un agente o costi di inferenza. Per ripetere il calcolo serve installare le versioni indicate nel file requirements.txt, quindi eseguire experiment.py; plot.py genera le quattro figure. Non serve un modello generativo né un collegamento a un dispositivo.

Che cosa rimane fuori dalla prova

Nel mondo reale una misura può avere tolleranze. Se il requisito ammette un errore assoluto epsilon espresso in metri, I può diventare |N(x)−q|≤epsilon. Il valore di epsilon deve derivare dal requisito, non essere scelto dopo aver visto la risposta per farla passare. Un confronto relativo richiede un’ulteriore politica vicino a zero. Qui usiamo una quantità esatta e tolleranza zero per rendere il controesempio controllabile: non proponiamo un criterio universale per la metrologia.

Un oggetto che soddisfa tutti e tre i predicati non è per questo autorizzato a comandare una macchina. Identità, permessi, stato dell’applicazione e condizioni fisiche non sono rappresentati dai nostri due campi. Il limite B è un limite del caso matematico: non dimostra sicurezza robotica, assenza di collisioni o certificazione. Anche le politiche sui campi JSON duplicati, i limiti di dimensione e le risorse del parser richiedono decisioni separate; questo esempio non è una validazione completa della sicurezza di una API.

Non attribuiamo a EL-AI un sistema agentico o robotico che abbia eseguito questi controlli in produzione. L’interesse espresso per robotica industriale e collaborativa è una direzione da esplorare; il collegamento applicativo qui è un’ipotesi didattica. L’evidenza disponibile in questo articolo è il comportamento del validatore e del confronto numerico sui dati sintetici pubblicati. Per valutare un agente reale occorrerebbero richieste rappresentative, un riferimento indipendente, errori classificati e un protocollo documentato: non possiamo sostituirli con i nostri 302 punti.

Risposta alla domanda iniziale

Un JSON valido dimostra conformità ai vincoli che abbiamo scritto, non a quelli che avevamo soltanto in mente. Nel caso svolto, controllare struttura e intervallo lascia passare 10 cm quando la richiesta è 10 mm. Normalizzare le quantità e confrontarle con un riferimento attendibile rende visibile quella differenza; consente anche di accettare 1 cm, che è una rappresentazione diversa della quantità giusta. Il punto non è rinunciare agli schemi, ma sapere quale domanda ciascun controllo risolve e quale informazione gli manca.

Fonti e riproducibilità

Documentazione primaria letta: Draft 2020-12 Validation, sezioni 6.1.1–6.1.3, 6.2.2, 6.2.4 e 6.5.3; Core, sezioni 10.2.1.1, 10.2.2 e 10.3.2.3. Il nome del dialetto contiene 2020-12, mentre i documenti consultati riportano la revisione Internet-Draft del 16 giugno 2022. Non li presentiamo come RFC definitiva. La versione del validatore eseguito è 4.26.0, verificata anche sulla pagina di distribuzione. Questo è un approfondimento metodologico con esperimento didattico, non una ricerca originale sottoposta a peer review.

JSON Schema Draft 2020-12 — Validation.

JSON Schema Draft 2020-12 — Core.

jsonschema 4.26.0 — PyPI.

from fractions import Fraction
proposal={"value":10,"unit":"cm"}
metres=Fraction(proposal["value"])*Fraction(1,100)
bounded=0 <= metres <= Fraction(3,20)
intent=metres == Fraction(1,100)
assert bounded and not intent
print(metres,bounded,intent)  # 1/10 True False

Codice, dati e istruzioni · JSON. Calcoli didattici eseguiti con Python 3.14.0; figure con Matplotlib 3.11.2. Analisi con assistenza AI, senza dichiarare peer review o revisione umana. Copertina originale ImageGen, illustrativa: non documenta persone, sedi o installazioni EL-AI. Fonti consultate il 21 ottobre 2026.