Inteligência artificial, sem ruído.
Tutoriais3 min

Como construir um banco de dados vetorial do zero em 10 passos (Python + NumPy)

Tutorial passo a passo para entender busca semântica por dentro: embeddings, similaridade por cosseno, filtros e escala — sem GPU e sem API.

Como construir um banco de dados vetorial do zero em 10 passos (Python + NumPy)

Por que entender um banco vetorial por dentro (e agora)

Em 2026, a busca semântica e o RAG (retrieval-augmented generation) deixaram de ser diferencial e viraram pré-requisito: assistentes, chatbots de atendimento e sistemas de busca corporativa dependem de encontrar documentos pelo significado, não por palavra-chave. Ferramentas gerenciadas (Pinecone, Weaviate, Qdrant, OpenSearch) resolvem isso, mas escondem o motor por trás da interface. Este tutorial mostra como construir um banco de dados vetorial do zero, em dez passos incrementais, usando apenas Python e NumPy — sem GPU, sem chave de API e com um modelo de embeddings que baixa na primeira execução.

O que você ganha (e o que não ganha)

✅ O que este tutorial entrega:

  • Entender como documentos viram vetores de tamanho fixo e como a busca funciona por direção, não por palavra.
  • Implementar similaridade por cosseno usando uma única multiplicação de matrizes.
  • Adicionar filtro por metadados, validação de entrada e persistência em disco.
  • Medir como a busca por força bruta escala e quando faz sentido migrar para índices aproximados (HNSW, IVF).
  • Código que roda localmente, sem depender de serviço externo.

⚠️ O que ele não é:

  • Não é um banco de produção: serve para aprendizado, não para milhões de documentos concorrentes.
  • Não cobre índices aproximados em detalhe — apenas mostra o ponto em que eles se tornam necessários.
  • O modelo de embeddings usado é o all-MiniLM-L6-v2 (384 dimensões), pequeno e em inglês; para português de alta qualidade você trocaria por um modelo multilíngue.

Requisitos

ComponenteMínimoRecomendado
Python3.9+3.11+
Dependênciasnumpy, sentence-transformersas mesmas + ambiente virtual
HardwareCPU comum8 GB de RAM (só por conforto)
Conhecimento prévioPython básicoNoções de álgebra linear (produto escalar)
Tempo estimado30 minutos1 hora com experimentação
Requisitos para acompanhar o tutorial em qualquer sistema (macOS, Windows ou Linux).

A ideia central em uma frase

Todo o banco se apoia em um único truque: se cada vetor tiver comprimento 1 (normalizado), o produto escalar entre dois vetores é exatamente a similaridade por cosseno. Com isso, ranquear um corpus inteiro vira uma única multiplicação de matrizes. Todo o resto — filtros, persistência, guardas de entrada — é “contabilidade” para manter documentos, metadados e vetores em sincronia, de modo que a multiplicação continue significativa.

Passo a passo

Passo 1 — Instalação e configuração

Instale as duas dependências e inicie o script com os imports e dois helpers de exibição:

pip install numpy sentence-transformers
import time
from pathlib import Path
import numpy as np
from corpus import DOCS, META
from vector_db import VectorDB

def header(title):
    print(f"\n{title}\n{'─' * len(title)}")

def show(results):
    for hit in results:
        print(f"  {hit.score:+.3f}  [{hit.meta['topic']:<7}]  {hit.text}")

Passo 2 — Construindo o índice

Criar o VectorDB carrega o modelo de embeddings; add() codifica cada documento em um vetor e armazena. O ponto crítico: o tamanho do índice não depende do tamanho do texto — cada documento, seja uma frase de seis palavras ou um ensaio de seis páginas, vira exatamente 384 números float32 (1.536 bytes). É isso que torna o índice previsível em tamanho e barato de varrer.

Passo 3 — A primeira busca

Com a query "what keeps a cell supplied with energy?", o topo da lista é "The mitochondria is the powerhouse of the cell" — o melhor hit compartilha apenas a palavra "cell" com a pergunta, e o segundo não compartilha nenhuma. Um índice por palavra-chave teria ranqueado de forma muito diferente, se é que encontraria algo.

Passo 4 — Buscar sem compartilhar uma palavra sequer

É o coração do exercício: as queries "why does my loaf taste sour" e "superheroes" não têm nenhuma palavra em comum com os documentos que retornam. A correspondência é por significado, não por termo literal.

Passo 5 — Lendo os scores

Uma busca vetorial sempre retorna k resultados, mesmo quando o corpus não tem nada relevante — ela simplesmente ranqueia o que existe. O score é o único sinal de qualidade: compare um +0.111 (irrelevante) com um +0.630 (relevante). Em produção você definiria um piso e retornaria nada abaixo dele.

Passo 6 e 7 — Filtro por metadados

Cada documento é adicionado com um dicionário {"topic": ...}. O argumento where mantém apenas documentos cujos metadados casam em todas as chaves. O filtro roda antes do ranqueamento: se você pedir k=5 e só houver 1 documento com aquele tópico, recebe 1 — nunca se preenche a lista com documentos que não casam só para atingir o k.

Passo 8 — Guardas de entrada

add() rejeita entradas malformadas com exceções claras: uma string solta em vez de lista, ou metadados que não batem um-para-um com os textos. Sem isso, uma string (que é iterável) seria desmembrada caractere a caractere, corrompendo o índice silenciosamente.

Passo 9 — Salvar e carregar

Vetores vão para .npy (compacto, sem parsing) e texto/metadados para .json (legível). O load() se recusa a abrir um índice criado por outro modelo — embeddings só fazem sentido em relação ao modelo que os produziu; misturá-los não seria "um pouco errado", seria um absurdo confiante.

Passo 10 — Como isso escala

Com um corpus sintético, o custo cresce linearmente: 1.000 documentos (1,5 MB) em ~0,01 ms, 100.000 (146,5 MB) em ~3,73 ms. Passado ~100 mil linhas, a ordenação começa a superar a varredura; em um milhão de documentos, o np.argpartition (top-k) fica em ~10 ms. Logo adiante está o ponto em que você adota um índice aproximado (HNSW, IVF) e troca um pouco de precisão por velocidade.

Tabela comparativa: força bruta vs. índices aproximados

AbordagemPrecisãoVelocidade (100k+ docs)ComplexidadeQuando usar
Força bruta (dot product)ExataLenta (linear)MínimaAté ~100 mil documentos
HNSWAproximada (~95%+)Muito rápidaMédiaMilhões de documentos
IVF (com PQ)AproximadaRápidaAltaFrotas gigantes, baixo custo de RAM
Quando cada estratégia de indexação faz sentido.

Casos de uso reais

  • Busca em base de conhecimento: encontrar a passagem certa em manuais e FAQs mesmo quando o usuário usa sinônimos.
  • RAG para atendimento: recuperar os trechos relevantes antes de o LLM gerar a resposta, reduzindo alucinação.
  • Deduplicação semântica: identificar documentos quase idênticos escritos com palavras diferentes.
  • Recomendação: sugerir artigos "semelhantes" por proximidade vetorial em vez de por tag manual.
  • Triagem automatizada: classificar tickets ou e-mails comparando-os com exemplos já rotulados.

Troubleshooting: erros comuns

  • ❌ ModuleNotFoundError: sentence_transformers → Causa: dependência não instalada. Solução: pip install numpy sentence-transformers dentro do ambiente virtual correto.
  • ❌ Busca retorna sempre os mesmos resultados irrelevantes → Causa: score baixo sem piso de corte. Solução: defina um limiar mínimo (ex.: 0,5) e descarte abaixo dele.
  • ❌ ValueError: got 3 texts but 1 metadata entries → Causa: listas de tamanhos diferentes. Solução: garanta um dicionário de metadados por documento.
  • ❌ Índice "confiante" mas errado após trocar de modelo → Causa: embeddings de modelos diferentes misturados. Solução: reindexe tudo ao mudar o modelo; o load() deve recusar modelos distintos.
  • ❌ Primeira consulta muito mais lenta que as seguintes → Causa: warm-up da thread pool do NumPy. Solução: descarte a primeira medição; é artefato de benchmark, não do seu código.

FAQ

  • Preciso de GPU? Não. O modelo all-MiniLM-L6-v2 é pequeno e roda em CPU.
  • Posso usar esse banco em produção? Para aprendizado sim; em produção, use uma solução gerenciada ou um índice aproximado com persistência e concorrência adequadas.
  • Por que normalizar os vetores? Para que o produto escalar vire similaridade por cosseno e todo o ranqueamento vire uma multiplicação de matrizes.
  • O que muda entre 25 e 25 milhões de documentos? O design não muda; só a estrutura de índice por baixo.
  • Funciona em português? O modelo padrão é treinado majoritariamente em inglês; para melhor qualidade em português, troque por um modelo multilíngue (ex.: paraphrase-multilingual-MiniLM).

Onde isso leva

A tendência é clara: com RAG virando padrão em produtos e agentes, entender a mecânica interna da busca semântica deixa de ser curiosidade acadêmica e vira vantagem prática — na hora de escolher uma ferramenta, dimensionar um índice ou debugar resultados estranhos. Em 2027, a fronteira deve se mover ainda mais para a combinação de busca semântica com re-ranking por modelos grandes, mas a fundação — normalizar, multiplicar, ranquear — permanece a mesma.



Descubra mais sobre noticiAI

Assine para receber nossas notícias mais recentes por e-mail.

R
Sobre o autorRedação Noticiai

Equipe editorial dedicada a explicar inteligência artificial com clareza, independência e contexto.