Objetivos de aprendizado
Depois de ler este artigo, você vai conseguir:
- Entender o que são embeddings e distância entre vetores
- Ver como a busca aproximada (HNSW e IVFFlat) ganha velocidade
- Decidir entre um banco vetorial dedicado e uma extensão vetorial num banco comum
Um banco de dados tradicional responde bem a perguntas exatas: "o usuário com id 42", "pedidos com valor acima de 100". Ele se sai mal em "me mostre os textos que falam de algo parecido com isto", porque semelhança de significado não é igualdade de palavras. "Subir meu código no servidor" e "fazer deploy" não têm nenhum termo em comum, mas dizem a mesma coisa.
O banco de dados vetorial resolve esse problema. Ele está por trás da busca semântica, das recomendações e de boa parte dos sistemas de RAG, que dão contexto a um LLM antes de ele responder.
O que é um embedding?#
Um embedding é uma lista de números (um vetor) que um modelo gera para representar o significado de um conteúdo. O mesmo modelo transforma "como hospedar um bot" e "colocar meu bot no ar" em vetores próximos, e "receita de bolo" em um vetor distante.
Cada posição do vetor é uma dimensão. Modelos reais usam centenas ou milhares delas. Você não interpreta os números individualmente: o que importa é a distância entre dois vetores.
Como o banco mede "parecido"?#
Pela distância entre vetores. As três medidas mais comuns:
- Distância cosseno: compara a direção dos vetores e ignora o tamanho. É a mais usada para texto.
- Produto interno: mede alinhamento e magnitude juntos. Equivale ao cosseno quando os vetores são normalizados.
- Distância euclidiana (L2): a distância em linha reta entre os dois pontos.
O exemplo abaixo faz a busca inteira em memória, com três "documentos" de 3 dimensões inventados (o vetor de consulta representa "subir meu código no servidor"):
import numpy as np
docs = {
"Como hospedar um bot": [0.9, 0.1, 0.0],
"Receita de bolo": [0.0, 0.1, 0.9],
"Deploy de API em Python": [0.8, 0.3, 0.1],
}
consulta = np.array([0.85, 0.2, 0.05])
def cosseno(a, b):
a, b = np.array(a), np.array(b)
return a @ b / (np.linalg.norm(a) * np.linalg.norm(b))
ranking = sorted(docs, key=lambda t: cosseno(docs[t], consulta), reverse=True)
for t in ranking:
print(f"{cosseno(docs[t], consulta):.3f} {t}")A saída é 0.991 Como hospedar um bot, 0.990 Deploy de API em Python e 0.082 Receita de bolo. Na prática, os vetores vêm de um modelo de embeddings, não de números escritos à mão. Chamar esse modelo por API também custa dinheiro, e a conta segue a lógica de quanto custa uma API de LLM.
Por que não comparar com todos os vetores?#
Comparar a consulta com todos os registros (busca exata, ou força bruta) dá o resultado perfeito, mas o custo cresce com o tamanho da base. Com poucos milhares de vetores, isso é aceitável. Com milhões, fica lento.
Por isso bancos vetoriais usam índices de vizinho mais próximo aproximado (ANN). Eles trocam um pouco de precisão (o recall) por muita velocidade. Dois índices aparecem em quase toda ferramenta:
- HNSW: monta um grafo em várias camadas. Oferece o melhor equilíbrio entre velocidade e recall, mas demora mais para ser construído e usa mais memória. Pode ser criado numa tabela vazia.
- IVFFlat: divide os vetores em listas (grupos) e busca só em algumas delas. É construído mais rápido e usa menos memória, mas as consultas têm desempenho pior. Deve ser criado depois que a tabela já tem dados.
Nos dois casos o resultado é aproximado: o índice pode, raramente, deixar de fora um vizinho que a busca exata traria.
Banco vetorial dedicado ou extensão num banco comum?#
Existem dois caminhos:
- Extensão num banco que você mesmo administra. Alguns bancos relacionais, como o PostgreSQL, aceitam extensões que adicionam um tipo de vetor e operadores de distância. Seus vetores ficam na mesma tabela e na mesma transação que o resto dos dados, e você filtra com SQL normal.
- Banco vetorial dedicado. Serviços e servidores feitos só para vetores, com recursos de escala e filtragem próprios. Fazem sentido quando o volume ou a latência passam do que uma extensão entrega.
Para a maioria dos projetos pequenos e médios, começar com a extensão evita operar mais um sistema. Migrar depois é possível, porque o dado é só uma lista de números mais um id.
Como é uma busca vetorial na prática?#
O fluxo é parecido em quase toda ferramenta:
- Gere o embedding de cada documento e grave o vetor junto com um id e os metadados.
- Crie o índice ANN com a mesma medida de distância que as consultas vão usar. Um índice feito para cosseno não acelera uma busca por L2.
- Na consulta, gere o embedding da pergunta com o mesmo modelo e peça os k vizinhos mais próximos, por exemplo os 5 primeiros.
- Ajuste o equilíbrio entre recall e velocidade no parâmetro de busca do índice: no HNSW, quantos candidatos o grafo examina; no IVFFlat, quantas listas são visitadas.
Use sempre o mesmo modelo para documentos e consultas. Vetores de modelos diferentes não são comparáveis, mesmo quando têm o mesmo número de dimensões.
Quando não usar um banco vetorial?#
- A busca é por palavra exata, código ou id. Um índice comum ou busca textual resolve melhor.
- A base é pequena. Poucos milhares de vetores cabem em memória e a força bruta basta.
- Você precisa de resultado exato sempre. Índices ANN são aproximados.
A qualidade da busca, aliás, depende do modelo de embeddings, não do banco. Um modelo ruim para o seu idioma ou domínio entrega vizinhos ruins, por mais rápido que seja o índice.
Como fazer isso na Square Cloud#
A Square Cloud não oferece banco vetorial, e o PostgreSQL hospedado é o nativo, sem extensões: nada de pgvector. Os vetores ficam num serviço vetorial externo ou, se a base for pequena, na memória da própria aplicação, com a busca por força bruta do exemplo acima. O que você hospeda é a aplicação que faz o fluxo: uma API que recebe a pergunta, gera o embedding, consulta os vetores e chama o LLM. Ela roda num container isolado e sempre ligado e, como aplicação web, usa no mínimo 512 MB do pool de RAM do plano. Veja como publicar em Hospedagem de APIs.
Cada peça tem o seu lugar:
- Embeddings: um provedor externo. O runtime Python tem wheels prontas de torch e transformers, mas um modelo local de embeddings não cabe no Hobby de 1 GB.
- Documentos e metadados: a hospedagem de bancos começa no Standard, a partir de R$ 44,99 por mês. O Hobby, a partir de R$ 12,49, não inclui banco.
- Resposta do LLM: a partir do Standard, o AI Gateway gera o texto final por uma API compatível com a da OpenAI, com o modelo
cubic. Ele não gera embeddings nem guarda vetores: entra só no último passo, com os trechos recuperados no prompt. Está em beta, com acesso antecipado sem custo extra, e os limites podem mudar quando o beta acabar.
IA
Hospede apps com IA em containers sempre ligados
Perguntas frequentes
Qual a diferença entre banco vetorial e banco SQL?
O banco SQL compara valores exatos (igual, maior, contém). O vetorial ordena os registros pela distância entre vetores, o que responde 'o que é mais parecido com isto'. Alguns bancos SQL fazem as duas coisas com uma extensão vetorial.
Preciso de banco vetorial para usar RAG?
Não é obrigatório. Com poucos milhares de trechos, uma busca por força bruta em memória resolve. O banco vetorial dedicado compensa quando o volume e a latência pedem índice.
O que é um embedding?
É uma lista de números que um modelo gera para representar o significado de um texto, imagem ou áudio. Conteúdos parecidos geram vetores próximos.
Mais em IA
Ver todos os 13 artigos- Quanto custa criar e manter um chatbot com IAManter um chatbot com IA custa tokens do modelo, hospedagem, banco e, com RAG, contexto extra por mensagem. Veja como fazer a conta por conversa.
- Quanto custa um sistema RAG: embeddings, banco e LLMUm RAG tem três custos: embeddings, banco vetorial e LLM. Veja a conta para mil perguntas por dia, com preços hipotéticos, e onde a fatura cresce.
- Como criar um bot do Discord com IACrie um bot do Discord com IA em discord.js: comando /ask, chave de API em variável de ambiente, memória por canal, limites do Discord e como colocar no ar.
- Como hospedar um chatbot com IAUm chatbot com IA é uma API que recebe mensagens, chama um modelo de linguagem e devolve a resposta em streaming. Veja o código e como colocá-lo no ar.