Gli sviluppatori possono ora tenere sotto controllo i costi per la correzione di JSON malformati provenienti dai modelli linguistici di grandi dimensioni (LLM) limitando i tentativi di riparazione a due, come dimostra un semplice ciclo Python. Il pattern — genera, analizza, valida, riprova, quindi accetta o fallisce — trasforma un "JSON forse valido" in un tasso di successo misurabile e previene l'uso incontrollato di token.

Perché un ciclo di riparazione è importante

Spesso agli LLM viene chiesto di "restituire JSON", eppure l'output contiene frequentemente errori di sintassi, chiavi mancanti o valori che violano le regole di business. Un sistema a valle che si aspetta un payload rigoroso andrà in crash o produrrà risultati errati se riceve un output del genere. Senza un modo sistematico per individuare e correggere questi problemi, i team sono costretti ad accettare un alto tasso di fallimento o a sprecare denaro sollecitando ripetutamente il modello finché non ottiene il risultato corretto.

I tre livelli di validazione

Un ciclo affidabile suddivide la validazione in:

  • Sintassi – la stringa è analizzabile come JSON?
  • Struttura (Shape) – l'oggetto di primo livello contiene esattamente le chiavi attese?
  • Semantica – i valori rispettano i vincoli del dominio (ad esempio, una "category" deve appartenere a un set predefinito)?

Fermandosi al primo livello che fallisce, il ciclo può fornire al modello un messaggio di errore preciso, il che migliora drasticamente le probabilità di una correzione accurata al tentativo successivo.

Esempio Python minimo

Il seguente codice utilizza solo la libreria standard. Definisce un piccolo schema (un dizionario con le chiavi category e summary) e un elenco fisso di categorie consentite.

import json

CATEGORIES = {"bug", "feature", "question"}

def validate(raw: str):
    try:
        value = json.loads(raw)
    except json.JSONDecodeError as exc:
        return None, f"syntax: {exc.msg}"

    if not isinstance(value, dict):
        return None, "shape: root must be an object"
    if set(value) != {"category", "summary"}:
        return None, "shape: expected category and summary"
    if value["category"] not in CATEGORIES:
        return None, "semantic: invalid category"
    if not isinstance(value["summary"], str) or not value["summary"].strip():
        return None, "semantic: summary must be text"

    return value, None

Il ciclo di riparazione stesso impone un limite massimo ai tentativi:

MAX_ATTEMPTS = 2
raw = model_response   # initial LLM output

for attempt in range(MAX_ATTEMPTS + 1):
    value, error = validate(raw)
    if error is None:
        break
    if attempt == MAX_ATTEMPTS:
        raise RuntimeError(f"failed after {MAX_ATTEMPTS} retries: {error}")

    # Send the error and schema back to the model for a fix
    raw = ask_model_to_repair(raw, error)

Se il JSON supera la validazione al primo passaggio, il ciclo termina immediatamente. In caso contrario, riprova fornendo al modello un payload conciso: l'output originale, la stringa di errore esatta e lo schema atteso. Dopo due tentativi falliti, il processo si interrompe con un'eccezione chiara.

Due regole per riparazioni economiche

  1. Mantieni i prompt di riparazione concisi – Includi solo il JSON grezzo, il messaggio di errore e lo schema. Aggiungere una lunga cronologia della chat gonfia l'uso dei token e può confondere il modello, aumentando il costo per ogni tentativo senza migliorare l'accuratezza.

  2. Separa la validazione dal fact-checking – Il parser può rilevare problemi strutturali, ma non può verificare se un'affermazione nel "summary" sia vera. Il fact-checking appartiene a una fase successiva; tentare di "riparare" un'affermazione falsa non fa altro che rendere la menzogna più pulita.

Misurare il successo

Registrare il tipo di errore di ogni tentativo (sintassi, struttura, semantica) e se il ciclo ha avuto successo fornisce una metrica concreta: la proporzione di risposte degli LLM che diventano utilizzabili entro i tentativi limitati. I team possono monitorare questo dato nel tempo, confrontare diversi stili di prompt o sperimentare con diverse definizioni di schema.

Possibili svantaggi

Limitare il ciclo significa che alcuni output malformati verranno scartati una volta raggiunto il limite, il che potrebbe aumentare il tasso di fallimento complessivo se la qualità grezza del modello è bassa. In ambienti in cui ogni risposta è critica, gli sviluppatori potrebbero preferire un limite di tentativi più alto a scapito di token extra. Il compromesso è esplicito: costi maggiori per una copertura più alta, o budget più ristretti con un limite prevedibile.

Cosa monitorare in seguito

  • Prompt engineering per la riparazione – Affinare il formato del messaggio di errore può ridurre il numero di tentativi necessari.
  • Parser alternativi – Alcune librerie offrono un parsing tollerante in grado di correggere automaticamente piccoli problemi; confrontare il loro costo rispetto a un ciclo limitato vale la pena.
  • Aggiornamenti del modello – Le nuove versioni degli LLM potrebbero produrre JSON più puliti nativamente, permettendo potenzialmente di abbassare ulteriormente il limite di tentativi.

Un ciclo di riparazione JSON limitato offre agli sviluppatori uno strumento pratico per trasformare l'output incerto degli LLM in dati affidabili senza costi fuori controllo. Trattando la validazione come un passaggio fondamentale e limitando i tentativi, i team possono mantenere soddisfatti sia i budget che i sistemi a valle.