- F0 blindaje: Redis/Celery, pg_hba sin trust LAN, secretos a env 600, backups, webhook auth, jobs muertos - F1 caja: bugs POS, reembolsos al corte, apartados, CFDI, topes de descuento en cotizacion/apartado - F2/F7 IA de catalogo: autocategorizacion, dedup de SKUs con embeddings, OCR de facturas, descripciones, claves SAT, limpieza de fitment - F3 IA en el flujo: vision Yonke, preguntas MeLi, venta cruzada, anomalias, sinonimos, busqueda semantica (rerank/embeddings) - F4/F8 escala y cartera: /metrics+Prometheus+Grafana, pool, score de credito, churn, metricas multi-tenant - Nexus Yonke (M1-M6): modelo de datos 1-de-1, alta por donador, vitrina publica, MeLi usado, consignacion, garantia por dias, reportes, etiquetas - Marketplace B2B entre yonkes (catalogo + pedidos cross-tenant) - Seguridad: auth en /api/admin/* (35 endpoints), alertas por tenant - Migraciones v4.27/v4.28; timers expire-demos y rollup-metrics
15 KiB
Auditoría completa de la plataforma + IA no conversacional — Nexus Autoparts
Fecha: 11-oct-2026 · Base: 6 investigaciones con evidencia en
archivo:línea, SQL real y estado del host. Énfasis solicitado: áreas de mejora + IA no conversacional (predicción, clasificación, visión, OCR, anomalías, matching).
0. Resumen ejecutivo
La plataforma es hoy, en los datos, un catálogo de autopartes con fitment masivo (≈21 M relaciones pieza-vehículo y ≈406 K equivalencias) más un POS funcional pero con poco volumen transaccional (máximo 24 ventas en un tenant; la única serie temporal real son 4,582 ventas históricas de Atlas en 19 meses). El código de IA existente es LLM + visión (QWEN/Hermes/Gemma) y Meilisearch; no hay ML clásico instalado.
Hay tres frentes con problemas:
- Operación/seguridad (grave y barato de arreglar): firewall abierto, Postgres con
trusten toda la LAN, secretos en texto claro, Redis caído 6 días → todo Celery muerto, alertas silenciosas (sinALERT_PHONES), backups que fallaron 5 días sin avisar. - Bugs de producto en caja (visibles al usuario hoy) y funciones construidas pero sin cablear (garantías, notificaciones de taller, lealtad, venta cruzada, flotillas).
- Calidad de datos que bloquea el catálogo, la publicación en MercadoLibre y el SEO: 0 imágenes, 0 descripciones,
reorder_point100% NULL, 0 precios de proveedor, 52 listings sininventory_id.
IA no conversacional: qué sí tiene sentido hoy (no depende de volumen de ventas): auto-categorización de productos (ML y taxonomía), deduplicación/matching de SKUs, OCR/VLM de facturas de proveedor, visión para graduar piezas usadas (Yonke), semantic search por embeddings, y detección de anomalías de negocio (descuentos/stock). Qué NO vale la pena todavía: forecast de demanda (cold start), triage de logs con LLM, agrupación de alertas con ML (overkill).
1. Operación y seguridad — P0 (hacer ya)
| # | Hallazgo | Evidencia | Acción |
|---|---|---|---|
| 1 | Postgres con trust para toda la LAN + puerto 5432 abierto en 0.0.0.0 |
pg_hba.conf: host all all 192.168.10.0/24 trust; iptables -S → INPUT ACCEPT sin reglas; ufw no instalado |
Quitar trust (usar scram-sha-256), listen_addresses a localhost/LAN acotada, firewall |
| 2 | Secretos en claro en unit files world-readable | nexus-pos.service (QWEN/FACTURAPI/JWT/INTERNAL/MEILI keys), nexus-manager.service, nexus.service, meilisearch.service |
EnvironmentFile modo 600 o systemd-creds; rotar llaves |
| 3 | Redis caído 6 días → Celery en bucle de muerte | systemctl status redis-server = failed; celery.log: Cannot connect to redis://localhost:6379/1 (55/100) desde hace 6 días |
Levantar Redis (o quitarlo del diseño); hoy toda tarea Celery está caída |
| 4 | Alertas silenciosas | /etc/nexus/alert.env sin ALERT_PHONES → alerts.log: ALERT no ALERT_PHONES configured; RedisDown activo 6 días sin consecuencia |
Poblar alert.env, segundo canal (email/Slack), alertas de backup/Celery/app-down |
| 5 | Backups: fallaron 5 días en silencio (7–11 oct); solo locales; 3.5 GB zombi | journalctl -u nexus-backup; BACKUP_S3_BUCKET sin definir; cleanup no borra .dump/.sql viejos |
Ya corregí el ((deleted++)); falta validar el timer 02:00, offsite S3 y limpieza |
| 6 | Sin TLS en este host | solo listen 80; no hay 443 |
Confirmar proxy/Cloudflare delante, o terminar TLS |
| 7 | Endpoints expuestos | Grafana admin/nexus2026; Prometheus/Alertmanager sin auth; webhook /pos/api/notifications/webhook POST sin auth |
Auth + bind a localhost |
| 8 | Servicios como root y timers muertos (nexus-quart, cache-warm, mv-refresh, cron.d inexistente) |
systemctl status |
Erradicar jobs muertos / matrices obsoletas |
Observabilidad: Prometheus/Grafana/Alertmanager corren y scrapean infra, pero la app no expone /metrics (:5001/metrics → 404; grep prometheus_client = 0) → el dashboard "Nexus — Application" está vacío. Falta pg_stat_statements cargado (shared_preload_libraries vacío) y log_min_duration_statement activo.
Escalabilidad: el pool es por proceso (tenant_db.py:24-32, minconn=2, maxconn=20 × 8 workers gunicorn) → ~160 conexiones/tenant en el peor caso; sin PgBouncer. 8 tenants van sobrados; a ~15-20 tenants calientes revienta max_connections=200. A 100 es insostenible.
2. Bugs de producto en caja — P0/P1
| Sev | Bug | Evidencia |
|---|---|---|
| P0 | Venta offline rota: totals['total'] no existe → ReferenceError, el ticket offline nunca se muestra |
pos/static/js/pos.js:1587 |
| P0 | Devolución muestra reembolso $0: lee res.refund_total, el API devuelve total_refund |
pos.js:978 vs pos_bp.py:2628 |
| P0 | Apartado liquidado no muestra la venta: lee res.sale_id, el API devuelve {id,...} |
pos.js:1054 vs pos_bp.py:2377 |
| P0 | "Facturar CFDI" no hace nada: se manda generate_cfdi pero ningún backend lo lee |
pos.js:1519; grep generate_cfdi en pos/**/*.py = 0 |
| P0 | Reembolsos no entran al corte de caja → efectivo esperado sobreestimado | pos_bp.py:2454 no inserta cash_movements; cashregister_bp.py:195-314 no resta devoluciones |
| P1 | Apartado cancelado no revierte contabilidad (pasivo fantasma 240) | pos_bp.py:2389-2449 |
| P1 | Apartado se liquida siempre como efectivo (aunque sea tarjeta) | pos_bp.py:2291,2332 |
| P1 | Descuento/tope de precio solo se valida en venta directa (cotización/apartado la evaden) | pos_engine.py:290-295; pos_bp.py:822,1212,1704,1864 |
| P1 | Devoluciones sin control de rol (solo pos.sell) |
pos_bp.py:2455 |
| P1 | 500 por query param sin validar (?per_page=abc) |
error.log; /pos/api/marketplace-ext/* |
Construido pero sin cablear: garantías/RMA nunca se registran al vender (warranty_bp.py:41); notify_order_ready/notify_maintenance_due existen y no se llaman; lealtad acumula pero no se puede canjear en el POS; venta cruzada muerta (part_kits.py no se importa); flotillas fuera del menú (sidebar.js no incluye fleet); analytics de cliente (LTV/churn) calculado y no mostrado (crm_bp.py:227, crm_engine.py:289).
3. Calidad de datos y catálogo
- Imágenes: 0 en todos los tenants (
image_urlvacío;inventory_images0 filas). Bloquea publicar en ML, el catálogo público y el SEO. supplier_catalog: 17,246 items, 0 con imagen, 0 con descripción.reorder_point/reorder_qty100% NULL;min_stock=0en ~95% → las alertas de stock bajo nunca disparan;reorder_alerts= 0 filas.- Filtro de sucursal roto en reorden: filtra por
inventory.branch_id, que es NULL al 100%. - Motor de Proveedores/Órdenes de Compra completo y sin uso (
purchase_orders= 0 filas en todos los tenants);supplier_catalog_prices= 0;catalog_part_id0/14,568 → sin precios/margen de proveedor. - Fitment:
inventory_vehicle_compatde Estrada = 20.7 M filas / 129,101 items ≈ 160 por pieza (99.5% salida QWEN, conf 0.843) — ruidoso; Capa/Rached usansupplier_catalog(0.95, limpio). - Búsqueda: Meili cubre inventario y proveedor, pero el índice
nexus_parts(1.55 M docs TecDoc) sigue re-indexándose y está muerto; los aliases/cross-refs no entran asmart_search; sin sinónimos. - Estrada y Originales Autopartes son clones exactos (mismos 157,243 SKUs y 20,687,557 fitments) → cuéntalos una vez.
4. IA no conversacional — qué construir (y en qué orden)
4.1 Lo que ya existe (reutilizable)
ai_chat.classify_part()(ai_chat.py:379) → número OEM a{name, brand, vehicle, category}.ai_chat.chat_with_image()(ai_chat.py:309) → visión: identifica una autoparte por foto (Gemma/QWEN).qwen_fitment.py→ compatibilidad pieza-vehículo validada contra la maestra (conconfidence).whisper_local.py→ ASR local (faster-whisper, CPU) para notas de voz.- Meilisearch (keyword + typos; soporta vectores) con índices poblados.
nexpart_taxonomy.py→ 558 etiquetas (dataset listo para clasificador);wa_learning.get_learning_pairs_for_training()(:73) → pares texto→producto etiquetados.- Infra lista para OCR:
tesseract+pytesseract+ffmpeg+pdftotextya instalados. - No hay
sklearn/torch/transformers/faiss; sin GPU (12 vCPU / 47 GB). Inferencia en CPU o vía QWEN/Hermes.
4.2 Propuestas priorizadas por viabilidad de datos (no por moda)
| Prio | Implementación | Punto de integración | Datos (reales hoy) | Enfoque | Beneficio |
|---|---|---|---|---|---|
| A1 | Auto-categorización de productos (taxonomía Nexpart + categoría ML) | inventory_bp.py:129 (hoy no persiste) y marketplace_external_service.py (publish); ML ya tiene domain_discovery en meli_service.py:280 |
171 K SKUs, 18 K sin categoría; taxonomía de 558 etiquetas | Reglas → embeddings (Meili) → LLM (classify_part) como fallback |
Habilita navegación/filtros y publicación en lote sin elegir categoría a mano |
| A2 | Deduplicación / matching de SKUs (poblar catalog_part_id + aliases) |
catalog_import_service.py:57-76 |
406 K interchange + 9 K + 4 K aliases | Normalización + blocking por prefijo + similitud de embeddings | Desbloquea precios de proveedor, margen y enriquecimiento (hoy link 0%) |
| A3 | OCR/VLM de facturas de proveedor | supplier_engine.receive_po:214 / compra directa inventory_bp.py:1198 |
Facturas PDF/foto (infra ya instalada) | pdftotext+Tesseract y VLM (chat_with_image) + validación contra catálogo |
Elimina captura manual y alimenta supplier_catalog_prices (hoy vacío) |
| A4 | Visión para graduar piezas usadas (Yonke) | yonke_bp.py:145 (subida de fotos) |
Fotos de piezas (hoy 0) | VLM que sugiera condition_grade (A/B/C/D) y defectos; validar encuadre antes de publicar |
Alta consistente <30 s, menos devoluciones, confianza del comprador |
| A5 | Anomalías de negocio (descuentos, merma de stock, precios mal capturados) | post-commit de venta pos_engine.py:462 + job nocturno sobre sale_items/inventory_operations/audit_log |
Ventas/ops por tenant | EWMA/z-score robusto o Isolation Forest por empleado/SKU/sucursal | Detecta fuga de margen que los topes no capturan (apartados/cotizaciones los evaden) |
| A6 | Semantic search (embeddings + sinónimos) | meili_search.py:23, index_meilisearch.py:47 |
Nombres/SKUs/cross-refs | Vectores en Meili (híbrido) + sinónimos + indexar interchange | "balatas"→"Brake Pad"; búsqueda por número cruzado |
| A7 | Clasificación/priorización de preguntas de MeLi (+ auto-respuesta por plantilla) | service.py:1151 (sync_questions → _upsert_question) |
18 preguntas (4 sin responder desde mayo) | Clasificador de intención (compat/precio/envío) + plantillas + vehicle_compatibility |
SLA (ML penaliza respuestas lentas) y más conversión |
| A8 | Enriquecimiento de imágenes (scraping + tagging por nombre de parte) | image_scraper.bulk_import:71, image_service.save_image:74 |
supplier_catalog 0 imágenes |
Scraping existente + VLM para validar/clasificar | Desbloquea catálogo público, SEO y publicación ML |
| A9 | Score de riesgo de crédito (fiado) | validación en pos_engine.py:314, get_customer customers_bp.py:133 |
Clientes + aging (pocas ventas aún) | Regresión logística/heurística ponderada (mora>30d) | Menos incobrables; subir límites a buenos clientes |
| A10 | Venta cruzada "esto + esto" | hook muerto part_kits.build_kit_text; pos.js:403 al agregar al carrito |
sale_items + KIT_SUGGESTIONS |
Reglas de asociación/co-ocurrencia, fallback a kits | +5-15% ticket típico |
| A11 | Forecast de demanda / punto de reorden | reorder_engine.py:26,78 |
Cold start: máx 24 ventas; única serie = Atlas historical_sales (4,582 / 19 meses) |
Media móvil/Exponencial (ROP = μ·LT + z·σ); ML real cuando haya volumen | ⚠️ Hoy bloqueado por datos; arrancar con reglas y migrar |
| A12 | Churn / próxima compra | crm_engine.py:289 (hoy umbrales fijos) |
52% de compradores de Atlas recompran | BG/NBD o regresión de intervalo intercompra | Campañas de reactivación dirigidas |
4.3 Lo que NO vale la pena (honestidad)
- Triage de logs con LLM: el volumen de errores es de KB/día; deduplicar por firma con reglas gana y cuesta cero.
- Agrupación de alertas con ML: hoy hay un receiver y cero alertas útiles; arreglar el routing da el 100% del valor.
- Predicción de capacidad con IA:
predict_linear(disk[6h], 7d)en PromQL ya responde; es una alerta, no un modelo. - Forecast de demanda hoy: sin historial transaccional, cualquier modelo sería ruido.
5. Roadmap propuesto
| Fase | Foco | Entregable verificable |
|---|---|---|
| F0 — Blindar (2-3 días) | Seguridad (pg_hba/firewall/secretos), Redis arriba, alerting, validar backup timer + offsite, 500 de query params, borrar jobs muertos | Celery vivo; alerta de backup llega a un humano; 5432 cerrado |
| F1 — Caja correcta (1 sem) | Bugs P0 de pos.js, reembolsos→caja, reversa de apartado, forzar topes de descuento también en cotización/apartado, cablear/eliminar generate_cfdi |
Corte de caja cuadra con devoluciones; ticket offline funciona |
| F2 — IA de datos (2-3 sem) | A1 auto-categorización, A2 dedup SKUs, A3 OCR facturas, A8 imágenes | ≥90% SKUs categorizados; factura de proveedor → líneas en <1 min; catálogo con imágenes |
| F3 — IA en el flujo (2-3 sem) | A4 visión Yonke, A6 semantic search, A5 anomalías, A7 preguntas, A10 venta cruzada | Grado sugerido por foto; búsqueda por sinónimo; alerta de descuento anómalo; sugeridos en carrito |
| F4 — Escala y producto (3-4 sem) | PgBouncer + pool, /metrics de app + pg_stat_statements, A9 crédito, A12 churn, A11 forecast cuando haya datos, WA CRM piloto |
30+ tenants sin agotar conexiones; dashboard con métricas reales; score de crédito en fiado |
6. Métricas de éxito
- Seguridad/ops: 0 servicios DB con
trust; 0 secretos en unit files legibles; alertas de backup/Celery entregadas; 30 días de backups verificados (local + offsite). - Producto: corte de caja cuadra con devoluciones; 0
ReferenceErroren POS; 100% de SKUs categorizados; catálogo público con >80% de items con imagen. - IA: ≥90% de facturas de proveedor parseadas sin captura manual; grado de pieza usada sugerido y aceptado en ≥70% de altas; búsquedas sin resultado reducidas >30% (semantic); ≥1 alerta real de anomalía accionada por mes.