Skip to content
Tutorials13 min read

Modo Solo: Como um Único Anotador Pode Rotular 10.000 Exemplos

Tutorial passo a passo do Modo Solo do Potato: rotule você mesmo um conjunto de sementes, deixe um LLM rotular o resto e use as verificações de acurácia para decidir quando o resultado está bom o bastante para parar.

Potato Team

Você tem 10.000 avaliações de produtos para rotular quanto ao sentimento (Positivo, Neutro, Negativo). Contratar três anotadores para rotular todas elas leva semanas e custa milhares de dólares. O Modo Solo permite que um único especialista do domínio obtenha qualidade comparável rotulando apenas 500-1.000 instâncias. Um LLM cuida do restante, e a pessoa revisa tudo aquilo sobre o que o LLM está inseguro.

Este tutorial percorre o processo inteiro.


O que você vai precisar

  • Potato 2.3.0 ou posterior, com os extras de IA para os provedores de LLM: pip install potato-annotation[ai]
  • Uma chave de API da OpenAI ou da Anthropic (para o componente LLM)
  • Seu conjunto de dados em formato JSONL
  • Um anotador com conhecimento do domínio (que pode ser você)

Passo 1: Prepare seus dados

Crie data/reviews.jsonl com uma avaliação por linha:

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, imagine que este arquivo contém 10.000 avaliações.


Passo 2: Crie a configuração

Crie 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: Inicie o servidor

bash
potato start config.yaml -p 8000

Abra http://localhost:8000 e faça login. O painel do Modo Solo aparece, mostrando o projeto em configuração, pronto para você revisar o prompt que ele sintetizou.


Passo 4: Anotação em paralelo, suas primeiras 50

O Potato escolheu 50 avaliações diversas usando clusterização baseada em embeddings. Elas não são aleatórias. Foram escolhidas para cobrir a distribuição dos seus dados da forma mais ampla possível.

Rotule cada uma delas. Esta é a fase que mais importa, pois a qualidade dos seus rótulos de semente define o teto do que o LLM consegue aprender. Vá com calma e mantenha a consistência.

Estimativa de tempo: 15-25 minutos, a 20-30 segundos por instância.

Quando houver rótulos suficientes, o LLM começa a rotular junto com você sem que seja preciso pedir.


Passo 5: O LLM alcança você

Esta fase roda sozinha. O Potato envia ao LLM um lote de 200 instâncias com seus 50 rótulos de semente como exemplos few-shot e depois compara as previsões com 10 rótulos de semente reservados para estimar a acurácia de base.

Um indicador de progresso aparece no painel. Isso normalmente leva 1-2 minutos, dependendo do provedor de LLM.

Resultado típico: O LLM fica em 75-85% de acurácia na primeira calibração. Isso é esperado. Ele ainda não aprendeu o seu estilo de anotação.


Passo 6: Análise de confusão

O Potato exibe uma matriz de confusão mostrando onde o LLM discorda dos seus rótulos. Uma saída 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%)

Isso aponta a principal fraqueza do LLM neste caso: ele continua promovendo avaliações neutras a positivas. É um erro comum, já que os LLMs tendem a pender para o positivo.

Sua ação: Examine os pares de confusão. Clique em cada par para ver as instâncias específicas que o LLM errou, que é a forma mais rápida de entender como ele falha.


Passo 7: De volta à revisão do prompt

Com base na análise de confusão, o Potato redige diretrizes refinadas para o LLM e as mostra lado a lado: de um lado o prompt atual, do outro as edições específicas que ele propõe a partir dos padrões de erro.

Por exemplo, o Potato pode sugerir adicionar:

"Avaliações que descrevem um produto como 'bom', 'ok' ou 'razoável' sem emoção forte devem ser rotuladas como Neutral, mesmo que mencionem comprar de novo."

Passe por cada edição sugerida e aprove, modifique ou rejeite. Você também pode escrever seus próprios esclarecimentos.

Estimativa de tempo: 5-10 minutos.


Passo 8: Funções de rotulagem

O Potato gera funções de rotulagem programáticas a partir de padrões nos seus rótulos de semente. São regras rápidas e determinísticas que tratam os casos fáceis:

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)

As funções de rotulagem cobrem 18% do seu conjunto de dados com mais de 92% de precisão. Essas instâncias são rotuladas automaticamente, o que libera o LLM e a pessoa para os casos mais difíceis.

Sua ação: Examine as funções geradas e desative as que parecerem pouco confiáveis. Este passo é opcional, já que o Potato só mantém, de qualquer forma, as funções acima do limiar de precisão que você configurou.


Passo 9: Anotação ativa (125-375 instâncias)

É aqui que você faz a maior parte da rotulagem. O Potato escolhe instâncias usando os grupos ponderados definidos acima em instance_selection:

  • low_confidence_weight (0.30): Avaliações em que a confiança do LLM é mais baixa
  • disagreement_weight (0.25): Avaliações em que o LLM e as funções de rotulagem discordam
  • diversity_weight (0.20): Avaliações escolhidas para se espalhar pelo espaço de embeddings em vez de se agrupar
  • llm_predicted_weight (0.15): Avaliações rotuladas pelo LLM trazidas de volta para comparação humana
  • random_weight (0.10): Avaliações aleatórias para calibração

Existem mais dois pesos, que aqui ficam no valor padrão de zero e por isso não contribuem com nada: edge_case_rule_weight, para instâncias que correspondem a uma regra de caso limítrofe descoberta, e cartography_weight, para instâncias cuja confiança oscila entre rodadas. O Potato verifica se os pesos somam 1.0 e registra um aviso quando não somam, mas usa os valores como estão escritos, então garanta você mesmo que a soma feche.

Você rotula essas instâncias em lotes de 25. Após cada lote, o Potato atualiza a estimativa de acurácia do LLM e decide se continua.

Trajetória típica:

  • Lote 1-3 (75 instâncias): A acurácia sobe de 82% para 87%
  • Lote 4-6 (150 instâncias): A acurácia chega a 90%
  • Lote 7-10 (250 instâncias): A acurácia estabiliza em 91-92%

Se a concordância atingir o seu end_human_annotation_agreement, o Modo Solo avança para a rotulagem autônoma. Caso contrário, continua alternando entre revisão e refinamento.

Estimativa de tempo: 45-90 minutos no total, dependendo de quantos lotes forem necessários.


Passo 10: O laço de refinamento

Se a acurácia ainda estiver abaixo do limiar depois da rotulagem ativa, o Potato roda mais uma rodada do laço de refinamento:

  1. O LLM re-rotula o conjunto de dados completo com as diretrizes atualizadas e mais exemplos few-shot
  2. O Potato recalcula a acurácia em relação a todos os rótulos humanos
  3. Ele encontra os novos padrões de confusão
  4. Ele refina as diretrizes de novo

Esta fase exige pouca intervenção. Você só precisa aprovar as mudanças nas diretrizes.

Resultado típico: A acurácia melhora 2-4% por rodada de refinamento.


Passo 11: Resolução de discordâncias

O Potato apresenta as instâncias mais controversas: casos em que o LLM, as funções de rotulagem e a análise de vizinhos mais próximos dão, cada um, respostas diferentes. Para cada instância, você vê:

  • O texto da avaliação
  • A previsão do LLM e sua confiança
  • Os votos das funções de rotulagem
  • Os 3 exemplos rotulados mais próximos com seus rótulos
  • O raciocínio chain-of-thought do LLM

Esses são os casos difíceis, e seus rótulos aqui pesam mais do que em qualquer outro ponto do processo.

Estimativa de tempo: 20-30 minutos para 100-150 instâncias.


Passo 12: Síntese de casos limítrofes

O Potato gera avaliações sintéticas voltadas para os padrões de confusão restantes. Por exemplo, se o LLM ainda tem dificuldade com "avaliações neutras que mencionam comprar de novo", ele gera exemplos como:

"É um produto ok pelo preço. Talvez eu compre outro se entrar em promoção."

Você rotula esses exemplos sintéticos, e o Potato os adiciona ao contexto few-shot do LLM.

Estimativa de tempo: 10-15 minutos para 30 exemplos.


Passo 13: Revisão periódica

A esta altura, o LLM já rotulou a maior parte do conjunto de dados. O Potato ordena todos os rótulos dele por confiança e envia a você os de menor confiança em 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

Quando a acurácia para de se mover por três lotes seguidos, o Modo Solo trata os rótulos de alta confiança restantes como confiáveis. Note que isso é estabilidade, não perfeição. A execução acima para depois de 92%, 96% e 100% porque o número se estabilizou, não porque o último lote saiu sem erros.

Estimativa de tempo: 15-20 minutos.


Passo 14: Otimização do prompt

Esta fase roda sozinha. O Potato testa 8 variantes de prompt e mantém aquela com a maior pontuação F1 nos rótulos humanos que você acumulou:

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

Em seguida, ele usa o melhor prompt em uma passagem final de re-rotulagem.


Passo 15: Validação final

O Potato separa 100 instâncias aleatórias rotuladas pelo LLM para você revisar. Você as rotula, e o Potato compara seus rótulos com os do LLM.

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

Se o LLM superar o seu limiar, o projeto está concluído. Caso contrário, ele volta para a anotação ativa para mais uma rodada. Esse retorno é a única transição permitida a partir da validação final.

Estimativa de tempo: 10-15 minutos.


Resumo dos resultados

Ao final, o painel informa de onde vieram os rótulos. O Modo Solo não tem interface de linha de comando (ele roda dentro do servidor e é operado pelas próprias páginas), então esses dados são lidos na instância em execução, e não em um 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%

A pessoa rotulou 612 de 10.000 instâncias, cerca de 6%. O LLM e as funções de rotulagem cuidaram do restante com mais de 94% de acurácia.


Exportando os resultados

O Modo Solo grava no mesmo armazenamento que qualquer outro projeto, então os rótulos saem pelo exportador comum:

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

Cada linha inclui o rótulo e sua origem:

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 exportação em Parquet:

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

annotations.parquet tem uma linha por instância e anotador, com cada esquema de anotação achatado em sua própria coluna. Por isso você seleciona o esquema pelo nome, e não por uma coluna genérica value.


Garantia de qualidade: verificação híbrida

Para conjuntos de dados com qualidade de publicação, adicione um segundo anotador para revisar uma amostra. A documentação de origem do Modo Solo descreve as opções de verificação em mais detalhe.

O Modo Solo não tem um bloco de verificação próprio, então isso é uma segunda passagem comum: exporte os rótulos, passe-os de novo pelo Potato como um projeto normal com um revisor sobre uma amostra de 10% e calcule a concordância entre o revisor e os rótulos do Modo Solo como faria para quaisquer dois anotadores. (icl_labeling tem um bloco verification, mas ele pertence à rotulagem in-context, não ao Modo Solo.)


Solução de problemas

A acurácia do LLM estaciona abaixo do limiar

  • Aumente a contagem de sementes: Tente 75-100 instâncias de semente em vez de 50
  • Troque de LLM: Tente claude-sonnet-4-20250514 no lugar do GPT-4o (ou vice-versa)
  • Reduza o limiar: Se 93% não for alcançável, considere se 90% é aceitável para o seu caso de uso
  • Verifique seus dados: Alguns conjuntos de dados são inerentemente ambíguos. Se a concordância entre humanos fosse de apenas 90%, não espere que o LLM faça melhor

A anotação ativa leva lotes demais

  • Aumente o tamanho do lote: Mude batch_size de 25 para 50
  • Ajuste os pesos dos grupos: Se a maioria das instâncias escaladas vem do grupo de baixa confiança, reduza low_confidence_weight e aumente disagreement_weight ou diversity_weight

As funções de rotulagem têm cobertura baixa

  • Isso é normal para tarefas sem sinais lexicais fortes (por exemplo, detecção de sarcasmo, sentimento implícito)
  • As funções de rotulagem funcionam melhor para padrões explícitos, baseados em palavras-chave
  • O Modo Solo funciona mesmo sem funções de rotulagem. Nesse caso, o LLM cobre essas instâncias

Leitura complementar