Skip to content
Tutoriales13 min de lectura

Solo Mode: cómo un solo anotador puede etiquetar 10 000 ejemplos

Tutorial paso a paso sobre el Solo Mode de Potato: etiqueta tú mismo un conjunto semilla, deja que un LLM etiquete el resto y usa las comprobaciones de precisión para decidir cuándo es suficientemente bueno para parar.

Potato Team

Tienes 10 000 reseñas de productos que etiquetar por sentimiento (Positivo, Neutral, Negativo). Contratar a tres anotadores para etiquetarlas todas lleva semanas y cuesta miles de dólares. Solo Mode permite que un único experto en el dominio obtenga una calidad comparable etiquetando solo entre 500 y 1000 instancias; un LLM se encarga del resto y la persona revisa todo aquello en lo que el LLM tiene dudas.

Este tutorial recorre el proceso completo.


Lo que necesitarás

  • Potato 2.3.0 o posterior, con los extras de IA para los proveedores de LLM: pip install potato-annotation[ai]
  • Una clave de API de OpenAI o Anthropic (para el componente LLM)
  • Tu conjunto de datos en formato JSONL
  • Un anotador con conocimiento del dominio (puedes ser tú)

Paso 1: Prepara tus datos

Crea data/reviews.jsonl con una reseña por línea:

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"}

Para este tutorial, imagina que el archivo contiene 10 000 reseñas.


Paso 2: Crea la configuración

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

Paso 3: Inicia el servidor

bash
potato start config.yaml -p 8000

Abre http://localhost:8000 e inicia sesión. Aparece el panel de Solo Mode, que muestra el proyecto en configuración, listo para que revises el prompt que ha sintetizado.


Paso 4: Anotación en paralelo (tus primeras 50)

Potato ha elegido 50 reseñas diversas mediante clustering basado en embeddings. No son aleatorias: se eligen para cubrir la distribución de tus datos de la forma más amplia posible.

Etiqueta cada una. Esta es la fase que más importa, porque la calidad de tus etiquetas semilla fija el techo de lo que el LLM puede aprender. Tómate tu tiempo y sé coherente.

Tiempo estimado: 15-25 minutos, a 20-30 segundos por instancia.

Cuando hay suficientes etiquetadas, el LLM empieza a etiquetar a la par que tú sin que se lo pidas.


Paso 5: El LLM se pone al día

Esta fase se ejecuta sola. Potato envía al LLM un lote de 200 instancias con tus 50 etiquetas semilla como ejemplos few-shot y luego compara las predicciones con 10 etiquetas semilla reservadas para estimar la precisión de partida.

En el panel aparece un indicador de progreso. Esto suele tardar 1-2 minutos según el proveedor de LLM.

Resultado típico: El LLM se sitúa en un 75-85% de precisión en la primera calibración. Es lo esperado. Todavía no ha aprendido tu estilo de anotación.


Paso 6: Análisis de confusión

Potato muestra una matriz de confusión con los puntos en que el LLM no coincide con tus etiquetas. Una salida típica:

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%)

Esto señala la principal debilidad del LLM en este caso: sigue subiendo reseñas neutrales a positivas. Es un fallo habitual, ya que los LLM tienden a inclinarse hacia lo positivo.

Tu acción: Revisa los pares de confusión. Haz clic en cada par para ver las instancias concretas en las que el LLM se equivocó, que es la forma más rápida de entender cómo falla.


Paso 7: De vuelta a la revisión del prompt

A partir del análisis de confusión, Potato redacta directrices refinadas para el LLM y las muestra lado a lado: el prompt actual a un lado y, al otro, las ediciones concretas que propone a partir de los patrones de error.

Por ejemplo, Potato podría sugerir añadir:

"Las reseñas que describen un producto como 'bien', 'aceptable' o 'decente' sin una emoción fuerte deben etiquetarse como Neutral, aunque mencionen volver a comprarlo."

Revisa cada edición sugerida y apruébala, modifícala o recházala. También puedes escribir tus propias aclaraciones.

Tiempo estimado: 5-10 minutos.


Paso 8: Funciones de etiquetado

Potato genera funciones de etiquetado programáticas a partir de patrones de tus etiquetas semilla. Son reglas rápidas y deterministas que resuelven los casos fáciles:

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)

Las funciones de etiquetado cubren el 18% de tu conjunto de datos con una precisión de más del 92%. Esas instancias se etiquetan automáticamente, lo que deja al LLM y a la persona libres para los casos más difíciles.

Tu acción: Revisa las funciones generadas y desactiva las que parezcan poco fiables. Este paso es opcional, porque Potato solo conserva de todos modos las funciones que superan el umbral de precisión que configuraste.


Paso 9: Anotación activa (125-375 instancias)

Aquí es donde haces la mayor parte del etiquetado. Potato elige las instancias con los grupos ponderados definidos arriba en instance_selection:

  • low_confidence_weight (0.30): Reseñas en las que la confianza del LLM es más baja
  • disagreement_weight (0.25): Reseñas en las que el LLM y las funciones de etiquetado no coinciden
  • diversity_weight (0.20): Reseñas elegidas para repartirse por el espacio de embeddings en lugar de agruparse
  • llm_predicted_weight (0.15): Reseñas etiquetadas por el LLM que se recuperan para compararlas con una persona
  • random_weight (0.10): Reseñas aleatorias para la calibración

Existen dos pesos más, que aquí se dejan en su valor por defecto de cero y por tanto no aportan nada: edge_case_rule_weight, para instancias que coinciden con una regla de caso límite descubierta, y cartography_weight, para instancias cuya confianza oscila entre rondas. Potato comprueba que los pesos sumen 1.0 y registra una advertencia si no es así, pero usa los valores tal como están escritos, así que asegúrate tú de que sumen 1.0.

Etiquetas estas instancias en lotes de 25. Después de cada lote, Potato actualiza la estimación de precisión del LLM y decide si continuar.

Trayectoria típica:

  • Lotes 1-3 (75 instancias): La precisión sube del 82% al 87%
  • Lotes 4-6 (150 instancias): La precisión llega al 90%
  • Lotes 7-10 (250 instancias): La precisión se estanca en el 91-92%

Si el acuerdo alcanza tu end_human_annotation_agreement, Solo Mode avanza hacia el etiquetado autónomo. Si no, sigue alternando entre revisión y refinamiento.

Tiempo estimado: 45-90 minutos en total, según cuántos lotes hagan falta.


Paso 10: El bucle de refinamiento

Si después del etiquetado activo la precisión sigue por debajo del umbral, Potato ejecuta otra ronda del bucle de refinamiento:

  1. El LLM vuelve a etiquetar todo el conjunto de datos con las directrices actualizadas y más ejemplos few-shot
  2. Potato recalcula la precisión frente a todas las etiquetas humanas
  3. Encuentra los nuevos patrones de confusión
  4. Vuelve a refinar las directrices

Esta fase apenas requiere intervención. Solo tienes que aprobar los cambios en las directrices.

Resultado típico: La precisión mejora un 2-4% por ronda de refinamiento.


Paso 11: Resolución de desacuerdos

Potato presenta las instancias más discutidas: casos en los que el LLM, las funciones de etiquetado y el análisis de vecinos más cercanos dan respuestas distintas. Para cada instancia ves:

  • El texto de la reseña
  • La predicción del LLM y su confianza
  • Los votos de las funciones de etiquetado
  • Los 3 ejemplos etiquetados más cercanos con sus etiquetas
  • El razonamiento chain-of-thought del LLM

Estos son los casos difíciles, y tus etiquetas aquí pesan más que en cualquier otro punto del proceso.

Tiempo estimado: 20-30 minutos para 100-150 instancias.


Paso 12: Síntesis de casos límite

Potato genera reseñas sintéticas dirigidas a los patrones de confusión que quedan. Por ejemplo, si el LLM sigue teniendo problemas con "reseñas neutrales que mencionan volver a comprar", genera ejemplos como:

"Es un producto aceptable para lo que cuesta. Puede que compre otro si hay una oferta."

Etiquetas estos ejemplos sintéticos y Potato los añade al contexto few-shot del LLM.

Tiempo estimado: 10-15 minutos para 30 ejemplos.


Paso 13: Revisión periódica

A estas alturas el LLM ha etiquetado la mayor parte del conjunto de datos. Potato ordena todas sus etiquetas por confianza y te envía las de menor confianza en lotes de 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

Cuando la precisión deja de moverse durante tres lotes seguidos, Solo Mode considera fiables las etiquetas de alta confianza restantes. Ten en cuenta que esto es estabilidad, no perfección: la ejecución anterior se detiene después de 92%, 96% y 100% porque la cifra se ha estabilizado, no porque el último lote saliera limpio.

Tiempo estimado: 15-20 minutos.


Paso 14: Optimización del prompt

Esta fase se ejecuta sola. Potato prueba 8 variantes del prompt y se queda con la que obtiene la mayor puntuación F1 sobre las etiquetas humanas que has acumulado:

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

Después usa el mejor prompt para una pasada final de reetiquetado.


Paso 15: Validación final

Potato toma 100 instancias aleatorias etiquetadas por el LLM para que las revises. Tú las etiquetas y Potato compara tus etiquetas con las del LLM.

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

Si el LLM supera tu umbral, el proyecto está terminado. Si no, vuelve a la anotación activa para otra ronda. Esa vuelta atrás es la única transición que se permite desde la validación final.

Tiempo estimado: 10-15 minutos.


Resumen de resultados

Al final, el panel informa de dónde salieron las etiquetas. Solo Mode no tiene interfaz de línea de comandos (se ejecuta dentro del servidor y se maneja desde sus propias páginas), así que estos datos se leen en la instancia en ejecución y no en una terminal:

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 etiquetó 612 de 10 000 instancias, alrededor del 6%. El LLM y las funciones de etiquetado se encargaron del resto con una precisión de más del 94%.


Exportación de resultados

Solo Mode escribe en el mismo almacenamiento que cualquier otro proyecto, así que las etiquetas salen con el exportador habitual:

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

Cada línea incluye la etiqueta y su origen:

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}

Para exportar a Parquet:

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

annotations.parquet tiene una fila por instancia y anotador, con cada esquema de anotación aplanado en su propia columna, así que seleccionas el esquema por su nombre y no mediante una columna genérica value.


Control de calidad: verificación híbrida

Para conjuntos de datos con calidad de publicación, añade un segundo anotador que revise una muestra. La documentación fuente de Solo Mode describe con más detalle las opciones de verificación.

Solo Mode no tiene un bloque de verificación propio, así que se trata de una segunda pasada normal: exporta las etiquetas, vuelve a pasarlas por Potato como un proyecto normal con un revisor sobre una muestra del 10% y calcula el acuerdo entre el revisor y las etiquetas de Solo Mode como lo harías para dos anotadores cualesquiera. (icl_labeling sí tiene un bloque verification, pero pertenece al etiquetado en contexto, no a Solo Mode.)


Solución de problemas

La precisión del LLM se estanca por debajo del umbral

  • Aumenta el número de semillas: Prueba con 75-100 instancias semilla en lugar de 50
  • Cambia de LLM: Prueba claude-sonnet-4-20250514 en lugar de GPT-4o (o al revés)
  • Baja el umbral: Si el 93% no es alcanzable, plantéate si un 90% es aceptable para tu caso de uso
  • Revisa tus datos: Algunos conjuntos de datos son ambiguos por naturaleza. Si el acuerdo entre dos personas solo llegaría al 90%, no esperes que el LLM lo haga mejor

La anotación activa requiere demasiados lotes

  • Aumenta el tamaño de lote: Cambia batch_size de 25 a 50
  • Ajusta los pesos de los grupos: Si la mayoría de las instancias escaladas vienen del grupo de baja confianza, reduce low_confidence_weight y sube disagreement_weight o diversity_weight

Las funciones de etiquetado tienen poca cobertura

  • Es normal en tareas sin señales léxicas fuertes (p. ej., detección de sarcasmo, sentimiento implícito)
  • Las funciones de etiquetado funcionan mejor con patrones explícitos basados en palabras clave
  • Solo Mode sigue funcionando sin funciones de etiquetado; en ese caso el LLM cubre esas instancias

Lecturas adicionales