Como servir embeddings em produção

Para servir embeddings em produção, você precisa de um endpoint HTTP que receba texto e retorne vetores de forma consistente. A opção mais simples é o Ollama, que serve qualquer modelo de embedding com um único pull e expõe uma API REST imediatamente. Para mais controle sobre o modelo e a lógica de pré-processamento, use sentence-transformers com FastAPI.

Servir embeddings com Ollama (mais simples)

O Ollama inclui suporte a modelos de embedding, basta baixar o modelo e chamar o endpoint /api/embeddings:

bash
# Baixar o modelo de embedding
ollama pull nomic-embed-text

# Gerar embedding via API
curl http://localhost:11434/api/embeddings \
  -H "Content-Type: application/json" \
  -d '{"model": "nomic-embed-text", "prompt": "Texto para vetorizar"}'

# Via endpoint compatível com OpenAI
curl http://localhost:11434/v1/embeddings \
  -H "Content-Type: application/json" \
  -d '{"model": "nomic-embed-text", "input": "Texto para vetorizar"}'

Modelos de embedding recomendados

Diferentes modelos têm trade-offs de qualidade, velocidade e dimensões do vetor:

Servir com sentence-transformers + FastAPI

Para mais controle, exponha um servidor FastAPI com sentence-transformers. Útil quando você precisa de pré-processamento customizado ou modelos não disponíveis no Ollama:

bash
# requirements.txt
sentence-transformers==3.x.x
fastapi
uvicorn

# server.py
from fastapi import FastAPI
from sentence_transformers import SentenceTransformer
from pydantic import BaseModel

app = FastAPI()
model = SentenceTransformer("nomic-ai/nomic-embed-text-v1", trust_remote_code=True)

class EmbedRequest(BaseModel):
    texts: list[str]

@app.post("/embed")
def embed(req: EmbedRequest):
    vectors = model.encode(req.texts, normalize_embeddings=True).tolist()
    return {"embeddings": vectors, "dimensions": len(vectors[0])}

Dockerfile e docker-compose para o servidor de embeddings

Para produção, containerize o servidor FastAPI:

bash
# Dockerfile
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY server.py .
CMD ["uvicorn", "server:app", "--host", "0.0.0.0", "--port", "8000"]

# docker-compose.yml
services:
  embeddings:
    build: .
    restart: always
    ports:
      - "8000:8000"
    volumes:
      - hf_cache:/root/.cache/huggingface

volumes:
  hf_cache:

Garantir consistência entre indexação e consulta

O maior erro em pipelines RAG é usar modelos diferentes para gerar os vetores do índice e os vetores da query. Defina o modelo como constante no código:

bash
# config.py — única fonte de verdade
EMBEDDING_MODEL = "nomic-embed-text"
EMBEDDING_DIMENSIONS = 768
VECTOR_DISTANCE = "Cosine"

# Use essa constante em todos os lugares:
# - ao criar a collection no banco vetorial
# - ao indexar documentos
# - ao vetorizar a query na busca

Não quer configurar manualmente?

Implante o Ollama em menos de 5 minutos com a Runstack. Infraestrutura da OPEN Datacenter, com servidores em São Paulo.

Perguntas frequentes

Com nomic-embed-text no Ollama em VPS Pro (2 vCPU), espere 50–200 embeddings/segundo para textos curtos (até 128 tokens). Para textos longos (512 tokens), o throughput cai para 10–50/segundo. GPU aumenta isso em 5–10×.

Para a maioria dos casos de RAG com volume moderado (até 1.000 requisições/minuto), CPU é suficiente para embeddings. GPU só é necessária para indexação em lote de documentos muito grandes (> 1 milhão de chunks) ou latência crítica.

Sim. text-embedding-3-small da OpenAI tem excelente qualidade e custo baixo para volumes moderados. A desvantagem é que os dados saem da sua infraestrutura, para dados sensíveis (LGPD), self-hosted é necessário.

Modelos de embedding têm limite de tokens (nomic-embed-text suporta até 8.192 tokens). Para textos maiores, divida em chunks antes de gerar o embedding, o pipeline de chunking deve acontecer antes da chamada ao servidor de embeddings, não dentro dele.

Ollama é mais simples de operar, zero código Python para expor a API. sentence-transformers dá mais controle (pré-processamento, múltiplos modelos, normalização custom). Para a maioria dos casos, Ollama é suficiente. sentence-transformers faz sentido quando você precisa de modelos específicos não disponíveis no Ollama ou lógica customizada.