Conteúdo da trilha
PadrãoContexto e conhecimento

Busca Híbrida

Hybrid Search

Busca Híbrida (Hybrid Search) é um padrão de projeto de IA para apps com LLM e agentes, do grupo Contexto e conhecimento. Combinar busca por palavra-chave e busca vetorial para não perder nem sinônimos nem códigos exatos.

Elemento
12/70
Nível
Intermediário
Leitura
7 min
Atualizado
Nesta página 17
  1. 01Intenção
  2. 02Problema
  3. 03Solução
  4. 04Estrutura
  5. 05Pseudocódigo
  6. 06Cenários reais
  7. 07Aplicabilidade
  8. 08Quando não usar
  9. 09Como implementar
  10. 10Variações
  11. 11Custo, latência e falhas
  12. 12Como avaliar
  13. 13Em produção
  14. 14Segurança
  15. 15Prós e contras
  16. 16Código
  17. 17Teste seu entendimento
  18. ··Perguntas rápidas

Intenção

Combinar busca por palavra-chave e busca vetorial numa única consulta, para não perder nem sinônimos nem termos exatos. Serve para sistemas de busca e RAG em que os usuários misturam linguagem natural com códigos, siglas e nomes próprios.

Em uma frase
Dois bibliotecários trabalhando juntos: um conhece o catálogo de cor, o outro entende o que você quis dizer.

Problema

Pedro cuida da busca de um e-commerce de peças automotivas. A busca vetorial funcionou bem para “pastilha de freio para carro popular”: achou pastilhas compatíveis com vários modelos. Mas falhou feio em “pastilha 4D0698151”: o vetor de um código de peça não carrega significado, e a busca devolveu pastilhas de outras marcas, nenhuma com aquele código.

A busca antiga, por palavra-chave, acertava o código e errava a linguagem natural. Cada método falha onde o outro acerta, e os clientes escrevem dos dois jeitos, muitas vezes na mesma frase: “pastilha dianteira 4D0698151 para A4”.

O mesmo padrão aparece fora de e-commerce. No chat interno, “VR no home” precisa da busca lexical para as siglas e da vetorial para a paráfrase de trabalho remoto. Numa NF-e, a chave de 44 dígitos só é encontrada por correspondência exata. Um embedding aproxima sentido; ele não memoriza identificadores.

A pergunta que este padrão responde: como uma busca acerta tanto o sentido da frase quanto o termo exato?

Solução

Rode as duas buscas sobre o mesmo conjunto de documentos e combine os resultados:

  • Busca lexical (BM25 ou equivalente): pontua por ocorrência de termos. Acerta códigos, siglas, nomes e palavras raras.
  • Busca vetorial: pontua por proximidade de sentido. Acerta sinônimos, paráfrases e perguntas mal formuladas.

A combinação mais comum é a fusão por posição (Reciprocal Rank Fusion, Cormack et al., 2009): cada documento ganha pontos pela posição em cada lista, e a soma decide a ordem final. Ela não exige calibrar escalas de pontuação diferentes. Filtros exatos (marca, categoria, estoque) se aplicam às duas buscas.

Estrutura

flowchart LR
  Q(["1. Consulta"]):::io --> L["2a. Busca lexical"]:::tool
  Q --> V["2b. Busca vetorial"]:::tool
  I[("Índice com texto e vetores")]:::store --> L
  I --> V
  L --> F{"3. Fusão por posição"}
  V --> F
  F --> R["4. Top N combinado"]:::tool
  R --> OUT(["5. Resultados ou contexto para o modelo"]):::io
  1. A consulta chega como o usuário escreveu.
  2. As duas buscas rodam em paralelo, com os mesmos filtros.
  3. A fusão combina as listas pela posição de cada documento.
  4. Os primeiros N resultados combinados seguem.
  5. Eles viram a resposta da busca ou o contexto de um RAG.

Exemplo trabalhado

Consulta: “pastilha dianteira 4D0698151 para A4”. Depois de normalizar o código (4d0698151), as duas listas de 50 vêm assim no topo:

lexical                         vector
1. sku-4D0698151-a  (código)    1. sku-outras-pastilhas-a4   (sentido: pastilha + A4)
2. sku-4D0698151-b  (código)    2. sku-pastilha-dianteira-gol
3. manual-instalacao-4D06       3. sku-4D0698151-a

RRF com constante 60. O documento na posição 0 ganha 1/60, na posição 2 ganha 1/62. sku-4D0698151-a aparece nas duas listas, soma os dois termos e sobe para o primeiro lugar combinado. As pastilhas “parecidas” do vetor, sem o código, ficam mais abaixo.

A frase mista deixa de ser um empate entre dois sistemas ruins. Cada um contribui com o que sabe, e a fusão promove quem apareceu nos dois.

Checagem +20 XP de primeiraUm cliente busca só o código 4D0698151. A busca vetorial devolve pastilhas de outros fabricantes. A lexical devolve as duas SKUs certas. Depois da fusão, as SKUs certas estão em 1 e 2. O que a fusão fez de útil aqui?

Pseudocódigo

function hybrid_search(query, filters, k=10):
    parallel {                                                       # 2
        lexical = index.bm25(query, filters, top=50)
        vector  = index.knn(embed(query), filters, top=50)
    }
    # 3. Reciprocal Rank Fusion
    scores = {}
    for rank, doc in enumerate(lexical): scores[doc] += 1 / (60 + rank)
    for rank, doc in enumerate(vector):  scores[doc] += 1 / (60 + rank)
    ranked = sort(scores, desc)
    trace.span("hybrid", lexical_top=lexical[:3], vector_top=vector[:3])
    return ranked[:k]                                                # 4

Cenários reais

  • Busca semântica em e-commerce. “Tênis para correr na chuva” pede vetor; “SKU 88213” pede lexical. A busca híbrida atende as duas na mesma caixa de busca.
  • Chat com a base de conhecimento interna. Siglas internas e nomes de sistemas são achados pela busca lexical; perguntas vagas, pela vetorial.
  • Assistente jurídico. Números de lei e de artigo exigem correspondência exata; conceitos jurídicos, proximidade de sentido.

Aplicabilidade

  • Os usuários misturam linguagem natural com códigos, siglas e nomes.
  • A busca vetorial sozinha erra termos exatos, ou a lexical sozinha erra paráfrases.
  • O domínio tem vocabulário próprio que os modelos de embedding não conhecem bem.

Quando não usar

  • As consultas são sempre por identificador. Use busca exata no banco.
  • O material é pequeno e cabe no contexto. Veja RAG Desnecessário.
  • A busca vetorial já atende no eval, sem falhas em termos exatos. Uma busca só é mais simples.

Híbrida é a base do RAG quando o vocabulário mistura os dois mundos. Ela não resolve ordenação fina: se o certo está na posição 20 da lista combinada, o próximo passo é Reordenação. Se a consulta chega vaga, o passo anterior é Reescrita de Consulta.

Checagem +20 XP de primeiraO eval mostra que consultas por SKU acertam 99% com um SELECT no banco, e ninguém busca por linguagem natural nessa tela. Um colega quer índice híbrido 'para o futuro'. O que você faz agora?

Como implementar

  1. Monte um eval de busca com consultas reais e os documentos que deveriam aparecer, incluindo consultas com códigos e com paráfrases.
  2. Indexe o mesmo conteúdo para as duas buscas, com os mesmos identificadores e metadados.
  3. Rode as duas em paralelo, com filtros iguais.
  4. Combine com fusão por posição; ajuste depois, se necessário, com pesos.
  5. Meça recall@k de cada busca sozinha e da combinação.
  6. Acrescente Reordenação se os primeiros resultados ainda precisarem de precisão.

Normalize identificadores antes de indexar e antes de buscar. 4D0-698-151, 4D0698151 e 4d0 698 151 precisam virar o mesmo token. Sem isso, a lexical erra o caso que ela deveria acertar.

Erros comuns

  • Filtros diferentes. A lexical respeita category = pastilha e a vetorial não. O resultado combinado vaza produto de outra categoria. O filtro entra nas duas, ou na fusão depois, nunca só numa.
  • Somar pontuações brutas. BM25 e cosseno não vivem na mesma escala. RRF usa posição justamente para não ter que calibrar isso no primeiro dia.
  • Eval só com frases naturais. Você vai achar que o vetor basta. Inclua códigos, NCM, chave de NF-e, siglas internas.
  • Dois conteúdos, dois ids. Se o mesmo produto tem id diferente no índice de texto e no de vetor, a fusão nunca soma os pontos.

Variações

  • Fusão ponderada. Um peso para cada lista, calibrado no eval. Útil quando um tipo de consulta domina.
  • Roteamento por tipo de consulta. Consultas que parecem código vão só para a lexical. Mais barato, mas exige uma regra confiável.
  • Vetores esparsos aprendidos. Modelos que geram vetores com pesos por termo, combinando as duas ideias num índice só.

Custo, latência e falhas

Aspecto O que acontece Como aparece no trace
Chamadas Duas buscas e um embedding da consulta; nenhuma chamada de modelo de linguagem span hybrid com as duas listas
Latência A da busca mais lenta, se em paralelo duração do span
Custo Índice duplicado (texto e vetores) armazenamento do índice
Falha: uma lista domina Pesos ou escalas mal ajustados resultados finais iguais a uma das listas
Falha: filtros diferentes Uma busca filtra e a outra não documentos fora do filtro no resultado
Falha: termos com grafia variada A lexical erra variações (“4D0 698 151”) consultas com código sem acerto; normalize antes

Como avaliar

  • Recall@k da lexical, da vetorial e da híbrida, separado por tipo de consulta (código, natural, mista).
  • MRR: a posição do primeiro resultado certo.
  • Consultas sem resultado relevante, por tipo.
{ "query": "pastilha dianteira 4D0698151 para A4", "relevant": ["sku-4D0698151-a", "sku-4D0698151-b"], "type": "mixed" }

Se a híbrida não ganhar da lexical em type=code nem da vetorial em type=natural, a fusão está mal feita (ids, filtros ou normalização), não “o domínio que não combina”.

Em produção

  • Span com as primeiras posições de cada lista e a lista final.
  • Painel de buscas sem clique ou sem resultado, por tipo de consulta.
  • Amostra semanal de consultas com resultados ruins, que viram casos no eval.

O span com as duas listas é o que deixa você ver, numa pergunta ruim, se quem falhou foi a lexical, a vetorial ou a fusão.

Segurança

Filtros de permissão entram nas duas buscas, antes da fusão. Filtrar depois, na lista combinada, é tarde: o ranking já misturou documentos que o usuário não pode ver, e um bug na pós-filtragem vaza trecho.

Conteúdo indexado continua sendo dado não confiável na hora do RAG. Híbrida só muda quem entra na lista, não o fato de que o trecho pode carregar instrução. Veja Prompt injection na prática.

Prós e contras

Prós Contras
Acerta sentido e termos exatos Dois índices para manter
Fusão por posição dispensa calibração Mais uma etapa para avaliar
Funciona bem com vocabulário de domínio Pesos podem precisar de ajuste por tipo de consulta
Base sólida para RAG Não resolve ordenação fina sem reranking

Código

As duas abas fazem a busca lexical e a vetorial em paralelo e combinam com Reciprocal Rank Fusion.

Python. Use quando o índice cabe em memória ou você quer entender a fusão sem um motor de busca.

import numpy as np
from rank_bm25 import BM25Okapi
from sentence_transformers import SentenceTransformer

embedder = SentenceTransformer("sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2")


def normalize(text: str) -> list[str]:
    return text.lower().replace("-", "").split()  # "4D0-698-151" e "4d0698151" ficam iguais


class HybridIndex:
    def __init__(self, docs: list[dict]):
        self.docs = docs
        self.bm25 = BM25Okapi([normalize(d["text"]) for d in docs])
        self.vectors = embedder.encode([d["text"] for d in docs], normalize_embeddings=True)

    def search(self, query: str, k: int = 10, pool: int = 50, c: int = 60) -> list[dict]:
        lexical = np.argsort(-self.bm25.get_scores(normalize(query)))[:pool]  # 2a
        q = embedder.encode(query, normalize_embeddings=True)
        vector = np.argsort(-(self.vectors @ q))[:pool]  # 2b
        scores: dict[int, float] = {}
        for ranking in (lexical, vector):  # 3. Reciprocal Rank Fusion
            for rank, i in enumerate(ranking):
                scores[int(i)] = scores.get(int(i), 0.0) + 1.0 / (c + rank)
        best = sorted(scores, key=scores.get, reverse=True)[:k]
        return [self.docs[i] for i in best]  # 4

O que você ganhou: a fusão completa em poucas linhas, fácil de testar no eval. O que você perdeu: em milhões de documentos, use um motor que já tenha as duas buscas.

TypeScript. Use quando os documentos estão no Postgres, com busca de texto e pgvector.

import { Pool } from "pg";

const db = new Pool();

// 2 e 3 numa consulta: as duas buscas como CTEs e a fusão por posição em SQL
export async function hybridSearch(query: string, queryEmbedding: number[], category: string, k = 10) {
  const { rows } = await db.query(
    `WITH lexical AS (
       SELECT id, ROW_NUMBER() OVER (ORDER BY ts_rank_cd(tsv, q) DESC) AS r
       FROM products, plainto_tsquery('portuguese', $1) q
       WHERE tsv @@ q AND category = $3
       ORDER BY r LIMIT 50
     ),
     vector AS (
       SELECT id, ROW_NUMBER() OVER (ORDER BY embedding <=> $2::vector) AS r
       FROM products WHERE category = $3
       ORDER BY embedding <=> $2::vector LIMIT 50
     )
     SELECT p.id, p.title, p.sku,
            COALESCE(1.0 / (60 + l.r), 0) + COALESCE(1.0 / (60 + v.r), 0) AS score
     FROM products p
     LEFT JOIN lexical l ON l.id = p.id
     LEFT JOIN vector v ON v.id = p.id
     WHERE l.id IS NOT NULL OR v.id IS NOT NULL
     ORDER BY score DESC LIMIT $4`,
    [query, JSON.stringify(queryEmbedding), category, k],
  );
  return rows; // 4
}

O que você ganhou: um banco só, com filtros iguais nas duas buscas por construção. O que você perdeu: o ajuste fino de cada busca fica limitado ao que o Postgres oferece.

Teste seu entendimento

Checagem +20 XP de primeiraA lexical filtra em_estoque = true. A vetorial não. Depois da fusão, um produto esgotado aparece em terceiro. Qual é o conserto?

Checagem +20 XP de primeiraNo eval, type=code tem recall@10 de 0,40 na híbrida e 0,92 na lexical sozinha. type=natural está melhor na híbrida. O que isso indica?

Checagem +20 XP de primeiraUm usuário busca 4D0-698-151 e a lexical não acha o documento indexado como 4D0698151. A vetorial também não. Qual é o primeiro conserto?

Checagem +20 XP de primeiraRecall@50 da híbrida é alto, mas o trecho certo chega na posição 18, e o modelo de RAG só lê os 5 primeiros. Qual padrão vem a seguir?

Perguntas rápidas

O que é Busca Híbrida?

Busca Híbrida (Hybrid Search) é um padrão de projeto de IA para apps com LLM e agentes, do grupo Contexto e conhecimento. Combinar busca por palavra-chave e busca vetorial para não perder nem sinônimos nem códigos exatos.

Onde Busca Híbrida aparece na prática?

Nos cenários reais Busca semântica em e-commerce, Chat com a base de conhecimento interna e Assistente jurídico.

Para ir além

Relações

Aparece nas trilhas 1
Buscar