Query Rewriting + Hybrid Search + Reranking: cómo llevar la base de conocimiento de IA de «poder buscar» a «buscar con precisión»
La búsqueda vectorial es solo el punto de partida. Hemos desplegado en CCTCRM tres grandes técnicas de mejora de RAG —Query Rewriting, Hybrid Search (fusión RRF) y Reranking— para empujar la precisión de la base de conocimiento un peldaño más arriba usando ingeniería. Este artículo recoge la selección técnica, los detalles de implementación y los tropiezos por el camino.
Nuestra base de conocimiento RAG lleva medio año en producción. Búsqueda vectorial con Embedding de 768 dimensiones, ordenada por similitud coseno, cubriendo 9 grandes dominios de negocio: clientes, pedidos, productos, envíos, producción, inventario, finanzas, etc. La funcionalidad funciona, y los usuarios ya la están usando.
Pero entre «funciona» y «es útil» hay un escalón.
Un comercial pregunta «cuántos contenedores de los pedidos enviados a Estados Unidos el mes pasado aún no han llegado al puerto», y el sistema devuelve como primer resultado un documento sobre la política arancelaria de EE. UU. Semánticamente es relevante: ambos mencionan «Estados Unidos» y «pedidos». Pero lo que el usuario quiere son datos de envío, no política arancelaria.
Este tipo de desajuste es demasiado común en la búsqueda vectorial. El modelo de Embedding entiende similitud semántica, no relevancia de negocio. Que dos fragmentos de texto estén cercanos en el espacio vectorial no significa que sean la misma cosa en el escenario de negocio.
Hemos invertido tres semanas en añadir una capa de procesamiento aguas arriba y aguas abajo de la búsqueda vectorial, empujando la precisión de búsqueda un poco más allá. Tres técnicas, un mismo objetivo: que lo que se encuentra sea lo que el usuario quiere.
Dónde está el problema
Antes de ponernos a optimizar, necesitamos entender bien los tres defectos estructurales del RAG naive.
Primero, el usuario habla en lenguaje natural, y el motor de búsqueda necesita palabras clave. Un comercial no va a escribir «Estados Unidos pedidos de exportación número de contenedores estadística de envíos». Va a decir «cuántos contenedores de los pedidos enviados a Estados Unidos el mes pasado aún no han llegado al puerto». Al meter esta frase en el modelo de Embedding, las palabras coloquiales como «el mes pasado», «cuántos» y «aún no han llegado al puerto» diluyen la señal de las entidades centrales. En el espacio vectorial, esta frase puede quedar muy cerca de un documento sobre «el proceso de despacho de aduanas en EE. UU.» —porque semánticamente se parecen— pero queda a un nivel de distancia de los datos reales de la tabla de estadística de envíos.
Segundo, la búsqueda vectorial es buena en «parecido semántico», pero mala en «coincidencia exacta». Supongamos que en la base de conocimiento hay un documento titulado exactamente «Normativa de estadística de envíos de pedidos a EE. UU.». La búsqueda vectorial no necesariamente lo pondrá en primer lugar —porque la distancia semántica entre el título y la pregunta coloquial del usuario puede que no sea la menor—. Pero la búsqueda por palabras clave (MySQL LIKE o BM25) lo caza a la primera. Y a la inversa, la búsqueda por palabras clave no entiende sinónimos ni relaciones semánticas. Los dos métodos tienen cada uno su zona ciega.
Tercero, en los resultados recuperados, una similitud vectorial alta no equivale a una alta relevancia de negocio. La similitud coseno mide la coherencia de dirección de dos fragmentos de texto en el espacio vectorial. Que dos textos compartan dirección puede deberse simplemente a que tratan el mismo tema, pero la postura, el escenario y el significado de negocio pueden ser totalmente distintos. El usuario pregunta «cómo gestionar la reclamación por una entrega retrasada», y el sistema devuelve un documento sobre «análisis de las causas del retraso en las entregas» —similitud 0,87, pero no responde a la pregunta.
Estos tres problemas no son un defecto de un modelo de Embedding concreto, sino carencias estructurales de la arquitectura RAG naive. La industria ya tiene soluciones maduras: Query Rewriting resuelve el problema en el extremo de entrada, Hybrid Search cubre las zonas ciegas en el extremo de búsqueda, y Reranking corrige el sesgo en el extremo de ordenación. Las tres técnicas se combinan para formar una pipeline de mejora de «reescritura → búsqueda híbrida → reordenación» $TRAE_REF.
Primera capa: Query Rewriting — deja que el LLM ayude al usuario a expresarse con claridad
La idea es directa: antes de enviar la pregunta del usuario a la búsqueda vectorial, deja que un modelo ligero (nosotros usamos el modelo del grupo lite, el de menor coste) reescriba la pregunta coloquial en una combinación de palabras clave más adecuada para la búsqueda.
El usuario dice: «cuántos contenedores de los pedidos enviados a Estados Unidos el mes pasado aún no han llegado al puerto»
Reescrito: «Estados Unidos pedidos de exportación número de contenedores estadística de envíos mes pasado volumen de exportación»
El texto reescrito se envía después a generar el vector de Embedding. El efecto es doble: por un lado se eliminan los adornos coloquiales como «el mes pasado», «cuántos» y «aún no han llegado al puerto», que no aportan nada a la búsqueda; por otro, se añaden sinónimos y términos relacionados como «estadística de envíos» o «volumen de exportación», ampliando el recall semántico.
En la implementación hay varios diseños clave.
El Prompt es el núcleo. La calidad de la reescritura depende por completo del diseño del system prompt. Nuestro prompt incluye cinco reglas: extraer las entidades centrales (nombres de personas, productos, países, tipos de documento), añadir sinónimos y términos relacionados, eliminar muletillas, generar texto plano sin explicaciones, y conservar el idioma original. Además de las reglas, se incluyen dos ejemplos few-shot que cubren los escenarios en chino y en inglés. Sin few-shot, el modelo produciría un formato con prefijo del estilo «consulta reescrita: XXX», rompiendo el parsing posterior.
Las consultas cortas no se reescriben. Las entradas por debajo de 6 caracteres saltan directamente la fase de reescritura. Este tipo de consultas suelen ser ya la palabra clave en sí, como «cliente» o «pedido»: reescribirlas no aporta valor y solo añade latencia. Este umbral es el punto de inflexión que observamos en las pruebas: las consultas de más de 6 caracteres obtienen un beneficio positivo tras la reescritura; por debajo, el beneficio no es significativo.
Caché de 5 minutos. La misma consulta no vuelve a invocar al modelo en un plazo de 5 minutos. Usamos un ConcurrentHashMap para guardar el mapeo hash → resultado reescrito, con limpieza automática al expirar. El límite superior de la caché es de 200 entradas; cuando se llena, se eliminan los elementos expirados. Este diseño evita que las consultas repetidas de alta frecuencia (por ejemplo, varios comerciales consultando al mismo cliente a la vez) disparen llamadas repetidas al modelo, manteniendo el coste bajo control.
Degradación en caso de fallo. Fallo en la llamada al modelo, cuota agotada, timeout de red: cualquier excepción se degrada silenciosamente y devuelve la consulta original. La reescritura es la guinda del pastel, no un elemento imprescindible. No se puede permitir que el servicio de reescritura deje tirada toda la pipeline RAG.
Segunda capa: Hybrid Search — vector + palabras clave, fusionados con RRF
Esta capa resuelve el problema de que «la búsqueda vectorial y la búsqueda por palabras clave tienen cada una su zona ciega».
La solución es ejecutar las dos búsquedas en paralelo y luego fusionar los resultados. La búsqueda vectorial se ocupa del recall semántico —entiende sinónimos, conceptos relacionados y coincidencias multilingües—. La búsqueda por palabras clave se ocupa de la coincidencia exacta: términos originales que aparecen en el título o en campos de palabras clave. Los resultados de ambas se fusionan con el algoritmo Reciprocal Rank Fusion (RRF).
El algoritmo RRF
RRF es un método de rank fusion propuesto por Cormack et al. en 2009 $TRAE_REF. La idea central es extremadamente sencilla: no le importan las puntuaciones originales de cada vía de búsqueda (la similitud vectorial y la puntuación BM25 no son comparables), solo le importa el ranking que aporta cada vía.
Fórmula:
RRF_score(d) = Σ 1 / (k + rank_i(d))
i∈lists
Donde rank_i(d) es la posición del documento d en los resultados de la i-ésima vía de búsqueda (empezando por 0), y k es una constante de suavizado (nosotros usamos 60).
Supongamos que el documento A queda en primer lugar en la búsqueda vectorial y en quinto lugar en la búsqueda por palabras clave:
RRF_score(A) = 1/(60+1) + 1/(60+6) = 0.0164 + 0.0152 = 0.0316
El documento B queda en tercer lugar en la búsqueda vectorial y en segundo lugar en la búsqueda por palabras clave:
RRF_score(B) = 1/(60+4) + 1/(60+3) = 0.0156 + 0.0159 = 0.0315
Las puntuaciones RRF de los documentos A y B son casi idénticas —aunque A esté muy por encima de B en la búsqueda vectorial—. Esta es precisamente la característica de RRF: no se inclina por la diferencia de puntuaciones absolutas de ninguna de las dos vías; solo mira el rendimiento global en el ranking. Si un documento aparece bien posicionado en ambas vías a la vez, su puntuación RRF será alta.
Implementación
Nuestra clase RagEnhancementService implementa la lógica de fusión RRF. El flujo es:
- La búsqueda vectorial devuelve los 20 resultados top (ordenados por similitud coseno descendente)
- La búsqueda por palabras clave devuelve los 20 resultados top (MySQL LIKE sobre los campos title/content/keywords)
- Ambos resultados entran en el método
reciprocalRankFusion, con k=60 - Tras la fusión RRF, se ordenan por puntuación descendente y se toman los top K
La implementación de la búsqueda por palabras clave es muy sencilla: se tokeniza por espacios y signos de puntuación, se toman los tokens de longitud ≥ 2, y se hace una consulta OR con condiciones LIKE de MyBatis-Plus. No usamos el índice de texto completo (FULLTEXT) de MySQL, porque el volumen de nuestra base de conocimiento no es grande (unos miles de registros) y el rendimiento de LIKE es suficiente. Si en el futuro la base de conocimiento crece hasta decenas de miles de registros o más, consideraremos migrar a BM25 o Elasticsearch.
La elección de k en RRF. Cuanto mayor sea k, más se suaviza el impacto de las diferencias de ranking sobre la puntuación; cuanto menor sea k, más se refuerza la ventaja de los documentos mejor posicionados. Nosotros usamos 60, que es el valor por defecto del paper original de RRF y de Elasticsearch $TRAE_REF. En las pruebas, k=60 se comporta de forma estable y no hay necesidad de ajustarlo en exceso.
Si un documento aparece en ambas vías de búsqueda, su puntuación RRF será mayor que la de un documento que solo aparece en una. Esta es una propiedad natural de RRF, y también el efecto que queremos: un documento que ambas vías consideran relevante muy probablemente es lo que el usuario busca.
Tercera capa: Reranking — deja que el LLM haga el último filtro
Hybrid Search resuelve el problema de «encontrar resultados». Pero en qué orden se muestran los resultados encontrados lo sigue decidiendo la puntuación RRF —y la puntuación RRF solo refleja el ranking, no la relevancia de negocio—.
Volviendo al ejemplo anterior. El usuario pregunta «cómo gestionar la reclamación por una entrega retrasada». Entre los resultados puede haber:
- Documento A: «Flujo de gestión de reclamaciones por entrega retrasada» (el realmente relevante)
- Documento B: «Análisis de las causas del retraso en las entregas» (relacionado por tema, pero no responde a la pregunta)
- Documento C: «Estadística de la tasa de entregas puntuales en 2024» (relacionado por datos, pero no encaja en el escenario)
La búsqueda vectorial puede poner B por delante de A (porque la redacción de B se parece más a la pregunta del usuario), y la búsqueda por palabras clave puede poner C en primer lugar (porque la palabra «entrega» coincide más veces). Tras la fusión RRF, el orden puede seguir siendo B > A > C.
El Reranking consiste en añadir, antes de devolver los resultados fusionados, una reordenación por LLM. Se envían juntos la consulta y los documentos candidatos al modelo lite, para que el modelo juzgue qué documento es el más relevante.
Cómo se implementa
Lo que le pedimos al LLM es una versión simplificada de la ordenación pointwise. Concretamente:
Los documentos candidatos (10 por defecto) se numeran y se envían al modelo lite junto con la consulta del usuario; el prompt pide al modelo que devuelva la secuencia de números ordenada de mayor a menor relevancia. Por ejemplo, si se introducen 10 documentos, el modelo devuelve «2,0,5,1,3,8,4,7,6,9», lo que indica que el documento número 2 es el más relevante y el número 9 el menos relevante.
Consideramos usar un modelo Cross-Encoder para el reranking —por ejemplo, el BGE-Reranker-v2-m3 del Instituto BAAI $TRAE_REF—. Un Cross-Encoder concatena la consulta y el documento en una secuencia de entrada que se pasa al modelo, y tras un modelado conjunto devuelve una puntuación de relevancia entre 0 y 1. La precisión es, efectivamente, mayor que la de una ordenación por prompt LLM.
Pero al final optamos por la solución LLM, por motivos de coste de despliegue. El Cross-Encoder exige desplegar un servicio de modelo adicional (BGE-Reranker-v2-m3 ocupa unos 2,8 GB $TRAE_REF), mientras que nosotros ya estamos usando el modelo lite para el Query Rewriting; el Reranking reutiliza el mismo API del modelo, con coste adicional de despliegue cero. Para la escala de nuestra base de conocimiento (unos pocos miles de documentos), la precisión del reranking con LLM es suficiente. Si en el futuro la base de conocimiento se expande a decenas de miles de documentos, o se exigen mayores niveles de precisión, se puede cambiar a Cross-Encoder: la interfaz ya está abstraída, basta con sustituir la clase de implementación.
Control del número de candidatos. El Reranking por defecto solo procesa los 10 primeros candidatos. Si se envían demasiados documentos al modelo, el prompt se alarga, la latencia aumenta y la precisión cae (la atención del modelo se diluye). 10 candidatos es el equilibrio entre latencia y cobertura.
Estrategia de degradación. Igual que con Query Rewriting, si la llamada al modelo falla se degrada silenciosamente y se devuelve el orden original tras la fusión RRF. El Reranking es una optimización de precisión, no una dependencia funcional.
Diseño de arquitectura: capa SDK + interfaz SPI
La implementación de las tres técnicas de mejora no está en el sistema de negocio cct-crm-backend, sino que se ha bajado a nuestra biblioteca central ai-sdk. El motivo es que estas capacidades no son específicas del negocio de comercio exterior: cualquier sistema que use RAG puede reutilizarlas.
La arquitectura se divide en tres capas:
┌─────────────────────────────────────────┐
│ cct-crm-backend (宿主系统) │
│ RagEnhancementProviderImpl │
│ → callLiteModel (HTTP 调用 LLM API) │
│ → keywordSearch (MySQL LIKE 检索) │
│ → getConfig (读取云端配置) │
└──────────────┬──────────────────────────┘
│ implements SPI
┌──────────────▼──────────────────────────┐
│ ai-sdk-core (SDK 核心层) │
│ RagEnhancementService │
│ → rewriteQuery (Prompt 构建 + 缓存) │
│ → reciprocalRankFusion (RRF 算法) │
│ → hybridSearch (编排: 调 Provider + RRF)│
│ → rerankResults (Prompt 构建 + 解析) │
│ → RagSearchResult (数据类) │
└──────────────┬──────────────────────────┘
│ uses
┌──────────────▼──────────────────────────┐
│ RagEnhancementProvider (SPI 接口) │
│ → callLiteModel │
│ → keywordSearch │
│ → getConfig │
└─────────────────────────────────────────┘
Los algoritmos centrales (RRF, construcción del Prompt, parsing de resultados) están en la capa SDK, sin dependencias de Spring. El sistema anfitrión solo necesita implementar la interfaz SPI, aportando las tres capacidades de llamada al LLM, búsqueda por palabras clave y lectura de configuración. La ventaja de este diseño es que si en el futuro otros sistemas se integran con ai-sdk, basta con que implementen la interfaz Provider para obtener todas las capacidades de mejora RAG, sin desarrollo duplicado.
Los interruptores de configuración están todos en la base de datos. Los cinco elementos de configuración se guardan en la tabla cloud pb_ai_config, con model_group = 'rag_enhancement':
| Clave de configuración | Valor por defecto | Descripción |
|---|---|---|
rag_query_rewrite_enabled |
true | Interruptor de Query Rewriting |
rag_hybrid_search_enabled |
true | Interruptor de Hybrid Search |
rag_reranking_enabled |
true | Interruptor de Reranking |
rag_hybrid_candidate_count |
20 | Número de candidatos recuperados por palabra clave |
rag_rerank_candidate_count |
10 | Número de candidatos para Reranking |
La configuración se refresca cada 5 minutos; al cambiarla en la base de datos surte efecto de inmediato (como mucho, 5 minutos de espera). Cada función de mejora tiene su propio interruptor y puede desactivarse de forma independiente. Si una de las funciones rinde mal en un escenario concreto, o hay que controlar costes, se puede desactivar solo esa sin afectar a las otras dos.
Resultados y coste
Tras el despliegue de las tres funciones de mejora, esto es lo que observamos en las pruebas internas:
Precisión de búsqueda. Preparamos 50 conjuntos de consultas de prueba (cubriendo cinco escenarios: consulta de cliente, de pedido, de producto, de envío y de política) y comparamos la tasa de acierto top-1 y top-3 antes y después de la mejora. Antes, la tasa de acierto top-1 rondaba el 68 %; después, el 82 %, 14 puntos porcentuales de mejora. La tasa top-3 pasa del 85 % al 94 %. Estos datos son de pruebas internas, no de una evaluación académica estricta, pero la tendencia es clara.
Incremento de latencia. Con las tres capas de mejora activas, la latencia de una sola búsqueda pasa de ~200 ms a ~800 ms-1,2 s (principalmente por las dos llamadas al LLM). Para el escenario de búsqueda en base de conocimiento, esta latencia es aceptable: que el usuario espere 1 segundo y obtenga un resultado más preciso es una experiencia mucho mejor que esperar 200 ms y obtener un resultado irrelevante. Si la latencia es sensible, se puede desactivar el Reranking (que aporta ~60 % de la latencia adicional) y conservar solo Query Rewriting + Hybrid Search, con un incremento de latencia de unos 300 ms.
Coste. Tanto Query Rewriting como Reranking usan el modelo lite, y cada llamada consume unos 200-500 tokens. Según nuestra tarifa de API, una búsqueda completa con mejora cuesta unos 0,001-0,002 RMB. Combinado con la caché de 5 minutos, las consultas repetidas de alta frecuencia no disparan llamadas repetidas al modelo, por lo que el coste real es aún menor.
Direcciones futuras
Tras aterrizar las tres técnicas de mejora, la precisión del RAG ha mejorado de hecho. Pero el techo del RAG naive no termina aquí. Al planificar el siguiente paso, vemos varias direcciones que merecen atención.
Cross-Encoder en sustitución del Reranking con LLM. Usar hoy el modelo lite para el Reranking es una concesión guiada por el coste. Si se desplegara un modelo de reordenación dedicado, como BGE-Reranker-v2-m3, la precisión podría subir otro peldaño $TRAE_REF. La ventaja del Cross-Encoder es que puede codificar conjuntamente la consulta y el documento, capturando relaciones de grano fino entre dos textos, mientras que el LLM se limita a hacer una ordenación por prompt. La interfaz ya está abstraída; el cambio es un problema de trabajo de ingeniería, no de arquitectura.
Context Engineering. En 2025 la industria empezó a evolucionar del RAG al Context Engineering $TRAE_REF. El RAG se preocupa por «qué se recupera»; el Context Engineering se preocupa por «cómo se construye el contexto que se entrega al modelo». Además de los resultados de búsqueda, debería integrar información multidimensional como el rol del usuario, los datos en tiempo real, el historial de conversación o los límites de permisos, de modo que el modelo no reciba una lista de documentos, sino una instrucción de trabajo estructurada y con contexto. Nuestro prototipo de modelo causal (aprendizaje por experiencia + enrutado de modelos) ya está haciendo en realidad Context Engineering: inyectar la experiencia en el system prompt es construir contexto. El siguiente paso es fusionar más profundamente los resultados de búsqueda RAG con la inyección de experiencia y el enrutado de modelos.
RAG Multimodal. En el negocio de comercio exterior hay gran cantidad de conocimiento en formato visual: fotos de producto, escaneados de informes de inspección de calidad, imágenes de listas de embalaje $TRAE_REF. El RAG actual solo procesa texto; esa información visual o bien se transcribe manualmente a una descripción textual (con pérdida de información) o bien es completamente invisible para la IA. El RAG multimodal usa modelos de lenguaje visual para hacer Embedding directamente de las imágenes, de modo que la IA pueda «entender» las fotos de producto y recuperarlas. Esto tiene mucho valor para nuestra base de conocimiento de producto y para el módulo de inspección de calidad.
Agentic RAG. El RAG actual es de un solo turno: el usuario pregunta una vez, el sistema busca una vez. El Agentic RAG deja que la IA decida autónomamente si necesita múltiples búsquedas, búsquedas entre dominios o búsquedas de seguimiento a partir de resultados preliminares $TRAE_REF. Por ejemplo, si el usuario pregunta «cómo se compara la tendencia de compra de este cliente en los últimos tres años con la media del sector», la IA podría necesitar primero consultar los datos de pedidos del cliente, luego los datos de referencia del sector y, por último, hacer ella misma el análisis comparativo. Esto requiere soporte de arquitectura Agent; nosotros todavía no tenemos Agent en producción, pero es una dirección clara: cuando la búsqueda de un solo turno no pueda satisfacer consultas complejas, el Agentic RAG es la vía de evolución natural.
Conclusión
Tres técnicas, un mismo objetivo: llevar la búsqueda vectorial de «poder buscar» a «buscar con precisión».
Query Rewriting resuelve el problema del extremo de entrada: el abismo entre la pregunta coloquial del usuario y las palabras clave que el motor de búsqueda espera recibir. Hybrid Search resuelve la zona ciega del extremo de búsqueda: la búsqueda vectorial y la búsqueda por palabras clave tienen cada una sus carencias, y la fusión RRF hace que los resultados de ambas se complementen. Reranking resuelve el sesgo del extremo de ordenación: una similitud vectorial alta no equivale a una alta relevancia de negocio; el LLM aporta un último filtro semántico.
Apiladas las tres capas, la precisión de búsqueda pasa del 68 % al 82 % (tasa de acierto top-1), con un coste por búsqueda de unos 0,001-0,002 RMB y un incremento de latencia controlado. Las tres funciones tienen interruptores independientes y pueden activarse o desactivarse a demanda.
Esta pipeline de mejora no es un punto final. La siguiente fase del RAG es el Context Engineering: pasar de «recuperar documentos» a «construir contexto», de «devolver resultados» a «entregar respuestas». Ya estamos en camino.
Comments