AI: Geração Aumentada por Recuperação (RAG)
Última atualização: 2026-08-26
1. O Que Você Aprenderá
❶ Quais Problemas o RAG Resolve—Corte de Conhecimento e Alucinações em LLMs
❷ Uma Compreensão Intuitiva de Incorporações Vetoriais
❸ Busca por Similaridade—Similaridade por Cosseno
❹ Processo RAG: Recuperação → Aumento → Geração
❺ Implementando um Sistema RAG Simples em Python
2. A História
Alice é responsável pela gestão do conhecimento interno em uma empresa de médio porte. A empresa acumulou mais de 1.000 documentos PDF—desde políticas de reembolso de despesas até procedimentos de segurança. Sempre que um novo funcionário faz uma pergunta, Alice precisa pesquisar manualmente nos documentos, o que é muito ineficiente.
Ela tentou usar o ChatGPT diretamente para responder perguntas internas, mas o ChatGPT não sabia nada sobre os documentos da empresa e frequentemente "inventava" políticas que não existiam—por exemplo, afirmando que "a ajuda de custo para viagem é de R$ 80 por dia", quando a política real era de R$ 45.
Charlie propôs uma solução: "Primeiro, encontrar parágrafos relacionados à pergunta na base de conhecimento e depois fazer o LLM responder com base nesses parágrafos—assim, ele não vai inventar coisas". Isso é RAG (Retrieval-Augmented Generation).
Alice seguiu esses passos: ela dividiu o documento em seções, gerou vetores e os armazenou em um banco de dados vetorial; quando um usuário fazia uma pergunta, primeiro ela recuperava os parágrafos mais relevantes e depois os incorporava ao prompt para o LLM responder. A partir de então, as respostas foram "baseadas em evidências", as alucinações foram significativamente reduzidas, e os novos funcionários conseguiram obter respostas precisas rapidamente.
3. Prazo de Corte do Conhecimento e Problemas de Alucinação em LLMs
(1) Prazo de Corte do Conhecimento
O conhecimento de um LLM vem de seus dados de treinamento e, uma vez que o treinamento é concluído, ele é “congelado”. Os dados de treinamento do GPT-4 são atuais até 2023, então ele não sabe nada sobre eventos ocorridos desde então. Mais importante, seus dados privados (documentos da empresa, anotações pessoais) nunca foram incluídos no conjunto de treinamento, então o LLM é inerentemente incapaz de responder perguntas sobre eles.
(2) Alucinações
Quando um LLM encontra uma pergunta cuja resposta ele não sabe, em vez de dizer “Eu não sei”, ele fabrica com confiança uma resposta que parece plausível—isso é alucinação. As alucinações são particularmente perigosas em tarefas de perguntas e respostas factuais.
(3) O Conceito Principal do RAG
Em vez de fazer um LLM “memorizar” todo o conhecimento, é melhor fornecer dinamicamente informações relevantes ao fazer uma pergunta, permitindo que o LLM responda com base no contexto fornecido. Isso é o RAG:
- Retrieval (Recuperação): Recuperar documentos relevantes da base de conhecimento
- Augmented (Aumentado): Incorporar os resultados da busca ao prompt como contexto
- Generation (Geração): O LLM gera uma resposta com base no prompt aprimorado
| Característica | LLM Puro | LLM com RAG Aprimorado |
|---|---|---|
| Fonte de Conhecimento | Apenas Dados de Treinamento | Dados de Treinamento + Documentação Externa |
| Dados Privados | Inacessíveis | Recuperáveis |
| Risco de Alucinações | Alto (Propenso à Fabricação) | Baixo (Constrangido Contextualmente) |
| Atualização do Conhecimento | Exige Retreinamento | Basta Atualizar a Documentação |
| Custo | Custo de Inferência | Custo de Inferência + Recuperação |
| Rastreabilidade | Nenhuma | Parágrafo Fonte Citável |
- Embedding Vetorial
(1) Como o texto é convertido em um vetor?
Embedding é o processo de mapear texto para vetores de alta dimensão. Por exemplo, após passar por um modelo de embedding, uma frase é convertida em uma matriz de 1.536 dimensões contendo números de ponto flutuante. Textos com significados semelhantes possuem vetores semelhantes, enquanto textos com significados diferentes têm vetores distantes entre si.
Você pode pensar nos embeddings como um "sistema de coordenadas semântico": cada texto possui uma posição nesse sistema, e textos mais próximos semanticamente são mais semelhantes.
(2) Modelos de Embedding Comuns
| Modelo | Dimensões | Fornecedor | Características |
|---|---|---|---|
| text-embedding-3-small | 1536 | OpenAI | Excelente custo-benefício, rápido |
| text-embedding-3-large | 3072 | OpenAI | Maior precisão, custo mais elevado |
| text-embedding-ada-002 | 1536 | OpenAI | Geração anterior, ainda amplamente utilizado |
| bge-large-en-v1.5 | 1024 | BAAI | Código aberto, desempenho excelente em inglês |
| bge-large-zh-v1.5 | 1024 | BAAI | Código aberto, desempenho excelente em chinês |
| m3e-base | 768 | Moka AI | Código aberto, suporta chinês e inglês |
▶ Exemplo: Gerando Embeddings de Texto com a API da OpenAI (Dificuldade: ⭐)
from openai import OpenAI
client = OpenAI()
def get_embedding(text: str, model: str = "text-embedding-3-small") -> list[float]:
response = client.embeddings.create(input=text, model=model)
return response.data[0].embedding
text = "O subsídio de viagem da empresa é de R$ 45 por dia."
vector = get_embedding(text)
print(f"Dimensão: {len(vector)}")
print(f"Primeiros 5 valores: {vector[:5]}")
Dimensão: 1536
Primeiros 5 valores: [0.0123, -0.0045, 0.0378, -0.0211, 0.0056]
4. Pesquisa de Similaridade e Similaridade do Cosseno
(1) Similaridade do Cosseno
A similaridade do cosseno mede o grau de similaridade entre as direções de dois vetores, com valores variando de [-1, 1]:
$$\text{cosine_similarity}(A, B) = \frac{A \cdot B}{|A| \times |B|}$$
- 1: Direção exatamente igual (mais similar em significado)
- 0: Ortogonal (não correlacionado)
- -1: Direção oposta (significado oposto)
(2) Bancos de Dados Vetoriais
Um banco de dados vetorial é um sistema projetado especificamente para armazenar e recuperar vetores, oferecendo suporte a buscas eficientes de vizinhos mais aproximados (ANN).
| Banco de Dados | Tipo | Características |
|---|---|---|
| Chroma | Open source, embarcado | Python nativo, adequado para prototipagem |
| FAISS | Open source, biblioteca | Desenvolvido pela Meta, extremamente rápido, totalmente local |
| Pinecone | Serviços em Nuvem | Totalmente gerenciado, escalável, pague pelo uso |
| Milvus | Open-source, distribuído | Adequado para ambientes de produção em larga escala |
| Qdrant | Open Source | Implementado em Rust, com excelente desempenho |
▶ Exemplo: Calcular a similaridade do cosseno entre duas frases (Dificuldade: ⭐)
import numpy as np
def cosine_similarity(a: list[float], b: list[float]) -> float:
a_arr = np.array(a)
b_arr = np.array(b)
dot = np.dot(a_arr, b_arr)
norm_a = np.linalg.norm(a_arr)
norm_b = np.linalg.norm(b_arr)
return float(dot / (norm_a * norm_b))
sentences = {
"travel_allowance": get_embedding("A diária de viagem da empresa é de US$ 45 por dia."),
"reimbursement": get_embedding("Como solicito o reembolso de viagem?"),
"unrelated": get_embedding("O tempo está ensolarado hoje."),
}
sim1 = cosine_similarity(sentences["travel_allowance"], sentences["reimbursement"])
sim2 = cosine_similarity(sentences["travel_allowance"], sentences["unrelated"])
print(f"Frases relacionadas: {sim1:.4f}")
print(f"Frases não relacionadas: {sim2:.4f}")
Frases relacionadas: 0.8234
Frases não relacionadas: 0.3012
▶ Exemplo: Recuperação vetorial simples — Encontrando o documento mais similar (Dificuldade: ⭐⭐)
def top_k_search(
query_embedding: list[float],
doc_embeddings: list[list[float]],
doc_texts: list[str],
k: int = 3,
) -> list[tuple[str, float]]:
similarities = [
(doc_texts[i], cosine_similarity(query_embedding, doc_embeddings[i]))
for i in range(len(doc_texts))
]
similarities.sort(key=lambda x: x[1], reverse=True)
return similarities[:k]
documents = [
"A diária de viagem é de US$ 45 para viagens nacionais.",
"Viagens internacionais requerem aprovação antecipada do VP.",
"Funcionários devem apresentar recibos dentro de 30 dias.",
"A política de trabalho remoto permite até 2 dias por semana em casa.",
"A cozinha do escritório é abastecida com café e lanches gratuitos.",
]
doc_embeddings = [get_embedding(doc) for doc in documents]
query = "Quanto posso gastar com viagem por dia?"
query_embedding = get_embedding(query)
results = top_k_search(query_embedding, doc_embeddings, documents, k=3)
for text, score in results:
print(f"[{score:.4f}] {text}")
[0.8912] A diária de viagem é de US$ 45 para viagens nacionais.
[0.7634] Funcionários devem apresentar recibos dentro de 30 dias.
[0.7102] Viagens internacionais requerem aprovação antecipada do VP.
5. Arquitetura RAG: Recuperação → Augmentação → Geração
(1) O Processo Completo do RAG
O RAG combina "recuperação" e "geração" para formar um fluxo de trabalho completo:
- Pergunta do Usuário: O usuário insere uma pergunta em linguagem natural
- Embedding da Consulta: Converte a consulta em um vetor
- Recuperação Vetorial: Busca pelos fragmentos de documento mais similares (Top-K) em um banco de dados vetorial
- Melhoria de Contexto: Incorpora os documentos recuperados no prompt
- Geração pelo LLM: O LLM responde a perguntas com base no prompt aprimorado
(2) Fluxograma Mermaid
graph TB
A["Pergunta do Usuário"] --> B["Embedding da Consulta"]
B --> C["Pesquisa Vetorial"]
D["Chunks de Documento<br/>+ Embeddings"] --> C
C --> E["Resultados Top-K"]
E --> F["Prompt Aumentado<br/>Contexto + Pergunta"]
F --> G["Geração pelo LLM"]
G --> H["Resposta"]
(3) Decisões de Design Chave
- Seleção Top-K: Se K for muito pequeno, informações relevantes podem ser omitidas; se K for muito grande, ruído pode ser introduzido. Tipicamente, K = 3–5.
- Template do Prompt: Instrua explicitamente o LLM a "responder com base SOMENTE no contexto fornecido" para reduzir alucinações.
- Reranking (Reordenação): Uma etapa opcional na qual um cross-encoder é usado para reordenar os resultados da busca para melhorar a precisão.
▶ Exemplo: Fluxo Completo de Chamada RAG (Dificuldade: ⭐⭐)
from openai import OpenAI
client = OpenAI()
def rag_query(question: str, documents: list[str], k: int = 3) -> str:
query_emb = get_embedding(question)
doc_embs = [get_embedding(doc) for doc in documents]
top_docs = top_k_search(query_emb, doc_embs, documents, k=k)
context = "\n\n".join([f"[Doc {i+1}] {text}" for i, (text, _) in enumerate(top_docs)])
prompt = f"""Responda à pergunta com base SOMENTE no seguinte contexto.
Se o contexto não contiver a resposta, diga "Eu não sei."
Contexto:
{context}
Pergunta: {question}
Resposta:"""
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": prompt}],
temperature=0,
)
return response.choices[0].message.content
docs = [
"A diária para viagens nacionais é de $45.",
"Viagens internacionais requerem aprovação do VP com pelo menos 2 semanas de antecedência.",
"Os funcionários devem enviar relatórios de despesas dentro de 30 dias após a viagem.",
"O reembolso máximo de hotel é de $200 por noite.",
"As passagens devem ser reservadas no portal de viagens da empresa.",
]
answer = rag_query("Qual é a diária para viagens?", docs)
print(answer)
A diária para viagens nacionais é de $45.
▶ Exemplo: Comparação da Qualidade da Resposta Com e Sem RAG (Dificuldade: ⭐⭐)
def ask_without_rag(question: str) -> str:
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": question}],
temperature=0,
)
return response.choices[0].message.content
question = "Qual é a diária de viagem da Acme Corp?"
print("=== Sem RAG ===")
print(ask_without_rag(question))
print("\n=== Com RAG ===")
print(rag_query(question, docs))
=== Sem RAG ===
Eu não tenho informações específicas sobre a diária de viagem da Acme Corp.
As diárias de viagem das empresas variam, mas uma faixa comum é de $50-$100 por dia.
Por favor, consulte a política de viagens da sua empresa para o valor exato.
=== Com RAG ===
A diária para viagens nacionais é de $45.
Sem RAG, os LLMs ou admitem que não sabem ou inventam uma resposta genérica; com RAG, suas respostas são precisas e bem fundamentadas.
6. Estratégia de Segmentação (Chunking)
(1) Por que a segmentação é necessária?
Os documentos costumam ser longos demais para serem convertidos diretamente em embeddings. Documentos longos precisam ser divididos em segmentos menores, e os embeddings gerados separadamente para cada segmento. A estratégia de segmentação afeta diretamente a qualidade da recuperação.
(2) Estratégias Comuns de Segmentação
| Estratégia | Princípio | Vantagens | Desvantagens |
|---|---|---|---|
| Segmentos de tamanho fixo | Dividir em partes a cada N tokens | Simples de implementar | Pode quebrar o significado |
| Segmentação em nível de sentença | Dividir por pontos/quebras de linha | Semanticamente completo | Segmentos podem ser muito pequenos ou grandes demais |
| Segmentação em nível de parágrafo | Segmentado por parágrafos | Coerência semântica | Comprimento irregular |
| Segmentação Semântica | Detecta limites semânticos com base na similaridade de embeddings | Semântica mais completa | Alto custo computacional |
| Segmentação recursiva de caracteres | Divisão recursiva por hierarquia de delimitadores | Balanceamento entre semântica e comprimento | Requer ajuste de parâmetros |
(3) Recomendações para Parâmetros de Segmentação
- Tamanho do Segmento (Chunk Size): Normalmente 256–1024 tokens, dependendo do modelo e do caso de uso
- Sobreposição do Segmento (Chunk Overlap): Segmentos adjacentes se sobrepõem em 10%–20% para evitar descontinuidade semântica
- Regra prática: Use blocos menores (256–512) para cenários de perguntas e respostas e blocos maiores (512–1024) para cenários de resumo.
(4) Segmentação recursiva usando LangChain
from langchain.text_splitter import RecursiveCharacterTextSplitter
text = """Acme Corp Travel Policy
1. Domestic Travel
The daily travel allowance is $45. This covers meals and incidental expenses.
Hotel reimbursement is capped at $200 per night.
2. International Travel
All international trips require VP approval at least 2 weeks in advance.
The daily allowance for international travel is $75.
Hotel reimbursement is capped at $300 per night.
3. Expense Reporting
All expense reports must be submitted within 30 days of trip completion.
Receipts are required for all expenses over $25."""
splitter = RecursiveCharacterTextSplitter(
chunk_size=200,
chunk_overlap=50,
separators=["\n\n", "\n", ". ", " "],
)
chunks = splitter.split_text(text)
for i, chunk in enumerate(chunks):
print(f"--- Chunk {i+1} ---")
print(chunk)
print()
--- Chunk 1 ---
Acme Corp Travel Policy
1. Domestic Travel
The daily travel allowance is $45. This covers meals and incidental expenses.
Hotel reimbursement is capped at $200 per night.
--- Chunk 2 ---
2. International Travel
All international trips require VP approval at least 2 weeks in advance.
The daily allowance for international travel is $75.
--- Chunk 3 ---
international travel is $75.
Hotel reimbursement is capped at $300 per night.
3. Expense Reporting
All expense reports must be submitted within 30 days of trip completion.
7. Exemplo Abrangente: Sistema de Perguntas e Respostas para Mini Base de Conhecimento
Construindo um pipeline RAG completo: 3 documentos → segmentação → embedding → armazenamento → consulta do usuário → recuperação → concatenação de prompt → resposta do LLM.
▶ Exemplo: Sistema de Perguntas e Respostas para Mini Base de Conhecimento (Dificuldade: ⭐⭐⭐)
import numpy as np
from openai import OpenAI
client = OpenAI()
# --- Etapa 1: Preparar documentos ---
documents = [
{
"title": "Política de Viagens",
"content": (
"Política de Viagens da Acme Corp\n\n"
"1. Viagens Domésticas\n"
"O subsídio diário de viagem é de $45 para viagens domésticas. "
"Isso cobre refeições e despesas incidentais. "
"O reembolso de hotel é limitado a $200 por noite.\n\n"
"2. Viagens Internacionais\n"
"Todas as viagens internacionais requerem aprovação do VP com pelo menos 2 semanas de antecedência. "
"O subsídio diário para viagens internacionais é de $75. "
"O reembolso de hotel é limitado a $300 por noite."
),
},
{
"title": "Política de Licenças",
"content": (
"Política de Licenças da Acme Corp\n\n"
"1. Licença Anual\n"
"Funcionários em tempo integral recebem 15 dias de licença anual remunerada por ano. "
"Licenças não utilizadas podem ser transportadas para o próximo ano, até um máximo de 5 dias.\n\n"
"2. Licença Médica\n"
"Os funcionários recebem 10 dias de licença médica remunerada por ano. "
"Um atestado médico é necessário para ausências que excedam 3 dias consecutivos."
),
},
{
"title": "Política de Segurança de TI",
"content": (
"Política de Segurança de TI da Acme Corp\n\n"
"1. Requisitos de Senha\n"
"Todas as senhas devem ter pelo menos 12 caracteres e incluir maiúsculas, "
"minúsculas, números e caracteres especiais. "
"As senhas devem ser alteradas a cada 90 dias.\n\n"
"2. Política de Dispositivos\n"
"Dispositivos pessoais não podem se conectar à rede corporativa. "
"Todos os dispositivos da empresa devem ter um software antivírus aprovado instalado."
),
},
]
# --- Etapa 2: Segmentar documentos ---
def chunk_text(text: str, chunk_size: int = 200, overlap: int = 50) -> list[str]:
words = text.split()
chunks = []
start = 0
while start < len(words):
end = start + chunk_size
chunks.append(" ".join(words[start:end]))
start += chunk_size - overlap
return chunks
all_chunks: list[str] = []
chunk_sources: list[str] = []
for doc in documents:
chunks = chunk_text(doc["content"])
for chunk in chunks:
all_chunks.append(chunk)
chunk_sources.append(doc["title"])
print(f"Total de chunks: {len(all_chunks)}")
# --- Etapa 3: Gerar embeddings ---
def get_embedding(text: str, model: str = "text-embedding-3-small") -> list[float]:
response = client.embeddings.create(input=text, model=model)
return response.data[0].embedding
chunk_embeddings = [get_embedding(chunk) for chunk in all_chunks]
print(f"Dimensão do embedding: {len(chunk_embeddings[0])}")
# --- Etapa 4: Busca vetorial ---
def cosine_similarity(a: list[float], b: list[float]) -> float:
a_arr = np.array(a)
b_arr = np.array(b)
return float(np.dot(a_arr, b_arr) / (np.linalg.norm(a_arr) * np.linalg.norm(b_arr)))
def search(query: str, k: int = 3) -> list[tuple[str, str, float]]:
query_emb = get_embedding(query)
scores = [
(all_chunks[i], chunk_sources[i], cosine_similarity(query_emb, chunk_embeddings[i]))
for i in range(len(all_chunks))
]
scores.sort(key=lambda x: x[2], reverse=True)
return scores[:k]
# --- Etapa 5: Consulta RAG ---
def rag_answer(question: str, k: int = 3) -> str:
results = search(question, k=k)
context_parts = []
for i, (chunk, source, score) in enumerate(results):
context_parts.append(f"[Fonte: {source}, Relevância: {score:.2f}]\n{chunk}")
context = "\n\n---\n\n".join(context_parts)
prompt = f"""Responda à pergunta APENAS com base no contexto a seguir.
Se o contexto não contiver informações suficientes, diga "Não tenho informações suficientes."
Sempre cite de qual documento fonte sua resposta vem.
Contexto:
{context}
Pergunta: {question}
Resposta:"""
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": prompt}],
temperature=0,
)
return response.choices[0].message.content
# --- Etapa 6: Teste ---
questions = [
"Qual é o subsídio diário de viagem para viagens domésticas?",
"Quantos dias de licença anual os funcionários recebem?",
"Quais são os requisitos de senha?",
"Qual é a política de trabalho remoto da empresa?",
]
for q in questions:
print(f"P: {q}")
print(f"R: {rag_answer(q)}")
print()
Total de chunks: 9
Dimensão do embedding: 1536
P: Qual é o subsídio diário de viagem para viagens domésticas?
R: O subsídio diário de viagem para viagens domésticas é de $45, de acordo com a Política de Viagens.
P: Quantos dias de licença anual os funcionários recebem?
R: Funcionários em tempo integral recebem 15 dias de licença anual remunerada por ano, de acordo com a Política de Licenças.
P: Quais são os requisitos de senha?
R: As senhas devem ter pelo menos 12 caracteres e incluir maiúsculas, minúsculas, números e caracteres especiais. Devem ser alteradas a cada 90 dias, de acordo com a Política de Segurança de TI.
P: Qual é a política de trabalho remoto da empresa?
R: Não tenho informações suficientes. O contexto fornecido não contém nenhuma política de trabalho remoto.
Observe a última pergunta: o RAG responde corretamente com "informações insuficientes" em vez de inventar uma resposta—e esse é precisamente o valor do RAG.
❓ Perguntas Frequentes
temperature=0, incorporar reranking e exigir que a resposta cite fontes.📖 Resumo
- Os LLMs sofrem com problemas de corte de conhecimento e alucinações, e não podem responder diretamente perguntas relacionadas a dados privados.
- O RAG usa um processo de "recuperação → aprimoramento → geração" para permitir que os LLMs respondam perguntas com base em documentos externos, reduzindo significativamente as alucinações.
- O embedding converte texto em vetores; vetores de texto com significados semelhantes também são semelhantes
- A similaridade de cosseno mede a consistência das direções dos vetores e é uma métrica fundamental na busca semântica.
- As estratégias de fragmentação afetam a qualidade da recuperação, e a fragmentação recursiva de caracteres é a abordagem de equilíbrio mais comumente utilizada
- Bancos de dados vetoriais (Chroma, FAISS, Pinecone) fornecem armazenamento e recuperação eficientes de vetores
- Fluxo completo do RAG: Fragmentação do documento → Embedding → Armazenamento → Consulta → Recuperação → Concatenação de prompts → Geração do LLM
📝 Exercícios
Básico (⭐)
Use a API de Embeddings da OpenAI para gerar vetores para os cinco segmentos de texto a seguir e calcule a similaridade de cosseno entre cada par:
texts = [
"Cats are popular pets known for their independence.",
"Dogs are loyal companions that love to play fetch.",
"The stock market rose 3% today on strong earnings.",
"Felines enjoy climbing and sleeping in sunny spots.",
"Interest rates were raised by the central bank.",
]
Apresente os resultados como uma matriz de similaridade 5×5 para visualizar quais textos são mais semelhantes.
Avançado (⭐⭐)
Implemente uma função genérica de recuperação Top-K que suporte as seguintes características:
def vector_search(
query: str,
corpus: list[str],
k: int = 5,
embedding_model: str = "text-embedding-3-small",
) -> list[dict]:
"""
Returns top-k most similar documents with scores.
Each result is a dict: {"text": ..., "score": ..., "rank": ...}
"""
# Sua implementação aqui
pass
Requisitos: Utilize um documento de teste composto por pelo menos 10 parágrafos para verificar a validade dos resultados da busca.
Desafio (⭐⭐⭐)
Construa um sistema mini de RAG contendo pelo menos 5 documentos e implemente totalmente o seguinte fluxo de trabalho:
- Carregamento de Documentos e Chunking Recursivo (chunk_size=300, overlap=60)
- Geração e armazenamento dos embeddings
- Consulta do usuário → Recuperação Top-3 → Concatenação e aprimoramento do prompt → Resposta do LLM
- Compare as diferenças nas respostas para a mesma pergunta com e sem RAG
- Teste uma pergunta que não exista na base de conhecimento para verificar se o RAG se recusa corretamente a respondê-la
Desafio de Extensão: Adicione uma etapa de re-ranking — primeiro realize uma recuperação Top-10, depois peça ao LLM para pontuar e classificar os 10 resultados, e alimente os Top-3 na geração final.