A Evolução das Arquiteturas RAG: do Naive ao Agentic
Quando comecei a trabalhar com LLMs em produção, a primeira frustração foi sempre a mesma: o modelo sabe muito sobre o mundo, mas nada sobre a empresa do cliente. Ele alucina contratos que não existem, inventa políticas internas e desconhece completamente o contexto do negócio.

RAG nasceu para resolver exatamente isso.
Retrieval-Augmented Generation é o padrão arquitetural que conecta o poder generativo dos LLMs ao conhecimento específico de uma organização. A ideia central é simples: antes de gerar uma resposta, busque informação relevante externamente e injete no contexto do modelo. O que mudou profundamente nos últimos dois anos foi a sofisticação dessa busca.
Neste artigo, percorro as seis arquiteturas RAG mais relevantes da atualidade, explicando a lógica por trás de cada evolução, os casos de uso onde cada uma brilha e as decisões de engenharia que você precisa tomar ao escolher entre elas.
O problema que RAG resolve
LLMs são treinados com um snapshot do mundo até uma data de corte. Eles não sabem o que aconteceu ontem, não conhecem os documentos internos da sua empresa e não têm acesso em tempo real a sistemas transacionais.
Existem três abordagens para lidar com isso:
- Fine-tuning: treinar o modelo com seus dados. Caro, lento e difícil de atualizar.
- Context stuffing: jogar tudo no prompt. Inviável para bases grandes; desperdiça tokens.
- RAG: recuperar apenas o que é relevante para aquela consulta específica e injetar no prompt.
RAG venceu na prática porque equilibra custo, velocidade de atualização e qualidade de resposta de forma que as outras abordagens não conseguem.
A estrutura básica tem três etapas:
Query → Recuperação → Geração aumentada
O que diferencia as arquiteturas é exatamente como essa recuperação acontece.
1.Naive RAG — a fundação
Como funciona
A arquitetura mais simples e ponto de partida para qualquer implementação:
- Os documentos são divididos em chunks e convertidos em vetores de embedding
- Esses vetores são armazenados em um banco vetorial (Qdrant, Pinecone, FAISS)
- A query do usuário é convertida em vetor
- O sistema busca os chunks com maior similaridade cosseno
- Os chunks recuperados são injetados no prompt junto com a query
from langchain.vectorstores import Qdrant
from langchain.embeddings import OpenAIEmbeddings
from langchain.chains import RetrievalQA
from langchain.llms import ChatOpenAI
embeddings = OpenAIEmbeddings()
vectorstore = Qdrant.from_documents(docs, embeddings)
qa_chain = RetrievalQA.from_chain_type(
llm=ChatOpenAI(model="gpt-4o"),
retriever=vectorstore.as_retriever(search_kwargs={"k": 5})
)
response = qa_chain.invoke({"query": "Qual é a política de reembolso?"})
Quando usar
- Base de conhecimento estável e bem estruturada
- Perguntas objetivas com vocabulário próximo ao dos documentos
- MVPs, provas de conceito e validação de produto
- Equipes com menor maturidade em IA
Limitações críticas
O Naive RAG falha em três cenários comuns:
Vocabulary mismatch: o usuário usa "devolução" mas o documento diz "reembolso". A similaridade semântica ajuda, mas não resolve tudo.
Chunking sem contexto: um chunk isolado perde a informação de qual documento pertence, qual seção, qual contexto anterior. Um parágrafo retirado do meio de um contrato pode gerar uma resposta completamente errada.
Queries vagas: "me fala sobre aquele processo de aprovação" não gera um vetor informativo o suficiente para encontrar os documentos certos.
2.HyDE — Hypothetical Document Embeddings
A virada conceitual
HyDE parte de uma observação simples: a distância no espaço vetorial entre uma pergunta e sua resposta é maior do que a distância entre duas respostas similares.
Em vez de buscar pelo vetor da pergunta, o HyDE instrui o LLM a imaginar como seria um documento que responderia perfeitamente à query. Esse documento hipotético é então usado como vetor de busca.
Query → LLM gera documento hipotético → Embedding do documento → Busca vetorial → Resposta real
Implementação
from langchain.chains import HypotheticalDocumentEmbedder
from langchain.prompts import PromptTemplate
hyde_prompt = PromptTemplate(
input_variables=["question"],
template="""Escreva um trecho de documentação técnica que responda a seguinte pergunta.
O trecho deve ter entre 100 e 150 palavras, no estilo de um manual corporativo.
Pergunta: {question}
Trecho:"""
)
hyde_embedder = HypotheticalDocumentEmbedder.from_llm(
llm=ChatOpenAI(model="gpt-4o"),
base_embeddings=OpenAIEmbeddings(),
custom_prompt=hyde_prompt
)
vectorstore = Qdrant.from_documents(docs, hyde_embedder)
Quando usar
- Usuários não-técnicos que formulam queries vagas ou em linguagem cotidiana
- Domínios onde o jargão técnico dos documentos é muito diferente da linguagem natural dos usuários
- Sistemas de atendimento ao cliente e help desks corporativos
- Quando a qualidade da recuperação é mais importante que a latência
Trade-offs
O custo é uma chamada extra ao LLM antes de cada busca. Em sistemas de alto volume, isso pode representar 30-50% de aumento no custo por query. A recomendação é combinar HyDE com cache de embeddings para queries frequentes.
3.Graph RAG — quando relacionamentos importam
O problema que os grafos resolvem
Busca por similaridade semântica responde bem à pergunta "quais chunks são parecidos com esta query?". Mas ela falha na pergunta "quais são todas as empresas que têm relação com o Contrato 2024-087, e quais cláusulas se aplicam a cada uma?".
Esse é um problema de grafo, não de similaridade.
Graph RAG extrai entidades e relacionamentos dos documentos e os representa como um grafo de conhecimento. A recuperação deixa de ser vetorial e passa a ser uma travessia do grafo.
Arquitetura
Documentos → Extração de entidades (NER) → Construção do grafo → Schema
↓
Query → Query Generator → Consulta ao grafo → Contexto estruturado → LLM → Resposta
from langchain_community.graphs import Neo4jGraph
from langchain_experimental.graph_transformers import LLMGraphTransformer
graph = Neo4jGraph(url="bolt://localhost:7687", username="neo4j", password="senha")
llm_transformer = LLMGraphTransformer(llm=ChatOpenAI(model="gpt-4o"))
# Extrai entidades e relações automaticamente
graph_documents = llm_transformer.convert_to_graph_documents(documents)
graph.add_graph_documents(graph_documents)
# Busca por grafo
query = """
MATCH (c:Contrato)-[:ENVOLVE]->(e:Empresa)
WHERE c.numero = '2024-087'
RETURN e.nome, e.cnpj, c.clausulas
"""
resultado = graph.query(query)
Quando usar
- Bases jurídicas e compliance com múltiplas entidades relacionadas
- Knowledge graphs corporativos (estrutura organizacional, processos, responsabilidades)
- Análise de risco com cadeias de relacionamento
- Sistemas financeiros com grafos de transações e contrapartes
- Qualquer domínio onde "quem se relaciona com quem" é tão importante quanto o conteúdo
Combinando com RAG tradicional
Na prática, Graph RAG raramente é usado sozinho. A arquitetura mais robusta combina grafo para navegação estrutural e vetor para conteúdo semântico — o que nos leva à próxima arquitetura.
4.Hybrid RAG — lexical + semântico
Por que embeddings sozinhos não bastam
Busca vetorial é ótima para similaridade semântica, mas tem um ponto cego: ela não é boa com termos exatos, siglas, nomes próprios e jargão técnico muito específico.
Um modelo de embedding pode entender que "autarquia federal" e "órgão público" são semanticamente próximos. Mas se você buscar por "BACEN" e o documento diz exatamente "BACEN", a busca léxica BM25 vai encontrar isso com precisão cirúrgica enquanto a busca vetorial pode vacilar.
Hybrid RAG combina as duas abordagens e usa um reranker para unir os resultados.
Arquitetura
Query
├── Busca Lexical (BM25/TF-IDF) → Top-K resultados léxicos
└── Busca Semântica (Embedding) → Top-K resultados vetoriais
↓
Reranker (Cross-Encoder)
↓
Top-N resultados unificados → LLM
Implementação com Qdrant
from qdrant_client import QdrantClient
from qdrant_client.models import SparseVector, NamedSparseVector, NamedVector
client = QdrantClient(url="http://localhost:6333")
# Busca híbrida: vetorial densa + esparsa (BM25)
results = client.query_points(
collection_name="documentos",
prefetch=[
# Busca densa (semântica)
models.Prefetch(
query=dense_embedding,
using="dense",
limit=20
),
# Busca esparsa (léxica)
models.Prefetch(
query=models.SparseVector(indices=sparse_indices, values=sparse_values),
using="sparse",
limit=20
),
],
# Reranking com RRF (Reciprocal Rank Fusion)
query=models.FusionQuery(fusion=models.Fusion.RRF),
limit=5
)
Quando usar
- Bases com muito jargão técnico, siglas e nomes próprios
- Sistemas regulatórios onde termos exatos têm peso legal
- Documentação de APIs, contratos e especificações técnicas
- Qualquer contexto onde falsos negativos (não encontrar o documento certo) têm alto custo
O papel do reranker
O reranker é um Cross-Encoder que avalia a relevância de cada resultado em relação à query de forma mais precisa do que os bi-encoders usados para gerar embeddings. Modelos como cross-encoder/ms-marco-MiniLM-L-6-v2 ou o Cohere Rerank são escolhas comuns.
O trade-off é latência: o reranker adiciona 50-200ms dependendo do número de candidatos e do hardware.
5.Contextual RAG — chunks com memória
O problema do chunking cego
Imagine um contrato de 50 páginas dividido em chunks de 512 tokens. O chunk que contém a cláusula de rescisão provavelmente não menciona as partes contratantes, o objeto do contrato ou a data de assinatura — porque essa informação está em chunks anteriores.
Quando esse chunk é recuperado isoladamente, o LLM não tem contexto para interpretar corretamente a cláusula. Ele não sabe a quem aquela rescisão se aplica.
Contextual RAG resolve isso fazendo o LLM enriquecer cada chunk com seu contexto antes da indexação.
Como funciona
Durante o pipeline de ingestão (não em tempo de query), o LLM processa cada chunk junto com o documento completo (ou uma janela de contexto) e gera uma descrição contextual que é concatenada ao chunk antes de ser indexada.
from anthropic import Anthropic
client = Anthropic()
def gerar_contexto_chunk(documento_completo: str, chunk: str) -> str:
prompt = f"""<documento>
{documento_completo}
</documento>
Aqui está o trecho específico que precisa ser contextualizado:
<trecho>
{chunk}
</trecho>
Forneça um contexto conciso (2-3 frases) que situe este trecho dentro do documento completo.
Inclua: qual documento é, qual seção pertence, quais entidades estão envolvidas.
Responda apenas com o contexto, sem prefácio."""
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=200,
messages=[{"role": "user", "content": prompt}]
)
return response.content[0].text
# Pipeline de ingestão
for chunk in chunks:
contexto = gerar_contexto_chunk(documento_completo, chunk.text)
chunk_contextualizado = f"{contexto}\n\n{chunk.text}"
# indexar chunk_contextualizado no vector store
Quando usar
- Documentos longos e densos (contratos, relatórios, manuais técnicos)
- Bases onde a relação entre partes do documento é crucial para a resposta
- Sistemas jurídicos, de compliance e auditoria
- Qualquer contexto onde "quem, quando, sobre o quê" precisa estar presente em todo chunk
Custo de ingestão vs qualidade
O trade-off aqui é no pipeline de ingestão: você paga uma chamada ao LLM por chunk durante o processamento dos documentos. Para uma base com 10.000 chunks, isso representa 10.000 chamadas extras. O custo unitário é baixo (chunks são pequenos), mas precisa estar no planejamento.
A Anthropic reportou melhorias de 49% no precision@20 e redução de 67% em falhas ao combinar Contextual RAG com BM25.
6.Adaptive RAG — roteamento inteligente
O problema do one-size-fits-all
As arquiteturas anteriores tratam todas as queries da mesma forma. Mas perguntas têm complexidades muito diferentes:
- "Qual o CEP da filial de São Paulo?" — resposta direta, não precisa de RAG
- "Quais são os riscos do projeto X?" — busca simples, resposta objetiva
- "Compare a performance dos produtos A, B e C nos últimos 3 trimestres e identifique os fatores que explicam as diferenças" — análise multi-step com múltiplas fontes
Jogar a terceira query num Naive RAG vai produzir uma resposta superficial. Jogar a primeira num sistema de raciocínio multi-step vai desperdiçar tokens e latência.
Adaptive RAG resolve isso com um Query Analyzer que classifica a complexidade da pergunta e roteia para o pipeline adequado.
Arquitetura de roteamento
Query
↓
Query Analyzer (classificador)
├── Simples (factual, direto) → Resposta direta sem RAG
├── Médio (busca + síntese) → RAG padrão + chain of thought
└── Complexo (multi-step) → Reasoning chain + múltiplas buscas iterativas
Implementação com LangGraph
from langgraph.graph import StateGraph, END
from typing import TypedDict, Literal
class State(TypedDict):
query: str
complexity: Literal["direct", "single_step", "multi_step"]
context: list[str]
response: str
def classify_query(state: State) -> State:
prompt = f"""Classifique a complexidade desta query:
Query: {state['query']}
Responda com exatamente uma das opções:
- "direct": pergunta factual simples, sem necessidade de busca
- "single_step": requer busca em documentos, resposta única
- "multi_step": requer múltiplas buscas, análise comparativa ou raciocínio encadeado
Resposta:"""
classification = llm.invoke(prompt).content.strip()
return {**state, "complexity": classification}
def route(state: State) -> str:
return state["complexity"]
workflow = StateGraph(State)
workflow.add_node("classify", classify_query)
workflow.add_node("direct_answer", direct_answer_node)
workflow.add_node("single_step_rag", single_step_rag_node)
workflow.add_node("multi_step_rag", multi_step_rag_node)
workflow.set_entry_point("classify")
workflow.add_conditional_edges("classify", route, {
"direct": "direct_answer",
"single_step": "single_step_rag",
"multi_step": "multi_step_rag"
})
app = workflow.compile()
Quando usar
- Sistemas com alta variabilidade nas queries dos usuários
- Ambientes onde custo por query precisa ser otimizado
- Assistentes corporativos que atendem desde perguntas simples a análises complexas
- Qualquer sistema onde latência e custo são KPIs explícitos
7.Agentic RAG — o estado da arte
A mudança de paradigma
Nas arquiteturas anteriores, o LLM é passivo: recebe contexto, gera resposta. No Agentic RAG, o LLM é um ator autônomo que decide como resolver o problema.
A diferença é fundamental:
| RAG tradicional | Agentic RAG |
|---|---|
| Pipeline fixo | Fluxo dinâmico decidido pelo agente |
| Uma busca por query | Múltiplas buscas iterativas |
| Sem memória entre interações | Memória de curto e longo prazo |
| Ferramentas predefinidas | Seleção dinâmica de ferramentas |
| Resposta única | Planejamento + execução + validação |
Componentes da arquitetura
Memória
- Curto prazo: contexto da conversa atual (janela de contexto)
- Longo prazo: memória persistente em banco de dados (Redis, PostgreSQL)
Planejamento
- ReAct (Reasoning + Acting): raciocina sobre o problema antes de agir
- Chain of Thought: encadeia passos de raciocínio explicitamente
Agentes especializados
- Agent 1: dados locais (vector store, SQL, APIs internas)
- Agent 2: busca web (Kagi, Bing, Google)
- Agent 3: cloud engines (AWS, Azure, GCP)
MCP Servers O Model Context Protocol padroniza a interface entre agentes e ferramentas externas, permitindo que qualquer agente acesse qualquer ferramenta de forma intercambiável.
Implementação com LangGraph
from langgraph.graph import StateGraph, END
from langgraph.prebuilt import ToolNode
from langchain_core.tools import tool
@tool
def buscar_documentos_internos(query: str) -> str:
"""Busca em documentos internos da empresa."""
return vectorstore.similarity_search(query, k=5)
@tool
def buscar_web(query: str) -> str:
"""Busca informações atualizadas na web."""
return web_search_tool.run(query)
@tool
def consultar_banco_dados(sql: str) -> str:
"""Executa consulta SQL no banco de dados transacional."""
return db.run(sql)
tools = [buscar_documentos_internos, buscar_web, consultar_banco_dados]
tool_node = ToolNode(tools)
# Agente com ReAct
agent = create_react_agent(
model=ChatOpenAI(model="gpt-4o"),
tools=tools,
system_prompt="""Você é um assistente especializado em análise financeira.
Para cada query:
1. Avalie quais ferramentas são necessárias
2. Execute as buscas em ordem de relevância
3. Sintetize as informações obtidas
4. Valide a resposta antes de retornar
Seja preciso, cite as fontes e indique quando a informação é incerta."""
)
# Grafo com memória persistente
from langgraph.checkpoint.redis import RedisSaver
checkpointer = RedisSaver.from_conn_string("redis://localhost:6379")
workflow = StateGraph(MessagesState)
workflow.add_node("agent", agent)
workflow.add_node("tools", tool_node)
workflow.set_entry_point("agent")
workflow.add_conditional_edges("agent", should_continue)
workflow.add_edge("tools", "agent")
app = workflow.compile(checkpointer=checkpointer)
# Execução com sessão persistente
config = {"configurable": {"thread_id": "usuario-123-sessao-456"}}
response = app.invoke({"messages": [HumanMessage(content=query)]}, config=config)
Quando usar
- Assistentes que precisam resolver problemas complexos de forma autônoma
- Sistemas que integram múltiplas fontes de dados heterogêneas
- Workflows de análise que requerem raciocínio multi-step
- Automação de processos que envolvem decisões intermediárias
Desafios de produção
Agentic RAG traz complexidade operacional significativa:
Observabilidade: é essencial rastrear cada passo do agente — qual ferramenta foi chamada, com quais parâmetros, qual foi o resultado. LangSmith é a solução mais madura para isso.
Controle de custos: loops de agente sem limite podem gerar centenas de chamadas ao LLM. Implemente max_iterations e alertas de custo.
Timeouts: agentes complexos podem demorar 30-60 segundos. Defina SLAs claros e implemente respostas parciais quando necessário.
Guardrails: em ambientes regulados (financeiro, saúde, jurídico), é indispensável validar o que o agente faz antes de executar ações irreversíveis.
Comparativo arquitetural
| Arquitetura | Complexidade | Custo relativo | Latência | Melhor caso de uso |
|---|---|---|---|---|
| Naive RAG | Baixa | $ | < 500ms | MVP, bases simples |
| HyDE | Baixa | $$ | 1-2s | Queries vagas, usuários leigos |
| Graph RAG | Alta | $$$ | 1-3s | Dados relacionais, ontologias |
| Hybrid RAG | Média | $$ | 500ms-1.5s | Jargão técnico, termos exatos |
| Contextual RAG | Média | $$ (ingestão) | < 500ms | Documentos longos e densos |
| Adaptive RAG | Alta | $-$$$ | variável | Alto volume, queries diversas |
| Agentic RAG | Muito alta | $$$$ | 5-60s | Problemas complexos, multi-fonte |
Como escolher a arquitetura certa
A escolha da arquitetura RAG não é sobre usar a mais sofisticada — é sobre usar a mais adequada para o contexto. Algumas perguntas que uso no processo de decisão:
Qual é a natureza das queries? Se são sempre simples e factuais → Naive ou Hybrid. Se variam muito → Adaptive. Se requerem raciocínio → Agentic.
Qual é a estrutura dos documentos? Textos longos com contexto entre seções → Contextual. Dados com entidades relacionadas → Graph. Bases com jargão técnico → Hybrid.
Quais são os SLAs de latência? Abaixo de 1 segundo → Naive, Hybrid ou Contextual. Até 3 segundos → a maioria. Sem restrição → Agentic.
Qual é o ambiente regulatório? Em financial services e saúde, priorize rastreabilidade, explainability e guardrails sobre sofisticação arquitetural.
Qual é a maturidade da equipe? Uma equipe que ainda está aprendendo RAG vai entregar mais valor com um Hybrid RAG bem implementado do que com um Agentic RAG mal implementado.
Conclusão
A progressão do Naive ao Agentic RAG conta uma história sobre como a indústria aprendeu que recuperação de informação é um problema multidimensional. Não existe uma solução universal.
O Naive RAG continua sendo a escolha certa para dezenas de casos de uso. O Agentic RAG é transformador para os problemas certos, mas traz consigo uma carga operacional que não pode ser ignorada.
A minha recomendação prática: comece simples, meça, e evolua a arquitetura conforme os dados indicarem onde estão os gargalos. Um Hybrid RAG com bom reranker resolve 80% dos problemas que as equipes tentam resolver com Agentic RAG. Guarde a complexidade para quando ela for realmente necessária.
E quando for necessária — o Agentic RAG com LangGraph, MCP e observabilidade via LangSmith é uma das combinações mais poderosas que já trabalhei.
Referências e leitura complementar
- Contextual Retrieval — Anthropic Blog
- From RAG to Rich: LangChain RAG Patterns
- Graph RAG — Microsoft Research
- HyDE Paper — Gao et al., 2022
- LangGraph Documentation
- Qdrant Hybrid Search
Alexsander Valente é Engenheiro de Dados e IA Architect na Threeger 360, com mais de 10 anos de experiência em instituições financeiras. Publica conteúdo técnico sobre IA Generativa, RAG e arquiteturas de dados no LinkedIn e no curso IA Generativa na Prática.


