Skip to content

Solo Mode

Eine vollständige Annotationspipeline allein in Potato durchführen. Ein Experte und ein LLM durchlaufen eine Phasen-Zustandsmaschine, die Prompt, parallele Annotation, Auflösung von Unstimmigkeiten und abschließende Validierung abdeckt.

Neu in v2.3.0

Traditionelle Annotationsprojekte erfordern mehrere Annotatoren, die Berechnung der Inter-Annotator-Übereinstimmung, Adjudikationsrunden und erheblichen Koordinationsaufwand. Für viele Forschungsteams liegt der größte Engpass nicht in der Annotationsoberfläche, sondern in der Logistik, ein Team einzustellen, zu schulen und zu führen.

Solo Mode ersetzt das Multi-Annotator-Paradigma durch einen einzelnen menschlichen Experten, der mit einem LLM zusammenarbeitet. Der Mensch liefert hochwertige Labels für eine kleine, strategisch ausgewählte Teilmenge. Das LLM lernt aus diesen Labels und schlägt Labels für den Rest vor, und der Mensch prüft nur die Fälle, in denen das LLM unsicher ist oder wahrscheinlich falsch liegt. Eine Phasen-Zustandsmaschine steuert diesen Ablauf. Sie schreitet anhand der von Ihnen gesetzten Schwellenwerte voran und kehrt zum Prompt zurück, sobald sich zeigt, dass der Prompt das Problem ist.

In internen Benchmarks erreichte Solo Mode über 95 % Übereinstimmung mit vollständigen Multi-Annotator-Pipelines und benötigte dabei nur 10-15 % der menschlichen Labels.

Der Workflow

Solo Mode ist eine Zustandsmaschine. SoloPhaseController verfolgt, in welcher Phase sich ein Projekt befindet und in welche Phasen es als Nächstes wechseln darf. Der Weg ist daher keine gerade Linie. Mehrere Phasen können zurückspringen, wenn der Prompt noch Arbeit braucht.

Einrichtung

Aufgabenbeschreibung und Daten. Generate prompt lässt ein Revisionsmodell (revision_models) den ersten Prompt aus der Beschreibung und den Annotationsschemata schreiben. Gibt es kein Revisionsmodell oder liefert es nichts zurück, wird die Beschreibung selbst zum ersten Prompt, und der Prompt-Verlauf vermerkt das. Die einzige Phase, in die es von der Einrichtung aus weitergeht, ist die Prompt-Prüfung.

Prompt-Prüfung

Potato erzeugt aus der Aufgabenbeschreibung einen Prompt, und Sie bearbeiten ihn. Mehrere spätere Phasen kehren hierher zurück, wenn sich der Prompt als das Problem herausstellt. Damit ist diese Phase der Knotenpunkt des gesamten Workflows und kein einmaliger Schritt.

Von hier aus durchläuft das Projekt entweder die Grenzfall-Schleife oder geht direkt zur parallelen Annotation.

Synthese von Grenzfällen, danach Beschriftung der Grenzfälle

Das LLM erzeugt Beispiele an der Grenze zwischen Labels, und Sie beschriften sie. Die Beschriftung kann zur Prompt-Prüfung zurückkehren, wenn die Beispiele zeigen, dass der Prompt falsch ist.

Prompt-Validierung

Ihre Labels für die Grenzfälle werden mit dem verglichen, was der Prompt liefert. Danach geht es entweder zur parallelen Annotation weiter oder zurück zur Prompt-Prüfung.

Parallele Annotation

Sie und das LLM annotieren gleichzeitig dieselben Daten. Diese Phase liefert den Übereinstimmungswert, an dem sich alles Weitere ausrichtet.

Auflösung von Unstimmigkeiten

Wo Sie und das LLM voneinander abweichen, entscheiden Sie. Die Phase kehrt zur parallelen Annotation zurück, oder zur Prompt-Prüfung, wenn die Unstimmigkeiten auf den Prompt statt auf einzelne Elemente hindeuten.

Aktive Annotation

Die Hauptphase der Annotation, mit Instanzen, die von den gewichteten Pools in instance_selection ausgewählt werden. Von hier aus kann Potato zur periodischen Prüfung, zur Regelprüfung oder weiter zur autonomen Beschriftung wechseln.

Periodische Prüfung

LLM-Labels mit niedriger Konfidenz, die alle thresholds.periodic_review_interval Elemente vorgelegt werden. Die Phase kehrt zur aktiven Annotation zurück, wechselt zur Regelprüfung oder fällt auf die Prompt-Prüfung zurück.

Regelprüfung

Die Grenzfallregeln, die Potato aus Ihrer Beschriftung abgeleitet hat, zur Freigabe vorgelegt. Die Phase kehrt zur aktiven Annotation oder zur Prompt-Prüfung zurück.

Autonome Beschriftung

Das LLM beschriftet, was übrig ist. Der einzige Ausgang ist die abschließende Validierung.

Abschließende Validierung

Sie prüfen eine Stichprobe der Labels, die nur vom LLM stammen. Wird thresholds.end_human_annotation_agreement erreicht, ist das Projekt abgeschlossen; andernfalls geht es zurück zur aktiven Annotation.

Abgeschlossen

Endzustand.


Konfiguration

Schnellstart

Eine minimale Konfiguration für 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"

Vollständige Konfigurationsreferenz

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"

Zentrale Funktionen

Konfusionsanalyse

Nach jeder Beschriftungsrunde erstellt Potato eine Konfusionsmatrix zwischen menschlichen Labels und LLM-Labels. Das Admin-Dashboard zeigt:

  • Precision, Recall und F1 pro Klasse aus Sicht des LLM
  • Die häufigsten Verwechslungspaare (z. B. "neutral fälschlich als positiv eingestuft: 23 Instanzen")
  • Beispielinstanzen für jedes Verwechslungspaar
  • Trenddiagramme, die die Verbesserung über die Verfeinerungsrunden zeigen

Solo Mode hat keine Kommandozeilenschnittstelle. Es läuft im Server und wird über eigene Seiten der laufenden Instanz gesteuert, daher lesen Sie die Analyse im Dashboard statt in einer CLI. Die Ausgabe sieht so aus:

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

Automatisierte Verfeinerungsschleife

Die Verfeinerungsschleife wechselt zwischen LLM-Beschriftung, Konfusionsanalyse und Aktualisierung der Richtlinien. Jede Iteration läuft so ab:

  1. Das LLM beschriftet den gesamten Datensatz mit den aktuellen Richtlinien
  2. Potato vergleicht das Ergebnis mit allen verfügbaren menschlichen Labels
  3. Liegt die Genauigkeit unter dem Schwellenwert, läuft die Konfusionsanalyse
  4. Das LLM schlägt anhand der Fehlermuster Änderungen an den Richtlinien vor
  5. Der Mensch prüft die Änderungen und gibt sie frei
  6. Der Zyklus wiederholt sich (bis zu 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

Labeling-Funktionen (nach dem Vorbild von ALCHEmist)

Potato erzeugt aus Mustern in den menschlichen Annotationen leichtgewichtige Labeling-Funktionen. Das sind keine LLM-Aufrufe, sondern schnelle, deterministische Regeln.

Beispiele für generierte Labeling-Funktionen:

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

Das Verhalten der Labeling-Funktionen konfigurieren:

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

Unstimmigkeits-Explorer

Der Unstimmigkeits-Explorer zeigt Instanzen, bei denen sich verschiedene Signale widersprechen. Für jede Instanz sieht der Annotator:

  • Das vom LLM vorhergesagte Label und seine Konfidenz
  • Die Stimmen der Labeling-Funktionen (falls vorhanden)
  • Die nächsten beschrifteten Nachbarn im Embedding-Raum
  • Den Rohtext bzw. Rohinhalt

Das ist die Annotationstätigkeit mit dem höchsten Wert, weil jedes Label eine echte Mehrdeutigkeit auflöst.

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

Kaskadierte Konfidenz-Eskalation

Nachdem das LLM den Großteil des Datensatzes beschriftet hat, ordnet Potato alle vom LLM beschrifteten Instanzen nach Konfidenz und eskaliert die mit der geringsten Konfidenz an den Menschen. Das geht in Batches weiter, bis sich die Qualität stabilisiert.

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

Entscheidungsmodelle

Ab Potato 2.11 kann in Solo Mode ein Entscheidungsmodell beschriften. Ein Entscheidungsmodell beantwortet eine Frage mit fester Auswahl in einem einzigen Forward Pass und gibt für jede Option eine Wahrscheinlichkeit zurück. Dadurch beschriftet es schneller und günstiger als ein LLM, kann aber keine Prompts, Regeln oder Erklärungen schreiben. Zwei Endpunkttypen sprechen ein solches Modell an. laya führt Laya im Potato-Prozess auf der CPU aus und benötigt pip install 'potato-annotation[decision]', und systemone spricht mit jedem Server, der die /v1/systemone-API anbietet, etwa gehostetes Jev oder OpenJev.

Üblicherweise sitzt ein Entscheidungsmodell in der ersten Stufe des Konfidenz-Routings. Dort beschriftet es alles, und nur die Instanzen, bei denen es unsicher ist, gehen weiter an das 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 muss ein generatives Modell sein. Standardmäßig übernimmt es labeling_models, daher muss eine Studie, deren einziges Modell ein Entscheidungsmodell ist, es selbst setzen. Entscheidungsmodelle beschriften radio-, select-, likert- und slider-Schemata, und Solo Mode startet nicht, wenn eines davon multiselect-, Span- oder Freitext-Schemata beschriften soll. Auf Bewertungsskalen folgen die Werte von Laya der Skala kaum, prüfen Sie deshalb mit Try on 10 items einige Instanzen, bevor Sie ein likert- oder slider-Schema darüber leiten. Die Konfidenz ist die Wahrscheinlichkeit, die das Modell der gewählten Option gegeben hat, und wie gut sie kalibriert ist, hängt von der Domäne ab. Legen Sie den Schwellenwert der Stufe deshalb anhand Ihrer eigenen Daten fest. config-decision-model.yaml ist eine lauffähige Version.

Verblindete Vorschläge

Standardmäßig zeigt die Annotationsseite das Label des LLM, seine Konfidenz und eine Schaltfläche zum Übernehmen, bevor Sie antworten. Das geht schnell, zieht Ihre Antwort aber auch in Richtung des Vorschlags. Solo Mode übergibt den Rest des Korpus an das LLM, sobald Ihre Übereinstimmung mit ihm end_human_annotation_agreement überschreitet. Eine Übereinstimmung, die an Labels gemessen wird, die Sie nach dem Anblick des Vorschlags vergeben haben, kann Ihren Teil der Arbeit deshalb zu früh beenden. Ab Potato 2.11 steuert solo_mode.suggestions, wann der Vorschlag erscheint:

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

blind_sample blendet den Vorschlag bei einem Anteil von blind_rate der Instanzen aus, und after_label zeigt ihn nie vorher an. Sobald minimum_blind_sample verblindete Vergleiche vorliegen, vergleicht die Übergabe die verblindete Übereinstimmung mit end_human_annotation_agreement. Die Karte Blind Agreement im Dashboard zeigt die verblindete und die sichtbare Übereinstimmung nebeneinander. Eine große positive Differenz bedeutet, dass Sie dem LLM häufiger zustimmen, wenn Sie seine Antwort sehen können.

In Potato 2.11 prüft Solo Mode außerdem die Kostengrenze des Projekts (ai_budget.cap_usd) vor dem Start der Beschriftung und vor jedem Batch, und neu sind ein Antwort-Cache (solo_mode.cache), ein Gold-Set mit Prompt-Tests (solo_mode.gold), eine Prüfung der Prompt-Empfindlichkeit, eine Beschriftungs-Jury (solo_mode.jury) und eine Curriculum-Reihenfolge (solo_mode.instance_selection.curriculum). Solo Mode Advanced Features dokumentiert jede davon. Wenn die Labeling-Engine aktiv ist, schreibt Solo Mode seine Modellvorhersagen außerdem in den Speicher der Engine, damit die Engine sie kalibrieren und zertifizieren kann.

Instanzpriorisierung mit mehreren Signalen

In allen Phasen, in denen der Mensch beschriftet, wählt Potato die informativsten Instanzen über ein System gewichteter Pools aus. Sieben Pools speisen eine gemeinsame Prioritätswarteschlange:

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: Die Konfidenz des LLM liegt unter thresholds.confidence_low
  • diversity: Weit entfernt von dem, was Sie bereits beschriftet haben, im Embedding-Raum
  • llm_predicted: Das LLM hat ein Label vergeben, und es wurde noch nicht geprüft
  • disagreement: Die für die Instanz verfügbaren Signale stimmen nicht überein
  • random: Eine kleine Zufallsstichprobe, damit die Schätzung ehrlich bleibt und blinde Flecken auffallen
  • cartography: Signal aus der Trainingsdynamik, standardmäßig aus
  • edge_case_rule: Passt zu einer Regel, die aus Ihrer früheren Beschriftung abgeleitet wurde, standardmäßig aus

Synthese von Grenzfällen

Potato lässt das LLM synthetische Beispiele erzeugen, die auf bekannte Schwächen zielen:

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

Das LLM erzeugt Beispiele, die zwischen den angegebenen Labelpaaren mehrdeutig sind. Der Mensch beschriftet sie, und diese Labels werden dem Few-Shot-Kontext für die folgenden LLM-Beschriftungsrunden hinzugefügt.

Prompt-Optimierung (nach dem Vorbild von DSPy)

Potato führt eine automatische Prompt-Optimierung durch, um das beste Anweisungsformat für das LLM zu finden, und sucht nach dem kleinsten Modell, das die Zielgenauigkeit noch hält:

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

Fortschritt überwachen

Das Admin-Dashboard zeigt den Fortschritt von Solo Mode in Echtzeit:

  • Die aktuelle Phase und den Fortschritt innerhalb jeder Phase
  • Erledigte menschliche Labels im Verhältnis zum Gesamtbudget
  • Die LLM-Genauigkeit im Zeitverlauf (pro Runde)
  • Abdeckung und Precision der Labeling-Funktionen
  • Ein Histogramm der Konfidenzverteilung
  • Die geschätzte Zeit bis zum Abschluss

Es gibt kein Modul potato.solo und keine CLI für Solo Mode. Der gesamte Workflow läuft über die eigenen Routen des Servers, daher lesen Sie den Fortschritt im Dashboard ab. Es zeigt:

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

Wann Solo Mode und wann die klassische Multi-Annotator-Methode

Verwenden Sie Solo Mode, wenn:

  • Sie einen Domänenexperten haben, der hochwertige Labels liefern kann
  • Budget oder Logistik es nicht erlauben, mehrere Annotatoren einzustellen
  • Die Aufgabe klare, gut definierte Kategorien hat
  • Sie einen großen Datensatz beschriften müssen (mehr als 1.000 Instanzen)
  • Geschwindigkeit wichtiger ist als die Messung der Inter-Annotator-Übereinstimmung

Verwenden Sie die klassische Multi-Annotator-Methode, wenn:

  • Sie Statistiken zur Inter-Annotator-Übereinstimmung für eine Publikation brauchen
  • Die Aufgabe stark subjektiv ist (z. B. Beleidigungsgehalt, Humor)
  • Sie Muster in den Unstimmigkeiten zwischen Annotatoren untersuchen wollen
  • Regulatorische Vorgaben mehrere unabhängige Annotatoren verlangen
  • Der Labelraum komplex ist oder sich noch verändert (die Annotationsrichtlinien werden noch entwickelt)

Hybrider Ansatz: Nutzen Sie Solo Mode für die anfängliche Massenbeschriftung und weisen Sie dann einem zweiten Annotator eine Zufallsstichprobe von 10-20 % zu, um Übereinstimmungsstatistiken zu berechnen. So verbinden Sie die Effizienz von Solo Mode mit der Qualitätssicherung einer Multi-Annotator-Verifikation.

Solo Mode hat keinen eigenen Verifikationsblock. Lassen Sie den zweiten Annotator in einem gewöhnlichen Potato-Durchgang über die exportierten Labels arbeiten, und berechnen Sie die Übereinstimmung zwischen seinen Labels und denen von Solo Mode so, wie Sie es für zwei beliebige Annotatoren tun würden. (icl_labeling hat zwar einen verification-Block, der gehört aber zum In-Context-Labeling, nicht zu Solo Mode.)


Weiterführende Informationen

Details zur Implementierung finden Sie in der Quelldokumentation.