Clasificar Texto Sucio a Gran Escala: cómo armar un pipeline con JEV y un LLM verificador
Las órdenes de trabajo de mantenimiento industrial las escribe gente con prisa, desde el campo, con el teclado del celular o con un campo del ERP limitado a unos 40 caracteres. Si alguna vez trabajaste con tickets de soporte, bitácoras o reportes de falla, conoces el género:
- Textos cortísimos y truncados: «fuga sello bba», «alineac motor», «cambio rodamiento».
- Abreviaturas que ningún diccionario estándar reconoce, y que a veces significan dos cosas.
- Categorías históricas marcadas a las carreras con lo primero que apareció en el menú desplegable.
Y sobre ese texto quieres decisiones limpias y estructuradas: ¿fue una falla o una rutina?, ¿preventivo, correctivo o predictivo?, ¿qué componente?, ¿qué modo de falla? Todo dentro de un catálogo cerrado, para poder guardarlo en una base de datos sin romper nada.
La tentación es «metámosle un prompt a un modelo grande y que resuelva». Funciona a medias, pero es lento, caro y poco consistente. Lo que fui armando es un pipeline de cuatro pasos, y en este artículo te cuento cómo funciona y por qué tomé cada decisión. Los experimentos entran al final como evidencia, con sus límites a la vista.
---
title: PIPELINE COMPLETO
---
flowchart TD
INPUT["<strong>Orden de mantenimiento</strong><br/><em>texto corto + texto largo opcional</em>"] --> NORM["<strong>PASO 1: NORMALIZAR</strong><br/>glosario, ortografía aprobada,<br/>limpieza del texto largo"]
NORM --> JEV["<strong>PASO 2: JEV</strong><br/>clasifica el 100%<br/>y devuelve confianzas"]
JEV --> ROUTER{"<strong>ROUTER POR REGLAS</strong><br/>¿contradice la clase ERP<br/>o es incoherente?"}
ROUTER -- "No (61,4%)" --> AUTO["<strong>Respuesta de Jev</strong>"]
ROUTER -- "Sí (38,6%)" --> LLM["<strong>PASO 3: LLM VERIFICADOR</strong><br/>JSON estricto + semáforo"]
AUTO --> OUT["<strong>RESULTADO</strong>"]
LLM --> OUT
OUT -.-> HUM["<strong>Revisión humana</strong><br/>donde Jev y LLM discrepan"]
Paso 1: normalizar antes de clasificar
Antes de que cualquier modelo vea el texto, lo paso por una sola función (prepare_text) con dos variantes: texto corto y texto largo. Esto es lo que hace, concretamente:
- Expansión con un glosario curado. Minero los tokens frecuentes del corpus que no están en un diccionario de español y armo un glosario de unas 340 entradas (338 en la versión actual):
bba→bomba,mtto→mantenimiento,sci→sistema contra incendio,cbm→monitoreo de condición. Revisado a mano, no autogenerado. - Reglas para lo ambiguo.
semes «semanal» al lado de una rutina, pero «semana» si lo precede un número;tecpuede ser técnico o técnica según las palabras vecinas. El normalizador mira una ventana de tres tokens a cada lado, y si no hay pista, usa la expansión por defecto. - Identificadores intocables. Cualquier fragmento con dígitos (
P-101A,E-204) no se modifica jamás. Algunas entradas tienen alcance por origen del dato (por ejemplo, códigos de ubicación que no se expanden). - Ortografía solo con pares aprobados. El corrector no hace correcciones difusas por su cuenta: aplica una lista de pares «original → corregido» que yo revisé (
Recircualcion→Recirculación). El modo difuso existe, pero solo para proponer candidatos que luego se aprueban. Prefiero no corregir a corregir mal. - Limpieza del texto largo. Quita encabezados y marcas de tiempo del ERP, firmas, boilerplate de permisos y seguridad (permiso de trabajo, ATS, EPP, LOTO…), oraciones repetidas, y pone primero los hallazgos y el trabajo realizado. Lo corta a ~400 caracteres en un límite de oración.
¿Por qué esto ayuda al modelo?
Un clasificador (sea Jev o un LLM) aprendió con texto donde «bomba» aparece escrito «bomba». Una abreviatura rara como bba se parte en fragmentos poco informativos y, peor, puede significar otra cosa en otro contexto. Expandirla le entrega al modelo el vocabulario con el que fue construido, en vez de pedirle que adivine.
¿Y por qué no dejar que el LLM lo resuelva solo? Tres razones: costo (gastar tokens de razonamiento en mtto miles de veces), consistencia (un glosario siempre expande igual; un modelo puede interpretar sci distinto cada vez) y porque Jev no razona en texto libre: clasifica lo que ve, así que el texto que le llega tiene que ser lo más explícito posible.
Evidencia, con su etiqueta honesta: en un piloto de 120 órdenes de bombas, la exactitud de Jev frente a la clase de orden del ERP (una referencia aproximada, no etiqueta humana) pasó de 69,2% a 74,2% al normalizar. Son unas 6 órdenes netas (83 → 89 aciertos; 7 mejoraron y 1 empeoró), y la prueba exacta de McNemar da p ≈ 0,07: sugerente, no concluyente. Es una muestra pequeña, de un solo tipo de equipo.
Paso 2: por qué Jev para la primera pasada
Jev es un modelo “System One”: en vez de generar texto token por token, evalúa preguntas tipadas y devuelve una opción del catálogo con su confianza. Lo expliqué por dentro en JEV y el paradigma System One; aquí solo lo que importa para el diseño.
En este pipeline le hago, por orden, preguntas como estas:
es_falla: pregunta de tiponoul, que devuelve una probabilidad.tipo: correctivo, preventivo, predictivo o no mantenimiento.actividad: reemplazar, reparar, inspeccionar, servicio, ajustar u otra.subunidadymodo_falla: con las opciones de la taxonomía de cada familia de equipo.item_mantenible: en una segunda pasada, con las opciones de la subunidad que salió en la primera.
Elegí Jev para esta etapa por cuatro razones:
- Siempre responde dentro del catálogo. No inventa categorías ni rompe el formato JSON, porque elige entre las opciones que le pasas. En un pipeline automatizado, un valor fuera del catálogo puede tumbar un lote entero (me pasó con un LLM, lo cuento más abajo).
- Entrega confianzas por campo. Esa señal es la que permite decidir qué se revisa y qué no.
- Es rápido: unos 0,3 s por petición; las 2.000 órdenes tardaron 77 segundos con concurrencia 10.
- Es barato: US $0,1074 por las 2.000 órdenes, unos US $0,054 por cada 1.000 (costo tomado del panel del proveedor).
Su límite: con una taxonomía amplia y multiequipo (21 familias de equipo, 4 disciplinas), las confianzas bajan en general. Eso me obligó a replantear el router, como verás en el paso 3.
Paso 3: un LLM como verificador, no como juez
Si Jev es rápido y barato, ¿para qué un LLM? Porque lee contexto. Detecta mejor cuándo algo es una falla real y no una rutina con palabras de falla. Medido contra etiquetas humanas, en es_falla el LLM (DeepSeek V4.1 Flash) acierta 95,4% contra 90,0% de Jev, y la diferencia es significativa (McNemar, p = 0,023).
Pero fíjate en lo que no pasa: en tipo de intervención, Jev y el LLM empatan (88,4% vs 89,8%, p = 1,0). Y en las órdenes donde discrepan entre sí, ninguno pasa de ~64%. Por eso lo llamo verificador, no juez: sirve para revisar las respuestas dudosas de Jev, pero los casos realmente difíciles hay que mandarlos a una persona. Ningún modelo adivina lo que el técnico no escribió: una orden que dice «revisión y cambio de empaque» puede ser preventiva (por calendario) o correctiva (porque goteaba), y el texto no lo dice.
El router: qué manda al LLM y por qué
La idea natural es usar las confianzas de Jev: si son bajas, la orden pasa al LLM. Lo probé con umbrales calibrados para bombas, y con la taxonomía multiequipo mandaron el 91% de las órdenes al LLM. El embudo era un colador al revés, y salió más caro que mandar todo al LLM.
La alternativa que funcionó fue un router solo por reglas, sin umbral de confianza. Una orden va al LLM si:
- la respuesta de Jev contradice la clase de orden del ERP (una señal débil y barata, pero independiente del modelo),
- su respuesta es internamente incoherente (dice falla pero el tipo no es correctivo, o al revés), o
- reporta una falla con modo de falla «otro».
Con eso va al LLM el 38,6% de las órdenes. Intenté acercarme al 20% que quería, pero no es alcanzable sin cambiar las reglas.
Paso 4: cuando hay texto adicional, úsalo
El texto corto no puede distinguir muchas veces entre preventivo y correctivo: «cambio de sello» cabe en ambos. La bitácora o texto largo suele traer justo lo que falta («se encontró fuga por el sello»). Más contexto reduce la ambigüedad antes que cualquier retoque de prompt.
Evidencia 1: 30 órdenes (A/B). Con el LLM verificador, comparé texto corto contra texto corto más texto largo limpio. Las órdenes con certeza alta (semáforo verde) pasaron de 20% a 80%; las de incertidumbre alta (rojo), de 70% a 13%; y las marcadas como «texto insuficiente», de 30% a 0%. Es una muestra de 30 y mide certeza del modelo, no exactitud, así que tómalo como señal.
{
"type": "bar-horizontal",
"title": "IMPACTO DEL TEXTO LARGO EN LA CERTEZA DEL MODELO (30 OTs)",
"subtitle": "Solo texto corto vs. texto corto + texto largo limpio",
"unit": "%",
"max": 100,
"categories": ["Solo texto corto", "Texto corto + texto largo"],
"series": [
{
"name": "Certeza alta (verde)",
"data": [20, 80]
},
{
"name": "Incertidumbre alta (rojo)",
"data": [70, 13]
}
],
"colors": ["#00ff9d", "#fc4269"]
}
Evidencia 2: 2.000 órdenes de bombas, corto vs. largo. Con texto largo, las órdenes en verde subieron de 74% a 83% y las rojas bajaron de 21% a 6%. Además, la respuesta de tipo cambió en 21,5% de las órdenes: el contexto mueve de verdad las decisiones (no sé cuántas para bien, porque este run no tiene etiquetas humanas).
El precio: más tokens de entrada. El costo subió +18% en las 30 órdenes y +33,5% en las 2.000 (de US$0,0528 a US$0,0705 por 1.000 órdenes). Por eso el consejo práctico es limpiar y acotar: el texto largo pasa por la misma normalización y se corta a ~400 caracteres, con los hallazgos primero. Texto largo sin limpiar es plata en boilerplate de seguridad.
Los experimentos: cómo lo medí
Los números de arriba salen de esta medición, que es la parte que hace que valgan algo:
- Muestra: 2.000 órdenes de mantenimiento industrial multiequipo. De ahí saqué 200 para etiquetar a mano, estratificadas por origen, clase de orden del ERP y acuerdo entre Jev y el LLM (sobremuestreé los desacuerdos, 25% de la muestra).
- Etiquetado ciego: el archivo de etiquetado no mostraba respuestas de Jev, del LLM ni del ERP. Un solo etiquetador, yo.
- Dos formas de reportar: cruda (sobre las 200, intervalo de Wilson) y ponderada a las 2.000 (pesos inversos a la probabilidad de selección, intervalo bootstrap). Las cifras de abajo son las ponderadas.
- El híbrido es una simulación honesta: corrí Jev y el LLM una vez sobre las 2.000 y compuse el híbrido tomando la respuesta del LLM para las órdenes que el router envía y la de Jev para el resto. Así comparo varios ajustes sin pagar cada uno.
Exactitud contra etiqueta humana
| Sistema | Tipo [IC 95%] | ¿Es falla? [IC 95%] |
|---|---|---|
| Clase de orden del ERP (regla de la casa) | 78,0% [73,1–82,7] | — |
| Jev solo | 88,4% [84,1–92,4] | 90,0% [86,5–93,2] |
| DeepSeek V4.1 Flash solo | 89,8% [85,9–93,3] | 95,4% [93,2–97,3] |
| Híbrido, router por reglas (38,6% al LLM) | 90,4% [86,4–94,0] | 96,0% [93,8–97,9] |
| Híbrido, router por confianza (91% al LLM) | 89,8% [85,9–93,3] | 95,4% [93,2–97,3] |
- Tipo: empate técnico. Jev y DeepSeek no se distinguen (p = 1,0) y los intervalos se solapan con el del híbrido. El número más alto del híbrido no me autoriza a decir que es mejor.
- ¿Es falla?: sí hay diferencia. DeepSeek le gana a Jev (p = 0,023) y el híbrido con reglas también (p = 0,004).
- Cualquiera de los tres supera a la clase de orden del ERP en tipo (78,0%).
{
"type": "bar-horizontal",
"title": "EXACTITUD DE TIPO VS. ETIQUETA HUMANA (PONDERADA)",
"subtitle": "2.000 OTs, 200 etiquetas humanas, un etiquetador",
"unit": "%",
"max": 100,
"categories": ["Clase de orden ERP", "Jev solo", "DeepSeek solo", "Híbrido (reglas)"],
"series": [
{
"name": "Exactitud de tipo",
"data": [78.0, 88.4, 89.8, 90.4]
}
],
"colors": ["#00ff9d"]
}
Una lección de método: acuerdo no es verificación
Jev y el LLM coincidieron en el 92% de las 2.000 órdenes (91,95%). La conclusión tentadora es «Jev resuelve el 92% y el LLM solo hace falta para el resto». Pero que dos modelos coincidan no significa que acierten. Dividí las 200 etiquetadas según ese acuerdo:
Tabla 2: Exactitud de tipo por estrato de la muestra
| Estrato (muestra cruda) | n | Exactitud de tipo |
|---|---|---|
| Jev y LLM coinciden | 150 | entre 92,0% y 93,3% |
| Jev y LLM discrepan | 50 | entre 60,0% y 64,0% |
Cuando coinciden, aciertan ~92-93%, no 100%. Cuando discrepan, ningún sistema pasa de ~64%. Por eso diseñé el flujo para que ese segundo grupo termine en una persona.
Costo real
Estas cifras salen de los paneles de administración de los proveedores (no de estimaciones), por 1.000 órdenes:
Tabla 3: Costo por 1.000 órdenes según configuración
| Configuración | Costo por 1.000 OTs |
|---|---|
| Jev solo | US$0,054 |
| Híbrido, router por reglas (38,6% al LLM) | US$0,106 |
| Todo al LLM (DeepSeek solo) | US$0,135 |
| Híbrido, router por confianza (91% al LLM) | US$0,177 |
El híbrido por reglas ahorra ~21% frente a mandar todo al LLM: real, pero modesto, lejos del «80%» que suele prometerse. Y un router mal calibrado cuesta más que no tener router, porque pagas Jev por todo y el LLM casi por todo. No medí la latencia del híbrido de punta a punta, porque lo compuse a partir de dos runs; solo sé que Jev tardó 77 s y DeepSeek 156 s para las 2.000.
Piloto: velocidad y obediencia al esquema
Antes de la medición grande hice un piloto con 246 órdenes de bombas contra tres modelos. Mide velocidad y obediencia al esquema, no exactitud:
Tabla 4: Piloto de 246 órdenes con tres modelos
| Métrica (piloto, 246 órdenes) | Gemini 3.8 Flash | DeepSeek Flash | OpenAI GPT-6 Luna |
|---|---|---|---|
| Órdenes respondidas con éxito | 246 (100%) | 226 (91,9%) (20 caídas) | 246 (100%) |
| Fallos de esquema (enums inválidos) | 0 | 2 lotes fallidos | 0 |
| Latencia mediana por lote (10 órdenes) | 4,61 s | 3,24 s | 29,53 s |
| Latencia p95 | 5,29 s | 5,17 s | 35,10 s |
Uso la mediana porque un solo lote de Gemini tardó 62,9 s por un reintento: con él, la media sube a 6,93 s, aunque los otros 24 lotes promedian 4,6 s.
Dos lecciones que sigo sosteniendo: rápido no es lo mismo que obediente con el esquema (DeepSeek inventó componentes fuera del enum en dos lotes del piloto; en la corrida grande, con un prompt por familia de equipo, fueron 209 de 209 peticiones sin fallos), y un modelo con razonamiento forzado es un lujo para una tarea de categorías cerradas: GPT-6 Luna tardó casi 30 s por lote.
Privacidad: anonimizar antes de llamar a cualquier API
No puedes mandar nombres de personas, códigos de activos o ubicaciones a una API externa sin control. En todas las corridas los payloads se anonimizaron (etiquetas de equipo, fechas, sitios y personas) y un escáner de fugas revisó cada texto antes de enviarlo: pasaron 2.000 de 2.000 órdenes en ambos proveedores.
El anonimizador que ya tenía se dejaba pasar códigos SAP decimales y etiquetas con guion bajo, así que añadí una capa extra de endurecimiento antes del escáner. Prueba tu anonimizador con tus propios formatos y deja el escáner como compuerta que cancele la petición.
Limitaciones
- Un solo etiquetador (yo). No hay acuerdo entre anotadores ni estimación del ruido de mis etiquetas, y los intervalos ponderados no incluyen esa incertidumbre.
- «Es falla» coincide con «correctivo» en mis etiquetas (200 de 200), así que no es una medida independiente de
tipo. - El mejor ajuste del router se eligió con las mismas 200 etiquetas con las que se evalúa: el resultado es optimista.
- Los intervalos se solapan en casi todo el tipo de intervención: la mayoría de las diferencias no son concluyentes.
- Los pilotos son pequeños (120, 246 y 30 órdenes), de bombas, y los de normalización y texto largo no se contrastaron con etiquetas humanas.
- La detección de familia de equipo es ruidosa, y eso afecta los campos finos (componente, modo) que aquí no evalué.
Conclusiones prácticas
- Normaliza antes de clasificar. Un glosario curado, ortografía solo con pares aprobados e identificadores intactos le dan al modelo el vocabulario que conoce. El efecto medido (+5 puntos en 120 órdenes) es sugerente, no concluyente.
- Usa un clasificador de catálogo cerrado para la primera pasada. Jev responde dentro del esquema, con confianzas, en ~0,3 s y ~US$0,054 por 1.000 órdenes.
- El LLM es verificador, no juez. Mejora la detección de fallas (95,4% vs 90,0%) pero no el tipo; donde los dos discrepan, ninguno pasa de ~64%, así que esas órdenes deben terminar en una persona.
- Calibra el router con tus datos. Umbrales de confianza hechos para bombas mandaron el 91% al LLM; un router por reglas lo bajó a 38,6% y ahorró ~21% frente a todo-LLM.
- Si hay texto adicional, límpialo y úsalo. Reduce la ambigüedad, pero cuesta entre +18% y +33% en tokens: acótalo a ~400 caracteres. Y mide siempre contra etiquetas humanas: el acuerdo entre modelos no es exactitud.
Entonces, ¿vale la pena tener los dos?
Si Jev y el LLM empatan en el tipo de intervención, la pregunta es obvia. Y la respuesta no es el costo. El híbrido cuesta ~US$0,106 por 1.000 órdenes: es más barato que mandar todo al LLM (US$0,135), pero el doble que Jev solo (US$0,054). Además, son centavos: incluso con un millón de órdenes la diferencia ronda los US$50–80, así que a esta escala el precio de la API no decide la arquitectura.
Lo que sí justifica tener los dos es otra cosa:
- Detectar fallas reales. El LLM sube la detección de 90,0% a 95,4%, y es la única diferencia significativa del experimento. En mantenimiento eso pesa: una falla registrada como preventivo desaparece del cálculo del MTBF y del análisis de modos de falla.
- Saber cuándo desconfiar. Cuando los dos coinciden aciertan ~92–93%; cuando discrepan, ninguno pasa de ~64%. Un solo modelo no te da esa señal. Con dos, el desacuerdo se vuelve una alarma que dice qué órdenes revisar a mano.
Si solo te importa el tipo de intervención, Jev solo es suficiente y es lo más barato. Si te importa detectar fallas y priorizar la revisión humana, el segundo modelo se paga solo, y el router existe para que lo pagues solo donde hace falta.
Si tienes un problema parecido (texto sucio, volumen grande, la duda de cuándo vale la pena un LLM) y quieres que lo midamos con tus datos antes de decidir, escríbeme.
Fuentes y lecturas recomendadas
- Deußer, T., Sparrenberg, L., & Sifa, R. (2026): Evaluating and Benchmarking the System One Model Jev (arXiv:2609.37647).
- arXiv (2026): JEV and Laya as System One Decision Layers for LLM-Driven Systems.
- Google Cloud: Gemini 3 Flash — model documentation.
- Artículos relacionados en este blog:
- JEV y el Paradigma System One: Por qué generar texto token por token es un desperdicio
- JEV vs. Laya: Comparativa Real en Inferencia System One y Lecciones Aprendidas
- Gemini 4 Argon: El fin de las respuestas cortadas y el salto al millón de tokens de salida
- Instalar 40 skills no te hace programador: desarrollo modular con IA
- La IA abarató el conocimiento, no el criterio
Ideas, herramientas y lecturas de IA que merecen ser probadas
Análisis sencillo del paper Transformer de 2017: qué problema resolvió, cómo funciona la atención (Query, Key, Value), por qué cambió la IA y cinco formas prácticas de aprovechar ese conocimiento para escribir mejores prompts.
Cualquiera puede pedirle a la IA un dashboard, una web o un sistema completo. Con datos de Harvard/BCG y benchmarks de agentes de datos, te explico por qué el juicio, la complejidad esencial y la metodología (CRISP-DM, TDD, ADR) son lo que separa un entregable bonito de una solución real.
Por qué llenar tu agente de código con skills y prompts genéricos empeora el resultado, y cómo construir aplicaciones modulares con IA usando especificaciones (SDD), contratos tipados, design tokens y TDD.



