Cómo construimos nuestro buscador en Mercadona Tech (y cómo construir el tuyo)
El playbook completo, abierto, para que cualquiera pueda inspirarse y montar el suyo.
Hace dos semanas publiqué un artículo sobre vibe coding donde mencioné, casi de pasada, que en Mercadona Tech habíamos construido nuestro propio buscador con Claude Code. Era un caso real, ilustrativo, dentro de un debate más amplio sobre dónde funciona programar conversando con una IA y dónde no.
No esperaba la reacción. Decenas de mensajes pidiendo detalles. Empresas pequeñas y grandes preguntando cómo lo habíamos hecho. Equipos de ingeniería contando que llevaban meses pensando en algo parecido pero no sabían por dónde empezar. Personas no técnicas queriendo entender qué hay realmente detrás de un buscador moderno.
Este artículo es la respuesta a todas esas preguntas. Y al final hay un fichero descargable que puedes darle a Claude Code para empezar tu propio proyecto siguiendo el mismo método.
Por qué lo cuento todo
En Mercadona tenemos un Modelo que se llama Calidad Total. No es un documento ni un manual: es el sistema que guía las decisiones de todos los que trabajamos en la compañía, desde la persona que repone en una tienda hasta el comité de dirección. Cuando hay que elegir entre dos caminos, el Modelo te dice cuál respeta lo que debe respetarse, y en qué orden.
El Modelo de Mercadona identifica cinco componentes a los que la empresa tiene que satisfacer simultáneamente, y lo hace en un orden concreto: primero El Jefe —que es como llamamos al cliente en Mercadona—, después el Trabajador, después el Proveedor, después la Sociedad y, finalmente, el Capital. Los cinco a la vez, pero con esa secuencia de prioridad. La frase que se repite internamente es que “para que el avión vuele, tienen que cumplirse todas las leyes de la física al mismo tiempo”: atender a todos, sin perder el orden.
El componente que me interesa hoy es el cuarto: la Sociedad. Las personas, entidades y lugares que rodean a la empresa. Juan Roig lo resume con una frase que cualquiera que trabaje cerca le ha oído alguna vez: mi sueño es compartir el modelo. Si alguien aprende a hacer las cosas bien, hay emprendedores. Si hay emprendedores, hay empresas. Si hay empresas, hay empleo. Si hay empleo, hay riqueza. Si hay riqueza bien gestionada, hay bienestar.
Compartir lo que aprendemos es, dentro del Modelo de Mercadona, una de las formas naturales de cumplir con el componente Sociedad.
Por eso este artículo no se queda en la anécdota. Voy a contar exactamente cómo está construido nuestro buscador: qué algoritmos lo componen, qué decisiones tomamos en cada capa, por qué descartamos algunas alternativas que parecían obvias, qué reglas de gobernanza aplicamos al modelo de aprendizaje, y qué stack abierto puede reproducirlo. Y voy a entregar al final un playbook descargable para que cualquier equipo, sin importar su tamaño, pueda usarlo como punto de partida.
Si lo que cuento sirve para que un equipo de tres personas en cualquier sitio reemplace un buscador caro por uno propio, mejor, y más controlable, este artículo habrá cumplido su función.
A quién le sirve esto
Antes de entrar en detalle, conviene aclarar para quién es útil lo que viene a continuación.
Este artículo está pensado para dos lectores muy distintos a la vez. El primero es alguien sin formación técnica que quiere entender realmente cómo funciona un buscador moderno: por qué a veces encuentra lo que busca y por qué otras veces no, qué está pasando cuando un sistema “aprende” de los clics, por qué unas tiendas online tienen buscadores que parecen leerte la mente y otras te muestran resultados absurdos. Para este lector, voy a explicar cada concepto antes de usarlo y a evitar la jerga gratuita.
El segundo lector es alguien técnico que quiere replicar el sistema. Para ese lector, voy a dar el detalle suficiente para que el playbook final tenga sentido: nombres concretos de algoritmos, parámetros, decisiones de validación, métricas. No voy a esconder nada relevante por miedo a que el artículo parezca denso.
Mi apuesta es que ambos lectores pueden convivir en el mismo texto si la estructura está bien. La parte técnica explica el porqué. La parte conceptual explica el qué. Y las dos juntas dan la única respuesta honesta a ¿cómo se hace un buscador?: no hay una respuesta corta, pero tampoco es magia.
Por qué un buscador propio
En una tienda online, el buscador es la puerta principal. La gente no navega catálogos cuando ya sabe lo que quiere: escribe el nombre y espera que aparezca. Si no aparece, se va. No reformula, no explora, no vuelve a probar dos veces. Se va.
En nuestra tienda online, el buscador maneja 4,4 millones de búsquedas a la semana. Si el 4% no devuelve resultados, hablamos de unos 176.000 usuarios a la semana que escriben algo razonable y no encuentran nada. Eso era exactamente lo que nos pasaba. Y era lo más educado que podía pasar: el resto de búsquedas, las que sí devolvían resultados, también podían ser mejores. Solo que ahí no teníamos un número rojo que nos avisara.
El problema con un buscador estándar —cualquier buscador estándar, sea un SaaS o el motor que viene con el e-commerce— es que está diseñado para ser bueno con cualquier catálogo. Eso suena bien hasta que recuerdas que tu catálogo no es cualquier catálogo. Tus usuarios no escriben como los de cualquier otra tienda. Tu negocio no premia los mismos resultados que el de tu competidor. Y, sobre todo, tienes datos de comportamiento real —qué buscan, qué clican, qué compran— que un buscador genérico no puede aprovechar bien porque no son suyos.
Construir el tuyo te da tres cosas concretas. La primera es control sobre el ranking: tú decides qué señales pesan más, cómo se ponderan, qué hacer con productos que aparecen mucho pero se compran poco, qué hacer con productos nuevos que todavía no tienen historial. La segunda es mejora dirigida: cada decisión que tomas se mide contra los datos reales de tu negocio, no contra un benchmark sintético. Si una decisión mejora un 1% el ranking de tu catálogo, te lo llevas tú. La tercera es propiedad de la pieza: una de las decisiones más críticas del negocio deja de depender de un proveedor externo y pasa a ser conocimiento que se queda dentro del equipo.
Hay una cuarta razón, menos romántica pero igual de relevante: el coste. Un buscador SaaS razonablemente serio cuesta varios miles de dólares al mes para un volumen como el nuestro. Un buscador propio bien diseñado cuesta una fracción. Eso no es razón suficiente por sí sola —si gastando dinero compras calidad, gasta dinero—, pero cuando construyendo el tuyo *además* mejoras la calidad, el cálculo deja de ser una decisión y se convierte en una conclusión.
Decidimos construir el nuestro. Lo que viene a continuación es exactamente cómo lo hicimos.
La arquitectura, en una página
Antes de entrar en cada componente, conviene tener una imagen mental. Un buscador moderno parece complejo, pero no lo es tanto si lo ves como un proceso de cuatro pasos.
Imagina que entras en una librería gigantesca con un papel donde has escrito tres palabras del libro que buscas. Para encontrarlo mandas a dos personas. La primera busca todos los libros cuyo título contenga literalmente esas tres palabras. La segunda busca libros que, aunque no usen exactamente esas palabras, traten del mismo tema. Vuelven las dos con su lista. Tú las cruzas, descartas los libros que no estén en esa librería concreta, y un experto en el catálogo te ordena lo que queda según lo que sabe del negocio: qué libros se prestan más, cuáles son recientes, cuáles encajan mejor con tu petición. Lo que tú ves es la lista final.
Eso es, casi literalmente, lo que hace un buscador como el nuestro. Cambia “libros” por “productos”, “dos personas” por “dos algoritmos de búsqueda” y “experto en el catálogo” por “modelo de aprendizaje”, y tienes toda la arquitectura.
Veamos las piezas.
1. Normalizar la consulta
Cuando alguien escribe “Café Molido”, el sistema convierte ese texto en su forma canónica: minúsculas, sin acentos, separado en palabras. “Café Molido” pasa a ser una lista con dos elementos: “cafe” y “molido”. La regla de oro: la normalización al consultar tiene que ser **exactamente la misma** que la normalización al indexar el catálogo. Si lo indexas con acento y lo buscas sin acento, no hay match. En nuestro catálogo descubrimos que el 100% de los usuarios escribe sin acentos: eso decidió la convención.
2. Dos búsquedas en paralelo
Sobre la consulta normalizada, el sistema lanza dos búsquedas simultáneas.
La primera es **léxica**: busca productos cuyo nombre, marca o descripción contenga literalmente las palabras del usuario. Si escribes “leche”, encuentra productos con “leche” en alguna parte. Lo hace con **BM25**, un algoritmo clásico que puntúa cada producto según cuántas veces aparece la palabra y lo rara que es esa palabra en el catálogo (las palabras raras puntúan más). Corre sobre **Tantivy**, un motor escrito en Rust, embebido en el servicio, sin clúster aparte. Devuelve los 100 mejores candidatos.
La segunda es **semántica**: convierte la consulta en un vector de 384 números que representa su “significado” y busca, en una matriz precomputada de todos los productos, cuáles son más parecidos en ese espacio. Encuentra cosas que la primera no encuentra: si buscas “para fregar”, puede traerte “estropajo” aunque no contenga la palabra “fregar”. El modelo que genera los vectores se llama **e5-small** —abierto, multilingüe, ligero— y lo ejecutamos como ONNX INT8, una versión optimizada que cabe en 6 MB de memoria y responde en milisegundos sin tarjeta gráfica. Devuelve los 50 mejores candidatos.
3. Fusionar las dos listas
Tenemos dos listas con candidatos que a veces se solapan y a veces no. La técnica que usamos para combinarlas se llama **Reciprocal Rank Fusion**: cada producto recibe puntos inversamente proporcionales a su posición en cada lista. Si aparece el 1º en una y el 5º en la otra, suma por ambas. Si solo aparece en una, suma por una. Es robusta y no requiere calibrar pesos: solo usa posiciones, no puntuaciones absolutas, lo que la hace ciega al hecho de que BM25 y similitud semántica viven en escalas distintas.
Tras la fusión queda una lista de unos 60 candidatos. A continuación se aplica un filtro: descartar los productos que no estén en el surtido de la tienda concreta del usuario. Cómo hacemos ese filtro de forma eficiente es una decisión interesante por sí misma — la cuento en la siguiente sección.
4. Reordenar con aprendizaje automático
Los 60 candidatos que quedan están razonablemente filtrados, pero no están bien ordenados. Decidir qué producto va arriba requiere algo más que las puntuaciones anteriores: requiere un modelo entrenado con datos reales del negocio.
Ese modelo se llama Learning To Rank. En nuestro caso es CatBoost YetiRank, un algoritmo basado en árboles de decisión optimizado para problemas de ordenación. Recibe los 60 candidatos junto con 14 características de cada uno —su puntuación BM25, su parecido semántico, cuántas veces se ha comprado en las últimas semanas, lo popular que es entre clientes habituales, si lleva poco tiempo en el catálogo— y produce el orden final. Tarda menos de un milisegundo en hacerlo.
A todo esto le acompaña una pieza separada: el autocompletado, las sugerencias que aparecen mientras el usuario escribe. Esto no es una búsqueda completa: es un Trie (un árbol de prefijos) que devuelve, en microsegundos, productos cuyo nombre empieza por lo que llevas escrito. Tres señales para ordenar las sugerencias: en qué campo aparece el match, si coincide la palabra entera o solo el prefijo, y la posición dentro del nombre.
El presupuesto total
Todas las piezas se ejecutan en un tiempo casi imperceptible: menos de 15 milisegundos en el 99% de las consultas. En la práctica nuestra mediana es de 12 ms. Parpadear tarda unos 300 ms — el buscador entero responde unas 20 veces más rápido que un parpadeo. Cada componente tiene su sub-presupuesto, y si alguno se pasa, el sistema deja de responder a tiempo y la experiencia se degrada. Esa restricción estructura las decisiones que vienen a continuación.
Cinco decisiones que separan un prototipo de un buscador real
Las decisiones que vienen a continuación son las que más nos costó tomar y las que más diferencia hicieron. Cada una es independiente: pueden adoptarse por separado en cualquier proyecto. Y cada una responde a una alternativa que parecía obvia al principio y resultó equivocada al final.
1. Búsqueda híbrida: ninguna de las dos por separado funciona
La tentación inicial es elegir uno: o lexical, o semántico. La búsqueda lexical es rápida, predecible y barata. La semántica es lista, encuentra sinónimos y maneja preguntas en lenguaje natural. ¿Por qué hacer las dos?
Porque por separado son malas. Si solo usas lexical, el 33% de las consultas no devuelven resultados: alguien escribe “para fregar”, no aparece la palabra “fregar” en ningún producto, y el sistema se rinde. Si solo usas semántica, todo encuentra algo, pero ese “algo” es a menudo ruido: el modelo cree que “agua mineral” se parece a “agua oxigenada” y te las mezcla en el ranking.
Las dos juntas se complementan. La semántica garantiza recall (que siempre haya candidatos) y la lexical garantiza precisión (que los candidatos obvios estén ahí). En nuestros datos, el recall@50 sube de 0,547 (solo lexical) a 0,853 (híbrido). El porcentaje de búsquedas sin resultados pasa del 33% al 0%. Y luego, sobre las dos listas combinadas, el modelo de aprendizaje hace de juez final: aprende de los clics qué resultados son realmente buenos y qué resultados, aunque parezcan relevantes, los usuarios ignoran.
Cómo decidirla en tu caso: si tu catálogo tiene vocabulario abierto, queries en lenguaje natural o sinónimos relevantes, necesitas la capa semántica. Si tu catálogo es pequeño y los usuarios escriben siempre con el vocabulario del catálogo, quizá puedas empezar solo con lexical y añadir la semántica después. Pero la mayoría de catálogos reales necesitan ambas.
2. Un solo índice maestro con bitsets, no un índice por tienda
El surtido de productos cambia de una tienda a otra: no todas las tiendas tienen los mismos productos en stock. La forma ingenua de manejar esto es construir un índice de búsqueda independiente para cada tienda. En nuestro caso, eso son 762 índices, replicarlos para distintos órdenes de resultados, mantenerlos actualizados, reindexar uno cada vez que cambia un surtido.
La alternativa que adoptamos: un solo índice maestro con todo el catálogo, y para cada tienda mantenemos un **bitset** —un mapa de bits, un array binario donde cada bit representa “este producto está disponible aquí, sí o no”—. Cuando alguien busca desde una tienda, ejecutamos la búsqueda contra el índice maestro y filtramos el resultado haciendo una operación AND entre los IDs de los productos encontrados y el bitset de su tienda.
Las cifras hablan solas: 254 bitsets, cada uno de 813 bytes, suman **200 KB en total**. Una operación AND sobre un bitset es cuestión de microsegundos. Actualizar un surtido es sustituir un bitset entero, otra operación trivial. Comparado con mantener 762 índices físicamente separados, multiplicas por mil la simplicidad operativa y por mil el ahorro de almacenamiento.
Cómo decidirla en tu caso: siempre que tengas multi-tenancy con catálogos solapado —tiendas, marcas, regiones, idiomas— el patrón “índice maestro + bitset por tenant” gana. La regla es: ¿la mayoría del catálogo es común a todos los tenants? Sí → bitsets. ¿Cada tenant tiene un catálogo radicalmente distinto? Entonces sí, índices separados.
3. Validación walk-forward: nunca mezcles clics al azar
Cuando entrenas un modelo de ranking, necesitas separar tus datos en entrenamiento y test. La forma estándar en machine learning es coger todos los datos, mezclarlos al azar, y reservar el 20% para test. Esto se llama validación cruzada aleatoria (random k-fold).
En un buscador esto está mal. Los clics tienen estructura temporal: estacionalidad, lanzamientos de producto, campañas internas, días con más tráfico que otros. Si mezclas clics aleatoriamente, mezclas pasado y futuro, y el modelo “aprende” cosas que en producción no podría haber sabido. El resultado son métricas infladas: tu modelo parece haber mejorado un 5-10% más de lo que realmente mejorará en producción.
La alternativa correcta se llama **walk-forward**: entrenas con las semanas 1, 2 y 3, validas con la semana 4. Después puedes deslizar la ventana: entrenas con 2, 3 y 4, validas con 5. Y así. El modelo siempre se evalúa contra un futuro real, no contra un futuro que ya conoce.
Cómo decidirla en tu caso: cuando los datos tengan dimensión temporal —y en un buscador siempre la tienen—, walk-forward es obligatorio. No es opcional. Es una de esas decisiones que parecen un detalle metodológico y son, en realidad, la diferencia entre desplegar un modelo que mejora la métrica de negocio y desplegar uno que la degrada.
4. Corregir el sesgo de posición: clics no es lo mismo que relevancia
Hay un problema sutil con entrenar un modelo a partir de los clics de los usuarios: los usuarios clican más los primeros resultados independientemente de si son relevantes o no. Hay estudios serios sobre esto: el primer resultado se clica unas seis veces más que el quinto, aunque el quinto sea exactamente igual de bueno. Si entrenas un modelo asumiendo que “clic = relevante”, el modelo aprende a poner siempre arriba los productos que ya estaban arriba. Tu modelo se refuerza a sí mismo, los productos del top dominan, los productos buenos pero menos visibles nunca emergen, la diversidad del catálogo colapsa, y la calidad cae sin que te des cuenta. Esto se llama feedback loop o Relevance Feedback para los padres de la Recuperación de Información.
La corrección estándar se llama Inverse Propensity Weighting (IPW): a cada clic le das un peso inversamente proporcional a la posición en la que apareció. La fórmula que usamos es 1 dividido entre el logaritmo en base 2 de la posición más uno. Un clic en la posición 1 cuenta poco; un clic en la posición 8 cuenta mucho más, porque el usuario tuvo que ignorar siete resultados antes de llegar a él. Eso sí es una señal fuerte de relevancia.
Y lo complementamos con exploración: en el 5% de las búsquedas, el sistema mete deliberadamente 2-3 resultados aleatorios en las posiciones 3, 5 y 7. Suena raro pero es necesario: sin exploración, los productos nuevos nunca reciben clics y se quedan atrapados abajo para siempre. El 5% es un coste tolerable para evitar un equilibrio subóptimo permanente.
Cómo decidirla en tu caso: si tu modelo aprende de clics, IPW es obligatorio y exploración también. No hay alternativa razonable.
5. Guardrail del −2%: ningún modelo peor pasa, automáticamente
Reentrenar un modelo cada semana suena bien, hasta que un día el reentrenamiento produce un modelo peor. Si lo despliegas sin más, los usuarios siguen buscando, los clics siguen llegando —porque no tienen alternativa— y tu siguiente reentrenamiento se hace con datos sesgados por un modelo malo. La degradación es invisible y se acumula.
La defensa que aplicamos es un guardrail automático: el pipeline de reentrenamiento solo despliega un modelo si **ninguna de cuatro métricas cae más de un 2%** respecto al modelo en producción. Las cuatro métricas son MRR y NDCG, evaluadas tanto sobre el conjunto de test temporal (walk-forward) como sobre un golden set estático de 500 consultas con la respuesta ideal anotada manualmente. El golden set no se modifica nunca: es la única referencia inmune al feedback loop.
El pipeline produce tres decisiones posibles. **PROMOTE** si el candidato mejora más de un 0,5%. **HOLD** si está en el rango neutro entre −2% y +0,5% (queda en cuarentena, no se despliega). **REJECT** si cae más de un 2%. Y aún en el caso de PROMOTE, el despliegue real espera una hora antes de activarse, durante la cual cualquier persona del equipo puede abortarlo. Es el último gate humano.
Cómo decidirla en tu caso: si despliegas modelos de forma automática, necesitas un guardrail. El umbral exacto depende de tu sensibilidad: un −2% es estricto pero adecuado para un buscador con tráfico crítico. Para un sistema con menos riesgo puedes usar −5%. Pero el patrón —reglas automáticas + métrica independiente del propio sistema (golden set) + ventana humana antes del deploy— es universal.
El stack (todo abierto, todo replicable)
Una de las cosas que más sorprende al construir esto es lo poco exótico que es el stack. No hay tarjetas gráficas dedicadas, no hay bases de datos vectoriales, no hay servicios externos de cobro. Todo lo que viene a continuación es código abierto, cabe en un repositorio Python, y se ejecuta sobre máquinas estándar.
El motor lexical: Tantivy
Para la búsqueda por palabras clave usamos Tantivy, una librería escrita en Rust inspirada en Apache Lucene (La madre de todos los motores de búsqueda que ves hoy en día creado por Doug Cutting hace más de 25 años en Xerox Park). Lo más importante de Tantivy no es el rendimiento (que es excelente: respuestas en milisegundos sobre catálogos de miles de productos), sino que se ejecuta dentro del propio servicio. No hay un clúster aparte, no hay servidores de búsqueda dedicados, no hay JVM que mantener. El índice ocupa unos 20 MB de memoria y vive en el mismo proceso que el resto del código.
Tantivy soporta de forma nativa lo que necesitas para un buscador real: tokenización configurable, búsqueda por prefijos para el autocompletado, *facetas* para filtros (por categoría, marca, etc.), y *highlighting* de los términos coincidentes. La alternativa habitual —Elasticsearch o OpenSearch— está pensada para catálogos del tamaño de los de Wikipedia: si tu catálogo tiene menos de 100.000 documentos, Tantivy es probablemente la elección correcta.
El modelo semántico: e5-small ejecutado con ONNX Runtime
Para la capa semántica usamos un modelo de embeddings abierto llamado multilingual-e5-small, publicado por Microsoft Research. “Small” significa que el modelo tiene unos 118 millones de parámetros: pequeño en términos de modelos modernos, pero más que suficiente para nombres de producto cortos. Genera vectores de 384 dimensiones por consulta y por documento.
Ejecutar este modelo con su forma original (PyTorch) tarda unos 20 ms por consulta en CPU. Demasiado para nuestro presupuesto de latencia. La solución estándar es convertirlo al formato ONNX (Open Neural Network Exchange) y ejecutarlo con ONNX Runtime, una librería de inferencia muy optimizada. Con la cuantización a enteros de 8 bits (INT8) —una técnica que reduce la precisión numérica a cambio de un 4x de velocidad sin pérdida medible de calidad— el modelo cabe en 6 MB de memoria y devuelve un vector en 3-5 ms en una CPU normal.
No hace falta GPU, no hace falta una base de datos vectorial. La matriz completa de embeddings de todo el catálogo —unos 4.300 productos por 384 dimensiones— ocupa 6 MB en RAM. La búsqueda por similitud es una multiplicación de matriz NumPy y un argsort: 1 ms para todos los productos.
El modelo de ranking: CatBoost YetiRank
El re-ranking final lo hace CatBoost, una librería de gradient boosting publicada como código abierto por Yandex. Lo elegimos tras una competición interna entre cinco algoritmos: CatBoost YetiRank, XGBoost, LightGBM con LambdaRank, una baseline Pointwise y una Listwise. CatBoost YetiRank ganó con menor varianza entre folds (MRR 0,867 ± 0,014) y con la mejor inferencia: el modelo entrenado pesa unos 5 MB y predice el orden de 60 candidatos en menos de un milisegundo.
YetiRank es la función de pérdida específica para problemas de ordenación que CatBoost incorpora: en lugar de optimizar la predicción de un valor (regresión) o una clase (clasificación), optimiza directamente el orden relativo entre documentos para una misma consulta. Es lo correcto técnicamente para learning-to-rank y, en nuestra competición, fue también lo correcto empíricamente.
El autocompletado: un Trie
El autocompletado no usa el motor de búsqueda. Usa una estructura de datos clásica llamada Trie (un árbol de prefijos), donde cada nodo representa una letra y cada camino desde la raíz hasta una hoja es un prefijo de una palabra del catálogo. Para encontrar las sugerencias de “atu”, recorres tres pasos en el árbol y devuelves todas las palabras que cuelgan de ahí.
La búsqueda en un Trie es del orden de microsegundos, no milisegundos. En nuestro caso, p50 = 3 microsegundos, p99 = 388 microsegundos. Eso permite responder a cada tecla que el usuario pulsa sin que la red sea el cuello de botella.
El resto: Python, NumPy, scikit-learn
El pegamento que une todas estas piezas es Python. La capa de servicio recibe la consulta, llama a Tantivy, llama al runtime ONNX para el embedding, hace el merge RRF con NumPy, aplica el bitset de la tienda, calcula las 14 features de los candidatos restantes, llama a CatBoost para el ranking final, y serializa el resultado. Toda la lógica matemática descansa en NumPy, y scikit-learn se usa solo durante el entrenamiento offline (split de datos, métricas, baselines).
No hay nada en este stack que no puedas instalar con un pip install o un cargo add. No hay licencias propietarias, no hay servicios externos de cobro recurrente, no hay infraestructura especializada. Esa es deliberadamente la apuesta: si la infraestructura es estándar, el conocimiento que generes es portable, y la pieza queda dentro del equipo.
Resumen de dependencias
- Búsqueda lexica: **Tantivy** (Rust, licencia MIT)
- Embeddings: **multilingual-e5-small** (MIT)
- Inferencia de embeddings: **ONNX Runtime** (MIT)
- Ranker: CatBoost (Apache 2.0)
- Pegamento: **Python + NumPy + scikit-learn** (BSD)
- Almacenamiento de matrices: NumPy en RAM, sin base de datos vectorial
Todo esto cabe en un proceso del orden de 100 MB de RAM (modelo + índice + matriz de embeddings + runtime). Una máquina modesta lo ejecuta sin despeinarse.
El workflow: cómo se trabaja con Claude Code en un proyecto así
He escrito ya, en artículos anteriores, sobre cómo cambia el trabajo cuando una parte del equipo lo hace conversando con un agente de IA. No voy a repetir aquí ese debate. Voy a contar, en concreto, cómo se distribuyó el trabajo en este proyecto, porque creo que es la parte más útil para alguien que quiera replicarlo.
El reparto: humano decide, agente ejecuta
La regla mental que aplicamos es simple. Todo lo que sea explorar —analizar datos, probar configuraciones, comparar alternativas, escribir scripts de evaluación, generar tablas— lo hace el agente. Todo lo que sea decidir —qué arquitectura adoptar, qué validación usar, qué guardrails poner, qué descartar— lo hacen las personas.
Esa distinción importa porque las dos partes son del mismo trabajo. Sin la exploración masiva, las decisiones se toman a ciegas. Sin las decisiones, la exploración se vuelve una pila de experimentos sin convergencia. La velocidad del agente es lo que permite explorar 175 configuraciones de BM25 en lugar de 5, comparar 3 modelos de embeddings en lugar de quedarse con el primero que funciona, y validar el ranker contra una competición de 5 algoritmos en lugar de adoptar el de moda. Es lo que convierte “una decisión basada en intuición” en “una decisión basada en datos reales del catálogo”.
Las cuatro fases del proyecto
El proyecto avanzó en cuatro fases bien delimitadas, cada una con un experimento canónico, un fichero de evaluación versionado y una decisión documentada al final.
Fase 0: exploración de datos. Empezamos sin escribir una sola línea de código de producto. Conectamos al agente los 479 MB de datos del catálogo, las analíticas, las consultas reales y los datos de compras, y le pedimos que respondiera preguntas concretas: ¿cuántas palabras tiene una consulta media?, ¿qué porcentaje contiene tildes?, ¿qué vocabulario aparece y con qué frecuencia? Aprendimos cosas que cambiaron decisiones posteriores: el 93,7% de las consultas tienen una sola palabra, el 100% se escriben sin acentos, el vocabulario activo son unos 1.300 términos. Sin estos datos, habríamos optimizado el sistema para problemas que no teníamos.
Fase 1: baseline lexico. Antes de complicarse, hay que tener un baseline. El agente probó 175 configuraciones de BM25 en una *grid search*. El ganador resultó ser BM25 con k1=0,5 y b=0 — ese cero en b es importante: significa no normalizar por longitud del documento, contraintuitivo en un buscador típico, pero correcto en un catálogo donde los nombres de producto son cortos y uniformes. Esto solo se descubre probando.
Fase 2: capa semántica. Con el baseline encima de la mesa, el agente comparó tres modelos de embeddings. e5-small ganó por equilibrio entre calidad y velocidad. Lo más interesante de esta fase no fue ganar un punto de MRR, sino constatar que la búsqueda semántica por sí sola produce demasiado ruido, y que la idea correcta era combinarla con la lexical, no sustituirla.
Fase 3: Learning To Rank. La que más tiempo nos llevó. Cinco modelos, validación cruzada con cinco particiones temporales, comparación de features, análisis de importancias. La decisión final —CatBoost YetiRank con 14 features— es producto de un experimento controlado, no de una intuición. La importancia de cada feature se midió: popularidad 37,5%, embeddings 29,8%, BM25 12,9%. Saber esto no fue accesorio: nos dio confianza para defender decisiones más adelante, por ejemplo descartar reglas manuales que solo replicaban señales que el modelo ya estaba capturando.
Fase 4: personalización. Aquí aprendimos negativamente. Probamos features personalizadas (afinidad por categoría, si el usuario es habitual). Su importancia offline resultó ser del 0%. La conclusión no fue “la personalización no funciona”, fue “no podemos validarla offline sin un mapeo consulta-usuario que no tenemos”. La decisión: aplazarla para test A/B en producción. A veces, el resultado más útil de una fase es saber que la fase no estaba lista.
El truco que sostiene todo: un CLAUDE.md no negociable
Si hay un solo elemento del que depende que este método funcione, es el fichero de reglas que vive en la raíz del proyecto y que el agente lee al principio de cada sesión. Lo llamamos CLAUDE.md. No es documentación; son restricciones.
Las reglas se dividen en cinco bloques: presupuestos de latencia (cada componente con su milisegundo máximo), reglas de arquitectura (qué algoritmos no se sustituyen sin proceso explícito), reglas de aprendizaje automático (IPW, walk-forward, golden set, guardrails), reglas de integración continua (qué tests bloquean un merge), y reglas de despliegue. Cada regla viene con su justificación —el porqué— y la consecuencia de violarla. Si el agente, en una sesión cualquiera, sugiere algo que viola una regla, hay un mecanismo de bloqueo que lo detiene antes de que entre al repositorio.
Este fichero es el conocimiento estable del proyecto. Es donde vive lo que hemos aprendido y no queremos volver a aprender. Es lo que se queda cuando el agente de IA cambia de versión, cuando el equipo rota, cuando el contexto de una conversación se corta. Es, literalmente, el componente que hace que un proyecto construido con vibe coding sea un proyecto, y no una colección de scripts que funcionaron una vez.
Y es, precisamente, lo que viene a continuación: el playbook completo en formato CLAUDE.md que puedes descargar y usar como punto de partida para tu propio buscador.
El playbook que liberamos
Al pie de este artículo encontrarás un fichero descargable: **searchmo-playbook.md**. No es un manifiesto ni una guía teórica. Es la misma plantilla de reglas que rige nuestro propio buscador, generalizada para que cualquiera pueda darle uso.
¿Qué contiene?
El fichero tiene cuatro bloques:
Reglas no negociables. Las restricciones que rigen el proyecto y que un agente de IA no puede violar sin proceso explícito. Incluye los presupuestos de latencia por componente (15 ms en total, distribuidos), las decisiones de arquitectura (no usar base de datos vectorial, no clúster externo, índice maestro con bitsets) y las reglas de aprendizaje automático (IPW obligatorio, walk-forward obligatorio, golden set obligatorio, guardrail −2%).
Las cuatro fases del proyecto. El orden en el que avanzar, con un objetivo medible al final de cada una. Fase 0: caracterización del catálogo y las consultas. Fase 1: baseline lexical con grid search. Fase 2: capa semántica con comparación de modelos. Fase 3: learning-to-rank con competición de algoritmos. Cada fase incluye prompts sugeridos para Claude Code: cómo pedirle que ejecute el grid search, cómo pedirle que monte el comparador de embeddings, cómo pedirle que entrene los cinco modelos de ranking.
Checklist de las cinco decisiones algorítmicas. Para cada una, los criterios que te ayudan a decidir cómo aplicarla en tu caso concreto. Si tu catálogo tiene tales características, decisión X. Si no, decisión Y.
Stack mínimo. Las dependencias concretas, con versiones probadas. Tantivy, multilingual-e5-small, ONNX Runtime, CatBoost, Python, NumPy, scikit-learn. Todo abierto, todo replicable.
¿Cómo usarlo?
1. Descarga el fichero y guárdalo como CLAUDE.md en la raíz de un repositorio nuevo.
2. Abre Claude Code en ese directorio.
3. Pídele que lea las reglas y empiece por la Fase 0.
4. A partir de ahí, trabajas conversación por conversación, fase por fase, con el agente ejecutando los experimentos y tú tomando las decisiones al final de cada uno.
El proceso completo nos llevó un mes, pero el 70% del trabajo —exploración de datos, baseline lexical, capa semántica y primera versión del ranker— se hizo en un fin de semana largo. El resto del mes fue refinamiento: gobernanza del modelo, golden set, pipeline de reentrenamiento y guardrails. No es un proyecto de un fin de semana en el sentido amateur del término. Pero tampoco un proyecto que requiera un equipo de quince personas: es un proyecto que un par de personas con criterio y un agente de IA pueden afrontar.
Lo que el playbook no resuelve por ti
Hay tres cosas que el fichero no puede resolver, y conviene saberlo antes de empezar.
Tu catálogo.El playbook describe el método. Los datos de tu catálogo son tuyos: qué productos tienes, cómo los describes, qué señales de comportamiento tienes registradas. Cuanta más calidad tengan estos datos —especialmente el log de clics— más rápido converge el sistema.
Tu juicio. Las cinco decisiones algorítmicas tienen un porqué; ese porqué se aplica al 80% de los casos. El 20% restante necesita criterio. El playbook te enseña qué preguntar, no qué responder.
Tu rigor. La parte más exigente no es la algorítmica: es la disciplina de medir, evaluar contra un golden set inmutable y respetar los guardrails cuando tu propio modelo se degrada. Esa parte la pones tú.
Lo que pedimos a cambio
Nada. El fichero se libera bajo licencia MIT. Puedes copiarlo, modificarlo, usarlo en proyectos comerciales, no atribuir, no devolver nada. El componente sociedad del Modelo de Mercadona no funciona como un trueque. Funciona como una multiplicación: si lo que aprendimos sirve para que otros equipos hagan algo mejor, estamos cumpliendo con el cuarto componente del Modelo de Mercadona a nuestra manera.
Si el playbook te sirve, nos encantaría saberlo. Pero no es una condición. Es solo curiosidad.
Al principio del artículo dije que no quería quedarme en la anécdota. He dado el detalle algorítmico, las decisiones críticas, el stack, el método de trabajo y un fichero descargable que reproduce todo lo que hemos aprendido. Si has llegado hasta aquí, ya tienes lo que necesitas para empezar tu propio buscador.
Compartir un playbook técnico es una forma de cumplir con el componente Sociedad del Modelo de Mercadona: una manera de operar en el día a día que también beneficie a quien está fuera del perímetro de la empresa.
Yo no espero que un equipo en otra empresa lea esto y construya el mejor buscador de la historia. Espero que alguien con un buscador caro, lento o poco controlable lea el artículo, descargue el fichero y se ahorre semanas de prueba y error. Si le ahorramos a un equipo el coste de aprender a tropezar, ya hemos cumplido nuestra parte.
El sueño de Juan Roig es compartir el modelo. Aplicado a un buscador parece pretencioso, pero es exactamente el mismo gesto: si alguien aprende a hacer algo bien, hay emprendedores; si hay emprendedores, hay empresas; si hay empresas, hay empleo; y, al final del camino, hay bienestar. Compartir lo que sabemos no es generosidad ni marketing. Es el modo en que un componente del Modelo de Mercadona se conecta con el siguiente.
Si construyes algo a partir de esto, escríbeme. No para felicitarme: para contarme qué decidiste de forma distinta y por qué. Eso es lo que más nos sirve a todos.
SearchMO Playbook — CLAUDE.md para construir tu propio buscador

