Onboarding: Enriquecimento de Notícias com AWS Bedrock¶
Guia prático para desenvolvedores que vão trabalhar com o sistema de enriquecimento de notícias usando Large Language Models (LLMs) via AWS Bedrock.
📋 Pré-requisitos¶
Antes de começar, você deve ter:
- [x] Python 3.11+ instalado
- [x] Poetry instalado (
pip install poetry) - [x] Acesso ao repositório
data-science - [x] Credenciais AWS com acesso ao Bedrock
- [x] Acesso ao PostgreSQL (Cloud SQL)
- [x] VPN/acesso à rede interna (se necessário)
🚀 Setup Inicial (30 minutos)¶
1. Clone e configure o repositório¶
# Clone
git clone https://github.com/inspire-cria/data-science.git
cd data-science
# Instale dependências
poetry install
# Ative o ambiente virtual
poetry shell
2. Configure variáveis de ambiente¶
Crie o arquivo .env na raiz do repositório:
# .env
# AWS Bedrock
AWS_ACCESS_KEY_ID=AKIA...
AWS_SECRET_ACCESS_KEY=...
AWS_DEFAULT_REGION=us-east-1
# PostgreSQL (Silver Layer)
POSTGRES_HOST=10.x.x.x # IP privado Cloud SQL
POSTGRES_PORT=5432
POSTGRES_DB=destaquesgovbr
POSTGRES_USER=seu_usuario
POSTGRES_PASSWORD=...
# Opcional: para testes locais com Ollama
OLLAMA_API_BASE=http://localhost:11434
⚠️ IMPORTANTE: Nunca commite o arquivo .env no Git! Ele já está no .gitignore.
3. Teste a conexão AWS Bedrock¶
# Verificar identidade AWS
aws sts get-caller-identity
# Listar modelos disponíveis
aws bedrock list-foundation-models --region us-east-1 | grep claude
Esperado: Você deve ver Claude 3 Haiku, Sonnet e Opus disponíveis.
4. Teste o classificador¶
cd source/news-enrichment
# Exemplo simples
python examples/classificacao_simples.py
Esperado: Uma notícia deve ser classificada em ~5-10 segundos sem erros.
🎯 Conceitos Fundamentais¶
Taxonomia Hierárquica (543 categorias)¶
O sistema usa uma taxonomia de 3 níveis:
01 - Economia # Nível 1 (10 temas principais)
├─ 01.01 - Política Econômica # Nível 2 (~50 subtemas)
│ ├─ 01.01.01 - Política Fiscal # Nível 3 (~410 categorias específicas)
│ ├─ 01.01.02 - Política Monetária
│ └─ 01.01.03 - Política Cambial
└─ 01.02 - Setor Produtivo
├─ 01.02.01 - Agropecuária
└─ 01.02.02 - Indústria
Arquivo: arvore.yaml
Como funciona: 1. LLM recebe a taxonomia completa no prompt 2. LLM classifica a notícia nos 3 níveis 3. Sistema valida e armazena no PostgreSQL
Providers LLM Suportados¶
| Provider | Modelo | Uso | Custo/1k docs |
|---|---|---|---|
| AWS Bedrock (prod) | Claude 3 Haiku | Produção | $0.74 |
| AWS Bedrock | Claude 3 Sonnet | Alta qualidade | $3.00 |
| AWS Bedrock | Claude 3 Opus | Máxima qualidade | $15.00 |
| Ollama (dev) | Llama 3.1 70B | Testes locais | Grátis |
| Ollama (dev) | Qwen 2.5 72B | Testes locais | Grátis |
Recomendação: Use Claude 3 Haiku para produção (melhor custo-benefício).
🛠️ Componentes Principais¶
1. llm_client.py - Cliente Base¶
from news_enrichment import BedrockLLMClient
# Inicializar cliente
client = BedrockLLMClient(
model_id="anthropic.claude-3-haiku-20240307-v1:0",
region="us-east-1",
batch_size=8, # Processa 8 notícias por vez
max_workers=4, # 4 threads paralelas
sleep_between_batches=0.2 # 200ms entre batches (evitar throttling)
)
# Classificar uma notícia
news = {
"title": "Governo anuncia reforma tributária",
"content": "O governo federal apresentou hoje..."
}
result = client.classify_single(news)
Saída:
{
"theme_level_1": "01 - Economia",
"theme_level_2": "01.01 - Política Econômica",
"theme_level_3": "01.01.01 - Política Fiscal",
"most_specific_theme": "01.01.01",
"summary": "Governo apresenta proposta de reforma tributária...",
"confidence": 0.95
}
2. llm_client_optimized.py - Cliente Otimizado¶
from news_enrichment.llm_client_optimized import LLMClientOptimized
# Cliente com otimizações para alto volume
client = LLMClientOptimized(
model_id="anthropic.claude-3-haiku-20240307-v1:0",
region="us-east-1",
batch_size=6, # ← Menor que o base (mais estável)
max_workers=4,
sleep_between_batches=0.3 # ← Maior que o base (evita throttling)
)
# Processar batch
news_list = [news1, news2, news3, ...]
results = client.enrich_news_batch(news_list)
Quando usar: - Volumes > 1000 notícias/dia - Precisa de processamento contínuo sem interrupções - Quer minimizar throttling AWS
3. taxonomy.py - Carregamento de Taxonomia¶
from news_enrichment.taxonomy import load_taxonomy_from_postgres
# Carregar taxonomia (cached)
taxonomy = load_taxonomy_from_postgres(
conn_string="postgresql://user:pass@host:5432/db"
)
print(f"Total de temas: {len(taxonomy['themes'])}") # 543
print(f"Nível 1: {len([t for t in taxonomy['themes'] if t['level'] == 1])}") # 10
print(f"Nível 2: {len([t for t in taxonomy['themes'] if t['level'] == 2])}") # ~50
print(f"Nível 3: {len([t for t in taxonomy['themes'] if t['level'] == 3])}") # ~410
⚡ Performance: Taxonomia é carregada 1x na inicialização usando @lru_cache.
4. enrichment_job.py - Job de Enriquecimento¶
from news_enrichment.enrichment_job import fetch_unenriched_news, update_enriched_news
import psycopg2
# Conectar ao PostgreSQL
conn = psycopg2.connect(
host="10.x.x.x",
port=5432,
dbname="destaquesgovbr",
user="enrichment_worker",
password="..."
)
# Buscar notícias não enriquecidas
unenriched = fetch_unenriched_news(conn, limit=100)
print(f"Encontradas {len(unenriched)} notícias para enriquecer")
# Enriquecer
results = client.enrich_news_batch(unenriched)
# Atualizar PostgreSQL
update_enriched_news(conn, results)
conn.commit()
Idempotência: A função fetch_unenriched_news() verifica se most_specific_theme_id IS NULL OR summary IS NULL.
📊 Exercícios Práticos¶
Exercício 1: Classificar uma notícia (5 min)¶
from news_enrichment import BedrockLLMClient
client = BedrockLLMClient()
news = {
"title": "Ministério da Saúde lança campanha de vacinação contra gripe",
"content": "O Ministério da Saúde anunciou hoje o início da campanha nacional de vacinação contra gripe para 2026. A meta é imunizar 90% do público-alvo, incluindo idosos, crianças e profissionais de saúde."
}
result = client.classify_single(news)
# Verifique:
# - theme_level_1 deve ser "03 - Saúde"
# - theme_level_3 deve ser algo como "03.03.01 - Prevenção e Controle de Doenças"
print(result)
Exercício 2: Processar batch (10 min)¶
Crie um arquivo test_batch.py:
from news_enrichment import BedrockLLMClient
import json
# Carregar amostra (você deve ter um arquivo sample.json com 10 notícias)
with open('sample.json') as f:
news_list = json.load(f)
client = BedrockLLMClient(batch_size=5, max_workers=2)
results = client.enrich_news_batch(news_list)
# Análise
for i, result in enumerate(results, 1):
print(f"\nNotícia {i}:")
print(f" Tema L1: {result['theme_level_1']}")
print(f" Tema L3: {result['theme_level_3']}")
print(f" Resumo: {result['summary'][:100]}...")
Esperado: 10 notícias processadas em ~30-60 segundos.
Exercício 3: Comparar Haiku vs Sonnet (15 min)¶
from news_enrichment import BedrockLLMClient
import time
news = {
"title": "Reforma da Previdência aprovada no Senado",
"content": "O Senado Federal aprovou hoje a proposta de reforma da Previdência Social..."
}
# Testar Haiku
client_haiku = BedrockLLMClient(
model_id="anthropic.claude-3-haiku-20240307-v1:0"
)
start = time.time()
result_haiku = client_haiku.classify_single(news)
time_haiku = time.time() - start
# Testar Sonnet
client_sonnet = BedrockLLMClient(
model_id="anthropic.claude-3-sonnet-20240229-v1:0"
)
start = time.time()
result_sonnet = client_sonnet.classify_single(news)
time_sonnet = time.time() - start
# Comparar
print(f"Haiku: {time_haiku:.2f}s - Tema: {result_haiku['theme_level_3']}")
print(f"Sonnet: {time_sonnet:.2f}s - Tema: {result_sonnet['theme_level_3']}")
print(f"\nDiferença de custo: {(time_sonnet/time_haiku - 1) * 100:.0f}% mais caro")
Exercício 4: Usar Ollama Local (20 min)¶
Pré-requisito: Instalar Ollama (ollama.ai)
# Instalar Ollama
curl -fsSL https://ollama.ai/install.sh | sh
# Baixar modelo
ollama pull llama3.1:70b # ~40GB, pode demorar
# Iniciar servidor
ollama serve
from news_enrichment.local_llm_client import LocalLLMClient
client = LocalLLMClient(
provider="ollama",
model="llama3.1:70b",
api_base="http://localhost:11434"
)
news = {
"title": "Governo anuncia aumento do salário mínimo",
"content": "O governo federal anunciou reajuste do salário mínimo..."
}
result = client.classify_single(news)
print(result)
Observação: Ollama é MUITO MAIS LENTO que Bedrock (~30s vs 5s), mas é grátis e roda localmente.
🎓 Conceitos Avançados¶
Batch Sizing e Paralelização¶
Trade-off: Batch size maior = mais rápido, mas maior risco de throttling.
# Configuração conservadora (recomendada para início)
client = BedrockLLMClient(
batch_size=8,
max_workers=4,
sleep_between_batches=0.2
)
# → ~200-300 docs/min
# Configuração agressiva (apenas se não houver throttling)
client = BedrockLLMClient(
batch_size=16,
max_workers=8,
sleep_between_batches=0.1
)
# → ~400-600 docs/min (mas pode dar ThrottlingException)
Retry Policy¶
O cliente implementa retry automático:
# Em llm_client.py (linhas 71-100)
for attempt in range(1, self.max_retries + 1):
try:
response = self.bedrock.invoke_model(...)
return response
except ThrottlingException:
if attempt < self.max_retries:
sleep_time = 2 ** attempt # 1s, 2s, 4s
time.sleep(sleep_time)
continue
raise
except ValidationException:
# Não retenta (erro de input)
raise
Cost Estimation¶
def estimate_cost(num_docs: int, model: str = "haiku") -> float:
"""
Estima custo de processamento.
Preços AWS Bedrock (maio 2026):
- Haiku: $0.00074/doc
- Sonnet: $0.003/doc
- Opus: $0.015/doc
"""
costs = {
"haiku": 0.00074,
"sonnet": 0.003,
"opus": 0.015
}
return num_docs * costs.get(model, 0.00074)
# Exemplo
print(f"Processar 100k docs com Haiku: ${estimate_cost(100_000, 'haiku'):.2f}")
# → $74.00
print(f"Processar 100k docs com Sonnet: ${estimate_cost(100_000, 'sonnet'):.2f}")
# → $300.00
🐛 Troubleshooting¶
Erro: "AccessDeniedException"¶
Causa: Credenciais AWS sem permissão para Bedrock.
Solução:
# Verificar identidade
aws sts get-caller-identity
# Verificar se tem acesso ao Bedrock
aws bedrock list-foundation-models --region us-east-1
Se der erro, peça ao admin AWS para adicionar a política AmazonBedrockFullAccess ao seu usuário.
Erro: "ThrottlingException"¶
Causa: Muitas requisições simultâneas ao Bedrock.
Solução:
# Reduzir paralelização
client = BedrockLLMClient(
batch_size=4, # ← Reduzir de 8 para 4
max_workers=2, # ← Reduzir de 4 para 2
sleep_between_batches=0.5 # ← Aumentar de 0.2 para 0.5
)
Erro: "ValidationException: The provided model identifier is invalid"¶
Causa: Modelo não disponível na região.
Solução:
# Verificar modelos disponíveis
aws bedrock list-foundation-models --region us-east-1 | jq '.modelSummaries[] | select(.modelId | contains("claude"))'
# Usar ID correto
model_id="anthropic.claude-3-haiku-20240307-v1:0" # ← versão correta
Problema: Latência alta (>20s por notícia)¶
Causas possíveis: - Conteúdo muito longo (>10k caracteres) - Throttling AWS - Rede lenta
Diagnóstico:
import time
start = time.time()
result = client.classify_single(news)
elapsed = time.time() - start
print(f"Tempo: {elapsed:.2f}s")
print(f"Tamanho conteúdo: {len(news['content'])} chars")
if elapsed > 20:
print("⚠️ Latência alta detectada!")
if len(news['content']) > 10000:
print(" → Conteúdo muito longo. Considere truncar.")
Problema: Taxonomia não carregada¶
Sintoma: Erro KeyError: 'themes' ou classificação incorreta.
Solução:
from news_enrichment.taxonomy import load_taxonomy_from_postgres
# Verificar se carregou
taxonomy = load_taxonomy_from_postgres(conn_string)
assert len(taxonomy['themes']) == 543, "Taxonomia incompleta!"
print(f"✅ Taxonomia carregada: {len(taxonomy['themes'])} temas")
📚 Documentação Relacionada¶
Interna¶
- News Enrichment Worker - Arquitetura do worker
- Pub/Sub Workers - Arquitetura event-driven
- Credenciais AWS Bedrock - Gerenciamento de credenciais
- Workers Pub/Sub - Template para criar novos workers
Externa¶
✅ Checklist de Onboarding¶
Antes de começar a trabalhar em produção, certifique-se de ter completado:
- [ ] Setup inicial (clone, poetry install, .env configurado)
- [ ] Teste de conexão AWS Bedrock (
aws sts get-caller-identity) - [ ] Teste de classificação simples (Exercício 1)
- [ ] Teste de batch processing (Exercício 2)
- [ ] Comparação Haiku vs Sonnet (Exercício 3)
- [ ] Leitura da documentação do News Enrichment Worker
- [ ] Entendimento da taxonomia hierárquica (543 categorias)
- [ ] Acesso ao PostgreSQL Cloud SQL
- [ ] Code review de pelo menos 1 PR existente
🤝 Próximos Passos¶
- Tarefa Prática: Pegar uma issue
good-first-issueno GitHub - Pair Programming: Sessão com outro dev do time
- Deploy: Acompanhar deploy do worker em staging
- Monitoramento: Revisar dashboards Cloud Monitoring
Dúvidas? Consulte o time no Slack #data-science ou abra uma issue no GitHub.
Última atualização: 05/05/2026
Responsável: Equipe Data Science - DestaquesGovbr