Skip to content

Content Moderation para Resumos LLM: Comparativo de Abordagens #39

Description

@ODenteAzul

Content Moderation para Resumos LLM: Comparativo de Abordagens


Objetivo de Pesquisa

Comparar diferentes estratégias de moderação de conteúdo para resumos gerados por LLMs, explorando abordagens desde regex simples até modelos especializados, visando otimizar custo-benefício e minimizar falsos positivos/negativos.

Hipótese: Abordagens híbridas (regex + LLM leve para casos ambíguos) oferecem melhor balanço custo-eficácia-precisão do que soluções únicas (só regex ou só LLM especializado), especialmente para conteúdo governamental brasileiro onde a taxa de violações é baixa (< 1%).


Contexto

Implementação atual (Issue #36):

  • Pipeline híbrido: regex (PII + palavras ofensivas) → Haiku para casos suspeitos
  • Custo: ~$0-2/mês
  • Taxa de bloqueio: 0.00% em 2.463 resumos reais
  • Cobertura: PII básico + lista conservadora de palavras ofensivas

Gaps identificados:

  • Lista de palavras ofensivas é limitada (pode ter falsos negativos)
  • Keywords suspeitas são hardcoded (não adaptam ao contexto)
  • Sem detecção de discurso de ódio sutil
  • Sem detecção de desinformação/fake news
  • Sem classificação de severidade (tudo é binário block/allow)

Abordagens a Explorar

1. Rule-Based (Baseline Atual)

Regex + Keywords

def rule_based_moderation(text):
    # PII patterns
    pii_patterns = [r'\d{3}\.\d{3}\.\d{3}-\d{2}', ...]
    
    # Offensive words list
    offensive_words = ['idiota', 'imbecil', ...]
    
    # Suspicious keywords (trigger secondary check)
    suspicious = ['polêmica', 'corrupção', ...]
    
    for pattern in pii_patterns:
        if re.search(pattern, text):
            return False, "PII detectado"
    
    for word in offensive_words:
        if re.search(rf'\b{word}\b', text, re.IGNORECASE):
            return False, "Linguagem ofensiva"
    
    if any(kw in text.lower() for kw in suspicious):
        return None, "Verificação LLM necessária"
    
    return True, None

Pros: Rápido (< 1ms), custo zero, determinístico
Contras: Alta manutenção, falsos positivos, não pega nuances


2. Modelos de Classificação Locais

Perspective API (Open Source Alternative)

from detoxify import Detoxify

model = Detoxify('multilingual')

results = model.predict(text)
# {
#   'toxicity': 0.1,
#   'severe_toxicity': 0.05,
#   'obscene': 0.02,
#   'threat': 0.01,
#   'insult': 0.15,
#   'identity_attack': 0.01
# }

if results['toxicity'] > 0.7:
    return False, f"Toxicity: {results['toxicity']:.2f}"

Pros: Rápido (~50ms), sem custo API, scores graduados
Contras: Modelo em inglês (multilingual tem gaps), requer GPU para escala

Hugging Face Modelos PT-BR

from transformers import pipeline

classifier = pipeline(
    "text-classification",
    model="nlptown/bert-base-multilingual-uncased-sentiment"
)

result = classifier(text)
# [{'label': 'NEGATIVE', 'score': 0.95}]

Modelos candidatos:

  • neuralmind/bert-base-portuguese-cased
  • rufimelo/bert-legal-mlm-base (domínio legal/governamental)
  • Fine-tune próprio em corpus de moderação

3. LLM-as-Judge (Abordagem Atual para Casos Ambíguos)

Claude Haiku (Current)

def llm_moderation_haiku(text):
    prompt = f"""Analise este resumo de notícia governamental.
Responda APENAS "SAFE" ou "UNSAFE: [razão]".

UNSAFE se contiver:
- Linguagem ofensiva, racista ou discriminatória
- Discurso de ódio
- Violência explícita
- Conteúdo sexual explícito
- PII (CPF, RG, telefone não-governamental)

RESUMO:
{text}

RESPOSTA:"""
    
    response = bedrock.invoke_model(
        modelId="anthropic.claude-3-haiku-20240307-v1:0",
        body={"messages": [{"role": "user", "content": prompt}], "max_tokens": 50}
    )
    return parse_response(response)

Custo: ~$0.10-2/mês (só casos suspeitos ~5%)
Latência: ~200ms
Precisão: Alta (contexto completo)

Modelos Alternativos

# Amazon Nova Micro (mais barato)
model_id = "us.amazon.nova-micro-v1:0"
# Custo: ~$0.035 / 1M input tokens (10x mais barato que Haiku)

# Llama 3.2 1B (local)
from transformers import pipeline
classifier = pipeline("text-classification", model="meta-llama/Llama-3.2-1B")
# Custo: $0 (roda local)
# Latência: ~50ms (GPU) / ~500ms (CPU)

4. AWS Bedrock Guardrails (Managed Service)

import boto3

guardrail_id = "gdrail-xxxxx"
guardrail_version = "1"

response = bedrock.apply_guardrail(
    guardrailIdentifier=guardrail_id,
    guardrailVersion=guardrail_version,
    source="INPUT",
    content=[{"text": {"text": text}}]
)

if response['action'] == 'GUARDRAIL_INTERVENED':
    return False, response['assessments']

Configuração visual (console AWS):

  • Content filters: hate, insults, sexual, violence (níveis: LOW, MEDIUM, HIGH)
  • Denied topics: Lista customizada
  • PII filters: CPF, telefone, email, etc.
  • Word filters: Lista de palavras bloqueadas

Custo: $0.75 / 1000 text units (1 unit = 1000 chars)
Latência: ~100-150ms
Pros: Managed, atualizações automáticas, auditoria integrada
Contras: Custo 15x maior que Haiku para uso integral


5. Multi-Model Ensemble

Voting System

def ensemble_moderation(text):
    votes = []
    
    # 1. Regex (fast fail)
    regex_result = rule_based_check(text)
    if not regex_result.is_safe:
        return regex_result  # Bloqueia imediatamente
    
    # 2. Detoxify (local model)
    detox_score = detoxify_model.predict(text)['toxicity']
    votes.append(detox_score > 0.7)
    
    # 3. LLM-as-judge (casos incertos)
    if 0.5 < detox_score < 0.7:  # Zona cinza
        llm_result = llm_moderate(text)
        votes.append(not llm_result.is_safe)
    
    # Decisão: maioria dos votos
    if sum(votes) >= len(votes) / 2:
        return False, "Bloqueado por ensemble"
    
    return True, None

Pros: Maior confiança (múltiplas perspectivas)
Contras: Latência acumulada, complexidade


Métricas de Avaliação

Dataset de Teste

Fontes:

  1. Corpus atual: 2.463 resumos reais (baseline: 0% bloqueio esperado)
  2. Casos sintéticos: 100 resumos com violações injetadas
    • PII: 20 casos (CPF, telefone, email pessoal)
    • Linguagem ofensiva: 30 casos (xingamentos, insultos)
    • Discurso de ódio: 20 casos (racismo, discriminação)
    • Falsos positivos prováveis: 30 casos (contexto gov.br legítimo com keywords sensíveis)

Anotação manual:

  • 2 anotadores independentes
  • Escala: SAFE / UNSAFE / BORDERLINE
  • Kappa > 0.8 para consistência

Métricas

from sklearn.metrics import classification_report, confusion_matrix

# Matriz de confusão
#               Pred SAFE  Pred UNSAFE
# True SAFE        TN          FP
# True UNSAFE      FN          TP

precision = TP / (TP + FP)  # Quanto dos bloqueados eram realmente unsafe
recall = TP / (TP + FN)     # Quanto dos unsafe foram detectados
f1 = 2 * (precision * recall) / (precision + recall)

# Taxa de falsos positivos (crítico para produção)
fpr = FP / (FP + TN)  # Meta: < 0.5% (menos de 1 em 200 bloqueios errados)

# Custo por resumo
cost = (api_calls * price_per_call) / total_summaries

# Latência p95
latency_p95 = np.percentile(latencies, 95)  # Meta: < 500ms

Experimentos Propostos

Experimento 1: Baseline (Regex + Haiku)

Setup: Implementação atual (Issue #36)
Dataset: 2.463 resumos reais + 100 sintéticos
Objetivo: Estabelecer baseline de precision, recall, custo, latência

Experimento 2: Detoxify Local

Setup: Detoxify multilingual (CPU) + regex fallback
Hipótese: Reduz latência vs Haiku, mantém precision
Custo esperado: $0/mês (local)

Experimento 3: Bedrock Guardrails

Setup: AWS Guardrails com content filters + PII
Hipótese: Maior recall (detecta mais casos), mas custo 10x maior
Custo esperado: ~$11/mês (50k resumos)

Experimento 4: Nova Micro (Budget LLM)

Setup: Amazon Nova Micro para verificação de casos ambíguos
Hipótese: Custo 10x menor que Haiku, precision similar
Custo esperado: ~$0.10/mês

Experimento 5: Ensemble (Detoxify + Haiku)

Setup: Detoxify (primeira camada) → Haiku (casos 0.5-0.7)
Hipótese: Melhor precision/recall trade-off, custo intermediário
Custo esperado: ~$1/mês


Análise de Trade-offs

Método              Custo/mês  Latência  Precision  Recall  Manutenção
─────────────────────────────────────────────────────────────────────
Regex only          $0         < 1ms     Média      Baixa   Alta
Detoxify            $0         ~50ms     Alta       Média   Baixa
Haiku (atual)       ~$2        ~200ms    Alta       Alta    Baixa
Nova Micro          ~$0.10     ~150ms    ?          ?       Baixa
Bedrock Guardrails  ~$11       ~100ms    Muito Alta Alta    Muito Baixa
Ensemble            ~$1        ~250ms    Muito Alta Alta    Média

Deliverables

  1. Notebook comparativo (notebooks/content_moderation_benchmark.ipynb)

    • Experimentos 1-5 com métricas
    • Análise de falsos positivos/negativos
    • Exemplos de casos edge
  2. Dataset anotado (data/moderation_test_set.jsonl)

    • 2.563 resumos anotados (2.463 reais + 100 sintéticos)
    • Ground truth para avaliação futura
  3. Relatório técnico (docs/moderation_research_report.md)

    • Comparação quantitativa (tabela de métricas)
    • Recomendações por cenário (produção atual vs escala futura)
    • Roadmap de evolução
  4. Modelos treinados (se aplicável)

    • Fine-tuned BERT para detecção de conteúdo gov.br impróprio
    • Upload no Hugging Face para reuso

Próximos Passos Pós-Pesquisa

Cenário 1: Manter Regex + Haiku (se baseline suficiente)

  • Expandir lista de palavras ofensivas (crowdsourcing)
  • Melhorar keywords suspeitas (aprendizado com falsos positivos)

Cenário 2: Migrar para Detoxify Local (se latência crítica)

  • Deploy em Cloud Run com GPU
  • Fallback para Haiku em casos borderline

Cenário 3: Adotar Bedrock Guardrails (se compliance exigir)

  • Configurar políticas customizadas
  • Integrar auditoria com CloudWatch

Cenário 4: Ensemble (se precisão > custo)

  • Detoxify (primeira linha) → Haiku (refinamento)
  • Monitoramento de drift (modelo degrada com o tempo?)

Referências


Status: Proposta de pesquisa
Estimativa: 2-3 semanas (experimentos + análise)
Dependências: Issue #36 em produção (baseline para comparação)

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions