Skip to content

Solo Mode

Esegui in Potato un'intera pipeline di annotazione con un solo esperto e un LLM, guidati da una macchina a stati di fasi che copre la scrittura del prompt, l'annotazione parallela, la risoluzione dei disaccordi e la validazione finale.

Novità della v2.3.0

I progetti di annotazione tradizionali richiedono più annotatori, il calcolo dell'accordo inter-annotatore, round di adjudication e un notevole carico di coordinamento. Per molti team di ricerca è questo il principale collo di bottiglia. Non l'interfaccia di annotazione, ma la logistica di assumere, formare e gestire un team.

Solo Mode sostituisce il paradigma multi-annotatore con un singolo esperto umano che collabora con un LLM. L'umano fornisce etichette di alta qualità su un sottoinsieme piccolo e scelto in modo strategico. L'LLM impara da quelle etichette, propone etichette per il resto e l'umano rivede solo i casi in cui l'LLM è incerto o probabilmente sbagliato. Una macchina a stati di fasi coordina il processo. Avanza in base alle soglie che imposti e torna al prompt ogni volta che il problema si rivela essere il prompt stesso.

In benchmark interni, Solo Mode ha raggiunto un accordo superiore al 95% con pipeline multi-annotatore complete, richiedendo solo il 10-15% delle etichette umane totali.

Il Flusso di Lavoro

Solo Mode è una macchina a stati. SoloPhaseController tiene traccia della fase in cui si trova un progetto e delle fasi verso cui può spostarsi, quindi il percorso non è una linea retta. Diverse fasi possono tornare indietro quando il prompt ha ancora bisogno di lavoro.

Configurazione iniziale

Descrizione del compito e dati. Generate prompt chiede a un modello di revisione (revision_models) di scrivere il primo prompt a partire dalla descrizione e dagli schemi di annotazione. Senza un modello di revisione, o quando questo non restituisce nulla, il primo prompt diventa la descrizione stessa e la cronologia dei prompt lo segnala. L'unica fase verso cui la configurazione iniziale può uscire è la revisione del prompt.

Revisione del prompt

Potato sintetizza un prompt a partire dalla descrizione del compito e tu lo modifichi. Diverse fasi successive tornano qui quando il problema si rivela essere il prompt, per cui questa fase è il perno dell'intero flusso di lavoro e non un passaggio da fare una volta sola.

Da qui il progetto passa per il ciclo dei casi limite oppure va direttamente all'annotazione parallela.

Sintesi dei casi limite, poi etichettatura dei casi limite

L'LLM genera esempi di confine e tu li etichetti. L'etichettatura può tornare alla revisione del prompt se gli esempi mostrano che il prompt è sbagliato.

Validazione del prompt

Le tue etichette sui casi limite vengono confrontate con ciò che produce il prompt. Da qui si procede all'annotazione parallela oppure si torna alla revisione del prompt.

Annotazione parallela

Tu e l'LLM annotate gli stessi dati nello stesso momento. È questa la fase che produce il valore di accordo da cui dipende tutto il resto.

Risoluzione dei disaccordi

Dove tu e l'LLM divergete, decidi tu. Si torna all'annotazione parallela, oppure alla revisione del prompt quando i disaccordi indicano il prompt anziché singoli elementi.

Annotazione attiva

La fase principale di annotazione, con le istanze scelte dai pool ponderati in instance_selection. Da qui Potato può passare alla revisione periodica, alla revisione delle regole oppure all'etichettatura autonoma.

Revisione periodica

Le etichette dell'LLM a bassa confidenza, proposte ogni thresholds.periodic_review_interval elementi. Si torna all'annotazione attiva, si passa alla revisione delle regole oppure si ricade sulla revisione del prompt.

Revisione delle regole

Le regole sui casi limite che Potato ha estratto dalla tua etichettatura, presentate per l'approvazione. Si torna all'annotazione attiva o alla revisione del prompt.

Etichettatura autonoma

L'LLM etichetta ciò che resta. La sua unica uscita è la validazione finale.

Validazione finale

Controlli un campione delle etichette assegnate solo dall'LLM. Superare thresholds.end_human_annotation_agreement completa il progetto. Se non viene superata, si torna all'annotazione attiva.

Completato

Stato terminale.


Configurazione

Avvio Rapido

Una configurazione minima di Solo Mode:

yaml
annotation_task_name: "Sentiment Classification"
task_dir: "."
 
data_files:
  - "data/reviews.jsonl"
 
item_properties:
  id_key: id
  text_key: text
 
solo_mode:
  enabled: true
 
  # The models that label. A list: the first is used, the rest are fallbacks.
  labeling_models:
    - endpoint_type: openai
      model: "gpt-4o"
      api_key: ${OPENAI_API_KEY}
      temperature: 0.1
      max_tokens: 1024
 
  # When the project may stop asking the human
  thresholds:
    end_human_annotation_agreement: 0.92
    minimum_validation_sample: 50
    confidence_low: 0.5
 
  state_dir: "solo_state_sentiment"
 
annotation_schemes:
  - annotation_type: radio
    name: sentiment
    description: "What is the sentiment of this text?"
    labels:
      - Positive
      - Neutral
      - Negative
 
output_annotation_dir: "output/"
export_annotation_format: "jsonl"

Riferimento Completo alla Configurazione

yaml
solo_mode:
  enabled: true
 
  # Models that label, and models that revise. Both are lists; revision_models
  # defaults to labeling_models when omitted.
  labeling_models:
    - endpoint_type: openai       # openai, anthropic, ollama, vllm, ...
      model: "gpt-4o"
      api_key: ${OPENAI_API_KEY}
      temperature: 0.1
      max_tokens: 1024
  revision_models:
    - endpoint_type: anthropic
      model: "claude-sonnet-4-20250514"
      api_key: ${ANTHROPIC_API_KEY}
      max_tokens: 1024
 
  # Sentence embeddings, used for diversity and neighbour lookups
  embedding:
    model_name: "all-MiniLM-L6-v2"
 
  # How the LLM's uncertainty is measured
  uncertainty:
    strategy: direct_confidence
    sampling_diversity:
      num_samples: 5
      temperature: 1.0
 
  # When the human can stop, and what counts as agreement per scheme type
  thresholds:
    end_human_annotation_agreement: 0.90
    minimum_validation_sample: 50
    confidence_low: 0.5
    confidence_high: 0.8
    periodic_review_interval: 100
    likert_tolerance: 1
    multiselect_jaccard_threshold: 0.5
    textbox_embedding_threshold: 0.7
    span_overlap_threshold: 0.5
 
  # Weights for the pools that choose what the human sees next
  instance_selection:
    low_confidence_weight: 0.3
    disagreement_weight: 0.1
    diversity_weight: 0.2
    llm_predicted_weight: 0.3
    random_weight: 0.1
    cartography_weight: 0.0
    edge_case_rule_weight: 0.0
 
  batches:
    llm_labeling_batch: 50
    max_parallel_labels: 200
 
  labeling_functions:
    enabled: true
    auto_extract: true
    max_functions: 50
    min_confidence: 0.85
    min_coverage: 3               # instances, not a fraction
    vote_threshold: 0.5
 
  edge_case_rules:
    enabled: true
    auto_extract_on_labeling: true
    confidence_threshold: 0.75
    min_rules_for_clustering: 10
    target_cluster_size: 15
    reannotation_enabled: true
    reannotation_confidence_threshold: 0.60
    max_reannotations_per_instance: 2
 
  confusion_analysis:
    enabled: true
    min_instances_for_pattern: 3
    max_patterns: 20
    auto_suggest_guidelines: false
 
  prompt_optimization:
    enabled: true
    find_smallest_model: true
    target_accuracy: 0.85
    optimization_interval_seconds: 300
    accuracy_weight: 0.7
    length_weight: 0.2
 
  # Send cheap cases to a small model and hard ones to a larger one
  confidence_routing:
    enabled: false
    tiers:
      - name: cheap
        confidence_threshold: 0.8
        model:
          endpoint_type: ollama
          model: "llama3.2"
 
  refinement_loop:
    enabled: true
    trigger_interval: 50
    min_improvement: 0.02
    max_cycles: 5
    patience: 2
 
  state_dir: "solo_state_sentiment"

Capacità Principali

Analisi della Confusione

Dopo ogni round di etichettatura, Potato costruisce una matrice di confusione tra le etichette umane e quelle dell'LLM. La dashboard di amministrazione mostra:

  • Precisione, recall e F1 per classe dal punto di vista dell'LLM
  • Le coppie di confusione più comuni (es. "neutral classificato come positive: 23 istanze")
  • Istanze di esempio per ogni coppia di confusione
  • Grafici di tendenza che mostrano il miglioramento tra i round di raffinamento

Solo Mode non ha un'interfaccia a riga di comando. Viene eseguito all'interno del server e si guida dalle sue pagine nell'istanza in esecuzione, quindi l'analisi si legge dalla dashboard e non da una CLI. Ecco come appare ciò che riporta:

text
Confusion Analysis (Round 2)
============================
Overall Accuracy: 0.87 (target: 0.92)

Top Confusion Pairs:
  neutral -> positive:  23 instances (15.3%)
  negative -> neutral:  11 instances (7.3%)
  positive -> neutral:   5 instances (3.3%)

Per-Class Performance:
  Positive:  P=0.91  R=0.94  F1=0.92
  Neutral:   P=0.78  R=0.71  F1=0.74
  Negative:  P=0.93  R=0.88  F1=0.90

Ciclo di Raffinamento Automatizzato

Il ciclo di raffinamento alterna l'etichettatura dell'LLM, l'analisi della confusione e gli aggiornamenti delle linee guida. In ogni iterazione:

  1. L'LLM etichetta l'intero dataset con le linee guida attuali
  2. Potato confronta il risultato con tutte le etichette umane disponibili
  3. Se l'accuratezza è sotto la soglia, viene eseguita l'analisi della confusione
  4. L'LLM propone modifiche alle linee guida in base ai pattern di errore
  5. L'umano rivede e approva le modifiche
  6. Il ciclo si ripete (fino a max_cycles)
yaml
solo_mode:
  revision_models:
    - endpoint_type: anthropic
      model: "claude-sonnet-4-20250514"
      api_key: ${ANTHROPIC_API_KEY}
 
  refinement_loop:
    enabled: true
    trigger_interval: 50           # run a cycle every 50 new human labels
    min_improvement: 0.02          # stop if a cycle gains less than 2%
    max_cycles: 5
    patience: 2                    # cycles below min_improvement before stopping

Funzioni di Etichettatura (Ispirate ad ALCHEmist)

Potato genera funzioni di etichettatura leggere dai pattern osservati nelle annotazioni umane. Non sono chiamate all'LLM. Sono regole veloci e deterministiche.

Esempi di funzioni di etichettatura generate:

python
# Auto-generated labeling function 1
# Precision: 0.96, Coverage: 0.08
def lf_strong_positive_words(text):
    positive = {"excellent", "amazing", "fantastic", "outstanding", "perfect"}
    if any(w in text.lower() for w in positive):
        if not any(neg in text.lower() for neg in {"not", "never", "no"}):
            return "Positive"
    return None  # abstain
 
# Auto-generated labeling function 2
# Precision: 0.93, Coverage: 0.05
def lf_explicit_negative(text):
    negative = {"terrible", "awful", "horrible", "worst", "disgusting"}
    if any(w in text.lower() for w in negative):
        return "Negative"
    return None

Configura il comportamento delle funzioni di etichettatura:

yaml
solo_mode:
  labeling_functions:
    enabled: true
    auto_extract: true
    max_functions: 50
    min_confidence: 0.85
    min_coverage: 3          # minimum instances a function must cover
    vote_threshold: 0.5      # agreement needed when functions disagree

Esploratore dei Disaccordi

L'esploratore dei disaccordi presenta le istanze in cui segnali diversi sono in conflitto. Per ogni istanza, l'annotatore vede:

  • L'etichetta prevista dall'LLM e la sua confidenza
  • I voti delle funzioni di etichettatura (se presenti)
  • I vicini etichettati più prossimi nello spazio degli embedding
  • Il testo o contenuto grezzo

È l'attività di annotazione che rende di più, perché ogni etichetta risolve un'ambiguità reale.

yaml
solo_mode:
  # Disagreement is a phase, not a configurable block. What you control is how
  # heavily disagreement weighs when Potato picks the next instance.
  instance_selection:
    disagreement_weight: 0.3
    low_confidence_weight: 0.3
    diversity_weight: 0.2
    llm_predicted_weight: 0.1
    random_weight: 0.1

Escalation a Cascata della Confidenza

Dopo che l'LLM ha etichettato la maggior parte del dataset, Potato ordina tutte le istanze etichettate dall'LLM per confidenza e passa all'umano quelle meno sicure. Il processo continua a batch finché la qualità non si stabilizza.

yaml
solo_mode:
  thresholds:
    confidence_low: 0.5           # below this, the human sees it
    confidence_high: 0.8          # above this, the LLM's label stands
    periodic_review_interval: 100 # surface low-confidence labels this often
 
  batches:
    llm_labeling_batch: 50
    max_parallel_labels: 200

Modelli Decisionali

Da Potato 2.11, in Solo Mode può etichettare anche un modello decisionale. Un modello decisionale risponde a una domanda a scelta fissa in un solo forward pass e restituisce una probabilità per ogni opzione, il che lo rende più veloce ed economico di un LLM nell'etichettatura, e non può scrivere prompt, regole o spiegazioni. Due tipi di endpoint vi accedono. laya esegue Laya dentro il processo di Potato su CPU e richiede pip install 'potato-annotation[decision]', e systemone comunica con qualsiasi server che espone l'API /v1/systemone, come Jev o OpenJev in hosting.

Il posto abituale di un modello decisionale è il primo livello del confidence routing, dove etichetta tutto e solo gli elementi su cui è incerto passano all'LLM:

yaml
solo_mode:
  labeling_models:
    - endpoint_type: laya
      model: convaiinnovations/laya
  revision_models:                     # required: decision models cannot write text
    - endpoint_type: vllm
      model: google/gemma-4-12B-it-qat-w4a16-ct
      base_url: http://localhost:8001/v1
  confidence_routing:
    enabled: true
    tiers:
      - name: laya
        confidence_threshold: 0.9
        model:
          endpoint_type: laya
          model: convaiinnovations/laya
          device: cpu
      - name: llm
        confidence_threshold: 0.5
        model:
          endpoint_type: vllm
          model: google/gemma-4-12B-it-qat-w4a16-ct
          base_url: http://localhost:8001/v1

revision_models deve essere un modello generativo. Il suo default è labeling_models, quindi uno studio il cui unico modello è decisionale deve impostarlo. I modelli decisionali etichettano schemi radio, select, likert e slider, e Solo Mode si rifiuta di avviarsi se uno di essi è impostato per etichettare schemi multiselect, di span o di testo libero. Sulle scale di valutazione i punteggi di Laya seguono a fatica la scala, quindi controlla qualche elemento con Try on 10 items prima di instradarvi uno schema likert o slider. La confidenza è la probabilità che il modello ha dato all'opzione scelta, e la sua calibrazione varia a seconda del dominio, quindi imposta la soglia del livello a partire dai tuoi dati. config-decision-model.yaml è una versione eseguibile.

Suggerimenti in Cieco

Per default la pagina di annotazione mostra l'etichetta dell'LLM, la sua confidenza e un pulsante Accept prima che tu risponda, il che è veloce e attira anche la tua risposta verso il suggerimento. Solo Mode affida il resto del corpus all'LLM quando il tuo accordo con esso supera end_human_annotation_agreement, quindi un accordo misurato su etichette date dopo aver visto il suggerimento può chiudere troppo presto la tua parte di lavoro. Da Potato 2.11, solo_mode.suggestions controlla quando compare il suggerimento:

yaml
solo_mode:
  suggestions:
    display: blind_sample      # before_label | after_label | blind_sample
    blind_rate: 0.2
    minimum_blind_sample: 20

blind_sample nasconde il suggerimento su una frazione blind_rate degli elementi, e after_label non lo mostra mai per primo. Quando ci sono minimum_blind_sample confronti in cieco, il passaggio di consegne confronta l'accordo in cieco con end_human_annotation_agreement. La scheda Blind Agreement della dashboard mostra affiancati l'accordo in cieco e quello con suggerimento visibile, e un ampio divario positivo significa che concordi con l'LLM più spesso quando puoi vedere la sua risposta.

In Potato 2.11 Solo Mode controlla anche il limite di spesa del progetto (ai_budget.cap_usd) prima che inizi l'etichettatura e prima di ogni batch, e aggiunge una cache delle risposte (solo_mode.cache), un gold set con prove dei prompt (solo_mode.gold), un controllo della sensibilità al prompt, una giuria di etichettatura (solo_mode.jury) e un ordinamento a curriculum (solo_mode.instance_selection.curriculum). Solo Mode Advanced Features documenta ciascuno di essi. Quando il motore di etichettatura è attivo, Solo Mode scrive anche le predizioni dei suoi modelli nell'archivio del motore, così il motore può calibrarle e certificarle.

Prioritizzazione Multi-Segnale delle Istanze

In tutte le fasi che coinvolgono l'etichettatura umana, Potato usa un sistema di pool ponderati per selezionare le istanze più informative. Sette pool alimentano un'unica coda di priorità:

yaml
solo_mode:
  instance_selection:
    low_confidence_weight: 0.3
    diversity_weight: 0.2
    llm_predicted_weight: 0.3
    disagreement_weight: 0.1
    random_weight: 0.1
    cartography_weight: 0.0
    edge_case_rule_weight: 0.0
  • low_confidence: la confidenza dell'LLM scende sotto thresholds.confidence_low
  • diversity: lontana, nello spazio degli embedding, da ciò che hai già etichettato
  • llm_predicted: l'LLM ha un'etichetta che non è ancora stata controllata
  • disagreement: i segnali disponibili per l'istanza non concordano
  • random: una piccola estrazione casuale, per mantenere onesta la stima e individuare i punti ciechi
  • cartography: segnale basato sulle dinamiche di addestramento, disattivato per impostazione predefinita
  • edge_case_rule: corrisponde a una regola estratta dalla tua etichettatura precedente, disattivato per impostazione predefinita

Sintesi dei Casi Limite

Potato usa l'LLM per generare esempi sintetici che mirano alle debolezze note:

yaml
solo_mode:
  edge_case_rules:
    enabled: true
    auto_extract_on_labeling: true   # derive rules as you label
    confidence_threshold: 0.75
    min_rules_for_clustering: 10
    target_cluster_size: 15
    reannotation_enabled: true       # revisit items a new rule contradicts
    reannotation_confidence_threshold: 0.60
    max_reannotations_per_instance: 2

L'LLM genera esempi ambigui tra le coppie di etichette specificate. L'umano li etichetta e queste etichette vengono aggiunte al contesto few-shot per i successivi round di etichettatura dell'LLM.

Ottimizzazione del Prompt (Ispirata a DSPy)

Potato esegue un'ottimizzazione automatica del prompt per trovare il formato di istruzioni migliore per l'LLM, e cerca il modello più piccolo che mantiene ancora l'accuratezza obiettivo:

yaml
solo_mode:
  prompt_optimization:
    enabled: true
    find_smallest_model: true          # prefer the cheapest model that holds up
    target_accuracy: 0.85
    optimization_interval_seconds: 300
    accuracy_weight: 0.7               # how the candidates are scored
    length_weight: 0.2

Monitoraggio dei Progressi

La dashboard di amministrazione mostra i progressi di Solo Mode in tempo reale:

  • Fase attuale e avanzamento all'interno di ogni fase
  • Etichette umane completate rispetto al budget totale
  • Accuratezza dell'LLM nel tempo (per round)
  • Copertura e precisione delle funzioni di etichettatura
  • Istogramma della distribuzione della confidenza
  • Tempo stimato al completamento

Non esistono un modulo potato.solo né una CLI di Solo Mode. L'intero flusso di lavoro si trova dietro le route del server, quindi i progressi si leggono dalla dashboard. Riporta:

text
Solo Mode Status
================
Current Phase: 6 (Active Labeling) - Batch 3/10
Human Labels: 142 / ~300 estimated total
LLM Accuracy: 0.89 (target: 0.92)
LF Coverage: 0.23 (labeling functions cover 23% of data)
Dataset Size: 10,000 instances
  - Human labeled: 142
  - LF labeled: 2,300
  - LLM labeled: 7,558
  - Unlabeled: 0

Quando Usare Solo Mode e Quando il Multi-Annotatore Tradizionale

Usa Solo Mode quando:

  • Hai un esperto di dominio in grado di fornire etichette di alta qualità
  • Il budget o la logistica impediscono di assumere più annotatori
  • Il compito ha categorie chiare e ben definite
  • Devi etichettare un dataset di grandi dimensioni (1.000+ istanze)
  • La velocità conta più della misurazione dell'accordo inter-annotatore

Usa il multi-annotatore tradizionale quando:

  • Ti servono statistiche sull'accordo inter-annotatore per la pubblicazione
  • Il compito è molto soggettivo (es. offensività, umorismo)
  • Devi studiare i pattern di disaccordo tra annotatori
  • Requisiti normativi impongono più annotatori indipendenti
  • Lo spazio delle etichette è complesso o in evoluzione (le linee guida di annotazione sono ancora in fase di sviluppo)

Approccio ibrido: usa Solo Mode per l'etichettatura iniziale in blocco, poi assegna a un secondo annotatore un campione casuale del 10-20% per calcolare le statistiche di accordo. Ottieni così l'efficienza di Solo Mode insieme al controllo di qualità della verifica multi-annotatore.

Solo Mode non ha un proprio blocco di verifica. Esegui il secondo annotatore come un normale passaggio di Potato sulle etichette esportate e calcola l'accordo tra le sue etichette e quelle 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.)


Ulteriori Letture

Per i dettagli di implementazione, consulta la documentazione sorgente.