ragmcpai-agentssqlembeddings

RAG vs SQL: Por Que Seu Agente de IA Deveria Consultar, Não Gerar Embeddings

Michael San Martim · 2026-04-22

Toda semana, outra equipe anuncia que está “construindo RAG para seu data warehouse”. Eles dividem suas tabelas em vetores, armazenam no Pinecone e pedem a um LLM para responder perguntas de negócio. Funciona para documentos. Falha catastroficamente para dados estruturados.

O Problema: RAG em Tabelas

Veja o que acontece quando você usa RAG para dados estruturados:

# The RAG approach to answering "What was revenue in Q3?"
# Step 1: Chunk your tables into text
chunks = []
for row in orders_table:
chunks.append(f"Order {row.id}: customer {row.customer}, "
f"amount ${row.amount}, date {row.date}")
# Step 2: Embed chunks
embeddings = openai.embed(chunks) # $0.0001 per chunk, 1M rows = $100
# Step 3: Store in vector DB
pinecone_index.upsert(vectors=embeddings) # $70/month for 1M vectors
# Step 4: At query time, retrieve "relevant" chunks
query_embedding = openai.embed("What was revenue in Q3?")
results = pinecone_index.query(query_embedding, top_k=20)
# Step 5: Ask LLM to synthesize
answer = openai.chat(
f"Based on these records: {results}\n"
f"Answer: What was revenue in Q3?"
)
# Returns: "Based on the retrieved records, Q3 revenue appears to be
# approximately $2.3M" (actual answer: $4.7M)

Por que falha:

  1. Recuperação perde linhas. Similaridade vetorial encontra chunks “relevantes”, não TODAS as linhas correspondentes. Se você tem 50.000 pedidos no Q3, recuperou 20.
  2. Agregação é impossível. Você não pode SUM, AVG ou COUNT a partir de 20 amostras aleatórias.
  3. Dados ficam desatualizados. Você precisa re-embed cada vez que dados mudam.
  4. É caro. Embeddings de milhões de linhas + hospedagem de vector DB + tokens LLM para contexto.
  5. Alucina com confiança. O LLM vê 20 linhas e extrapola.

A Abordagem Correta: SQL

A mesma pergunta respondida com SQL:

SELECT SUM(amount) AS revenue
FROM orders
WHERE created_at >= '2024-07-01'
AND created_at < '2024-10-01'

Resultado: $4.712.843,00 — exato, instantâneo, grátis.

MCP + SQL com DataSpoc Lens

O DataSpoc Lens expõe seu data lake como um servidor MCP. Agentes de IA executam consultas SQL reais em vez de adivinhar a partir de embeddings.

Setup

Terminal window
pip install dataspoc-lens[mcp]
dataspoc-lens add-bucket s3://company-lake
dataspoc-lens mcp

Como o Agente Funciona

from dataspoc_lens import LensClient
client = LensClient()
# Agent discovers what data exists
tables = client.list_tables()
# ['raw.postgres.orders', 'raw.postgres.customers', 'raw.postgres.products']
# Agent inspects schema
schema = client.get_schema("raw.postgres.orders")
# [{"name": "id", "type": "INTEGER"},
# {"name": "customer_id", "type": "INTEGER"},
# {"name": "amount", "type": "DECIMAL"},
# {"name": "created_at", "type": "TIMESTAMP"}, ...]
# Agent writes and executes SQL
result = client.query("""
SELECT SUM(amount) AS revenue
FROM raw.postgres.orders
WHERE created_at >= '2024-07-01'
AND created_at < '2024-10-01'
""")
# [{"revenue": 4712843.00}]

Sem embeddings. Sem vector DB. Sem alucinação. Dados reais, respostas reais.

Comparação de Código Lado a Lado

Pipeline RAG (o que equipes estão construindo)

# ingest.py — runs daily, takes 45 minutes for 1M rows
import pandas as pd
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings
from langchain_pinecone import PineconeVectorStore
df = pd.read_parquet("s3://company-lake/raw/postgres/orders/")
# Convert rows to text chunks
documents = []
for _, row in df.iterrows():
text = f"Order {row['id']}: {row['customer_name']} purchased "
text += f"${row['amount']:.2f} on {row['created_at']}"
documents.append(text)
# Split and embed
splitter = RecursiveCharacterTextSplitter(chunk_size=500)
chunks = splitter.create_documents(documents)
embeddings = OpenAIEmbeddings()
vectorstore = PineconeVectorStore.from_documents(chunks, embeddings, index_name="orders")
# query.py — at query time
retriever = vectorstore.as_retriever(search_kwargs={"k": 50})
docs = retriever.get_relevant_documents("What was Q3 revenue?")
# Gets 50 random-ish order records out of 250,000 Q3 orders

Pipeline SQL (o que você deveria construir)

# query.py — that's it, no ingest step needed
from dataspoc_lens import LensClient
client = LensClient()
result = client.query("SELECT SUM(amount) FROM orders WHERE quarter = 'Q3-2024'")
# Scans all 250,000 rows in 0.3 seconds, returns exact answer

A Comparação

DimensãoRAG em TabelasMCP + SQL
Precisão~60% (perde linhas, não agrega)99%+ (computação exata)
Velocidade3-8 segundos (embed query + recuperar + LLM)0,1-2 segundos (execução SQL)
AtualidadeDesatualizado até re-embed (horas/dias)Tempo real (consulta dados vivos)
Custo por consulta$0,02-0,10 (embedding + tokens LLM)$0,001 (apenas compute DuckDB)
Infra mensal$200+ (vector DB + API de embedding)$0 (DuckDB é grátis)
Auditabilidade”Baseado no contexto recuperado…”Consulta SQL exata, reproduzível
AgregaçõesImpossível (SUM de 20 linhas != SUM de 250K)Nativo (é pra isso que SQL serve)
JoinsNão dá para fazer join entre embeddingsJoins multi-tabela nativos
AtualizaçõesRe-embed dataset inteiroAutomático (lê o Parquet mais recente)

Quando RAG É a Escolha Certa

RAG é excelente para documentos não estruturados:

  • Relatórios em PDF, contratos legais, tickets de suporte
  • Busca em documentação
  • Bases de conhecimento com conteúdo em prosa
  • Qualquer dado onde busca por palavra-chave/semântica é o padrão de acesso

A regra é simples:

Tabelas e números = SQL. Documentos e prosa = RAG.

Uma Arquitetura Híbrida

Para a maioria das empresas, a melhor arquitetura combina ambos:

┌─────────────────────────────────────────────────┐
│ AI Agent │
├─────────────────────────────────────────────────┤
│ │
│ Structured data? ──► DataSpoc Lens (MCP + SQL) │
│ │
│ Unstructured docs? ──► RAG (vector search) │
│ │
└─────────────────────────────────────────────────┘
from dataspoc_lens import LensClient
from langchain_pinecone import PineconeVectorStore
lens = LensClient()
vectorstore = PineconeVectorStore(index_name="docs")
def route_question(question: str) -> str:
"""Route to SQL for data questions, RAG for document questions."""
data_keywords = ["revenue", "count", "average", "total", "growth",
"last month", "quarterly", "how many", "top 10"]
if any(kw in question.lower() for kw in data_keywords):
# SQL path — exact answers from structured data
result = lens.ask(question)
return result["answer"]
else:
# RAG path — semantic search over documents
docs = vectorstore.similarity_search(question, k=5)
return synthesize_from_docs(docs, question)

O Custo de Errar Isso

Um exemplo real de uma startup Series B:

MétricaSetup RAG delesApós mudar para SQL
Custo mensal$2.400 (Pinecone + embeddings OpenAI)$0 (DuckDB em Parquet)
Precisão das consultas58% (validado contra números reais)99,7%
Atualidade dos dadosAtraso de 6 horas (re-embedding diário)Tempo real
Manutenção2 engenheiros parcialmenteZero (self-service)
Tempo para responder4,2 segundos em média0,8 segundos em média

Eles estavam gastando $2.400/mês para obter respostas erradas lentamente.

Começando

Terminal window
# If your data is already in Parquet/S3:
pip install dataspoc-lens[mcp]
dataspoc-lens add-bucket s3://your-bucket
dataspoc-lens mcp # Start MCP server for AI agents
# If your data is in Postgres/MySQL:
pip install dataspoc-pipe
dataspoc-pipe init && dataspoc-pipe add postgres --tables orders
dataspoc-pipe run # Extracts to Parquet
# Then query with SQL
dataspoc-lens query "SELECT SUM(amount) FROM raw.postgres.orders WHERE created_at > '2024-01-01'"

Pare de gerar embeddings das suas tabelas. Comece a consultá-las.

Recomendados