Preparação para entrevista
PadrãoComposição

Votação

Voting

Votação (Voting) é um padrão de projeto de IA para apps com LLM e agentes, do grupo Composição. Rodar a mesma tarefa várias vezes ou com vários modelos e decidir por maioria ou limiar.

Elemento
04/70
Nível
Intermediário
Leitura
8 min
Atualizado
Nesta página 16
  1. 01Intenção
  2. 02Problema
  3. 03Solução
  4. 04Estrutura
  5. 05Pseudocódigo
  6. 06Cenários reais
  7. 07Aplicabilidade
  8. 08Quando não usar
  9. 09Como implementar
  10. 10Variações
  11. 11Custo, latência e falhas
  12. 12Como avaliar
  13. 13Em produção
  14. 14Prós e contras
  15. 15Código
  16. 16Teste seu entendimento
  17. ··Perguntas rápidas

Intenção

Rodar a mesma tarefa várias vezes, ou com vários modelos ou prompts, e decidir por maioria ou por limiar. Serve para decisões de classificação em que um erro custa caro e uma única resposta do modelo não é confiável o bastante.

Em uma frase
Uma banca de três juízes: uma opinião pode errar, a maioria erra menos.

Problema

Natália cuida da moderação de comentários de uma rede de cursos online. Um classificador decide se cada comentário viola a política de assédio. Em casos claros, ele acerta. Em casos de fronteira (ironia, gíria, alguém citando uma ofensa para criticá-la), a mesma mensagem às vezes é aprovada e às vezes removida, dependendo da execução.

Remover um comentário legítimo gera reclamação e apelação. Deixar um assédio no ar gera algo muito pior: um aluno exposto e uma marca na imprensa. O time quer duas coisas. Que a decisão seja estável, e que os casos realmente duvidosos cheguem a uma pessoa em vez de serem decididos no cara ou coroa.

Natália reescreveu o prompt três vezes. Cada versão melhorou os casos claros. Nos ambíguos, uma chamada só continua oscilando, porque a ambiguidade está no comentário, não no prompt.

A pergunta que este padrão responde: como tornar uma decisão mais confiável quando uma única resposta do modelo varia?

Solução

Peça a mesma decisão várias vezes, de formas independentes, e combine os votos no código. A diversidade dos votos pode vir de três fontes:

  • Mesma configuração, várias amostras. A variação natural do modelo vira informação. Só funciona com temperatura acima de zero.
  • Prompts diferentes. Cada um olha o caso por um ângulo: o texto literal, a intenção do autor, o contexto da conversa.
  • Modelos diferentes. Os erros de um modelo tendem a não coincidir com os de outro, porque foram treinados de jeitos diferentes.

A combinação é uma regra explícita: maioria simples, unanimidade para ações graves ou um limiar (“remove se 2 de 3 votarem por remover”). A discordância entre votos é o sinal mais útil do padrão. Ela diz que o caso é difícil, e casos difíceis vão para revisão humana.

O post “Building effective agents” da Anthropic (dezembro de 2024) descreve a votação como uma das duas formas de paralelização, ao lado do Seccionamento. Os exemplos do post são os mesmos deste padrão: vários prompts revisando código em busca de vulnerabilidades e vários prompts avaliando se um conteúdo é impróprio, com limiares diferentes para equilibrar falso positivo e falso negativo.

Por que a maioria erra menos

Uma conta ilustrativa mostra o mecanismo. Suponha que cada voto acerte 90% das vezes e que os erros sejam independentes. A maioria de três só erra quando dois ou três votos erram juntos. Isso acontece em cerca de 2,8% dos casos, então a maioria acerta cerca de 97%.

A palavra que segura a conta é “independentes”. Se os três votos usam o mesmo modelo, o mesmo prompt e temperatura zero, eles são a mesma opinião repetida três vezes. Erram juntos, e a maioria continua acertando 90%. Você pagou três vezes por uma resposta.

Na prática os erros nunca são totalmente independentes. Todos os votos leem o mesmo comentário e tropeçam na mesma ironia. Por isso o ganho real é menor que o da conta, e só o eval diz quanto ele vale no seu caso.

Exemplo trabalhado

Um aluno escreve no fórum:

Alguém me chamou de "burro" ontem no grupo da turma. Isso não é jeito de tratar colega.

Os três votos, cada um com saída estruturada:

[
  { "voter": "literal",  "decision": "remove", "policy": "assedio", "rationale": "contém o insulto 'burro'" },
  { "voter": "intencao", "decision": "allow",  "policy": "assedio", "rationale": "o autor relata uma ofensa, não ataca ninguém" },
  { "voter": "contexto", "decision": "allow",  "policy": "assedio", "rationale": "a conversa é sobre convivência na turma" }
]

Com maioria simples, o comentário fica no ar. Com a regra de Natália (remover só com unanimidade), ele também fica, e a divisão de 1 a 2 manda o caso para a fila de revisão com as três justificativas. A moderadora decide em segundos, porque já recebe o argumento de cada lado.

Repare que a regra mudou o resultado mais que o prompt. Escolher a regra é decidir qual erro você prefere cometer.

Checagem +20 XP de primeiraSeu time configura três votos com o mesmo modelo, o mesmo prompt e temperatura zero. No eval, a decisão combinada acerta exatamente o mesmo que uma chamada só. Por quê?

Estrutura

flowchart LR
  IN(["1. Comentário"]):::io --> V1(["2. Voto A"]):::llm
  IN --> V2(["2. Voto B"]):::llm
  IN --> V3(["2. Voto C"]):::llm
  V1 --> AG{"3. Combinar votos"}
  V2 --> AG
  V3 --> AG
  AG -->|"consenso"| OUT(["4. Decisão automática"]):::io
  AG -->|"divididos"| H((("5. Revisão humana"))):::human
  1. A entrada chega, junto com o contexto de que os votos precisam.
  2. Três votos independentes rodam em paralelo, cada um com saída estruturada.
  3. O código combina os votos com uma regra explícita. O modelo não decide a regra.
  4. Com consenso (ou acima do limiar), a decisão é automática.
  5. Com divisão, o caso vai para uma pessoa, com os votos e as justificativas.

Pseudocódigo

schema Vote { decision: "allow" | "remove", policy: string, rationale: string }

VOTERS = [
    { prompt: LITERAL_PROMPT, model: "medium" },
    { prompt: INTENT_PROMPT,  model: "medium" },
    { prompt: CONTEXT_PROMPT, model: "other_provider" },
]

function moderate(comment, thread):
    parallel {                                                   # 2
        votes = [llm(v.prompt, comment, thread, model=v.model) -> Vote for v in VOTERS]
    }
    removes = count(v.decision == "remove" for v in votes)       # 3
    trace.span("votes", votes)

    if removes == len(votes): return remove(comment, votes)        # 4. unanimidade
    if removes == 0:           return allow(comment)
    return human.review(comment, votes)                            # 5. divididos

Cenários reais

  • Moderação de conteúdo. Remoções automáticas só com unanimidade. Casos divididos vão para moderadores, com as três justificativas lado a lado.
  • Triagem de currículos com controle de viés. Três avaliações independentes com a mesma rubrica. Uma diferença grande entre as notas leva a revisão humana, porque é ali que o viés de um avaliador costuma aparecer.
  • Triagem automática de tickets. Um classificador barato decide sozinho os tickets claros. A votação entra só nos que ele marcou como incertos, para não pagar três vezes em tudo. O roteamento em si segue o padrão Roteamento.

Aplicabilidade

  • Classificações ou decisões com resposta discreta: aprovar, remover, uma categoria de uma lista fechada.
  • Erros com custo alto, em que estabilidade importa mais que o custo extra.
  • Casos ambíguos frequentes, em que uma chamada só oscila entre execuções.
  • Existe um caminho humano para os casos divididos, com capacidade para o volume esperado.

Quando não usar

  • Tarefas abertas, como escrever um texto. Não dá para contar votos em três redações diferentes. Para escolher a melhor, você precisa de um juiz com critério, como no Avaliador e Otimizador.
  • Respostas com cálculo ou raciocínio longo. Gerar vários caminhos de raciocínio e ficar com a resposta final mais frequente é a Autoconsistência (self-consistency, Wang et al., 2022). A mecânica é parecida, mas o voto é sobre o resultado, não sobre o caminho.
  • Volume alto com casos quase sempre claros. Use uma Cascata de Modelos e vote só nos incertos.
  • O problema é um prompt ruim. Três votos ruins não fazem um bom. Se todos os votos erram o mesmo caso, conserte o prompt ou os exemplos antes.
  • Não existe ninguém para revisar os divididos. Sem esse caminho, a divisão vira um sorteio com três vezes o custo.

Erro comum: votar em texto livre

Quem começa costuma pedir “responda se o comentário deve ser removido” e contar votos procurando a palavra “sim” na resposta. Um voto diz “Sim, mas com ressalvas”, outro diz “Removeria”, outro “Não vejo problema grave”. A contagem vira interpretação.

Cada voto precisa de uma Saída Estruturada com valores fechados. A justificativa vai num campo separado, para a pessoa que revisa, e nunca entra na contagem.

Checagem +20 XP de primeiraUma plataforma recebe 2 milhões de comentários por dia. Cerca de 97% são claramente aceitáveis e o classificador atual já acerta esses. Qual desenho faz mais sentido?

Como implementar

  1. Defina a decisão como saída estruturada com valores fechados e uma justificativa curta.
  2. Escolha a fonte de diversidade: amostras com temperatura acima de zero, prompts com ângulos diferentes ou modelos diferentes.
  3. Escreva a regra de combinação a partir do custo de cada erro. Por exemplo: unanimidade para remover, maioria para aprovar.
  4. Rode os votos em paralelo, para a latência ser a do voto mais lento e não a soma.
  5. Mande os casos divididos para revisão humana, com os votos e as justificativas.
  6. Meça no eval a acurácia da combinação, a taxa de divisão e a acurácia de cada voto sozinho.
  7. Se o volume for alto, vote só nos casos incertos ou use votação adaptativa.

Variações

  • Maioria simples. Número ímpar de votos, decisão pela maioria. Boa para categorias sem ação grave.
  • Unanimidade para ações graves. Remover, recusar ou bloquear só com todos de acordo. Troca falso positivo por mais revisão humana.
  • Voto ponderado. Votos de modelos mais confiáveis pesam mais. Os pesos vêm do eval, nunca do palpite.
  • Votação adaptativa. Começa com dois votos e só chama o terceiro se eles divergirem. Economiza nos casos claros.

Uma conta ilustrativa da votação adaptativa: suponha 100 mil comentários por dia e que os dois primeiros votos concordem em 85% deles. Você paga 200 mil chamadas pelos dois primeiros votos e mais 15 mil pelo terceiro. São 215 mil chamadas, contra 300 mil com três votos sempre.

Custo, latência e falhas

Aspecto O que acontece Como aparece no trace
Chamadas Uma por voto spans vote irmãos
Custo Multiplicado pelo número de votos custo por item comparado com uma chamada
Latência A do voto mais lento, se em paralelo spans sobrepostos
Falha: votos correlacionados Mesmo prompt e modelo erram juntos unanimidade em casos que o eval marca como errados
Falha: divisão demais Prompts muito diferentes nunca concordam taxa de revisão humana acima da capacidade
Falha: custo sem ganho Casos claros pagam três votos unanimidade em quase todos os itens

Como avaliar

  • Acurácia da decisão combinada contra cada voto sozinho. Se a combinação não ganha do melhor voto, a votação não está pagando a conta.
  • Taxa de casos divididos e acurácia dentro deles. Divididos com acurácia baixa são casos realmente difíceis, e é bom que cheguem a uma pessoa.
  • Concordância entre votos no dataset. Alta demais sugere pouca diversidade; baixa demais sugere prompts que medem coisas diferentes.

Um caso de eval para o sarcasmo:

{ "comment": "Esse professor é um gênio... de tão ruim que é", "thread": "fixtures/thread-88.json", "expected": "allow", "note": "sarcasmo, sem assédio" }

Monte o dataset com os casos que hoje oscilam. Rode o classificador antigo cinco vezes em cada comentário de uma amostra e guarde os que mudaram de resposta: esses são exatamente os casos que a votação precisa melhorar.

Em produção

  • Um span por voto com decisão e justificativa, e um span de combinação com a regra aplicada.
  • Painel com a taxa de unanimidade e de divisão por política.
  • Alerta quando a taxa de divisão passa da capacidade da fila de revisão.
  • Fila de revisão para os divididos, com tempo de resposta.
  • As decisões humanas nos divididos viram casos novos no eval.

Prós e contras

Prós Contras
Decisões mais estáveis Custo multiplicado
Discordância como sinal de ambiguidade Votos correlacionados dão falsa confiança
Ações graves só com consenso Exige um caminho humano para os divididos
Diversidade de modelos reduz erros sistemáticos Mais spans e configurações para manter

Código

As duas abas rodam três votos e decidem por unanimidade, mandando divisões para revisão.

Python. Use quando a moderação roda em Python e você quer os votos em asyncio.

import asyncio
from typing import Literal

import anthropic
from pydantic import BaseModel

client = anthropic.AsyncAnthropic()


class Vote(BaseModel):
    decision: Literal["allow", "remove"]
    policy: str
    rationale: str


VOTERS = [  # diversidade de ângulo e de modelo
    ("claude-sonnet-5-5", "Julgue só o texto literal contra a política de assédio."),
    ("claude-sonnet-5-5", "Julgue a intenção do autor: ataque a uma pessoa ou crítica a ideias?"),
    ("claude-haiku-4-5", "Julgue o comentário no contexto da conversa contra a política de assédio."),
]


async def vote(model: str, angle: str, comment: str, thread: str) -> Vote:
    reply = await client.messages.parse(
        model=model, max_tokens=500,
        system=f"Você modera comentários de uma plataforma de cursos. {angle}",
        messages=[{"role": "user", "content": f"<conversa>{thread}</conversa>\n<comentario>{comment}</comentario>"}],
        output_format=Vote,
    )
    return reply.parsed_output


async def moderate(comment: str, thread: str) -> dict:
    votes = await asyncio.gather(*(vote(m, a, comment, thread) for m, a in VOTERS))  # 2
    removes = sum(v.decision == "remove" for v in votes)  # 3
    if removes == len(votes):
        return {"action": "remove", "votes": votes}  # 4. unanimidade
    if removes == 0:
        return {"action": "allow", "votes": votes}
    return {"action": "human_review", "votes": votes}  # 5

O que você ganhou: decisões automáticas só quando todos concordam. O que você perdeu: três chamadas por comentário; vote só nos incertos se o volume for alto.

TypeScript. Use quando a moderação roda em Node e você quer votação adaptativa.

import Anthropic from "@anthropic-ai/sdk";
import { zodOutputFormat } from "@anthropic-ai/sdk/helpers/zod";
import { z } from "zod";

const client = new Anthropic();
const Vote = z.object({ decision: z.enum(["allow", "remove"]), policy: z.string(), rationale: z.string() });
type Vote = z.infer<typeof Vote>;

const VOTERS: [string, string][] = [
  ["claude-sonnet-5-5", "Julgue só o texto literal contra a política de assédio."],
  ["claude-sonnet-5-5", "Julgue a intenção do autor: ataque a uma pessoa ou crítica a ideias?"],
  ["claude-haiku-4-5", "Julgue o comentário no contexto da conversa contra a política de assédio."],
];

async function vote([model, angle]: [string, string], comment: string, thread: string): Promise<Vote> {
  const { parsed_output } = await client.messages.parse({
    model, max_tokens: 500,
    system: `Você modera comentários de uma plataforma de cursos. ${angle}`,
    messages: [{ role: "user", content: `<conversa>${thread}</conversa>\n<comentario>${comment}</comentario>` }],
    output_config: { format: zodOutputFormat(Vote) },
  });
  if (!parsed_output) throw new Error("voto inválido");
  return parsed_output;
}

export async function moderate(comment: string, thread: string) {
  // votação adaptativa: dois votos primeiro
  const first = await Promise.all(VOTERS.slice(0, 2).map((v) => vote(v, comment, thread)));
  if (first[0].decision === "allow" && first[1].decision === "allow") return { action: "allow", votes: first };
  const votes = [...first, await vote(VOTERS[2], comment, thread)]; // terceiro só se precisar
  const removes = votes.filter((v) => v.decision === "remove").length;
  if (removes === votes.length) return { action: "remove", votes };
  return { action: "human_review", votes };
}

O que você ganhou: comentários claramente aceitáveis pagam dois votos, não três. O que você perdeu: a latência de quem precisa do terceiro voto sobe, porque ele roda depois.

Teste seu entendimento

Checagem +20 XP de primeiraNa regra de Natália, remover exige unanimidade dos três votos. O que essa escolha troca?

Checagem +20 XP de primeiraNo eval, a taxa de divisão da votação é 40%, e a fila de revisão só dá conta de 5%. Os moderadores dizem que a maioria dos divididos é óbvia. Qual é o diagnóstico mais provável?

Checagem +20 XP de primeiraSeu time quer usar votação para escolher o melhor resumo entre três gerados para cada artigo. O que você sugere?

Checagem +20 XP de primeiraA votação está em produção há um mês. O que fazer com as decisões que os moderadores tomaram nos casos divididos?

Perguntas rápidas

O que é Votação?

Votação (Voting) é um padrão de projeto de IA para apps com LLM e agentes, do grupo Composição. Rodar a mesma tarefa várias vezes ou com vários modelos e decidir por maioria ou limiar.

Onde Votação aparece na prática?

Nos cenários reais Moderação de conteúdo, Triagem de currículos com controle de viés e Triagem automática de tickets.

Para ir além

Relações

Buscar