{
  "title": "Reparar la búsqueda en el comercio B2B en la era de la IA",
  "excerpt": "Audité 775 000 registros de productos que alimentan la búsqueda de cuatro distribuidores B2B. Los gigantes del consumo ya han publicado cómo arreglar catálogos como estos con LLMs, pero la distribución comercial no ha adoptado el manual. Aquí está, adaptado para HVAC, plomería y electricidad, con costos reales.",
  "content_html": "<p>La búsqueda en el comercio B2B tiene un secreto incómodo: el algoritmo de ranking rara vez es el problema.</p>\n<p>Cuando los resultados son malos, los equipos tiran de las palancas divertidas. Boosts, sinónimos, embeddings, rerankers y, últimamente, comprensión de consultas con LLMs. Yo mismo he tirado de todas esas palancas. La semana pasada hice lo poco glamoroso en su lugar: volqué todos los registros de productos de cuatro índices de búsqueda en producción —775 051 registros de cuatro distribuidores B2B en HVAC, plomería, suministro industrial y fijaciones— y audité los datos en sí. Usé doce agentes de IA ejecutándose en paralelo, cada uno asignado a una dimensión: cobertura, calidad del texto, números de parte, marcas, precios, especificaciones, categorías, medios, duplicados.</p>\n<p>Lo que volvió no sorprendería a nadie que haya trabajado en el catálogo de un distribuidor, y sería impactante para quien no lo haya hecho. Pero lo que se me quedó grabado es esto: la solución ya está publicada. Amazon, DoorDash e Instacart han pasado dos años documentando, en blogs públicos de ingeniería, exactamente cómo usan LLMs para limpiar, etiquetar y enriquecer catálogos desordenados a escala. La distribución comercial en su mayoría no se ha dado cuenta. Lo cual es extraño, porque sus catálogos son más desordenados, sus consultas valen más (un contratista ordenando un condensador de $4 000, no un almuerzo de $12) y el tamaño de sus catálogos hace que la economía sea más sencilla.</p>\n<p>Así que esta publicación es ese manual, adaptado para la distribución comercial: los problemas de datos que rompen la búsqueda B2B, cómo encontrarlos esta tarde con un agente de codificación, cómo solucionarlos con LLMs incluyendo la categorización UNSPSC, y cuánto cuesta todo. Spoiler del último: menos de lo que imaginas por dos órdenes de magnitud.</p>\n<h2>El manual ya existe, solo no en nuestra industria</h2>\n<p>Algo de lo que las empresas de marketplaces han publicado:</p>\n<ul>\n<li>DoorDash usa LLMs con generación aumentada por recuperación para <a href=\"https://careersatdoordash.com/blog/how-doordash-leverages-llms-for-better-search-retrieval/\">construir su grafo de conocimiento de productos y mejorar la recuperación en búsqueda</a>: extracción automatizada de marcas, extracción de atributos y vinculación de entidades entre millones de artículos proporcionados por comerciantes.</li>\n<li>Amazon construyó <a href=\"https://www.amazon.science/blog/building-commonsense-knowledge-graphs-to-aid-product-recommendation\">COSMO</a>, un grafo de conocimiento generado por LLMs que conecta lo que los clientes quieren decir (\"zapatos para mujeres embarazadas\") con lo que son los productos (\"zapatos antideslizantes\"). Reportan hasta un 60 % de mejora en el rendimiento de recomendaciones en pruebas offline.</li>\n<li>Instacart integró LLMs en su stack de búsqueda para <a href=\"https://tech.instacart.com/supercharging-discovery-in-search-with-llms-556c585d4720\">generar contenido de descubrimiento</a> y está <a href=\"https://www.instacart.com/company/tech-innovation/building-the-intent-engine-how-instacart-is-revamping-query-understanding-with-llms\">reconstruyendo la comprensión de consultas en torno a ellos</a> —con la generación pesada hecha offline por lotes, precisamente para mantener los costos bajos.</li>\n</ul>\n<p>El hilo común es fácil de pasar por alto: las victorias de LLMs están del lado del catálogo y son offline. Estas empresas enriquecen los datos antes de que llegue una sola consulta. Los sistemas de descubrimiento se dividen en un lado offline que construye artefactos (el catálogo, los índices, los embeddings) y un lado online que recupera y ordena sobre ellos, y casi todo el valor publicado de LLMs cae del lado offline. La capa de ranking solo puede ser tan buena como lo que se le alimenta.</p>\n<p>Ahora imagina el catálogo de un distribuidor típico de HVAC o plomería junto a eso. Está cosido a partir de exportaciones de ERP, hojas de cálculo de proveedores, feeds de grupos de compra y un parser de CSV que alguien escribió hace una década. Los \"títulos\" de producto son abreviaturas de factura escritas para los pickers de almacén. Y el tráfico de búsqueda que lo golpea es de los más densos en intención que existen: la mitad son números de parte exactos, el resto jerga comercial como \"3/4 cxc 90 ell\". Honestamente, no se me ocurre un mejor entorno para este manual. Las consultas son valiosas, los datos son reparables y, con unos pocos cientos de miles de SKU, un pase completo de LLMs sobre el catálogo cuesta cientos de dólares. No millones. Cientos.</p>\n<p>Así es la forma de la auditoría, porque querrás reproducirla:</p>\n<pre><code class=\"language-mermaid\">flowchart TD\n    D[\"Volcado crudo del catálogo&lt;br/&gt;(JSONL, un registro por línea)\"] --> C1[\"Agentes de cobertura por catálogo&lt;br/&gt;tasas de nulos, distribuciones, campos muertos\"]\n    D --> C2[\"Agentes de dimensión&lt;br/&gt;texto · números de parte · marcas · precios&lt;br/&gt;especificaciones · categorías · medios · duplicados\"]\n    C1 --> S[\"Síntesis: hallazgos ordenados por&lt;br/&gt;severidad, con registros de ejemplo\"]\n    C2 --> S\n    S --> F[\"Pipeline de corrección: reglas + enriquecimiento con LLMs&lt;br/&gt;+ clasificación UNSPSC + compuertas de ingesta\"]\n</code></pre>\n<p>Una nota sobre el formato antes de los prompts. Todo lo de abajo funciona con un volcado genérico: un objeto JSON por línea, cada uno con un <code>id</code> único y los campos del registro. Elasticsearch, OpenSearch, Solr, Algolia, Typesense, un PIM, una exportación simple de base de datos —no importa. Cada plataforma puede producir esta forma, y cada prompt aquí se ejecuta contra ella.</p>\n<h2>Categoría 1: Fallos silenciosos del pipeline</h2>\n<p>Lo peor que encontré no fueron datos malos que alguien escribiera. Fueron datos que el pipeline destruyó en el camino de entrada, sin decírselo a nadie.</p>\n<p>Estos catálogos construyen su texto de búsqueda principal en el momento de la ingesta, usando una regex scripteada que extrae dimensiones y unidades de los títulos de producto. En títulos largos, la regex supera el límite de complejidad del motor, el procesador falla y el registro se indexa de todos modos —con un campo de error que nadie lee y sin campo de búsqueda principal en absoluto.</p>\n<p><img src=\"/assets/images/b2b-search-silent-failures.png\" alt=\"Documentos indexados sin campo de búsqueda principal, por distribuidor\" /></p>\n<p>En toda la flota, 52 570 registros estaban sentados en el índice, invisibles para la ruta principal de consulta. En el peor catálogo, eso es un producto de cada siete. Ningún dashboard lo detectó porque nada falló. El pipeline devolvió éxito todo el día, todos los días, durante meses.</p>\n<p>Dos hallazgos más de la misma familia. El espejo obsoleto: un campo contiene la verdad (un mapa de derechos por cuenta) y un segundo campo aplanado lo refleja para filtrado. En un catálogo, el espejo se había desviado en el 19,85 % de los registros —31 000 productos activos ocultos erróneamente de la búsqueda filtrada por cuenta. Y el catálogo congelado: un índice no se había actualizado en diez semanas mientras sus hermanos se actualizaban diariamente. El uptime se monitoreaba. La frescura no.</p>\n<p>Las correcciones son aburridas y de eso se trata. Alertar sobre la cobertura de campos derivados (campo derivado vacío mientras su fuente no lo está). No mantengas dos representaciones de un mismo hecho; deriva el campo filtrable en el momento de escritura. Vigila la frescura de los datos por catálogo de la misma manera que vigilas el uptime.</p>\n<p>Aquí está el prompt para encontrar todo esto en tu propio catálogo (Claude Code o <code>codex exec</code>; la mecánica al final):</p>\n<pre><code class=\"language-text\">Aquí tienes un volcado de mi catálogo de productos como archivos JSONL en ./dump/ - un registro\nJSON por línea, cada uno con un \"id\" único y los campos del producto. Mi definición de campo/\nesquema (si la hay) está en schema.json.\n\nEscribe Python en streaming (no lo cargues todo en memoria) para encontrar fallos silenciosos del pipeline:\n1. Registros que lleven cualquier campo de error/excepción que el pipeline les haya estampado.\n2. Para cada campo DERIVADO (concatenaciones, variantes *_search, *_normalized):\n   registros donde el campo derivado está vacío pero su campo fuente obvio no lo está.\n3. Pares de campos que parezcan espejos el uno del otro (uno anidado/rico,\n   uno aplanado/filtrable): cuantifica con qué frecuencia discrepan.\n4. La distribución de timestamps de actualización/indexación: ¿hay alguna porción del\n   catálogo congelada mientras el resto se refresca?\n\nReporta cada hallazgo con conteos, % del catálogo, 5 ids de ejemplo, y si los\nregistros afectados están activos/visibles. Ordena por severidad.\n</code></pre>\n<h2>Categoría 2: Placeholders y centinelas, o: datos que mienten</h2>\n<p>Los chequeos de nulos son la herramienta de calidad de datos que todos ya tienen. Los placeholders los derrotan, porque un placeholder está poblado. Simplemente no es verdad.</p>\n<p>Mi ejemplo favorito: en un catálogo de 313K SKU, el campo de marca tenía 741 valores distintos y un 99,99 % de población. Suena saludable. El valor principal, en el 88,8 % de todos los registros, era \"Approved Vendor\". Un placeholder de ERP. La marca real más popular cubría el 0,3 % del catálogo. Así que cada faceta de marca, boost de marca y reescritura consciente de marca estaba efectivamente muerta para nueve décimas partes del catálogo, mientras que la UI de facetas ofrecía alegremente \"Approved Vendor\" como el filtro de marca #1.</p>\n<p>Una vez que empiezas a mirar, estas cosas están por todas partes:</p>\n<ul>\n<li>Un centinela de \"sin precio\" de <code>99999999.000000</code> en el 55 % de un catálogo. La mediana de precios de ese índice era noventa y nueve millones de dólares.</li>\n<li>Nombres de producto literalmente <code>\"unknown\"</code> en 64 registros, que luego rankean para la consulta \"unknown\".</li>\n<li>Un booleano <code>true</code> escrito en un campo de descripción de texto en 1 516 productos activos. El motor lo coaccionó a la cadena \"true\", lo que hizo que esos productos se pudieran encontrar buscando \"true\".</li>\n<li>227 UPC almacenados como <code>\"6.71E+11\"</code> —notación científica de Excel, con 51 productos no relacionados compartiendo ese \"identificador\"— más 15 121 UPC adicionales sin su cero inicial. Excel vuelve a atacar.</li>\n<li>Un catálogo de fijaciones cuyo campo de marca contenía nombres de líneas de producto (\"C6L Lockbolts\", \"Tool Parts\") mientras que la marca real estaba en el campo de al lado.</li>\n</ul>\n<p>La lección que saqué de esta categoría: audita las distribuciones de valores top-N por campo, no las tasas de nulos. Un campo que está 100 % poblado puede ser 88 % basura, y ningún chequeo de nulos te lo dirá jamás. Luego pon los centinelas en una lista de bloqueo en la ingesta, mapéalos a nulos reales con banderas explícitas <code>has_price</code> / <code>has_real_image</code>, haz aserciones de tipo en el límite y valida los identificadores estructuralmente. Trata cualquier cosa que haya pasado por una hoja de cálculo como sospechosa.</p>\n<pre><code class=\"language-text\">Mismo volcado JSONL. Para cada campo en mi catálogo, calcula la distribución\nde valores top-20 (pase completo, streaming).\n\nMarca: (a) cualquier valor único que cubra &gt;10 % de los registros —candidatos a\nplaceholder/centinela como \"Approved Vendor\", \"unknown\", 99999999, 0.0; (b) valores cuyo\ntipo difiere del tipo dominante del campo (booleanos en campos de texto, floats\nentre strings); (c) campos identificadores (UPC/EAN/GTIN/números de parte): valida\nchecksum y longitud, marca notación científica, ceros iniciales eliminados,\nespacios/unicode embebidos, e identificadores compartidos por múltiples registros;\n(d) valores tipo categoría o línea de producto sentados en campos de marca.\n\nPara cada marca: conteo, % del catálogo, 5 ejemplos textuales con ids, y una\nregla de ingesta propuesta de una línea que lo habría rechazado.\n</code></pre>\n<h2>Categoría 3: Contenido empobrecido</h2>\n<p>Los catálogos de distribuidores están escritos por ERP, no por merchandisers. El texto es abreviatura de factura: <code>PROPRESS 2-1/2X1 CXC RED CPLG</code>, <code>HC HX 18X8 W</code>. En un catálogo, un tercio de los nombres de producto tenían menos del 60 % de palabras de diccionario. En otro, los campos crudos de nombre y descripción eran 100 % nulos —el texto solo existía dentro de blobs de búsqueda derivados, así que todo lo que leía los campos crudos (títulos de resultado, inputs de embedding, prompts de juez de relevancia) no leía nada y nadie lo sabía.</p>\n<p><img src=\"/assets/images/b2b-search-field-coverage.png\" alt=\"Cobertura de campos en cuatro índices B2B de producción\" /></p>\n<p>Ese mapa de calor es mi artefacto favorito de toda la auditoría. Cada uno de esos campos existe en cada esquema. Los números son cuánto de cada uno realmente contiene datos. La fila que me afecta es la de ML-descriptions: la capa de enriquecimiento que los esquemas prometían en cada registro estaba poblada en dieciocho documentos de 775 051. Dieciocho. Cada rama de \"usa enriquecimiento si está presente\" en el pipeline de scoring era un no-op silencioso. El esquema es una aspiración; solo la cobertura es un hecho.</p>\n<p>Esta categoría es donde el manual publicado aplica más directamente —la extracción de atributos de DoorDash, la generación offline de Instacart— y la sección de costos de abajo lo cotiza. Pero cuatro reglas importan en un negocio de números de parte, aprendidas en parte de lo que el intento de enriquecimiento anterior hizo mal:</p>\n<ul>\n<li>Expandir, no reemplazar. Genera el título legible para el cliente junto a la cadena cruda de factura e indexa ambos. La consulta del técnico de mostrador y la consulta del propietario deberían ambas acertar.</li>\n<li>Nunca verbalices códigos. ¿Esos dieciocho registros pioneros? El generador había deletreado los números de modelo en palabras: \"RGF one hundred eighty\". Ningún contratista escribirá eso jamás. Los códigos permanecen textuales; las expansiones son adiciones, no reemplazos.</li>\n<li>Extrae estructura mientras estás ahí. El mismo pase que reescribe un título puede emitir <code>{size, material, connection_type}</code> para facetas.</li>\n<li>La jerga comercial es un problema de sinónimos, no de reescritura. <code>CXC</code>, <code>ELL</code>, <code>CPLG</code>, \"t-stat\" pertenecen a la expansión de sinónimos a nivel de analizador, arreglada una vez. No pagues por reescribirlos en medio millón de registros.</li>\n</ul>\n<pre><code class=\"language-text\">Mismo volcado. Evalúa la calidad del texto de los campos orientados al cliente (nombre, descripción\ncorta/larga) con un pase completo en streaming:\n\n1. Cobertura de título efectivo: % de registros ACTIVOS con un nombre de visualización no vacío.\n2. Legibilidad: % de nombres en MAYÚSCULAS; % con menos del 60 % de tokens de palabras de diccionario\n   (ensalada de abreviaturas); nombres de menos de 10 caracteres; descripciones de solo dígitos.\n3. Basura: marcado HTML/CMS, artefactos de codificación, notas operativas embebidas\n   (*** NOT A PHYSICAL ITEM ***), desbordamiento de columnas CSV.\n4. Duplicación: descripciones cortas/largas idénticas; texto boilerplate compartido por 20+\n   SKU; clusters de longitud exacta (254/255/500 caracteres) que indican límites VARCHAR upstream.\n5. Verificación de realidad del enriquecimiento: para cada campo ML/enriquecido/embedding que el esquema\n   promete, su cobertura real.\n\nNúmeros, 5 ejemplos textuales cada uno, severidad, y qué problemas necesitan un pase de enriquecimiento\ncon LLMs vs. una corrección del pipeline vs. una corrección de la capa de sinónimos.\n</code></pre>\n<h2>Categoría 4: Los números de parte son sagrados</h2>\n<p>La mitad del tráfico de búsqueda B2B es alguien escribiendo un número de parte. La coincidencia exacta es todo el juego ahí, y falla de formas silenciosas.</p>\n<p>En un catálogo, el número de parte indexado era un SKU numérico interno en el 99,99 % de los registros. El número del fabricante —el que está impreso en la caja— existía solo dentro de descripciones de texto libre. 183 000 productos inalcanzables por el número que un cliente realmente escribiría.</p>\n<p>Otro catálogo tenía un sistema de expansión de variantes: quita los guiones, colapsa los segmentos, indexa las variantes para que la búsqueda tolerante de PN funcione. Buena idea. Pero el generador era combinatorio (hasta 80 variantes por producto), y en 11 826 registros una variante generada de un producto equivalía exactamente al número de parte canónico de un producto diferente. Las fijaciones son el caso brutal, porque la puntuación codifica dimensiones físicas: <code>702-1-5/32</code> es una pieza de 1-5/32 pulgadas y <code>702-15/32</code> es una pieza de 15/32 pulgadas, y ambas normalizan a <code>7021532</code>. Un cliente escribe un número de parte exacto y obtiene un volado entre la pieza correcta y su prima dimensional.</p>\n<p>También en esta categoría: referencias cruzadas de competidores (\"tengo el número de Ferguson, ¿cuál es el tuyo?\") almacenadas como blobs delimitados por tubería sin dividir como <code>19MU82|Fastenal 0269214|Ferguson M48222407</code>, en campos configurados como no buscables, así que un movimiento central de venta B2B simplemente no funciona. Y bifurcaciones de espacios en blanco —el mismo número de parte indexado tres veces (plano, con relleno NBSP, espacio final)— más gemelos mojibake de UTF-8 doblemente decodificado.</p>\n<p>El principio que arregla la mayor parte de esto: lo exacto debe vencer a lo difuso estructuralmente, no probabilísticamente. Una cláusula exacta que preserva puntuación puntuada estrictamente por encima de cualquier nivel normalizado, nunca una coincidencia plana donde una variante pueda empatar con un acierto canónico. Verifica las variantes generadas contra el espacio de PN canónicos antes de indexar y descarta las colisiones. Normaliza Unicode antes de derivar IDs de registro. Divide los blobs de referencias cruzadas en arrays; esa es una sola línea de código de ingesta que desbloquea toda una clase de consulta.</p>\n<pre><code class=\"language-text\">Mismo volcado. Mis usuarios buscan por número de parte; audita la integridad de coincidencia exacta:\n\n1. Qué campos tipo PN existen (part_number, mpn, PN alt/competidor/cliente,\n   UPC), su cobertura, y cuáles están configurados como no buscables mientras contienen\n   datos reales.\n2. Registros cuyo único token de PN buscable es un SKU interno (el PN real del\n   fabricante aparece solo dentro del texto de descripción —muestra el patrón).\n3. Si hay un campo de expansión de variantes/keywords de PN: encuentra cada variante\n   generada que iguale el PN canónico de un registro DIFERENTE (normaliza:\n   minúsculas, quita puntuación). Muestra pares colisionantes —especialmente\n   tamaños de fijaciones con fracciones.\n4. PN que difieren solo por espacios en blanco/caracteres invisibles/mayúsculas de otro\n   PN de registro (bifurcaciones de identidad duplicada).\n5. Campos de referencia cruzada multivalor almacenados como blobs delimitados en lugar de\n   arrays.\n\nConteos, ejemplos, y para cada problema la regla de ingesta o cambio en el constructor de consultas\nque lo arregla.\n</code></pre>\n<p>Algunas cosas que no encajaron arriba pero merecen una oración: un generador de sinónimos que convertía cualquier número en términos de calibre de alambre, así que <code>REF#259286</code> se convirtió en \"259286 awg\" y una rosca de tornillo <code>#8-32</code> se convirtió en \"8 gauge wire\" (miles de registros ahora coinciden con consultas eléctricas con las que no tienen nada que ver); banderas internas de una plataforma de comercio electrónico indexadas como especificaciones de producto buscables, 2,1 millones de pares clave-valor basura; y 18 campos de esquema poblados en cero registros en cualquier lugar. Los generadores de expansión necesitan compuertas de contexto. El esquema muerto es una falsa promesa sobre la que alguien eventualmente construirá.</p>\n<h2>La capa faltante: UNSPSC, y productos que no saben lo que son</h2>\n<p>Hay un quinto problema que merece su propia sección, porque es donde la distribución comercial se queda más atrás de los marketplaces: la categorización.</p>\n<p>En mi auditoría, los campos de categoría faltaban en el 83 % de un catálogo. Las claves de atributo no tenían taxonomía alguna —7 068 claves de especificación distintas en un catálogo de 156K registros, el 64 % de ellas usadas en menos de diez productos, con deriva como <code>horse_power</code> vs <code>horsepower</code> dividiendo el mismo atributo entre facetas. Y el detalle al que vuelvo una y otra vez: dos de los cuatro catálogos tenían campos UNSPSC sentados en sus esquemas, mapeados y listos, poblados en exactamente cero registros. Alguien sabía que la categorización importaba, construyó el hueco y nunca lo llenó. Apostaría dinero a por qué: clasificar 300K SKU en una taxonomía a mano es un año de trabajo del equipo de catálogo, así que se quedó en el roadmap para siempre.</p>\n<p>Si estás fuera de B2B, UNSPSC es el Código de Productos y Servicios Estándar de la ONU, la taxonomía que hablan los sistemas de compras. Esta es la parte que la búsqueda de consumo no tiene: los sistemas de compras de tus clientes lo requieren. Los catálogos punchout, las plataformas de e-procurement y las herramientas de análisis de gasto están construidos alrededor de códigos UNSPSC. Un distribuidor cuyo catálogo lleve códigos limpios puede conectarse al sistema de compras de un contratista o un hospital. Uno sin ellos es una lista de precios en PDF con una caja de búsqueda. Internamente, también es lo que potencia las facetas de categoría, el ranking con alcance de categoría (\"un número que se parsea como calibre de alambre solo debería boostear alambre\"), la deduplicación entre catálogos y las compuertas de contexto para cada generador de expansión en la Categoría 4.</p>\n<p>Y ahora es un trabajo por lotes. Esta es la misma forma de problema que DoorDash describe resolviendo con LLMs —etiquetado de alto volumen contra un vocabulario controlado— y el enfoque se transfiere directamente:</p>\n<ul>\n<li>Clasificar jerárquicamente, no plano. UNSPSC tiene cuatro niveles (segmento, familia, clase, commodity). Haz que el modelo elija la familia entre ~450 opciones, luego la clase dentro de esa familia. Dos pequeñas elecciones restringidas vencen a una elección de 50 000 vías, y puedes alimentar la porción relevante de la taxonomía en el contexto de la manera en que DoorDash restringe la vinculación de entidades a su vocabulario.</li>\n<li>Detente primero a nivel de clase. Los códigos de clase ya desbloquean facetas e integración de compras; la precisión a nivel de commodity puede venir después donde se gane su lugar.</li>\n<li>Enrutar por confianza. Un modelo pequeño toma los casos claros, los registros de baja confianza escalan a un modelo más grande, y los desacuerdos persistentes van a una cola humana que también sirve como tu conjunto de evaluación.</li>\n</ul>\n<p>El costo es casi embarazoso de escribir. La clasificación es una tarea de salida corta: aproximadamente 300 tokens de entrada por registro y 30 de salida. En Claude Haiku 4.5 con precios de Batch API, eso es unos 17 centavos por mil SKU. Toda mi flota de 775K registros se clasificaría por unos $130. Lo que se quedó en el roadmap durante años porque era un año de trabajo manual cuesta menos que el almuerzo del equipo donde lo discutirías.</p>\n<pre><code class=\"language-text\">Clasificas productos de distribuidores B2B en UNSPSC. Adjunto: la lista de\nfamilias UNSPSC (nivel 2, ~450 entradas) como datos de referencia.\n\nPara cada registro de producto (título, descripción, marca, número de parte, cualquier texto de\ncategoría existente), emite JSON:\n- unspsc_family: el código de familia de 4 dígitos —elige SOLO de la lista adjunta\n- family_confidence: high | low\n- rationale: una frase corta (p. ej. \"copper press fitting -> pipe fittings\")\n\nReglas: juzga por la función del producto, no por su marca. Abreviaturas comerciales:\nCXC/FPT/MPT son conexiones de tubería, ELL es codo, CPLG es acoplamiento, una fracción+material suelta\nusualmente es un tamaño de accesorio. Si el registro no es un producto físico\n(líneas de flete, cargos adicionales, \"*** NOT A PHYSICAL ITEM ***\"), emite\nNOT_A_PRODUCT. Usa low confidence libremente —los registros de baja confianza reciben un\nsegundo pase con la lista a nivel de clase.\n</code></pre>\n<h2>El manual de limpieza, con la factura real</h2>\n<p>El pipeline de corrección tiene tres niveles más el trabajo de clasificación. El truco es gastar en el nivel correcto, porque casi nunca necesitas una llamada a LLM por registro.</p>\n<pre><code class=\"language-mermaid\">flowchart LR\n    A[\"Nivel 0 - Reglas&lt;br/&gt;código determinístico&lt;br/&gt;~$0\"] --> B[\"Nivel 1 - LLM sobre valores únicos&lt;br/&gt;marcas · claves de espec · unidades&lt;br/&gt;decenas de dólares\"]\n    B --> C[\"Nivel 2 - LLM por registro&lt;br/&gt;títulos · atributos · UNSPSC&lt;br/&gt;cientos de dólares\"]\n    C --> G[\"Compuertas de ingesta&lt;br/&gt;cada corrección se convierte en un validador\"]\n</code></pre>\n<p><strong>Nivel 0 es código determinístico y básicamente es gratis.</strong> Quita HTML y decodifica entidades. Convierte centinelas en nulos reales con banderas explícitas. Repara UPC (relleno izquierdo, rechaza notación científica, valida el dígito de chequeo). Normaliza Unicode en IDs. Divide las referencias cruzadas delimitadas por tubería. Descarta variantes de PN que colisionen con el número canónico de otro producto. Elimina esquemas muertos. Un agente escribe estos scripts en una sesión, y este nivel arregló aproximadamente la mitad de todo lo que encontró mi auditoría. Hazlo antes de cualquier enriquecimiento, o pagarás a un modelo para que reescriba bellamente títulos de productos que tu pipeline descartó silenciosamente.</p>\n<p><strong>Nivel 1 ejecuta el LLM sobre valores únicos, no registros.</strong> Este es el truco que hace que los problemas de vocabulario sean baratos: mi peor catálogo tenía 313K registros pero solo 741 cadenas de marca distintas. La normalización de marcas es un trabajo sobre 741 cadenas —agrupa las variantes de mayúsculas/minúsculas, mapea sub-marcas a padres, marca los placeholders— no 313K llamadas. El mismo movimiento para claves de especificación y sufijos de unidades. Cada trabajo de vocabulario cuesta un número de un solo dígito y su salida es una tabla de alias estática que tu ingesta aplica para siempre, determinísticamente. Llama a todo el nivel $20-50 para una flota, gastado principalmente en pases de revisión.</p>\n<pre><code class=\"language-text\">Adjunto: la distribución completa de valores distintos de mis campos de marca y fabricante\n(valor, record_count). Produce una tabla de normalización:\n\ncanonical_brand, parent_manufacturer, [raw variants], confidence\n\nReglas: agrupa variantes de mayúsculas/minúsculas/puntuación/espacios; mapea sub-marcas a\nfabricantes padres como una columna separada (no las fusiones); marca valores placeholder como\nNULL_SENTINEL; marca líneas de producto o categorías mal archivadas como marcas como\nNOT_A_BRAND. Marca clusters inciertos en lugar de adivinar. Emite CSV, luego\nescribe un script que lo aplica en la ingesta y registra valores nuevos no emparejados.\n</code></pre>\n<p><strong>Nivel 2 es el pase por registro</strong>: un título legible para el cliente, expansiones de búsqueda, atributos estructurados y un número de parte de fabricante recuperado para cada producto. Por SKU es pequeño —alrededor de 400 tokens de entrada (el registro, más instrucciones compartidas que el caching de prompts hace casi gratis) y 250 de salida. Dos palancas antes de siquiera elegir un modelo: enriquece lo que está activo (en mi peor catálogo solo el 14 % de los registros estaban activos, un recorte del 86 % ahí mismo), y usa una API de batch donde el proveedor la tenga, ya que el enriquecimiento no tiene requisito de latencia y tanto Anthropic como OpenAI rebajan un 50 % por batch.</p>\n<p>Coticé el mismo trabajo en todo lo que plausiblemente usarías en 2026 —Claude, <a href=\"https://openai.com/index/advancing-the-price-performance-frontier-with-gpt-5-6/\">los tres niveles de GPT-5.6</a> (Sol, Terra, Luna), Kimi K3, GLM-5.2, Qwen3.5 y <a href=\"https://deepseek.ai/pricing\">DeepSeek V4</a>. Misma carga de trabajo, tarifas publicadas por los proveedores, descuento por batch aplicado donde existe:</p>\n<p><img src=\"/assets/images/b2b-search-enrichment-cost-by-model.png\" alt=\"Costo de enriquecer 775K SKU entre modelos\" /></p>\n<p>Tuve que verificar dos veces estos números porque parecían incorrectos. La versión del peor caso de este proyecto —un modelo frontera, cada SKU, sin filtrado alguno— llega a unos $3 800. El piso está bajo los cien dólares: DeepSeek V4-Flash enriquecería toda la flota por el precio de un taladro decente. La versión que realmente ejecutarías mezcla niveles: un modelo económico (Haiku, Luna, Qwen Flash, DeepSeek) para la mayoría mecánica, un modelo de nivel medio (Sonnet, Terra, GLM-5.2) para la cola de ensalada de abreviaturas que el pequeño marca como baja confianza, y un modelo frontera haciendo spot-check a una muestra del 1-2 % como compuerta de calidad. Eso aterriza en unos pocos cientos de dólares para los SKU activos sin importar qué proveedores elijas.</p>\n<p>Dos advertencias que el gráfico no puede mostrar. Los modelos baratos solo son baratos si su salida sobrevive tu evaluación —ejecuta la comparación de 200 muestras antes de comprometerte, porque un modelo económico que destroza el 5 % de los números de parte cuesta más que el modelo frontera que no lo hace. Y tu catálogo es dato competitivo: precios, referencias cruzadas y números de parte de clientes están en esos registros, así que revisa los términos de retención de datos y entrenamiento de cada proveedor antes de enviar 775K registros al endpoint más barato de la lista. Para muchos distribuidores, esa sola verificación incluye o excluye a algunos proveedores, y las opciones de peso abierto (DeepSeek, GLM, Qwen, Kimi) tienen la propiedad extra de que puedes autoalojarlos si los datos no pueden salir en absoluto.</p>\n<p>Suma el pase UNSPSC de ~$130 (que escala de la misma manera entre modelos —en el nivel económico cae a calderilla) y el trabajo de vocabulario, y toda la transformación del catálogo corre en algún lugar entre unos pocos cientos y unos pocos miles de dólares, contra un trabajo que solía ser un año del equipo de merchandising. Es la misma economía de batch offline que describe Instacart, solo que a escala de catálogo comercial donde los números se vuelven lo suficientemente pequeños para ponerlos en una tarjeta de crédito.</p>\n<p>Cómo se ve en un registro real de mi auditoría:</p>\n<pre><code class=\"language-text\">IN:  part_number: 1678619\n     short_desc:  PROPRESS 2-1/2X1 CXC RED CPLG 20685\n     brand:       (placeholder)\n\nOUT: display_title:      2-1/2\" x 1\" ProPress Copper Reducing Coupling\n     search_expansions:  [\"copper press fitting\", \"reducing coupling\",\n                          \"press x press coupling\"]\n     attributes:         {size_1: {value: 2.5, unit: in},\n                          size_2: {value: 1, unit: in},\n                          material: copper, connection: press}\n     extracted_mpn:      \"20685\"\n     unspsc_family:      4017 (pipe fittings)\n     confidence:         high\n</code></pre>\n<p>Mira la línea <code>extracted_mpn</code>. El mismo pase recupera números de parte de fabricante que antes estaban enterrados en el texto de descripción —en uno de mis catálogos eso son 183K productos volviéndose encontrables por el número impreso en la caja. Eso solo paga el trabajo por lotes.</p>\n<pre><code class=\"language-text\">Enriqueces registros de productos de distribuidores B2B para búsqueda. Para cada registro de entrada\n(nombre crudo de ERP, descripciones, marca, número de parte, categoría) produce JSON:\n\n- display_title: legible para el cliente, &lt;=80 caracteres, Title Case. Expande\n  abreviaturas comerciales (CXC -> copper x copper connection, ELL -> elbow, CPLG ->\n  coupling, RED -> reducing). Mantén cada número de parte, código de modelo y\n  dimensión VERBATIM como se escribió —nunca deletrees códigos en palabras.\n- search_expansions: 2-5 frases que un cliente podría escribir que no aparecen en\n  el texto crudo (tipo de producto en inglés plano, nombres comerciales comunes). Nunca inventes\n  especificaciones ausentes en la fuente.\n- attributes: {name: {value, unit}} extraídos SOLO del texto fuente.\n- extracted_mpn: número de parte del fabricante si está presente en el texto de descripción\n  (usualmente el token después de la marca) y ausente en los campos de PN, si no null.\n- confidence: high | low. Usa low siempre que tengas que adivinar; una respuesta low\n  enrutada a revisión vence a una alucinación confiada.\n</code></pre>\n<p>Dos notas de producción: pon las instrucciones compartidas en un prefijo de prompt en caché (el caching reduce el lado de entrada hasta un 90 % además del descuento por batch), y usa salidas estructuradas con un esquema JSON para que nunca pagues un impuesto de fallo de parseo.</p>\n<p><strong>El último nivel es el que la gente se salta, y es el que se capitaliza.</strong> La auditoría vale una limpieza. Los validadores que genera valen cada feed futuro. Termina la sesión de corrección con:</p>\n<pre><code class=\"language-text\">Convierte cada problema que acabamos de arreglar en un chequeo automatizado que falle el pipeline\nde ingesta ruidosamente: aserciones de cobertura de campos derivados, listas de bloqueo de centinelas,\naserciones de tipo en campos de texto, validación de checksum de identificadores, chequeos de\ncolisión de variantes de PN, reglas de higiene de id, seguimiento de cobertura UNSPSC, y una alarma\nde frescura de datos por catálogo. Emítelos como tests que CI ejecuta contra una muestra de\ncada nuevo feed.\n</code></pre>\n<p>Omite esto y las mismas exportaciones de ERP regenerarán la misma basura dentro de un trimestre, y pagarás la limpieza dos veces. Lo sé porque dos de los bugs que encontró mi auditoría claramente habían sido arreglados antes, y habían vuelto.</p>\n<h2>Ejecutando los prompts</h2>\n<p>Un paso de configuración: vuelca tu catálogo a JSONL, un objeto JSON por línea con un <code>id</code> único y los campos del registro. Sea cual sea tu plataforma —Elasticsearch, OpenSearch, Solr, Algolia, Typesense, un PIM, la base de datos detrás de todo— solo pídele al agente que escriba el exportador:</p>\n<pre><code class=\"language-text\">Escribe un script que exporte cada registro de producto de mi catálogo [describe:\nnombre del índice de búsqueda / tabla de base de datos / exportación de PIM] a ./dump/records-{n}.jsonl\nen trozos de 50k registros —un objeto JSON por línea con un \"id\" único más todos los\ncampos— y mi definición de campo/esquema (si la plataforma tiene una) a\nschema.json. Verifica que el conteo exportado coincida con el conteo fuente.\n</code></pre>\n<p>Con Claude Code, suelta cualquier prompt de categoría en una sesión en ese directorio. Escribe y ejecuta el análisis él mismo, pases completos sin excusas de muestreo, y vuelve con recibos a nivel de registro. Cuando dices \"ahora arréglalo\", la misma sesión produce el validador de ingesta, el script de backfill y el trabajo de enriquecimiento por lotes. Ejecuta las categorías como sesiones paralelas o subagentes; así ejecuté las mías, y toda la auditoría tomó unos doce minutos de tiempo de reloj. Con Codex, <code>codex exec</code> maneja los pases de análisis de solo lectura usando los mismos prompts; mantén las correcciones del lado de escritura en una sesión que revises.</p>\n<p>Tres reglas que aprendí a la mala:</p>\n<ol>\n<li>Exige pases completos y recibos. \"Analiza estos datos\" invita al muestreo y a las vibras. Pide conteos exactos y cinco IDs de registro de ejemplo por hallazgo, y cada afirmación se vuelve verificable.</li>\n<li>Reglas antes que enriquecimiento. Arregla la verdad antes de agregar prosa, o alucinarás marcas para el 88 % de los registros etiquetados \"Approved Vendor\".</li>\n<li>Muestra, evalúa, luego batch. Nunca dispares un batch de 775K registros desde un prompt no validado. 200 muestras, un pase de scoring, luego escala. El arnés de evaluación cuesta una sesión de agente y desriesga todo el gasto.</li>\n</ol>\n<h2>Los oficios merecen búsqueda de nivel marketplace</h2>\n<p>Amazon, DoorDash e Instacart no publicaron su trabajo de catálogo-LLMs como caridad. Lo publicaron porque las técnicas son generales y la ventaja competitiva es la ejecución. Y las técnicas se transfieren a la distribución comercial mejor que casi en cualquier otro lugar que se me ocurra: los catálogos son lo suficientemente pequeños para que un pase completo sea barato, los datos son lo suficientemente malos como para que el margen de mejora sea enorme, las consultas llevan dinero real, y el mundo de las compras ya funciona con una taxonomía que un trabajo por lotes ahora puede poblar por unos $130.</p>\n<p>De las docenas de problemas que mi auditoría sacó a la superficie en cuatro catálogos de distribuidores, ni uno solo era visible desde la capa de ranking. Todos la degradaban. La cadena de suministro que alimenta esos índices —las exportaciones de ERP, las hojas de cálculo de proveedores, los antiguos parsers de CSV— es exactamente lo que el manual publicado arregla. Los distribuidores que lo ejecuten tendrán búsqueda de nivel marketplace en catálogos donde sus competidores todavía no pueden encontrar una bobina de condensador.</p>\n<p>La pregunta \"¿hay algo mal con mis datos?\" solía costar dos semanas del tiempo de alguien, por eso nadie la hacía. Ahora cuesta un prompt. Hazla.</p>",
  "source_hash": "sha256:729970b39c1b22c143ba55272659d6cc431b0f8889282089a4ea4eaa71f75000",
  "model": "moonshotai/kimi-k2.6",
  "generated_at": "2026-08-06T08:15:48.668315+00:00"
}