🌐 Detecting your location…
📢 Advertisement — Configure AdSense in Appearance → Customize → AdSense Settings

Tutorial RAG 2026: Crie aplicativos de IA com LLMs e pesquisa vetorial

⏱️5 min read  ·  1,098 words

A Geração Aumentada de Recuperação (RAG) é o padrão dominante para a construção de aplicações de IA de produção em 2026. O RAG fundamenta as respostas LLM em dados reais, elimina alucinações sobre o conhecimento do domínio e mantém sua IA atualizada sem ajustes caros. Este guia cria um sistema RAG de produção do zero.

Por que RAG?

Os LLMs têm um limite de conhecimento e não podem acessar seus dados privados. RAG resolve ambos os problemas:

  • Sem alucinações sobre fatos– modele respostas de documentos recuperados, não de memória
  • Dados privados— indexe seus próprios PDFs, bancos de dados, wikis
  • Respostas atualizadas— atualize a base de conhecimento, não o modelo
  • Mais barato que o ajuste fino— sem custos de treinamento, atualizações instantâneas
  • Atribuição de fonte— citar quais documentos foram usados ​​para cada resposta

Arquitetura RAG

RAG Pipeline:

[Your Documents]
     ↓ chunk + embed
[Vector Database] (Pinecone, Qdrant, ChromaDB)
     ↑ semantic search
[User Query] → embed → search → [Top-K Chunks]
                                      ↓
                              [LLM Prompt]
                              "Using these documents: {chunks}
                               Answer: {query}"
                                      ↓
                              [Grounded Answer]

Configuração: Instalando Dependências

pip install anthropic chromadb sentence-transformers          pypdf langchain langchain-community          fastapi uvicorn python-dotenv

Etapa 1: ingestão de documentos

import anthropic
from pathlib import Path
from pypdf import PdfReader
import chromadb
from chromadb.utils import embedding_functions

# Initialize ChromaDB (local vector store)
client = chromadb.PersistentClient(path="./chroma_db")

# Use sentence-transformers for embeddings (free, local)
ef = embedding_functions.SentenceTransformerEmbeddingFunction(
    model_name="all-MiniLM-L6-v2"  # fast, good quality, 384 dims
)

collection = client.get_or_create_collection(
    name="documents",
    embedding_function=ef
)

def chunk_text(text: str, chunk_size: int = 500, overlap: int = 50) -> list[str]:
    # Split text into overlapping chunks.
    words = text.split()
    chunks = []
    for i in range(0, len(words), chunk_size - overlap):
        chunk = " ".join(words[i:i + chunk_size])
        chunks.append(chunk)
    return chunks

def ingest_pdf(pdf_path: str) -> int:
    # Ingest a PDF into the vector store.
    reader = PdfReader(pdf_path)
    all_chunks = []
    metadatas = []
    ids = []

    for page_num, page in enumerate(reader.pages):
        text = page.extract_text()
        if not text.strip():
            continue

        chunks = chunk_text(text)
        for i, chunk in enumerate(chunks):
            chunk_id = f"{Path(pdf_path).stem}_p{page_num}_c{i}"
            all_chunks.append(chunk)
            metadatas.append({
                "source": pdf_path,
                "page": page_num + 1,
                "chunk": i
            })
            ids.append(chunk_id)

    # Add to ChromaDB
    collection.add(documents=all_chunks, metadatas=metadatas, ids=ids)
    return len(all_chunks)

# Ingest documents
print(f"Ingested: {ingest_pdf('company_docs.pdf')} chunks")
print(f"Ingested: {ingest_pdf('product_manual.pdf')} chunks")

Etapa 2: recuperação

def retrieve(query: str, n_results: int = 5) -> list[dict]:
    # Retrieve relevant chunks for a query.
    results = collection.query(
        query_texts=[query],
        n_results=n_results,
        include=["documents", "metadatas", "distances"]
    )

    chunks = []
    for doc, meta, dist in zip(
        results["documents"][0],
        results["metadatas"][0],
        results["distances"][0]
    ):
        chunks.append({
            "text": doc,
            "source": meta["source"],
            "page": meta["page"],
            "similarity": 1 - dist  # convert distance to similarity
        })

    # Filter low-relevance chunks
    return [c for c in chunks if c["similarity"] > 0.3]

Passo 3: Geração com Claude

import anthropic

claude = anthropic.Anthropic()  # uses ANTHROPIC_API_KEY env var

def rag_query(question: str, n_results: int = 5) -> dict:
    # Answer a question using RAG.

    # Retrieve relevant context
    chunks = retrieve(question, n_results)

    if not chunks:
        return {
            "answer": "I couldn't find relevant information in the documents.",
            "sources": []
        }

    # Build context string
    context = "

---

".join([
        f"[Source: {c['source']}, Page {c['page']}]
{c['text']}"
        for c in chunks
    ])

    # Create prompt
    system = (
        "You are a helpful assistant that answers questions based ONLY on "
        "the provided documents. If the answer is not in the documents, say so clearly. "
        "Always cite the source document and page number for your answers."
    )

    user_message = (
        f"Documents:
{context}

"
        f"Question: {question}

"
        "Answer based only on the provided documents, citing sources."
    )

    # Call Claude
    response = claude.messages.create(
        model="claude-opus-4-5",
        max_tokens=1024,
        system=system,
        messages=[{"role": "user", "content": user_message}]
    )

    return {
        "answer": response.content[0].text,
        "sources": [{"source": c["source"], "page": c["page"]} for c in chunks],
        "tokens_used": response.usage.input_tokens + response.usage.output_tokens
    }

# Use it
result = rag_query("What is the return policy?")
print(result["answer"])
print("Sources:", result["sources"])

API RAG FastAPI

from fastapi import FastAPI, HTTPException
from pydantic import BaseModel

app = FastAPI(title="RAG API")

class QueryRequest(BaseModel):
    question: str
    n_results: int = 5

class QueryResponse(BaseModel):
    answer: str
    sources: list[dict]
    tokens_used: int

@app.post("/query", response_model=QueryResponse)
async def query(request: QueryRequest):
    if not request.question.strip():
        raise HTTPException(400, "Question cannot be empty")

    result = rag_query(request.question, request.n_results)
    return QueryResponse(**result)

@app.post("/ingest")
async def ingest_document(file_path: str):
    count = ingest_pdf(file_path)
    return {"status": "ok", "chunks_indexed": count}

# Run: uvicorn main:app --reload

Padrões RAG Avançados

Pesquisa híbrida (palavra-chave + semântica)

# Combine BM25 keyword search with vector search
from rank_bm25 import BM25Okapi

def hybrid_search(query: str, docs: list[str], alpha: float = 0.5) -> list[str]:
    # Semantic search scores
    semantic_results = collection.query(query_texts=[query], n_results=10)
    semantic_scores = {doc: score for doc, score in
                      zip(semantic_results["ids"][0], semantic_results["distances"][0])}

    # BM25 keyword scores
    tokenized = [doc.split() for doc in docs]
    bm25 = BM25Okapi(tokenized)
    bm25_scores = bm25.get_scores(query.split())

    # Combine (Reciprocal Rank Fusion)
    combined = alpha * (1 - semantic_scores.get(id, 1)) + (1-alpha) * bm25_score
    return sorted_by_combined_score

Reclassificação

# Use a cross-encoder to rerank retrieved chunks
from sentence_transformers import CrossEncoder

reranker = CrossEncoder("cross-encoder/ms-marco-MiniLM-L-6-v2")

def rerank(query: str, chunks: list[str], top_k: int = 3) -> list[str]:
    pairs = [(query, chunk) for chunk in chunks]
    scores = reranker.predict(pairs)
    ranked = sorted(zip(chunks, scores), key=lambda x: x[1], reverse=True)
    return [chunk for chunk, score in ranked[:top_k]]

Considerações de produção

  • Estratégia de fragmentação— experimente o tamanho do bloco (200-1000 tokens), sobreposição (10-20%)
  • Modelo de incorporação— OpenAI text-embedding-3-small para qualidade, modelos locais para custo
  • Banco de dados vetorial— ChromaDB/Qdrant para auto-hospedado, Pinecone para gerenciado
  • Cache— incorporações de cache e resultados de consultas frequentes
  • Avaliação— Estrutura RAGAS para métricas específicas de RAG (fidelidade, relevância)
  • Transmissão– transmita as respostas de Claude para uma melhor experiência do usuário

RAG é agora o padrão básico para IA empresarial em 2026. Comece com uma configuração simples de ChromaDB + Claude, meça a qualidade da resposta com RAGAS e, em seguida, otimize a fragmentação e a recuperação. A combinação de pesquisa vetorial e raciocínio LLM é incrivelmente poderosa para aplicações com uso intensivo de conhecimento.

✍️ Leave a Comment

Your email address will not be published. Required fields are marked *

🌐 Read in:🇬🇧 English🇩🇪 Deutsch🇧🇷 Português🇸🇦 العربية🇮🇳 हिन्दी🇧🇩 বাংলা