← Voltar ao hub MariaDB
MariaDB 27 de outubro de 2026 12 min de leitura

RAG no MariaDB na prática: monte um recomendador de produtos em 30 minutos

Passo a passo reproduzível: tabela com VECTOR, índice HNSW, carga de embeddings, busca por similaridade e filtros. Por que a busca é aproximada e por que entende qualquer idioma.

RAG no MariaDB na prática: monte um recomendador de produtos em 30 minutos

Chega de teoria (se pulou, ela está em MariaDB e IA: tokens, embeddings e vetores). Hoje é teclado. Vamos montar a parte de recuperação de um RAG de recomendação de produtos: o cliente escreve o que quer, do jeito dele, e o MariaDB devolve os produtos mais parecidos. É exatamente esse resultado que você entrega ao LLM depois.

Notebook com SQL ao lado de prateleira com tênis, fone e mochila ligados por linhas de luz

O que você precisa

  • MariaDB 11.8 ou mais recente (vetores chegaram na 11.7; a 11.8 é a primeira LTS com eles).
  • Python 3 com mariadb e sentence-transformers.
  • Um modelo de embedding multilíngue. Uso aqui o paraphrase-multilingual-MiniLM-L12-v2, que gera 384 dimensões e roda na CPU.
docker run -d --name mdb -e MARIADB_ROOT_PASSWORD=senha -p 3306:3306 mariadb:11.8
pip install mariadb sentence-transformers

Passo 1: a tabela

CREATE DATABASE loja;
USE loja;

CREATE TABLE produtos (
  id         INT PRIMARY KEY AUTO_INCREMENT,
  nome       VARCHAR(200) NOT NULL,
  descricao  TEXT NOT NULL,
  categoria  VARCHAR(60),
  preco      DECIMAL(10,2),
  embedding  VECTOR(384) NOT NULL,
  VECTOR INDEX idx_emb (embedding) M=16 DISTANCE=cosine
);

Três detalhes que importam:

  • VECTOR(384) precisa ter exatamente a dimensão do seu modelo. Trocou de modelo, recalcula tudo.
  • A coluna indexada precisa ser NOT NULL, e só cabe um índice vetorial por tabela.
  • DISTANCE=cosine tem que bater com a função que você usa na consulta, senão o índice não é usado.

Passo 2: o que vira embedding

Erro clássico: gerar embedding só do nome. "Air Max 90" não diz nada ao modelo. Junte o que descreve o produto:

def texto_para_embedding(p):
    return f"{p['nome']}. {p['categoria']}. {p['descricao']}"

Passo 3: carregar os produtos

import json, mariadb
from sentence_transformers import SentenceTransformer

modelo = SentenceTransformer("paraphrase-multilingual-MiniLM-L12-v2")
con = mariadb.connect(host="127.0.0.1", user="root", password="senha", database="loja")
cur = con.cursor()

produtos = [
  {"nome":"Tênis de corrida leve","categoria":"Calçados","preco":399.90,
   "descricao":"Amortecimento macio, ideal para longas distâncias no asfalto."},
  {"nome":"Fone Bluetooth com cancelamento de ruído","categoria":"Áudio","preco":899.00,
   "descricao":"Bateria de 30 horas, ótimo para avião e escritório barulhento."},
  {"nome":"Mochila impermeável 25L","categoria":"Acessórios","preco":249.90,
   "descricao":"Compartimento para notebook de 15 polegadas, resiste à chuva."},
  {"nome":"Garrafa térmica 1L","categoria":"Acessórios","preco":129.90,
   "descricao":"Mantém café quente por 12 horas e água gelada por 24."},
]

for p in produtos:
    vetor = modelo.encode(texto_para_embedding(p), normalize_embeddings=True)
    cur.execute(
      "INSERT INTO produtos (nome, descricao, categoria, preco, embedding) "
      "VALUES (?, ?, ?, ?, VEC_FromText(?))",
      (p["nome"], p["descricao"], p["categoria"], p["preco"], json.dumps(vetor.tolist())))
con.commit()

VEC_FromText() converte o texto [0.1, 0.2, ...] no formato binário da coluna. O inverso é VEC_ToText(), útil para depurar.

Passo 4: recuperar (o "R" do RAG)

def recomendar(pergunta, k=3):
    v = modelo.encode(pergunta, normalize_embeddings=True)
    cur.execute("""
      SELECT nome, preco,
             VEC_DISTANCE_COSINE(embedding, VEC_FromText(?)) AS distancia
      FROM produtos
      ORDER BY distancia
      LIMIT ?""", (json.dumps(v.tolist()), k))
    return cur.fetchall()

print(recomendar("algo para ouvir música no voo sem barulho"))
print(recomendar("bag for my laptop when it rains"))      # inglês
print(recomendar("algo para llevar café caliente"))       # espanhol

Repare: nenhuma das perguntas tem a palavra "fone", "mochila" ou "garrafa". E duas nem estão em português. Mesmo assim, o primeiro resultado deve ser o produto certo.

Passo 5: filtros de negócio

Similaridade não sabe que o cliente só tem R$ 300. Combine com SQL normal:

SELECT nome, preco
FROM produtos
WHERE preco <= 300
ORDER BY VEC_DISTANCE_COSINE(embedding, VEC_FromText(@v))
LIMIT 5;

Cuidado: com filtro muito seletivo, o índice aproximado pode trazer menos de K linhas, porque ele pega os vizinhos primeiro e o WHERE descarta depois. Se acontecer, aumente o LIMIT e corte na aplicação.

Entendendo o índice e a aproximação

Camada 2:  A ─────────────── F            (poucos nós, saltos longos)
Camada 1:  A ──── C ──── F ──── H
Camada 0:  A─B─C─D─E─F─G─H─I─J─K          (todos os nós, vizinhos curtos)
           busca entra no topo e "desce" chegando cada vez mais perto
  • M (3 a 200, padrão 6): quantos vizinhos cada nó guarda. Maior = mais preciso, mais memória e carga mais lenta.
  • mhnsw_ef_search (variável de sessão): quantos candidatos olhar na busca. Maior = mais preciso e mais lento.
SET SESSION mhnsw_ef_search = 40;

Para conferir se o índice está sendo usado, rode um EXPLAIN e procure idx_emb. Para medir a precisão, compare o resultado com o de uma tabela sem índice (busca exata) numa amostra de perguntas.

Por que funciona em qualquer idioma

O banco não sabe português, inglês nem espanhol. Quem sabe é o modelo multilíngue: ele coloca frases de mesmo sentido em pontos próximos, independentemente do idioma. O MariaDB só mede distância. Se você usar um modelo treinado só em inglês, essa mágica some. Escolha do modelo é decisão de arquitetura.

E o resto do RAG?

Com os produtos em mãos, você monta o prompt: "Cliente pediu X. Produtos disponíveis: [lista]. Recomende e explique". Essa parte é do LLM e fica para outro dia. A parte difícil, que é achar o contexto certo, já está resolvida no banco.

Checklist antes da produção

  • Guarde o nome e a versão do modelo junto dos dados.
  • Recalcule o embedding sempre que nome ou descrição mudarem.
  • Normalize os vetores e use sempre a mesma distância.
  • Meça a precisão com perguntas reais dos seus clientes.

Leia também MariaDB Vector e o índice HNSW e, se seu banco ainda é MySQL, o guia de migração do MySQL para o MariaDB. Mais conteúdo em /mariadb.


Sobre o autor: Alexandre Almeida é MariaDB Ambassador no Brasil, autor do livro Aprendendo MySQL e trabalha com bancos de dados há mais de 30 anos.

Fontes: MariaDB Knowledge Base: Vector Overview, VECTOR data type, VEC_DISTANCE_COSINE, VEC_FromText e Vector System Variables (mariadb.com/kb). Documentação do sentence-transformers (modelo paraphrase-multilingual-MiniLM-L12-v2). Imagem: ilustração gerada por IA.