Curso
A busca por palavras-chave funciona bem para consultas simples, mas rapidamente falha quando o que importa é o significado e não a redação exata. Se um documento menciona “aumentar o tempo de resposta” e outro fala em “melhorar a performance”, um mecanismo de busca tradicional os trata como não relacionados — apesar de abordarem a mesma ideia. Perdemos as relações semânticas só porque a formulação é diferente.
A busca vetorial resolve essa limitação ao representar textos e imagens como embeddings: vetores numéricos gerados por modelos de machine learning que capturam contexto, intenção e similaridade semântica. Depois que armazenamos esses embeddings no MongoDB, podemos consultá-los para encontrar itens genuinamente relacionados, independentemente dos termos usados.
Neste tutorial, vamos construir um exemplo prático de ponta a ponta desse fluxo usando FastAPI e MongoDB Atlas Vector Search. Vamos gerar embeddings de texto com SentenceTransformers e embeddings de imagem com um modelo baseado em CLIP, armazenar ambos no MongoDB e executar buscas por similaridade. No fim, você terá uma base funcional que pode sustentar busca semântica de artigos, recomendações de produtos, ferramentas de similaridade visual e muitas outras aplicações reais.
Dito isso, vamos começar.
Pré-requisitos
Antes de construir, precisamos de alguns itens. Este tutorial pressupõe familiaridade básica com Python, FastAPI e MongoDB, mas não vamos depender de nada avançado — tudo que usarmos será apresentado ao longo do caminho.
Veja o que vamos precisar:
- Python 3.10+
- Um cluster do MongoDB Atlas com Vector Search habilitado
- Ambiente FastAPI
- Postman ou cURL
- Acesso à internet para baixar e usar os modelos de embedding
Bibliotecas Python que usaremos
Vamos instalá-las na etapa de setup:
- FastAPI: nosso framework web
- uvicorn: servidor de desenvolvimento
- pymongo: driver do MongoDB
- sentence-transformers: modelo de embedding de texto
- open_clip: modelo de embedding de imagem baseado em CLIP
- torch: requerido por CLIP e SentenceTransformers
- Pillow: suporte a processamento de imagem (PIL)
Essas ferramentas nos dão tudo o que precisamos para gerar embeddings, armazená-los no MongoDB e consultá-los de forma eficiente com busca vetorial.
Entendendo a busca vetorial
Antes de escrever código, ajuda entender o que acontece por trás do fluxo. Vector search não é complicado quando o dividimos em partes, e conhecer os fundamentos facilita o restante do tutorial.
O que é um embedding?
Um embedding é simplesmente uma lista de números (um vetor) que representa o significado de um dado.
Para texto, o embedding captura aspectos como contexto e intenção. Para imagens, ele captura padrões e características visuais.
Um exemplo bem simplificado de um embedding seria assim:
[0.21, -0.17, 0.89, ...]
Duas coisas semanticamente semelhantes — por exemplo, duas frases que descrevem a mesma ideia ou duas imagens com objetos parecidos — acabam com vetores matematicamente próximos. A busca vetorial mede essa proximidade.
Como o MongoDB usa embeddings
O MongoDB em si não gera embeddings. Em vez disso, ele foca no que faz melhor:
- Armazenar vetores de embedding
- Indexá-los usando um índice vetorial
- Recuperar vizinhos mais próximos com eficiência
Em outras palavras, nossos modelos geram os embeddings e o MongoDB cuida da busca.
Essa separação clara torna o sistema flexível e escalável.
Tipos de modelos de embedding
Podemos escolher qualquer modelo de embedding, desde que ele gere um vetor numérico. Modelos diferentes se especializam em tipos diferentes de dados:
|
Tipo de dado |
Modelo de exemplo |
Observações |
|
Texto |
|
Leve e amplamente usado em PLN |
|
Imagem |
CLIP/OpenCLIP |
Converte imagens em vetores semânticos |
|
Multimodal |
Multimodal |
Lida com texto e imagem juntos |
Esse é o fluxo que vamos implementar com FastAPI e MongoDB Atlas.
Depois de configurado, você poderá buscar textos e imagens por significado — algo que a busca por palavra-chave não faz.
Configurando o MongoDB Atlas
Antes de construir a aplicação FastAPI, precisamos de um lugar para armazenar e consultar nossos embeddings. MongoDB Atlas torna isso simples, e criar índices vetoriais leva só alguns minutos.
1. Crie um cluster
Se você ainda não tem, crie um novo cluster no MongoDB Atlas. O nível gratuito (M0) é perfeito para este tutorial. Consulte a documentação do MongoDB para mais detalhes sobre como criar um cluster.
2. Crie um banco e coleções
Dentro do cluster:
- Crie um banco de dados. Vamos chamá-lo de
vector_db. Para mais detalhes, consulte a documentação. - Crie duas coleções nele:
texts: onde vamos armazenar documentos de texto e seus embeddingsimages: onde vamos armazenar nomes de arquivos de imagem e embeddings de imagem
Veja a documentação para mais detalhes sobre como criar uma coleção.
Mantivemos as coleções de imagem e de texto separadas porque elas usam modelos e dimensões de vetores diferentes.
3. Crie índices vetoriais
O MongoDB precisa de um índice vetorial para rodar buscas por similaridade rápidas. Vamos criar um índice para embeddings de texto e outro para embeddings de imagem.
Você pode criá-los em Atlas → Collections → Search Indexes. Vamos começar configurando a coleção de texto.
A imagem a seguir explica melhor o processo.

Na próxima tela, clique em Vector Search -> JSON Editor -> Next. A imagem a seguir deve ajudar a entender melhor o processo


Índice vetorial para textos
No índice de busca vetorial, ajuste o JSON para corresponder ao nosso código.
Estamos usando o modelo all-MiniLM-L6-v2 para texto, que produz embeddings de 384 dimensões.
Veja a definição JSON:
{
"fields": [
{
"type": "vector",
"path": "embedding",
"numDimensions": 384,
"similarity": "cosine"
}
]
}
Dê a este índice o nome: vector_texts_search e salve.
Isso permite que o MongoDB execute buscas por similaridade por cosseno entre embeddings de texto. A imagem abaixo ilustra o processo em mais detalhes.
Índice vetorial para imagens
Para imagens, usamos um modelo baseado em CLIP que gera vetores de 512 dimensões, então o índice de imagens precisa refletir essa dimensão. A definição JSON deve corresponder ao código abaixo. Fora isso, o restante do fluxo é o mesmo.
{
"fields": [
{
"type": "vector",
"path": "embedding",
"numDimensions": 512,
"similarity": "cosine"
}
]
}
Nomeie esse índice: vector_images_search.
Quando ambos os índices estiverem ativos, o MongoDB Atlas estará pronto para consultas de busca vetorial.
Configurando o projeto FastAPI
Com o cluster do MongoDB Atlas e os índices vetoriais prontos, podemos configurar a aplicação FastAPI que vai gerar embeddings e armazená-los no MongoDB. Vamos manter a estrutura do projeto pequena e focada, já que o objetivo aqui é entender o fluxo, não construir um serviço de produção completo.
1. Instale as dependências necessárias
Vamos instalar FastAPI, Uvicorn, o driver Python do MongoDB e as bibliotecas para gerar embeddings de texto e imagem. Rode:
pip install fastapi uvicorn pymongo python-dotenv sentence-transformers torch pillow open_clip_torch python-multipart
Resumo rápido do porquê de cada uma:
- fastapi — nosso framework de API
- uvicorn — servidor ASGI para rodar FastAPI
- pymongo — driver do MongoDB
- python-dotenv — carrega variáveis de ambiente do
.env - sentence-transformers — modelo de embedding de texto
- torch — requerido pelos modelos de embedding
- pillow — manipulação básica de imagens
- open_clip_torch — modelo de embedding de imagem baseado em CLIP
- python-multipart — necessário para upload de arquivos no FastAPI
Instalado isso, podemos estruturar o projeto.
2. Estrutura do projeto
Vamos manter tudo em um único arquivo Python, com a configuração via variáveis de ambiente:
fastapi-mongodb-vector-search/
│── main.py
│── .env
│── venv/
main.py vai conter toda a aplicação: os endpoints de texto, de imagem e a conexão com o MongoDB.
MONGODB_URI="your connection string"
DB_NAME="vector_db"
TEXT_COLLECTION="texts"
IMAGE_COLLECTION="images"
- MONGODB_URI — a URI do Atlas do seu cluster
- DB_NAME — o banco que criamos
- TEXT_COLLECTION — onde texto + embeddings serão armazenados
- IMAGE_COLLECTION — onde os embeddings de imagem serão salvos
O FastAPI vai carregar isso na inicialização, mantendo a configuração separada do código.
Implementando a busca em textos
Com o projeto FastAPI pronto, começamos pela busca em textos. Esta parte introduz a ideia central da busca vetorial: geramos embeddings para o texto, armazenamos no MongoDB e consultamos usando o índice vetorial criado antes.
Vamos dividir em duas etapas pequenas para facilitar os testes e depuração.
a. Adicionar texto e gerar embeddings
O primeiro endpoint vai receber um título e um conteúdo de texto. Vamos combinar ambos em uma única string, gerar um embedding com SentenceTransformers e salvar o documento junto com o vetor de embedding.
Veja como fica no código:
# main.py (trecho)
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from pymongo import MongoClient
from sentence_transformers import SentenceTransformer
from dotenv import load_dotenv
import os
load_dotenv()
app = FastAPI()
# --- Configuração do BD ---
client = MongoClient(os.getenv("MONGODB_URI"))
db = client[os.getenv("DB_NAME")]
texts_coll = db[os.getenv("TEXT_COLLECTION")]
Images_coll = db[os.getenv("IMAGE_COLLECTION")]
# --- modelo de embedding de texto ---
MODEL_NAME = "all-MiniLM-L6-v2"
text_model = SentenceTransformer(MODEL_NAME)
# --- schema da requisição ---
class AddTextRequest(BaseModel):
title: str
content: str
@app.post("/add-text")
def add_text(payload: AddTextRequest):
try:
# Combina os campos para gerar embeddings mais ricos
text = f"{payload.title}\n\n{payload.content}"
# Gera o embedding de texto
embedding = text_model.encode(text).tolist()
# Armazena o documento no MongoDB
doc = {
"title": payload.title,
"content": payload.content,
"embedding": embedding,
"model": MODEL_NAME,
}
result = texts_coll.insert_one(doc)
return {
"inserted_id": str(result.inserted_id),
"title": payload.title
}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
Como testar este endpoint
cURL, Postman e outros podem ser usados para testar. Neste tutorial, vamos usar o Postman.
Também precisamos expor nosso endpoint FastAPI na internet para testar o Postman na web. Vamos usar o Ngrok para isso. Se você é novo no Ngrok, visite a documentação. Para testar, primeiro rode o FastAPI com o comando abaixo:
uvicorn main:app --reload
Depois, crie um túnel do seu localhost para a internet usando o comando do Ngrok abaixo:
Ngrok http 8000
Isso vai gerar uma URL para testes, como na imagem abaixo:

Envie uma requisição POST com o Postman para o endpoint add-text. A imagem abaixo ilustra o envio e a resposta esperada.

Resposta esperada:
{
"inserted_id": "65f...",
"title": "Why FastAPI is great."
}
Isso confirma que seu modelo de embedding carregou corretamente e que o MongoDB armazenou o vetor. Se isso funcionou, seu pipeline de texto está operacional.
b. Buscar texto com busca vetorial
Agora que podemos adicionar texto, o próximo passo é buscá-lo usando o estágio $vectorSearch do MongoDB. Aqui, vamos:
- Converter a consulta do usuário em um embedding.
- Pedir ao MongoDB para recuperar os vetores mais próximos.
- Retornar os documentos mais similares.
Adicione o endpoint abaixo ao main.py:
class SearchTextRequest(BaseModel):
query: str
limit: int = 5
@app.post("/search-text")
def search_text(payload: SearchTextRequest):
try:
# Converte a consulta em embedding
query_vector = text_model.encode(payload.query).tolist()
# Pipeline de busca vetorial
pipeline = [
{
"$vectorSearch": {
"queryVector": query_vector,
"path": "embedding",
"numCandidates": 100,
"limit": payload.limit,
"index": "vector_texts_search" # o nome DEVE ser exatamente o mesmo do índice no Atlas
}
},
{
"$project": {
"title": 1,
"content": 1,
"score": {"$meta": "vectorSearchScore"},
"_id": 0
}
}
]
results = list(texts_coll.aggregate(pipeline))
return {"results": results}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
> O parâmetro numCandidates determina quantos potenciais matches são avaliados antes de retornar os melhores resultados.
> Valores maiores melhoram a precisão, mas aumentam o tempo da consulta. Uma boa regra prática é 10–20x o seu limit.
Como testar a busca em texto
Depois de adicionar um ou mais documentos, faça uma requisição POST ao endpoint /search-text pelo Postman usando o link gerado pelo Ngrok, como na imagem abaixo:

Saída esperada:
{
"results": [
{
"title": "Why FastAPI is great",
"content": "FastAPI makes building APIs simple and fast.",
"score": 0.85
}
]
}
Se você vir um resultado com pontuação de similaridade, sua pipeline de busca vetorial em texto está funcionando de ponta a ponta. Com essa parte estável, podemos partir para o fluxo de imagens.
Implementando a busca em imagens
Com o fluxo de texto rodando bem, vamos estender a ideia para imagens. O fluxo geral é similar — a grande diferença é como geramos os embeddings. Em vez de SentenceTransformers, usaremos um modelo baseado em CLIP, feito para converter imagens em vetores de 512 dimensões.
Assim como antes, vamos implementar passo a passo para facilitar testes e depuração.
a. Adicionar imagem e gerar embeddings
Este endpoint aceita um arquivo de imagem, converte para um tensor que o CLIP entende, gera um embedding de 512 dimensões e o armazena no MongoDB.
Veja como fica:
# --- configuração do modelo CLIP ---
import torch
import open_clip
from PIL import Image
import io
from fastapi import UploadFile, File
# Carrega o modelo CLIP uma vez na inicialização
clip_model, _, preprocess = open_clip.create_model_and_transforms(
"ViT-B-32",
pretrained="openai"
)
device = "cuda" if torch.cuda.is_available() else "cpu"
clip_model.to(device)
@app.post("/add-image")
async def add_image(file: UploadFile = File(...)):
"""
Faça upload de uma imagem, gere o embedding com CLIP e armazene no MongoDB.
"""
try:
# Lê o conteúdo do arquivo
image_bytes = await file.read()
image = Image.open(io.BytesIO(image_bytes)).convert("RGB")
# Pré-processa para o CLIP e codifica
image_tensor = preprocess(image).unsqueeze(0).to(device)
with torch.no_grad():
image_embedding = clip_model.encode_image(image_tensor)
# Normaliza o embedding (importante para similaridade por cosseno)
image_embedding /= image_embedding.norm(dim=-1, keepdim=True)
vector = image_embedding.cpu().numpy().flatten().tolist()
# Armazena no MongoDB
doc = {
"filename": file.filename,
"embedding": vector
}
result = images_coll.insert_one(doc)
return {
"inserted_id": str(result.inserted_id),
"filename": file.filename
}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
Por que a normalização importa
Embeddings do CLIP funcionam melhor com similaridade por cosseno. Para deixar as comparações mais precisas e estáveis, nós normalizamos os vetores:
image_embedding /= image_embedding.norm(dim=-1, keepdim=True)
Isso garante que a distância por cosseno reflita a similaridade real.
Teste o endpoint de upload de imagem
Para testar no Postman, faça um POST para o endpoint /add-image. Envie um form-data no corpo da requisição com qualquer imagem .jpg ou .png. A imagem abaixo demonstra o processo.

Se tudo estiver certo, você verá algo como:
{
"inserted_id": "692454f6156fbb762ed865a0",
"filename": "file-name.png"
}
Neste ponto, seus embeddings de imagem estão armazenados com sucesso no MongoDB.
b. Buscar imagens com busca vetorial
Agora, vamos construir o endpoint que recebe uma imagem de consulta, converte em embedding e encontra as imagens armazenadas mais próximas usando nosso índice vetorial de imagens.
Adicione isto ao main.py:
@app.post("/search-image")
async def search_image(file: UploadFile = File(...)):
"""
Faça upload de uma imagem e encontre as mais visualmente similares no MongoDB.
"""
try:
# Lê o arquivo enviado
image_bytes = await file.read()
image = Image.open(io.BytesIO(image_bytes)).convert("RGB")
# Gera o embedding para a imagem de consulta
image_tensor = preprocess(image).unsqueeze(0).to(device)
with torch.no_grad():
query_embedding = clip_model.encode_image(image_tensor)
query_embedding /= query_embedding.norm(dim=-1, keepdim=True)
query_vector = query_embedding.cpu().numpy().flatten().tolist()
# Pipeline de busca vetorial
pipeline = [
{
"$vectorSearch": {
"queryVector": query_vector,
"path": "embedding",
"numCandidates": 100,
"limit": 5,
"index": "vector_images_search"
}
},
{
"$project": {
"filename": 1,
"score": {"$meta": "vectorSearchScore"},
"_id": 0
}
}
]
results = list(images_coll.aggregate(pipeline))
return {"results": results}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
Teste o endpoint de busca por imagem
Para testar no Postman, faça um POST para o endpoint /search-image. Envie o form-data no corpo da requisição com alguma imagem similar às que você armazenou antes. A imagem abaixo demonstra o processo.

A saída esperada deve ser assim:
{
"results": [
{
"filename": "image1.png",
"score": 1.0
},
{
"filename": "image2.png",
"score": 0.81
}
]
}
Uma pontuação de 1,0 significa que corresponde à mesma imagem. Qualquer valor próximo (0,7–0,9) indica forte similaridade visual.
Se você vir um array com resultados, sua busca vetorial por imagens está funcionando.
Neste ponto, se tudo funcionou, temos um sistema de busca semântica completo para texto e imagem.
Conclusão
Neste tutorial, construímos um workflow completo de busca semântica com FastAPI e MongoDB Atlas. Geramos embeddings de texto com SentenceTransformers, criamos embeddings de imagem com um modelo CLIP, armazenamos tudo no MongoDB e consultamos usando o Vector Search do MongoDB e o Postman. Esse padrão é a base para aplicativos como busca semântica de documentos, ferramentas de similaridade de imagens, sistemas de recomendação e recursos de IA multimodal.
O que torna essa arquitetura poderosa é a flexibilidade: o MongoDB não prende você a modelos específicos. Desde que os embeddings sejam vetores numéricos e as dimensões batam com a configuração do índice, você pode plugar qualquer modelo de texto, imagem ou multimodal que seu caso de uso exigir.
Com essa base, você pode expandir o projeto — armazenar metadados, criar lógica de ranqueamento ou integrar com apps para usuários finais. E o mesmo fluxo escala facilmente com os serviços gerenciados do MongoDB Atlas.
Você encontra o projeto completo em funcionamento no GitHub.

Engenheiro de Software


