04-01-02 — Arquitetura RAG clássica

⏱ 12 minFontes validadas em: 2026-04-29

TL;DR

RAG clássico tem dois momentos distintos: indexação offline (chunkar documentos → embeddar → armazenar no vector store) e query online (embeddar a pergunta → buscar chunks similares → montar prompt com contexto → chamar o LLM). Entender essa separação é fundamental para diagnosticar problemas de performance e escalar o sistema.

Os dois tempos do RAG

RAG não é só "busca + LLM". É um pipeline com duas fases temporais bem distintas que rodam em momentos diferentes:

flowchart TD subgraph OFFLINE["⚙️ INDEXAÇÃO (offline — acontece uma vez)"] D[Documentos brutos\nPDF, DOCX, HTML] --> C[Chunking\nDividir em trechos] C --> E[Embedding Model\nTexto → vetor numérico] E --> VS[(Vector Store\nPinecone / AI Search / pgvector)] end subgraph ONLINE["⚡ QUERY (online — acontece a cada pergunta)"] Q[Pergunta do usuário] --> QE[Embedding da query] QE --> R[Retrieval\nBuscar top-k chunks similares] VS --> R R --> A[Augmentation\nMontar prompt com contexto] Q --> A A --> LLM[LLM\nGPT-4 / Claude / Mistral] LLM --> ANS[Resposta fundamentada] end OFFLINE --> ONLINE

Fase 1: Indexação offline

Esta fase acontece quando você adiciona ou atualiza documentos. Não fica no caminho crítico das queries dos usuários.

1.1 Chunking — dividir para conquistar

Documentos completos são grandes demais para virar um único embedding. Um contrato de 80 páginas precisa ser cortado em trechos menores (chunks) antes de ser indexado. A estratégia de corte afeta diretamente a qualidade do retrieval.

Regra de bolso inicial: chunks de 512 tokens com 10% de overlap. Veremos estratégias avançadas em 04-03-01.

1.2 Embedding — texto vira número

Cada chunk passa por um embedding model — uma rede neural que transforma texto em um vetor de números de alta dimensão (ex: 1536 dimensões para text-embedding-3-small). Textos semanticamente similares ficam próximos nesse espaço vetorial.

from openai import AzureOpenAI

client = AzureOpenAI(
    azure_endpoint="https://seu-recurso.openai.azure.com/",
    api_key="SUA_CHAVE",
    api_version="2024-02-01"
)

def embed(text: str) -> list[float]:
    response = client.embeddings.create(
        input=text,
        model="text-embedding-3-small"  # nome do deployment no Azure
    )
    return response.data[0].embedding  # lista de 1536 floats

1.3 Vector Store — armazenar com índice

O vetor gerado é armazenado junto com metadados (nome do arquivo, página, data, permissões) num banco de dados otimizado para busca por similaridade. Esse é o componente que diferencia RAG de um simples grep.

Fase 2: Query online

Esta fase acontece em tempo real a cada pergunta do usuário. Latência importa aqui.

2.1 Embed da query

A pergunta do usuário passa pelo mesmo modelo de embedding usado na indexação. Isso é crítico: modelos diferentes geram espaços vetoriais incompatíveis.

2.2 Retrieval — buscar os chunks certos

O vetor da query é comparado com todos os vetores no store. Os top-k chunks mais próximos (tipicamente k=5 a 20) são retornados. "Próximos" aqui significa semanticamente similares, não textualmente idênticos.

2.3 Augmentation — montar o prompt

Os chunks recuperados são inseridos no prompt enviado ao LLM. Um template típico:

def build_prompt(question: str, chunks: list[str]) -> str:
    context = "\n\n---\n\n".join(chunks)
    return f"""Você é um assistente especializado. Responda à pergunta abaixo
usando SOMENTE o contexto fornecido. Se a resposta não estiver no contexto,
diga "Não encontrei essa informação nos documentos disponíveis."

CONTEXTO:
{context}

PERGUNTA: {question}

RESPOSTA:"""

2.4 Generation — o LLM responde

O LLM recebe o prompt aumentado e gera a resposta. Agora ele tem os documentos relevantes na "mesa de trabalho" — muito menos chance de alucinar sobre o assunto específico.

💡 Insight

A qualidade da resposta RAG tem um teto: se o chunk certo não foi recuperado, nem o melhor LLM vai gerar uma boa resposta. Por isso 80% do esforço em sistemas RAG de produção está no retrieval, não no LLM.

O pipeline completo em código

def rag_query(question: str, vector_store, llm_client, k: int = 5) -> str:
    # 1. Embed a pergunta
    query_vector = embed(question)

    # 2. Buscar chunks similares
    chunks = vector_store.similarity_search(query_vector, k=k)

    # 3. Montar o prompt
    prompt = build_prompt(question, [c.text for c in chunks])

    # 4. Chamar o LLM
    response = llm_client.chat.completions.create(
        model="gpt-4o",
        messages=[{"role": "user", "content": prompt}]
    )

    return response.choices[0].message.content
⚠️ Armadilha comum

Não misture modelos de embedding na indexação e na query. Se você indexou com text-embedding-3-small e na hora da query usou text-embedding-ada-002, os vetores são incompatíveis e o retrieval vai falhar silenciosamente — retornando resultados aleatórios sem lançar erro.

🏢 Microsoft

No ecossistema Azure, esse pipeline se traduz em: Azure Blob Storage (documentos) → Azure AI Document Intelligence (extração de texto) → Azure OpenAI Embeddings → Azure AI Search (vector store) → Azure OpenAI GPT-4o (LLM). Cada componente tem SLA e integração nativa.

Como isso se conecta

Fontes

  1. Lewis, P. et al. (2020). Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks. arxiv.org/abs/2005.11401
  2. LangChain. RAG — Conceptual Guide. python.langchain.com/docs/concepts/rag
  3. Microsoft. Build a RAG solution using Azure AI Search. learn.microsoft.com
  4. OpenAI. Embeddings API Reference. platform.openai.com