Como Implementar um Sistema de Recomendação Semântica no Laravel com Embeddings (Sem Tags, Sem Categorias)

Neste tutorial prático, você vai aprender como construir um sistema de recomendação semântica real no Laravel, usando embeddings e PostgreSQL (pgvector) — sem depender de categorias, tags ou heurísticas frágeis. Vamos transformar posts em vetores, calcular o Embedding Centroid de cada conteúdo e substituir o tradicional “posts relacionados” por uma recomendação baseada em proximidade semântica real. Tudo isso sem pacotes mágicos, entendendo cada linha do processo.

Publicado em: 09, janeiro 2026

Sistema de recomentação estilo netflix com Laravel

🧠 Resumo Rápido

  • O problema dos sistemas de recomendação baseados em categoria
  • O que são embeddings (na prática)
  • Por que comparar todos os pedaços de texto não escala
  • O que é Embedding Centroid e por que ele funciona
  • Como armazenar e calcular o centroid no Laravel
  • Como ordenar posts por similaridade semântica
  • Resultado final: recomendações muito mais relevantes
  • Próximo passo: recomendação baseada no usuário

🚀 Introdução: O Problema dos “Posts Relacionados”

A maioria dos blogs faz isso:

“Posts relacionados” = mesma categoria ou mesmas tags

Isso funciona… até não funcionar mais.

Se você só tem 3 categorias (ex: tutoriais, notícias, pacotes), o que você realmente está recomendando é quase aleatório. O usuário lê sobre performance no Laravel e recebe um post sobre Groq ou alguma release aleatória — sem relação real de conteúdo.

A solução?
 👉 Recomendação semântica baseada em significado, não em rótulos.

🧩 Relembrando: O que são Embeddings?

Se você não assistiu ao vídeo anterior, aqui vai a definição direta:

Embeddings são vetores numéricos que representam o significado de um texto.

Dois textos semanticamente parecidos geram vetores próximos no espaço vetorial.
 Textos diferentes → vetores distantes.

No vídeo anterior, já fizemos isso:

  • Quebramos cada post em vários pedaços
  • Geramos embeddings para cada pedaço
  • Salvamos tudo no banco

Agora vem o desafio real.

⚠️ O Problema de Escala: Comparar Tudo com Tudo

Imagine:

  • Post A tem 8 embeddings
  • Post B tem 6 embeddings
  • Post C tem 10 embeddings

Comparar todos os pedaços com todos os pedaços vira rapidamente um problema de complexidade absurda.

Isso:

  • Não escala
  • Fica lento
  • Fica caro
  • Complica queries

Precisamos de uma forma de comprimir o significado do post inteiro em um único vetor.

🎯 A Solução: Embedding Centroid

O Embedding Centroid resolve exatamente isso.

O que é?

É a média vetorial de todos os embeddings de um conteúdo.

Se um post tem vários vetores, o centroid representa o “centro semântico” daquele conteúdo.

Matematicamente:

centroid[d] = (v1[d] + v2[d] + v3[d] + ... + vn[d]) / n
  • Cada dimensão é somada
  • Divide-se pela quantidade de embeddings
  • Resultado: um único vetor representativo

Simples. Poderoso. Escalável.

🛠️ Passo 1 — Criando a Coluna embedding_centroid

Criamos uma migration adicionando uma coluna do tipo vector na tabela posts:

php artisan make:migration add_embedding_centroid_to_posts_table

Na migration:

$table->vector('embedding_centroid')->nullable();

No model Post, adicionamos o cast:

protected $casts = [
    'embedding_centroid' => 'array',
];

Sem pacote. Sem mágica.

⚙️ Passo 2 — Calculando o Centroid (Job)

Criamos um Job responsável por:

  1. Buscar todos os embeddings do post
  2. Validar dimensões
  3. Somar cada dimensão
  4. Dividir pela quantidade
  5. Salvar no post

Isso permite:

  • Reuso futuro
  • Processamento assíncrono
  • Atualização automática ao criar novos posts

Esse job vira um bloco fundamental da arquitetura.

🔁 Passo 3 — Gerando Centroids para Posts Existentes

Criamos um Command que percorre todos os posts e dispara o Job:

Post::all()->each(function ($post) {
    dispatch_sync(new PostEmbeddingCentroidJob($post));
});

Resultado:

  • Toda base atual já fica preparada
  • Sistema pronto para escalar

🔍 Passo 4 — Recomendação por Similaridade Semântica

Agora vem a parte mais interessante.

Antes, tínhamos algo assim:

->where('category_id', $post->category_id)

Agora, substituímos por:

orderByRaw(
    'embedding_centroid <=> ?',
    [$post->embedding_centroid]
)

O que esse operador faz?

  • <=> calcula a distância vetorial
  • Quanto menor a distância, maior a similaridade
  • Ordenação automática do mais parecido → menos parecido

Resultado real:

  • Conteúdos sobre performance se recomendam entre si
  • Nada de posts aleatórios
  • Experiência muito mais próxima de Netflix, YouTube ou Medium

✅ Resultado Final

Antes:

  • Posts relacionados por categoria
  • Baixa relevância
  • Experiência pobre

Depois:

  • Recomendação semântica real
  • Conteúdo altamente contextual
  • Mais tempo de leitura
  • Mais engajamento
  • Mais valor percebido

Tudo isso sem um time de Machine Learning, apenas usando bem as ferramentas que já temos hoje.

🔮 Próximo Passo: Recomendação Baseada no Usuário

No próximo conteúdo, vamos avançar ainda mais:

  • Criar o embedding do usuário
  • Levar em conta cliques, leituras e histórico
  • Normalização
  • Escala
  • Indexação vetorial

Ou seja: sair do conteúdo → entrar no comportamento.

🚀 CTA — Quer Aprender Isso de Verdade, do Jeito Certo?

Se você curtiu esse tipo de implementação prática, aplicada e sem hype, eu quero te fazer um convite direto.

🔥 Laravel Lab — AI-First na Prática

📅 24 e 25 de janeiro
🎯 Dois dias intensivos
🧠 Laravel + IA como core de negócio, não como acessório

Você vai aprender:

  • RAG de verdade
  • Tooling com LLMs
  • Engenharia de contexto (não só prompt)
  • Sistemas AI-First do zero
  • Deploy
  • E ainda construir um planner estratégico de carreira para 2026

Se inscreva: https://beerandcode.com.br/laravel-lab/construa-um-saas-real-e-transforme-sua-carreira-em-2026-com-laravel-livewire-e-ia/

/ Veja o passo a passo no youtube

/ Autor

Foto do autor do post Lucas Souza (Virgu)

Lucas Souza (Virgu)

{Full-Stack Specialist Engineer}

Mais de 10 anos de experiência com Laravel e sólidos conhecimentos em frameworks front-end, como ReactJS, React Native e Vue JS.
Experiência em Design de Serviço.
No primeiro projeto profissional como júnior, desenvolveu em e-commerce para a maior indústria de equipamentos odontológicos da América Latina. Atualmente, atua como Full Stack Engineer Specialist em uma grande multinacional.
Lidera decisões técnicas e é um suporte fundamental para a equipe de desenvolvimento.

/ Conteúdos Relacionados