Skip to content
Tutorial12 min di lettura

Solo Mode: Come un Solo Annotatore Può Etichettare 10.000 Esempi

Tutorial passo per passo su Solo Mode di Potato: etichetta tu stesso un insieme seed, lascia che un LLM etichetti il resto e usa i controlli di accuratezza per decidere quando il risultato è abbastanza buono per fermarti.

Potato Team

Hai 10.000 recensioni di prodotti da etichettare per sentiment (Positivo, Neutro, Negativo). Far etichettare tutto a tre annotatori richiede settimane e costa migliaia di dollari. Con Solo Mode un singolo esperto del dominio ottiene una qualità comparabile etichettando solo 500-1.000 istanze. Un LLM gestisce il resto e la persona rivede tutto ciò su cui l'LLM è incerto.

Questo tutorial percorre l'intero processo.


Cosa Ti Servirà

  • Potato 2.3.0 o successivo, con gli extra AI per i provider LLM: pip install potato-annotation[ai]
  • Una API key di OpenAI o Anthropic (per il componente LLM)
  • Il tuo dataset in formato JSONL
  • Un annotatore esperto (potresti essere tu)

Passo 1: Prepara i Tuoi Dati

Crea data/reviews.jsonl con una recensione per riga:

json
{"id": "rev_001", "text": "Absolutely love this product! Best purchase I've made all year.", "source": "amazon"}
{"id": "rev_002", "text": "It works fine. Nothing special but gets the job done.", "source": "amazon"}
{"id": "rev_003", "text": "Broke after two weeks. Complete waste of money.", "source": "amazon"}
{"id": "rev_004", "text": "The quality is decent for the price point. I might buy again.", "source": "amazon"}
{"id": "rev_005", "text": "Arrived damaged and customer service was unhelpful.", "source": "amazon"}

Per questo tutorial, immagina che il file contenga 10.000 recensioni.


Passo 2: Crea la Configurazione

Crea config.yaml:

yaml
annotation_task_name: "Product Review Sentiment (Solo Mode)"
task_dir: "."
 
data_files:
  - "data/reviews.jsonl"
 
item_properties:
  id_key: id
  text_key: text
 
# --- Solo Mode Configuration ---
solo_mode:
  enabled: true
 
  # The model that labels. A list, so you can give fallbacks.
  labeling_models:
    - endpoint_type: openai
      model: "gpt-4o"
      api_key: ${OPENAI_API_KEY}
      temperature: 0.1
      max_tokens: 1024
 
  embedding:
    model_name: "all-MiniLM-L6-v2"
 
  uncertainty:
    strategy: direct_confidence
 
  # When you are allowed to stop labeling
  thresholds:
    end_human_annotation_agreement: 0.93
    minimum_validation_sample: 50
    confidence_low: 0.5
    confidence_high: 0.85
    periodic_review_interval: 100
 
  # What decides which instance you see next
  instance_selection:
    low_confidence_weight: 0.30
    disagreement_weight: 0.25
    diversity_weight: 0.20
    llm_predicted_weight: 0.15
    random_weight: 0.10
 
  batches:
    llm_labeling_batch: 25
    max_parallel_labels: 200
 
  labeling_functions:
    enabled: true
    auto_extract: true
    max_functions: 15
    min_confidence: 0.92
    min_coverage: 3
 
  edge_case_rules:
    enabled: true
    auto_extract_on_labeling: true
 
  confusion_analysis:
    enabled: true
    min_instances_for_pattern: 3
    max_patterns: 20
 
  prompt_optimization:
    enabled: true
    target_accuracy: 0.93
 
  refinement_loop:
    enabled: true
    trigger_interval: 50
    min_improvement: 0.02
    max_cycles: 3
 
  state_dir: "solo_state_reviews"
 
# --- Annotation Schema ---
annotation_schemes:
  - annotation_type: radio
    name: sentiment
    description: "What is the overall sentiment of this review?"
    labels:
      - "Positive"
      - "Neutral"
      - "Negative"
    label_requirement:
      required: true
    sequential_key_binding: true
 
output_annotation_dir: "output/"
export_annotation_format:
  - jsonl
  - parquet

Passo 3: Avvia il Server

bash
potato start config.yaml -p 8000

Apri http://localhost:8000 ed effettua il login. Compare la dashboard di Solo Mode, che mostra il progetto in fase di configurazione, pronto perché tu riveda il prompt che ha sintetizzato.


Passo 4: Annotazione in Parallelo, le Tue Prime 50

Potato ha scelto 50 recensioni diverse tramite clustering basato su embedding. Non sono casuali: sono scelte per coprire la distribuzione dei tuoi dati nel modo più ampio possibile.

Etichettale una per una. È la fase che conta di più, perché la qualità delle tue etichette seed fissa il limite massimo di ciò che l'LLM può imparare. Prenditi il tuo tempo e resta coerente.

Stima del tempo: 15-25 minuti a 20-30 secondi per istanza.

Quando ne hai etichettate abbastanza, l'LLM inizia a etichettare insieme a te senza che tu debba chiederlo.


Passo 5: L'LLM Recupera Terreno

Questa fase procede da sola. Potato invia all'LLM un batch di 200 istanze con le tue 50 etichette seed come esempi few-shot, poi confronta le previsioni con 10 etichette seed tenute da parte per stimare l'accuratezza di base.

Nella dashboard compare un indicatore di avanzamento. Di solito servono 1-2 minuti, a seconda del provider LLM.

Risultato tipico: Alla prima calibrazione l'LLM si attesta su un'accuratezza del 75-85%. È normale. Non ha ancora imparato il tuo stile di annotazione.


Passo 6: Analisi della Confusione

Potato mostra una matrice di confusione che indica dove l'LLM non concorda con le tue etichette. Un output tipico:

text
Confusion Analysis (Round 1)
============================
Overall Accuracy: 0.82 (target: 0.93)

Top Confusion Pairs:
  Neutral -> Positive:  14 instances (7.0%)
  Negative -> Neutral:   9 instances (4.5%)
  Positive -> Neutral:   4 instances (2.0%)

Questo indica il principale punto debole dell'LLM in questo caso: continua a promuovere recensioni neutre a positive. È un errore comune, perché gli LLM tendono a pendere verso il positivo.

La tua azione: Esamina le coppie di confusione. Clicca su ciascuna coppia per vedere le istanze specifiche in cui l'LLM ha sbagliato: è il modo più rapido per capire come sbaglia.


Passo 7: Di Nuovo alla Revisione del Prompt

In base all'analisi della confusione, Potato prepara linee guida raffinate per l'LLM e le mostra affiancate: da un lato il prompt attuale, dall'altro le modifiche specifiche che propone a partire dagli schemi di errore.

Ad esempio, Potato potrebbe suggerire di aggiungere:

"Le recensioni che descrivono un prodotto come 'va bene', 'ok' o 'discreto' senza un'emozione forte vanno etichettate come Neutral, anche se menzionano un nuovo acquisto."

Esamina ogni modifica suggerita e approvala, modificala o rifiutala. Puoi anche scrivere chiarimenti tuoi.

Stima del tempo: 5-10 minuti.


Passo 8: Funzioni di Etichettatura

Potato genera funzioni di etichettatura programmatiche a partire dagli schemi presenti nelle tue etichette seed. Sono regole veloci e deterministiche che gestiscono i casi facili:

text
Generated Labeling Functions:
  LF1: Strong positive words (love, amazing, best, excellent)
       Precision: 0.97, Coverage: 0.06
  LF2: Strong negative words (terrible, awful, worst, waste)
       Precision: 0.95, Coverage: 0.04
  LF3: Exclamation + positive adjective
       Precision: 0.94, Coverage: 0.03
  LF4: Return/refund mention + negative context
       Precision: 0.92, Coverage: 0.02
  ...
  Total coverage: 0.18 (1,800 of 10,000 instances)

Le funzioni di etichettatura coprono il 18% del dataset con una precisione superiore al 92%. Quelle istanze vengono etichettate automaticamente, il che lascia l'LLM e la persona liberi per i casi più difficili.

La tua azione: Esamina le funzioni generate e disattiva quelle che sembrano inaffidabili. Questo passo è facoltativo, perché Potato conserva comunque solo le funzioni sopra la soglia di precisione che hai configurato.


Passo 9: Annotazione Attiva (125-375 Istanze)

Qui svolgi la maggior parte dell'etichettatura. Potato sceglie le istanze usando i pool ponderati definiti sopra in instance_selection:

  • low_confidence_weight (0.30): Recensioni in cui la confidenza dell'LLM è più bassa
  • disagreement_weight (0.25): Recensioni in cui l'LLM e le funzioni di etichettatura non concordano
  • diversity_weight (0.20): Recensioni scelte per distribuirsi nello spazio degli embedding invece di raggrupparsi
  • llm_predicted_weight (0.15): Recensioni etichettate dall'LLM riprese per un confronto con una persona
  • random_weight (0.10): Recensioni casuali per la calibrazione

Esistono altri due pesi, lasciati qui al valore predefinito di zero, quindi non contribuiscono: edge_case_rule_weight, per le istanze che corrispondono a una regola di caso limite scoperta, e cartography_weight, per le istanze la cui confidenza oscilla tra un ciclo e l'altro. Potato verifica che i pesi sommino a 1.0 e registra un avviso quando non è così, ma usa i valori così come sono scritti, quindi assicurati tu che la somma torni.

Le etichetti in batch da 25. Dopo ogni batch, Potato aggiorna la stima dell'accuratezza dell'LLM e decide se continuare.

Traiettoria tipica:

  • Batch 1-3 (75 istanze): L'accuratezza sale dall'82% all'87%
  • Batch 4-6 (150 istanze): L'accuratezza raggiunge il 90%
  • Batch 7-10 (250 istanze): L'accuratezza si stabilizza al 91-92%

Se l'accordo raggiunge il tuo end_human_annotation_agreement, Solo Mode passa all'etichettatura autonoma. Altrimenti continua ad alternare revisione e raffinamento.

Stima del tempo: 45-90 minuti in totale, a seconda di quanti batch servono.


Passo 10: Il Loop di Raffinamento

Se dopo l'etichettatura attiva l'accuratezza è ancora sotto la soglia, Potato esegue un altro ciclo del loop di raffinamento:

  1. L'LLM ri-etichetta l'intero dataset con le linee guida aggiornate e più esempi few-shot
  2. Potato ricalcola l'accuratezza rispetto a tutte le etichette umane
  3. Individua i nuovi schemi di confusione
  4. Raffina di nuovo le linee guida

Questa fase richiede pochissimo intervento. Devi solo approvare le modifiche alle linee guida.

Risultato tipico: L'accuratezza migliora del 2-4% per ogni ciclo di raffinamento.


Passo 11: Risoluzione dei Disaccordi

Potato presenta le istanze più controverse: casi in cui l'LLM, le funzioni di etichettatura e l'analisi dei vicini più prossimi danno tutti risposte diverse. Per ogni istanza vedi:

  • Il testo della recensione
  • La previsione dell'LLM e la sua confidenza
  • I voti delle funzioni di etichettatura
  • I 3 esempi etichettati più vicini con le loro etichette
  • Il ragionamento chain-of-thought dell'LLM

Sono i casi difficili, e qui le tue etichette pesano più che in qualunque altro punto del processo.

Stima del tempo: 20-30 minuti per 100-150 istanze.


Passo 12: Sintesi dei Casi Limite

Potato genera recensioni sintetiche mirate agli schemi di confusione rimanenti. Ad esempio, se l'LLM ha ancora difficoltà con le "recensioni neutre che menzionano un nuovo acquisto", genera esempi come:

"È un prodotto discreto per il prezzo. Magari ne prendo un altro se c'è uno sconto."

Etichetti questi esempi sintetici e Potato li aggiunge al contesto few-shot dell'LLM.

Stima del tempo: 10-15 minuti per 30 esempi.


Passo 13: Revisione Periodica

A questo punto l'LLM ha etichettato la maggior parte del dataset. Potato ordina tutte le sue etichette per confidenza e ti invia quelle con la confidenza più bassa in batch da 25.

text
Confidence Escalation Progress:
  Batch 1: 25 instances, 23/25 correct (92%)
  Batch 2: 25 instances, 24/25 correct (96%)
  Batch 3: 25 instances, 25/25 correct (100%)
  -> Stopping: last 3 batches stable

Quando l'accuratezza smette di muoversi per tre batch consecutivi, Solo Mode considera affidabili le restanti etichette ad alta confidenza. Nota che si tratta di stabilità, non di perfezione. L'esecuzione qui sopra si ferma dopo 92%, 96% e 100% perché il valore si è assestato, non perché l'ultimo batch fosse privo di errori.

Stima del tempo: 15-20 minuti.


Passo 14: Ottimizzazione del Prompt

Questa fase procede da sola. Potato prova 8 varianti del prompt e tiene quella con il punteggio F1 più alto sulle etichette umane che hai accumulato:

text
Prompt Optimization Results:
  Variant 1 (direct, 5 examples):     F1=0.91
  Variant 2 (CoT, 5 examples):        F1=0.93
  Variant 3 (direct, 10 examples):    F1=0.92
  Variant 4 (CoT, 10 examples):       F1=0.94  <-- selected
  Variant 5 (direct, 15 examples):    F1=0.92
  Variant 6 (CoT, 15 examples):       F1=0.93
  Variant 7 (self-consistency, 5x):   F1=0.94
  Variant 8 (self-consistency, 10x):  F1=0.94

Poi usa il prompt migliore per un passaggio finale di ri-etichettatura.


Passo 15: Validazione Finale

Potato estrae 100 istanze casuali etichettate dall'LLM da farti rivedere. Le etichetti, e Potato confronta le tue etichette con quelle dell'LLM.

text
Final Validation:
  Reviewed: 100 instances
  LLM correct: 94/100 (94%)
  Threshold: 93%
  -> PASSED

Se l'LLM supera la tua soglia, il progetto è completo. In caso contrario, torna all'annotazione attiva per un altro ciclo. Questo ritorno è l'unica transizione consentita a partire dalla validazione finale.

Stima del tempo: 10-15 minuti.


Riepilogo dei Risultati

Alla fine la dashboard riporta da dove provengono le etichette. Solo Mode non ha un'interfaccia a riga di comando (gira all'interno del server e si gestisce dalle sue pagine), quindi questi dati si leggono dall'istanza in esecuzione e non da un terminale:

text
Solo Mode Complete
==================
Dataset: 10,000 instances
Total human labels: 612
  Seed: 50
  Active labeling: 275
  Disagreement exploration: 137
  Edge case synthesis: 30
  Confidence escalation: 75
  Final validation: 45

LLM labels: 8,200 (accuracy: 94.1%)
LF labels: 1,800 (precision: 95.3%)
Unlabeled: 0

Final label distribution:
  Positive: 4,823 (48.2%)
  Neutral:  3,011 (30.1%)
  Negative: 2,166 (21.7%)

Total human time: ~3.5 hours
Estimated multi-annotator cost (3x): ~$4,500
Solo Mode cost: ~$450 (API fees) + ~$175 (annotator time)
Savings: ~88%

La persona ha etichettato 612 istanze su 10.000, circa il 6%. L'LLM e le funzioni di etichettatura hanno gestito il resto con un'accuratezza superiore al 94%.


Esportazione dei Risultati

Solo Mode scrive sullo stesso storage di qualsiasi altro progetto, quindi le etichette escono con il normale esportatore:

bash
python -m potato.export -c config.yaml -f jsonl -o ./export/

Ogni riga include l'etichetta e la sua fonte:

json
{"id": "rev_001", "sentiment": "Positive", "source": "human", "confidence": 1.0}
{"id": "rev_002", "sentiment": "Neutral", "source": "llm", "confidence": 0.91}
{"id": "rev_003", "sentiment": "Negative", "source": "labeling_function", "confidence": 0.97}

Per l'esportazione Parquet:

python
import pandas as pd
df = pd.read_parquet("output/exports/parquet/annotations.parquet")
print(df["sentiment"].value_counts())

annotations.parquet contiene una riga per ogni coppia di istanza e annotatore, con ogni schema di annotazione appiattito in una colonna propria, quindi selezioni lo schema per nome e non tramite una colonna generica value.


Garanzia della Qualità: Verifica Ibrida

Per dataset di qualità da pubblicazione, aggiungi un secondo annotatore che riveda un campione. La documentazione sorgente di Solo Mode descrive più nel dettaglio le opzioni di verifica.

Solo Mode non ha un proprio blocco di verifica, quindi si tratta di un normale secondo passaggio: esporta le etichette, rimettile in Potato come un progetto normale con un revisore su un campione del 10% e calcola l'accordo tra il revisore e le etichette di Solo Mode come faresti per due annotatori qualsiasi. (icl_labeling ha un blocco verification, ma appartiene all'etichettatura in-context, non a Solo Mode.)


Risoluzione dei Problemi

L'accuratezza dell'LLM si stabilizza al di sotto della soglia

  • Aumenta il numero di seed: Prova con 75-100 istanze seed invece di 50
  • Cambia LLM: Prova claude-sonnet-4-20250514 invece di GPT-4o (o viceversa)
  • Abbassa la soglia: Se il 93% non è raggiungibile, valuta se il 90% è accettabile per il tuo caso d'uso
  • Controlla i tuoi dati: Alcuni dataset sono intrinsecamente ambigui. Se l'accordo tra persone arrivasse solo al 90%, non aspettarti che l'LLM faccia meglio

L'annotazione attiva richiede troppi batch

  • Aumenta la dimensione del batch: Cambia batch_size da 25 a 50
  • Regola i pesi dei pool: Se la maggior parte delle istanze escalate proviene dal pool a bassa confidenza, riduci low_confidence_weight e aumenta disagreement_weight o diversity_weight

Le funzioni di etichettatura hanno una bassa copertura

  • È normale per task senza forti segnali lessicali (ad es. rilevamento del sarcasmo, sentiment implicito)
  • Le funzioni di etichettatura funzionano meglio con schemi espliciti basati su parole chiave
  • Solo Mode funziona anche senza funzioni di etichettatura, e in quel caso è l'LLM a coprire quelle istanze

Ulteriori Letture