O que é embedding — e por que sua busca semântica devolve resultado errado
Você montou a busca semântica. Subiu o pgvector, gerou os embeddings, rodou o <=> e... o resultado volta sem nexo. O usuário busca "como cancelar assinatura" e o sistema devolve um artigo sobre "novos planos". Frustração na veia.
A primeira reação de todo dev é a mesma: "o banco vetorial tá ruim". Quase nunca é. O problema mora antes — no embedding. É lá que o sentido do seu texto vira número, e é lá que ele se perde.
Neste post você vai entender o que é embedding de verdade (sem fórmula mágica), por que a busca por similaridade erra, e os três pontos onde 90% das buscas semânticas quebram: modelo errado, vetor não normalizado e chunking mal feito.
TL;DR
- O que é: embedding é a tradução de um texto (ou imagem, ou áudio) para um vetor de números que representa o sentido daquilo, não as palavras.
- Pra que serve: busca semântica, RAG, recomendação, deduplicação, classificação — tudo que precisa medir "isso é parecido com aquilo".
- Por que erra: modelo de embedding inadequado, vetores sem normalização, chunk grande demais ou cortado no meio da frase.
- Stack do exemplo: PostgreSQL + pgvector, modelo
text-embedding-3-smallda OpenAI.
O que é embedding, sem enrolação
Computador não entende "cachorro". Ele entende número.
Embedding é o processo de pegar um pedaço de texto e transformá-lo num vetor — uma lista de números de tamanho fixo. O text-embedding-3-small da OpenAI, por exemplo, devolve um vetor de 1536 dimensões. Cada texto vira um ponto num espaço de 1536 eixos.
O pulo do gato: esse espaço é organizado por significado. Textos com sentido parecido caem perto um do outro, mesmo sem nenhuma palavra em comum. "Adoro programar em Python" e "curto codar numa linguagem com símbolo de cobra" praticamente não compartilham palavras — mas seus vetores ficam colados (Pinecone).
É por isso que embedding destrava busca semântica, recomendação de conteúdo, memória de usuário e classificação inteligente. Você para de comparar palavras e passa a comparar sentido.
E como o computador mede "perto"? Quase sempre com similaridade de cosseno — o ângulo entre dois vetores. Quanto menor o ângulo, mais parecido o sentido. O valor vai de -1 (oposto) a 1 (idêntico), ignorando o tamanho do vetor e olhando só pra direção (Milvus).
Guarda essa palavra: direção. Ela é a raiz de um dos bugs mais comuns.
Por que sua busca devolve lixo
Quando a busca erra, o instinto é mexer no banco — trocar índice, aumentar o k, adicionar filtro. Na maioria das vezes isso é tratar o sintoma. A doença está no embedding. São três focos.
Foco 1: você está usando o modelo errado
O ranking do MTEB — o benchmark que compara modelos de embedding — é ótimo pra ter um ponto de partida. Mas tem uma armadilha: o modelo que lidera o MTEB pode ir mal no seu domínio.
Dois erros clássicos aqui:
- Modelo monolíngue num conteúdo em português. Muito embedding foi treinado majoritariamente em inglês. Joga texto técnico em PT-BR e a noção de similaridade fica grossa — "fatura" e "boleto" deveriam estar colados, e não estão. (E tem domínio onde nenhum embedding sozinho resolve: código, SKU, part-number. "RX-7000" e "RX-5000" são quase idênticos pro vetor. Aí a saída é busca híbrida com BM25 + vetor, não trocar de embedding.)
- Modelos diferentes pra query e pra documento. Esse é o erro silencioso. Você indexou os documentos com um modelo, e na hora da busca embedou a query com outro. São dois espaços vetoriais diferentes. A similaridade vira número aleatório. O sistema não dá erro — só devolve resultado ruim, e você fica caçando bug no lugar errado.
Regra de ouro: o mesmo modelo, a mesma versão, dos dois lados. Sempre.
Foco 2: você esqueceu de normalizar
Lembra da palavra direção? Cosseno só olha pro ângulo. Mas várias implementações, por performance, usam inner product (produto interno) em vez de cosseno puro.
Aqui está o detalhe que derruba gente: inner product e cosseno só dão o mesmo ranking se os vetores estiverem normalizados — com comprimento 1 (McClarence). Se não estiverem, o produto interno passa a premiar vetor "grande" em vez de vetor "parecido". Documento comprido sobe no ranking só por ser comprido. Resultado: lixo relevante por acidente.
No pgvector isso aparece na escolha do operador:
-- distância de cosseno: seguro, normaliza implicitamente
SELECT id, conteudo
FROM documentos
ORDER BY embedding <=> '[0.12, -0.04, ...]'
LIMIT 5;
-- inner product (<#>): mais rápido, MAS só correto se os vetores
-- já vierem normalizados na hora de gravar
SELECT id, conteudo
FROM documentos
ORDER BY embedding <#> '[0.12, -0.04, ...]'
LIMIT 5;
Os operadores do pgvector: <=> é distância de cosseno, <#> é o produto interno negativo, <-> é distância euclidiana (L2). Modelos modernos como os da OpenAI já entregam vetores normalizados — mas no instante em que você trunca um vetor, faz média de chunks ou mistura fontes, a normalização vai embora. Se for usar <#>, normalize na escrita. Se não quer pensar nisso, use <=> e durma tranquilo.
Foco 3: seu chunking está sabotando tudo
Embedding não tem memória infinita. Cada modelo tem uma janela de contexto, e texto que passa do limite é truncado antes de virar vetor — a parte cortada simplesmente não existe na representação (OpenSearch). Você acha que indexou o documento inteiro. Indexou metade.
Aí entra o chunking — quebrar o documento em pedaços antes de embedar. E é onde mora o segundo grande erro:
- Chunk grande demais: uma frase relevante enterrada em nove irrelevantes. O vetor vira uma média borrada de dez assuntos, e a precisão despenca (Pinecone).
- Chunk pequeno demais: falta contexto. O pedaço "ele deve ser cancelado em até 7 dias" sem saber que "ele" é a assinatura não serve pra nada.
- Corte burro: chunking de tamanho fixo ignora a estrutura do texto e corta no meio da frase, partindo o sentido em dois vetores capengas.
Não existe tamanho de chunk universal. Trate chunking como problema de avaliação, não de chute (Pinecone): monte um conjunto de 30–50 queries reais com a resposta certa, e meça. É a única forma honesta de saber se o seu chunking ajuda ou atrapalha. Se quiser ver o chunking certo aplicado de ponta a ponta, com overlap e índice HNSW, o post RAG do zero: chunking, embeddings e busca que funciona mostra o pipeline inteiro.
Tutorial te mostra o caminho — no Clã você constrói junto. Aula ao vivo toda semana, projetos reais de Engenharia de IA, ao lado de quem já está em produção.
Entrar no ClãConsertando, na ordem certa
Antes de trocar de banco vetorial — que é o passo mais caro e o que menos resolve — siga esta ordem:
- Confira o modelo. Mesmo modelo e versão na indexação e na query. Modelo com bom desempenho em português se o conteúdo é PT-BR.
- Confira a normalização. Se usa
<#>, garanta vetores normalizados na escrita. Na dúvida, use<=>. - Confira o chunking. Pedaços com começo e fim de sentido, com um pouco de sobreposição entre eles. Mede com um dataset de avaliação.
- Só então mexa em índice,
ke filtros de metadata.
Repara que três dos quatro passos acontecem antes do banco. É exatamente por isso que "trocar o pgvector" raramente conserta a busca.
FAQ rápido
Embedding e LLM são a mesma coisa? Não. O modelo de embedding só transforma texto em vetor — ele não gera texto. Num RAG, o embedding faz a recuperação (achar os pedaços certos) e o LLM faz a geração (escrever a resposta com base neles). São dois trabalhos distintos.
Quantas dimensões meu embedding precisa ter?
Não é "quanto mais, melhor". Mais dimensões custam mais memória e busca mais lenta. O text-embedding-3-small tem 1536 e resolve a grande maioria dos casos. Comece pequeno, meça, só aumente se a avaliação pedir.
Posso embedar query com um modelo e documentos com outro? Não. São espaços vetoriais diferentes e a similaridade perde o sentido. Mesmo modelo dos dois lados — esse é, de longe, o erro silencioso mais comum.
Preciso re-embedar tudo se trocar de modelo? Sim. Cada modelo cria seu próprio espaço. Trocou o modelo, reprocessa a base inteira — query e documentos.
O embedding é o alicerce, não o detalhe
Busca semântica que erra quase nunca é problema de banco vetorial. É embedding mal escolhido, vetor não normalizado ou chunk mal cortado. Acerte esses três e o pgvector vira coadjuvante — que é o lugar dele.
E embedding é só a porta de entrada. Recuperação é uma peça de um sistema maior: reranking, avaliação, contexto, custo, latência. Decidir onde cada peça entra na arquitetura é o que separa um protótipo de RAG de um produto que aguenta produção — e é exatamente o tipo de decisão que a gente coloca na mesa no Workshop Arquitetando Soluções de IA, com código rodando e arquitetura de verdade, não slide.
O próximo passo, depois de domar o embedding, é colocar a mão na massa: o tutorial de como implementar busca semântica no Laravel com pgvector leva isso pra dentro de uma aplicação real, e se você ainda confunde recuperação com memória, vale ler o que é RAG também.
E depois disso, mede. Monta teu dataset de avaliação antes de qualquer otimização. Sem número, você não está consertando busca — está adivinhando.
{AI Engineer} — apaixonado por Laravel, arquitetura de software e construir produtos com impacto. Compartilho aqui tutoriais, descobertas e reflexões sobre o dia a dia de engenharia.
Conteúdo é o que não falta. Falta quem desembaralhe: o que importa agora é como implementar do jeito certo. No Clã você tem isso ao vivo, toda semana, com quem já filtrou o ruído.
Entrar no Clã