Tu Búsqueda Vectorial Tiene un Punto Ciego
La búsqueda vectorial sola falla sistemáticamente con términos exactos, siglas y códigos de producto. Hybrid search y reranking no son optimizaciones — son el baseline mínimo en producción.
El primer RAG que monté en producción tenía un problema que tardé semanas en diagnosticar. Las respuestas funcionaban bien para preguntas generales — “cómo configurar la autenticación”, “qué hacer si el servicio no responde”. Pero cuando los usuarios buscaban algo concreto — un código de error específico, un número de versión, una referencia de ticket — el sistema devolvía resultados vagamente relacionados. O directamente irrelevantes.
Lo frustrante era que los documentos correctos estaban ahí. Indexados, chunkeados, vectorizados. Pero la búsqueda no los encontraba.
Después de darle muchas vueltas entendí algo que ahora me parece obvio: un embedding comprime todo el significado en un vector, y en esa compresión los datos específicos — el “429”, el “/v2/users”, el “ERR_CONNECTION_RESET” — se diluyen porque al modelo no le parecen semánticamente importantes. El pipeline solo veía significado. Le faltaba un ojo para ver las palabras.
El traductor y el contable
Piensa en la búsqueda vectorial como alguien que entiende de qué hablas pero no se fija en los detalles. Pregúntale “cómo gestionar errores en la API” y te devolverá la sección correcta aunque el documento hable de “manejo de excepciones” — entiende que es lo mismo. Pero pregúntale “error 429 rate limiting en endpoint /v2/users” y te traerá cualquier cosa que hable de errores en APIs. El 429, el endpoint y la versión se pierden en la traducción a vector.
BM25 — la búsqueda léxica de toda la vida — es lo opuesto. Un contable que cuenta cada palabra exacta. “429” es “429”, no “500”. “/v2/users” no es “/v1/users”. Pero si le preguntas “cómo gestionar errores”, no sabe que “manejo de excepciones” es lo mismo.
Hybrid search los pone a trabajar juntos. El traductor cubre la semántica, el contable cubre los datos exactos. Ninguno solo es suficiente — juntos cubren el tipo de queries que ves en producción real.
Dónde se nota
Lo he visto en todos los dominios que he tocado. Un equipo con un catálogo de componentes electrónicos donde “LM7805” y “LM7812” — reguladores de voltaje que hacen cosas muy distintas — quedan en la misma zona del espacio vectorial porque ambos son “reguladores lineales de Texas Instruments”. Para el embedding son casi intercambiables. Para el ingeniero que busca el datasheet correcto, confundirlos es un error serio.
Otro caso: documentación médica donde “ACE inhibitor” puede significar varias cosas según el contexto, pero el embedding elige la interpretación más frecuente en sus datos de entrenamiento, no la de tu dominio. O bases de conocimiento técnico donde “PostgreSQL 17 performance improvements” devuelve contenido genérico de rendimiento de PostgreSQL porque al modelo le parece que “17” no aporta significado semántico.
BM25 no tiene estos problemas — “LM7805” es “LM7805”, punto. Pero se pierde con sinónimos, paráfrasis y cualquier query que no use las palabras exactas del documento. “Cómo reiniciar el sistema” no encuentra “procedimiento de reset”.
La combinación de los dos es lo que resuelve esto. Y los datos que hay detrás no son marginales.
BM25 en dos minutos
Si ya sabes cómo funciona BM25, sáltate esta sección.
TF-IDF fue el primer intento: cuenta cuántas veces aparece un término en un documento (TF) y lo pesa por lo raro que es en el corpus (IDF). El problema: documentos largos ganan artificialmente porque tienen más repeticiones, y no hay tope — si “machine learning” aparece 100 veces, el peso sigue sumando.
BM25 corrige ambos problemas con dos parámetros. k1 (por defecto 1.5) pone un techo a las repeticiones — después de cierto punto, repetir un término no suma casi nada. b (por defecto 0.75) penaliza documentos largos para que no ganen solo por tener más palabras. BM25 superó a TF-IDF hace décadas y es el estándar de facto. Elasticsearch, OpenSearch, Lucene — todos usan BM25 por debajo.
SPLADE: interesante en paper, complejo en producción
Existe un punto medio entre BM25 y embeddings que merece mención: SPLADE, de NAVER Labs (publicado en SIGIR 2021/2022). En lugar de contar frecuencias como BM25, usa el MLM head de BERT para generar pesos aprendidos. Cuando procesa “study”, no solo activa ese token — también activa “learn”, “research”, “investigate” con pesos proporcionales. Captura relaciones semánticas pero almacena el resultado en un índice invertido estándar.
En teoría, lo mejor de los dos mundos. En la práctica, tiene dos problemas que me echan para atrás. Primero, el tokenizer de BERT no está diseñado para retrieval: códigos de producto e identificadores técnicos se fragmentan en subwords o se convierten en [UNK] — exactamente el tipo de queries que nos motivó a añadir búsqueda léxica. Segundo, necesita GPU para inferencia a latencia razonable, mientras que BM25 corre en CPU sin despeinarse. Los benchmarks de ECIR 2025 confirman la intuición: BM25 es 10-70x más rápido que SPLADE en percentiles P90/P99.
BM25 con un buen motor de indexación (Elasticsearch, tantivy) sigue siendo la opción más pragmática para producción. SPLADE tiene sentido cuando el vocabulary mismatch es tu problema principal y puedes asumir el overhead de GPU.
Cómo se fusionan los resultados
Tienes dos listas de resultados — una del vectorial, otra de BM25. Hay que combinarlas, y cómo lo hagas importa más de lo que parece.
RRF (Reciprocal Rank Fusion) ignora los scores y solo mira posiciones. Si un documento está en la posición 3 del vectorial y en la 7 del léxico, su score RRF es 1/(60+3) + 1/(60+7). El 60 controla cuánto pesa estar arriba: con valores bajos el top-1 domina; con valores altos, la posición 1 y la 20 se tratan casi igual. 60 es el equilibrio empírico — aunque para búsquedas de identificadores exactos puede funcionar mejor un k=40, y para QA semántica un k=80.
La gracia de RRF: no necesitas normalizar nada. Los scores de BM25 van de 0 a infinito; la similitud coseno va de -1 a 1. Da igual — RRF solo mira posiciones. Sin tuning, funciona razonablemente bien desde el primer día. Doug Turnbull tiene una frase que me parece clave: “RRF’ing bad search into good search just drags down the good search”. RRF mejora recall pero no arregla un retriever mal calibrado — lo arrastra.
Weighted Combination sí usa los scores: score = α × score_vectorial + (1-α) × score_léxico. Supera a RRF cuando está bien calibrada según Bruch et al. (Pinecone/ACM TIS, 2024). Pero tiene una trampa que he visto morder a más de un equipo: un alpha mal calibrado es peor que usar solo un retriever. En benchmarks de AIMultiple, hybrid con weighted combination sin calibrar dio un MRR de 0.390 — peor que el 0.410 del vectorial solo. Fusionar sin calibrar no suma: resta.
Empieza con RRF. Es robusto, no necesita datos de calibración, y Elasticsearch, Milvus, Weaviate y OpenSearch lo implementan nativamente. Solo pasa a weighted combination cuando tengas un validation set con queries reales y puedas ajustar α con confianza.
Los números detrás de cada capa
De vectorial solo a hybrid:
IBM Research probó la combinación de múltiples vías de retrieval sobre los 18 datasets de BEIR (Blended RAG). El recall pasó de ~0.72 con vectorial solo a ~0.91 con hybrid. En MS MARCO, la diferencia es más dramática: dense solo consiguió un recall@10 del 13.9%, BM25 solo un 11.9%, y hybrid con α=0.5 alcanzó un 80.8%. No es una mejora marginal — es la diferencia entre un sistema que funciona y uno que no.
De hybrid a hybrid + reranking:
Los benchmarks de LlamaIndex cuentan la otra mitad: Hit Rate subió de 0.79 a 0.86, pero lo que importa es el MRR — de 0.53 a 0.71, un 34% de mejora. Eso significa que el documento más relevante pasa de estar enterrado en la posición 4-5 a estar arriba del todo. El LLM recibe mejor contexto, la respuesta mejora.
Esto lo vi de primera mano en un proyecto de soporte técnico. Teníamos el hybrid search montado y el recall era bueno — los documentos correctos aparecían en los resultados. Pero las quejas no bajaban. El problema: aparecían en la posición 5 o 6. Con un context window de top-3 para el LLM, se perdían. Añadimos un cross-encoder y las quejas bajaron a la mitad en una semana. No porque encontrásemos documentos nuevos, sino porque los que ya encontrábamos subían donde el modelo los necesitaba.
Por qué el reranking funciona tan bien
Un bi-encoder — lo que usas para generar embeddings — codifica la query por un lado y el documento por otro. Dos vectores, similitud coseno. Rápido porque los embeddings están precomputados. Pero el modelo nunca “ve” query y documento juntos — toda la compresión ocurre sin contexto de la pregunta. Es como valorar un CV sin saber para qué puesto es.
Un cross-encoder recibe query y documento concatenados y los procesa juntos con cross-attention completa. Cada token de la query interactúa con cada token del documento. Mucho más preciso, pero lineal con el número de candidatos — procesar 100 documentos de 256 tokens tarda ~150ms, y con documentos largos de 4.096 tokens sube a 7 segundos. Pasarlo por un corpus entero es inviable.
De ahí las dos etapas. El bi-encoder reduce millones de documentos a 50-100 candidatos en milisegundos. El cross-encoder aplica precisión sobre esos finalistas en 100-600ms adicionales. Latencia total por debajo del segundo.
Qué reranker elegir
Hay un leaderboard de rerankers de Agentset actualizado a febrero de 2026 que, teniendo en cuenta que Agentset tiene interés comercial, da una foto útil del mercado. Los perfiles que me parecen prácticos:
Managed, sin complicaciones: Cohere Rerank 4. Dos variantes — Pro (máxima precisión, ~600ms) y Fast (~450ms). Contexto de 32K tokens, 100+ idiomas. Para equipos que no quieren self-hostear.
Self-hosting: BGE-Reranker-v2-M3 de BAAI o Jina Reranker v2. Open source, comparables a Cohere — Jina v2 consiguió un Recall@20 de 96.30 frente al 96.10 de Cohere en el dataset de Contextual Retrieval de Anthropic. Sin dependencia de API.
Latencia ultra-baja: Un cross-encoder ligero tipo TinyBERT ronda los 50ms para 20 documentos. Para e-commerce o autocompletado, puede ser la única opción viable.
Lo que no usaría en tiempo real: reranking con LLMs (GPT-4, Claude). 1-3 segundos por query. Solo para búsquedas donde la latencia no importa — legal, research, análisis financiero.
Un apunte sobre ColBERT: existe un punto medio entre bi-encoder y cross-encoder que genera embeddings por token en lugar de por documento, con late interaction (MaxSim). 180x menos operaciones que un cross-encoder, latencia sub-100ms. Interesante si la latencia del cross-encoder te mata. Para el resto, un cross-encoder sobre top-50 candidatos es más simple y suficiente.
Cuándo hybrid NO ayuda
Sería deshonesto no decir esto: hybrid search no siempre mejora las cosas.
En NFCorpus (dominio médico), dense solo consiguió un NDCG del 17.6% y hybrid un 17.4%. Cuando tu corpus es homogéneo y semántico, y las queries son puramente conceptuales, el componente léxico no aporta. Es ruido.
Y repito: hybrid mal calibrada es peor que vectorial solo. No fusiones por fusionar.
Hybrid ayuda más cuando tu corpus mezcla términos exactos y queries conceptuales: catálogos de productos, documentación técnica, bases de conocimiento con siglas. Es decir, casi todo lo que toco en producción.
Hybrid ayuda menos en corpus puramente narrativos con queries exclusivamente semánticas. Artículos de opinión, ensayos, ficción. Si nadie va a buscar un código de producto, el componente léxico sobra.
El pipeline completo
Esto es lo que monto cuando un proyecto necesita funcionar con queries variadas:
Query del usuario
→ Embedding (bi-encoder) → 100 candidatos (~5ms)
+ BM25 sobre índice léxico → 100 candidatos (~5ms)
→ RRF fusion → 100 rankeados (~1ms)
→ Cross-encoder reranker → top 10 (100-600ms)
→ LLM genera respuesta con los top 5-10 (1-3s)
Latencia total del retrieval: 200-800ms. Con generación, 1-4 segundos.
¿Siempre las tres capas? No. Si tu corpus tiene menos de 1.000 documentos y las queries son semánticas, un bi-encoder con buen chunking puede bastar. Pero en cuanto aparezcan búsquedas exactas — y en producción, siempre aparecen — necesitas el componente léxico. Y si tienes más de 10.000 documentos con queries variadas, el reranker no es opcional.
Lo que me llevo
El artículo anterior de esta serie hablaba de chunking como el techo de tu sistema: si el contenido entra mal indexado, nada lo compensa. Hybrid search y reranking son el suelo — el mínimo para que el retriever haga su trabajo con queries reales.
La búsqueda vectorial sola es una demo. Funciona cuando controlas las preguntas. En producción, los usuarios pegan mensajes de error, citan números de versión, buscan referencias exactas. Si tu pipeline solo entiende semántica, está ignorando exactamente las queries donde más necesitan una respuesta precisa.
RRF con k=60, un bi-encoder decente y un cross-encoder sobre los top 50 candidatos. No es arquitectura sofisticada — es el baseline.
Y un sistema que falla en silencio porque el código de error no matcheó es peor que uno que dice “no sé”. Porque el primero te da la respuesta equivocada con total confianza.
Este artículo es parte de una serie sobre arquitectura de sistemas RAG en producción. El anterior cubre chunking como decisión arquitectónica. El siguiente cubrirá evaluación con RAGAS: cómo medir si tu pipeline funciona de verdad.