Solo Mode
Menez seul une chaîne d'annotation complète dans Potato. Un expert et un LLM avancent dans une machine à états de phases qui couvre le prompt, l'annotation en parallèle, la résolution des désaccords et la validation finale.
Nouveau dans la v2.3.0
Un projet d'annotation classique demande plusieurs annotateurs, un calcul d'accord inter-annotateurs, des tours d'arbitrage et beaucoup de coordination. Pour beaucoup d'équipes de recherche, le principal goulot d'étranglement n'est pas l'interface d'annotation, mais la logistique du recrutement, de la formation et de la gestion d'une équipe.
Le Solo Mode remplace le paradigme multi-annotateurs par un seul expert humain qui collabore avec un LLM. L'humain fournit des étiquettes de haute qualité sur un petit sous-ensemble choisi de façon stratégique. Le LLM apprend de ces étiquettes et propose des étiquettes pour le reste, et l'humain ne relit que les cas où le LLM hésite ou se trompe probablement. Une machine à états de phases orchestre le tout. Elle avance selon les seuils que vous fixez et revient au prompt chaque fois qu'il s'avère que c'est le prompt qui pose problème.
Sur nos tests internes, le Solo Mode a atteint plus de 95 % d'accord avec des chaînes multi-annotateurs complètes, tout en ne demandant que 10 à 15 % du total des étiquettes humaines.
Le flux de travail
Le Solo Mode est une machine à états. SoloPhaseController suit la phase dans laquelle se trouve un projet et les phases vers lesquelles il a le droit de passer ensuite. Le parcours n'est donc pas linéaire, et plusieurs phases peuvent revenir en arrière quand le prompt a encore besoin de travail.
Mise en place
Description de la tâche et données. Generate prompt demande à un modèle de révision (revision_models) d'écrire le premier prompt à partir de la description et des schémas d'annotation. Sans modèle de révision, ou quand celui-ci ne renvoie rien, la description elle-même devient le premier prompt, et l'historique des prompts le signale. La seule phase vers laquelle la mise en place peut passer est la relecture du prompt.
Relecture du prompt
Potato synthétise un prompt à partir de la description de la tâche, et vous le modifiez. Plusieurs phases ultérieures reviennent ici quand il s'avère que le problème vient du prompt, ce qui fait de cette phase le pivot de tout le flux de travail plutôt qu'une étape ponctuelle.
À partir d'ici, le projet passe soit par la boucle des cas limites, soit directement à l'annotation en parallèle.
Synthèse de cas limites, puis étiquetage des cas limites
Le LLM génère des exemples à la frontière et vous les étiquetez. L'étiquetage peut revenir à la relecture du prompt si les exemples montrent que le prompt est faux.
Validation du prompt
Vos étiquettes sur les cas limites sont comparées à ce que produit le prompt. La phase passe ensuite à l'annotation en parallèle ou revient à la relecture du prompt.
Annotation en parallèle
Vous et le LLM annotez les mêmes données en même temps. C'est cette phase qui produit le chiffre d'accord dont dépend tout le reste.
Résolution des désaccords
Là où vous et le LLM divergez, c'est vous qui tranchez. La phase revient à l'annotation en parallèle, ou à la relecture du prompt quand les désaccords désignent le prompt plutôt que des éléments isolés.
Annotation active
La phase d'annotation principale, avec des instances choisies par les pools pondérés de instance_selection. À partir d'ici, Potato peut passer à la relecture périodique, à la relecture des règles ou à l'étiquetage autonome.
Relecture périodique
Des étiquettes du LLM à faible confiance, présentées tous les thresholds.periodic_review_interval éléments. La phase revient à l'annotation active, passe à la relecture des règles ou retombe sur la relecture du prompt.
Relecture des règles
Les règles de cas limites que Potato a extraites de votre étiquetage, présentées pour approbation. La phase revient à l'annotation active ou à la relecture du prompt.
Étiquetage autonome
Le LLM étiquette ce qui reste. Sa seule sortie est la validation finale.
Validation finale
Vous vérifiez un échantillon des étiquettes posées par le LLM seul. Dépasser thresholds.end_human_annotation_agreement termine le projet ; échouer le renvoie à l'annotation active.
Terminé
État final.
Configuration
Démarrage rapide
Une configuration minimale du Solo Mode :
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"Référence complète de la configuration
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"Principales capacités
Analyse de confusion
Après chaque tour d'étiquetage, Potato construit une matrice de confusion entre les étiquettes humaines et celles du LLM. Le tableau de bord d'administration affiche :
- La précision, le rappel et le F1 par classe, du point de vue du LLM
- Les paires de confusion les plus fréquentes (par exemple « neutre classé à tort comme positif : 23 instances »)
- Des instances d'exemple pour chaque paire de confusion
- Des graphiques de tendance montrant l'amélioration au fil des tours d'affinement
Le Solo Mode n'a pas d'interface en ligne de commande. Il tourne dans le serveur et se pilote depuis ses propres pages sur l'instance en cours d'exécution, donc l'analyse se lit dans le tableau de bord et non dans une CLI. Voici à quoi ressemble ce qu'il affiche :
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
Boucle d'affinement automatisée
La boucle d'affinement alterne entre l'étiquetage par le LLM, l'analyse de confusion et la mise à jour des consignes. À chaque itération :
- Le LLM étiquette le jeu de données complet avec les consignes actuelles
- Potato compare le résultat à toutes les étiquettes humaines disponibles
- Si la précision est sous le seuil, l'analyse de confusion s'exécute
- Le LLM propose des modifications des consignes à partir des motifs d'erreur
- L'humain relit et approuve les modifications
- Le cycle se répète (jusqu'à
max_cycles)
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 stoppingFonctions d'étiquetage (inspirées d'ALCHEmist)
Potato génère des fonctions d'étiquetage légères à partir des motifs observés dans les annotations humaines. Ce ne sont pas des appels au LLM, mais des règles rapides et déterministes.
Exemples de fonctions d'étiquetage générées :
# 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 NoneConfigurez le comportement des fonctions d'étiquetage :
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 disagreeExplorateur de désaccords
L'explorateur de désaccords présente les instances pour lesquelles différents signaux se contredisent. Pour chaque instance, l'annotateur voit :
- L'étiquette prédite par le LLM et sa confiance
- Les votes des fonctions d'étiquetage (s'il y en a)
- Les plus proches voisins étiquetés dans l'espace des plongements
- Le texte ou contenu brut
C'est l'activité d'annotation qui rapporte le plus, puisque chaque étiquette lève une véritable ambiguïté.
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.1Escalade de confiance en cascade
Une fois que le LLM a étiqueté l'essentiel du jeu de données, Potato classe toutes les instances étiquetées par le LLM selon leur confiance et fait remonter à l'humain celles dont la confiance est la plus faible. Cela se poursuit par lots jusqu'à ce que la qualité se stabilise.
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: 200Modèles de décision
À partir de Potato 2.11, un modèle de décision peut étiqueter en Solo Mode. Un modèle de décision répond à une question à choix fixe en un seul passage avant et renvoie une probabilité pour chaque option, ce qui le rend plus rapide et moins coûteux qu'un LLM pour étiqueter, mais il ne peut écrire ni prompts, ni règles, ni explications. Deux types de points de terminaison y donnent accès. laya exécute Laya dans le processus Potato, sur CPU, et exige pip install 'potato-annotation[decision]', et systemone dialogue avec tout serveur qui expose l'API /v1/systemone, comme Jev hébergé ou OpenJev.
La place habituelle d'un modèle de décision est le premier niveau du routage par confiance, où il étiquette tout et où seuls les items sur lesquels il hésite passent au LLM :
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/v1revision_models doit être un modèle génératif. Il vaut labeling_models par défaut, donc une étude dont le seul modèle est un modèle de décision doit le définir. Les modèles de décision étiquettent les schémas radio, select, likert et slider, et le Solo Mode refuse de démarrer si l'un d'eux doit étiqueter des schémas multiselect, de spans ou de texte libre. Sur les échelles de notation, les scores de Laya suivent à peine l'échelle, alors vérifiez quelques items avec Try on 10 items avant de faire passer un schéma likert ou slider par lui. La confiance est la probabilité que le modèle a attribuée à l'option choisie, et sa calibration varie selon le domaine, donc fixez le seuil du niveau à partir de vos propres données. config-decision-model.yaml en est une version exécutable.
Suggestions en aveugle
Par défaut, la page d'annotation affiche l'étiquette du LLM, sa confiance et un bouton Accept avant que vous répondiez, ce qui va vite mais tire aussi votre réponse vers la suggestion. Le Solo Mode confie le reste du corpus au LLM quand votre accord avec lui franchit end_human_annotation_agreement, si bien qu'un accord mesuré sur des étiquettes données après avoir vu la suggestion peut mettre fin trop tôt à votre part du travail. À partir de Potato 2.11, solo_mode.suggestions contrôle le moment où la suggestion apparaît :
solo_mode:
suggestions:
display: blind_sample # before_label | after_label | blind_sample
blind_rate: 0.2
minimum_blind_sample: 20blind_sample masque la suggestion sur une fraction blind_rate des items, et after_label ne la montre jamais en premier. Une fois atteintes minimum_blind_sample comparaisons en aveugle, le passage de relais compare l'accord en aveugle à end_human_annotation_agreement. La carte Blind Agreement du tableau de bord affiche côte à côte l'accord en aveugle et l'accord avec suggestion visible, et un large écart positif signifie que vous êtes plus souvent d'accord avec le LLM quand vous voyez sa réponse.
Potato 2.11 fait aussi vérifier par le Solo Mode le plafond de dépense du projet (ai_budget.cap_usd) avant le début de l'étiquetage et avant chaque lot, et ajoute un cache des réponses (solo_mode.cache), un jeu de référence avec essais de prompts (solo_mode.gold), une vérification de la sensibilité au prompt, un jury d'étiquetage (solo_mode.jury) et un ordonnancement par curriculum (solo_mode.instance_selection.curriculum). Solo Mode Advanced Features documente chacun d'eux. Quand le moteur d'étiquetage est activé, le Solo Mode écrit aussi ses prédictions de modèle dans le stockage du moteur, afin que le moteur puisse les calibrer et les certifier.
Priorisation des instances à signaux multiples
Dans toutes les phases où l'humain étiquette, Potato utilise un système de pools pondérés pour choisir les instances les plus informatives. Sept pools alimentent une file de priorité unique :
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 confiance du LLM est inférieure à
thresholds.confidence_low - diversity : loin de ce que vous avez déjà étiqueté, dans l'espace des plongements
- llm_predicted : le LLM a une étiquette et personne ne l'a vérifiée
- disagreement : les signaux disponibles pour l'instance ne concordent pas
- random : un petit tirage aléatoire, pour garder une estimation honnête et repérer les angles morts
- cartography : signal de dynamique d'entraînement, désactivé par défaut
- edge_case_rule : correspond à une règle extraite de votre étiquetage antérieur, désactivé par défaut
Synthèse de cas limites
Potato utilise le LLM pour générer des exemples synthétiques qui ciblent des faiblesses connues :
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: 2Le LLM génère des exemples ambigus entre les paires d'étiquettes indiquées. L'humain les étiquette, et ces étiquettes s'ajoutent au contexte few-shot des tours d'étiquetage suivants du LLM.
Optimisation du prompt (inspirée de DSPy)
Potato lance une optimisation automatique du prompt pour trouver le meilleur format d'instructions pour le LLM, et cherche le plus petit modèle qui tient encore la précision cible :
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.2Suivre l'avancement
Le tableau de bord d'administration affiche l'avancement du Solo Mode en temps réel :
- La phase en cours et l'avancement dans chaque phase
- Les étiquettes humaines réalisées par rapport au budget total
- La précision du LLM au fil du temps (par tour)
- La couverture et la précision des fonctions d'étiquetage
- Un histogramme de la distribution des confiances
- Le temps estimé jusqu'à la fin
Il n'existe ni module potato.solo ni CLI du Solo Mode. Tout le flux de travail passe par les routes propres au serveur, donc l'avancement se lit dans le tableau de bord. Celui-ci affiche :
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
Solo Mode ou multi-annotateurs classique ?
Utilisez le Solo Mode quand :
- Vous disposez d'un expert du domaine capable de fournir des étiquettes de haute qualité
- Le budget ou la logistique empêchent de recruter plusieurs annotateurs
- La tâche a des catégories claires et bien définies
- Vous devez étiqueter un grand jeu de données (plus de 1 000 instances)
- La vitesse compte davantage que la mesure de l'accord inter-annotateurs
Utilisez l'approche multi-annotateurs classique quand :
- Vous avez besoin de statistiques d'accord inter-annotateurs pour une publication
- La tâche est très subjective (par exemple le caractère offensant, l'humour)
- Vous devez étudier les motifs de désaccord entre annotateurs
- La réglementation impose plusieurs annotateurs indépendants
- L'espace des étiquettes est complexe ou encore en évolution (les consignes d'annotation sont toujours en cours d'élaboration)
Approche hybride : utilisez le Solo Mode pour l'étiquetage en masse initial, puis confiez à un second annotateur un échantillon aléatoire de 10 à 20 % pour calculer des statistiques d'accord. Vous obtenez ainsi l'efficacité du Solo Mode avec l'assurance qualité d'une vérification multi-annotateurs.
Le Solo Mode n'a pas de bloc de vérification propre. Faites travailler le second annotateur dans une passe Potato ordinaire sur les étiquettes exportées, puis calculez l'accord entre ses étiquettes et celles du Solo Mode comme vous le feriez pour deux annotateurs quelconques. (icl_labeling a bien un bloc verification, mais il relève de l'étiquetage en contexte, pas du Solo Mode.)
Pour aller plus loin
- Tutoriel Solo Mode : étiqueter 10 000 exemples : guide pas à pas
- Apprentissage actif : le système d'apprentissage actif sous-jacent
- Support IA : configuration de l'intégration des LLM
- Contrôle de la qualité : assurance qualité des annotations
- MACE : estimation de la compétence (utile pour la vérification en mode hybride)
Pour les détails d'implémentation, consultez la documentation source.