De fotos en bruto a un inventario multicanal: diseñar un pipeline de ingesta robusto
Retrospectiva de arquitectura sobre un pipeline que identifica objetos a partir de fotos, prepara sus anuncios y sincroniza su ciclo de vida en varios marketplaces.
De fotos en bruto a un inventario multicanal
Crear un anuncio a partir de unas cuantas fotos parece sencillo: reconocer el objeto, generar un título, estimar un precio y llamar a la API de un marketplace. Esa visión funciona para una demostración. Se queda corta en cuanto se procesa una caja entera, las fotos llegan desordenadas o el mismo objeto debe difundirse en varios canales.
El verdadero problema deja entonces de ser la generación de contenido. Es el control del ciclo de vida de un objeto físico único, desde entradas imperfectas hasta su venta, sin mezclas, duplicados ni colisiones de stock.
Este artículo describe primero un pipeline operativo que va de una carpeta de fotos a borradores de anuncios. Propone después su evolución hacia una arquitectura multicanal en la que una base interna sigue siendo la fuente de verdad, mientras adaptadores sincronizan eBay, Leboncoin u otros distribuidores.
Idea directriz: la IA puede producir observaciones y propuestas. La identidad, el stock y las transiciones irreversibles deben permanecer bajo el control de reglas aplicativas explícitas.
Una foto, un objeto y un anuncio son tres identidades distintas
Una caja de libros ilustra bien el problema. Un mismo libro puede estar representado por su portada, su contraportada, su lomo, su portadilla y varias páginas interiores. A la inversa, dos obras parecidas pueden compartir autor, colección, encuadernación o una estética casi idéntica.
Hay que distinguir, como mínimo:
- el archivo de medios, identificado por su contenido y su hash;
- el objeto físico, aquí un ejemplar único que puede tener varias fotos;
- la ficha canónica, que reúne título, estado, atributos y precio;
- el anuncio remoto, propio de un proveedor y de sus restricciones;
- la venta, que consume o reserva la unidad de stock.
Confundir estas identidades produce los errores más costosos: fotos asociadas al libro equivocado, anuncios duplicados, un producto modificado desde un dato específico de un marketplace o la doble venta de un objeto disponible en un único ejemplar.
El pipeline inicial: convertir una caja en libros verificables
La primera versión del sistema trata correctamente un libro cuando sus fotos ya están agrupadas. Extenderlo a una caja añade por tanto un paso previo: encontrar los conjuntos de fotos que corresponden a cada objeto, sin depender del orden de la carpeta, de los nombres de archivo ni de separadores artificiales.
1. Inventariar entradas no fiables
Cada imagen se trata primero como una entrada no fiable:
- formato real verificado por decodificación, no solo por extensión;
- límites al número de archivos, su peso y sus píxeles descomprimidos;
- corrección local de la orientación EXIF sin modificar el original;
- cálculo de un hash para la identidad, la reanudación y la trazabilidad;
- lectura de la marca de tiempo EXIF de captura cuando existe;
- conservación de los originales hasta validar el resultado.
Las fechas del sistema de archivos no se usan como señal de negocio: cambian con demasiada facilidad al copiar, exportar o pasar por la nube.
2. Describir antes de agrupar
Un modelo multimodal económico describe las imágenes por lotes según un esquema cerrado. No elige ninguna ruta ni mueve ningún archivo. De cada foto extrae, entre otras cosas:
- el tipo de vista: portada, contraportada, lomo, portadilla, interior;
- los elementos bibliográficos visibles: título, autor, editorial, ISBN;
- las marcas distintivas: encuadernación, defectos, motivos, anotaciones;
- la presencia real de un libro y la confianza de la observación.
Estas observaciones estructuradas alimentan después un grafo parsimonioso de candidatos. Dos imágenes pueden convertirse en candidatas gracias a un ISBN idéntico, un solapamiento de texto, indicios visuales compatibles o una proximidad temporal.
La marca de tiempo es deliberadamente un indicio blando. Dos fotos tomadas con pocos minutos de diferencia tienen buenas probabilidades de mostrar el mismo objeto, pero dos libros distintos también pueden haberse fotografiado uno tras otro. La proximidad temporal abre por tanto una verificación; nunca desencadena una fusión por sí sola.
3. Preferir una separación de más a una mezcla equivocada
Cada par o grupo candidato pasa por una verificación visual dirigida. Una fusión solo se acepta con prueba positiva. Un conflicto fuerte -dos ISBN distintos, por ejemplo- bloquea el emparejamiento. Una decisión incierta no crea ninguna relación.
Esta elección optimiza la precisión antes que la exhaustividad: una foto aislada en la cola unassigned cuesta una verificación humana; dos libros mezclados en un mismo anuncio pueden generar una descripción incorrecta, un precio incoherente y una venta en disputa.
El resultado es una partición completa y trazable:
- grupos considerados seguros;
- grupos señalados para revisión;
- fotos no asignadas;
- imágenes ignoradas por no detectarse en ellas ningún objeto aprovechable.
4. Mantener a un humano en la frontera irreversible
La interfaz traduce directamente esta secuencia de entradas y salidas: importar, agrupar, verificar y crear. Los grupos seguros pueden preseleccionarse; los casos ambiguos, nunca. Las fotos no asignadas e ignoradas siguen visibles, pero no se envían silenciosamente al pipeline siguiente.
Tras la confirmación, cada grupo se convierte en la entrada del pipeline unitario:
- análisis detallado del objeto y de su estado;
- construcción de los atributos de categoría;
- búsqueda de comparables y estimación del precio;
- preparación fiel de las fotos;
- subida de los medios;
- creación de un borrador, sin publicación automática.
Las ramas «metadatos y precio» y «preparación de fotos» pueden ejecutarse en paralelo, porque solo dependen una de otra en el momento de componer el borrador.
Lo que esta primera arquitectura ya resuelve
Cuatro propiedades importan más que la elección precisa del modelo:
- La prudencia: ninguna semejanza débil basta para fusionar dos objetos.
- La reanudación: las observaciones y decisiones costosas se cachean a partir de los hashes, el modelo y la versión del prompt.
- La trazabilidad: cada medio aparece exactamente una vez en el manifiesto final, con su ubicación y las decisiones asociadas.
- La separación de responsabilidades: el modelo observa, el núcleo de negocio decide, la interfaz pide confirmación y luego un adaptador habla con el proveedor.
Esta arquitectura basta mientras un marketplace sea el único canal de difusión y sus borradores puedan hacer de inventario remoto. Alcanza, sin embargo, un límite estructural en cuanto un objeto único se publica en varios sitios.
De un pipeline de publicación a un sistema de inventario
En un sistema multicanal, eBay, Leboncoin u otro distribuidor no deben convertirse en la fuente de verdad del objeto. Cada plataforma tiene sus categorías, sus estados y sus identificadores. Ninguna tiene una visión fiable de las demás.
La base interna debe contener el estado canónico: identidad del objeto, unidad de stock, medios, precio de referencia, ciclo de vida, anuncios remotos y venta. La aplicación lee esta base; los proveedores reciben proyecciones adaptadas a sus contratos.
La arquitectura objetivo combina tres patrones clásicos:
- puertos y adaptadores para aislar las particularidades de cada marketplace;
- transactional outbox/inbox para sincronizar la base y las llamadas remotas sin perder ni reproducir peligrosamente una intención;
- reconciliación periódica para corregir la deriva inevitable entre sistemas.
Un modelo de datos centrado en el objeto físico
Para objetos vendidos por unidad, el modelo mínimo puede seguir siendo sencillo:
| Entidad | Responsabilidad |
|---|---|
media_asset | Original, hash, metadatos EXIF y derivados |
item | Ficha canónica independiente de los marketplaces |
inventory_unit | Ejemplar físico y disponibilidad real |
listing | Proyección del objeto para un proveedor dado |
provider_binding | Identificadores remotos, versión y último estado conocido |
reservation | Bloqueo temporal del stock durante una transacción |
sale | Venta aceptada, proveedor de origen y datos financieros |
inbox_event | Evento entrante deduplicado |
outbox_event | Intención de sincronización por ejecutar |
sync_attempt | Intento, latencia, respuesta normalizada y error |
El contenido comercial puede variar por canal, pero la identidad y la cantidad no deben duplicarse en cada anuncio. Para un libro único, inventory_unit.available_quantity vale cero o uno: esa restricción debe garantizarla transaccionalmente la base.
Publicar sin un double-write frágil
Una implementación ingenua realiza dos operaciones sucesivas: actualizar la base y después llamar al marketplace. Si el proceso cae entre ambas, la base y el proveedor divergen. Invertir el orden no resuelve nada: el anuncio puede crearse mientras la transacción local falla.
El transactional outbox evita esta trampa:
- una transacción local modifica el objeto e inserta una intención en
outbox_event; - un worker lee esa intención y llama al adaptador correspondiente;
- el adaptador usa una clave de idempotencia o un identificador de negocio estable;
- la respuesta remota actualiza
provider_bindingy el estado del anuncio; - un reintento reproduce la misma intención, no una nueva creación lógica.
No se busca un hipotético «exactly once» distribuido. Se acepta una entrega at least once y luego se hace idempotente cada tratamiento.
El adaptador expone un vocabulario común, por ejemplo:
upsert_draft(item);publish(listing);update_price(listing, price);reserve_or_pause(listing);end_listing(listing, reason);fetch_status(binding);fetch_recent_sales(cursor).
Cada proveedor traduce después este contrato a su API -o al modo de sincronización que la plataforma permita- sin filtrar sus detalles hacia el dominio de negocio.
Gestionar el ciclo de vida en lugar de booleanos
Un campo published = true no basta. Una máquina de estados explícita hace observables las transiciones e impide las combinaciones imposibles.
Los anuncios tienen su propio estado -borrador, publicándose, activo, pausándose, terminado, error-, pero siguen siendo proyecciones de la unidad de stock. Un anuncio activo nunca hace disponible un objeto si inventory_unit ya indica sold.
Una venta en un canal debe cerrar los demás
Cuando un marketplace notifica una venta, el tratamiento entrante sigue también un inbox idempotente. El evento bruto se conserva, se deduplica por su identificador de proveedor y se aplica en una transacción local.
La transición available → sold debe usar un bloqueo, una comparación de versión o una actualización condicional. Dos eventos concurrentes no pueden así consumir la misma unidad en la base.
Esto no anula por completo el riesgo físico: dos compradores pueden validar casi simultáneamente en dos plataformas externas antes de que la retirada se propague. La arquitectura reduce mucho esa ventana, detecta la colisión y ofrece un proceso de resolución. La garantía máxima depende después de los webhooks, los mecanismos de reserva y los plazos que permita cada proveedor.
Los webhooks no sustituyen a la reconciliación
Un webhook puede perderse, llegar tarde o llegar varias veces. Algunas plataformas no lo ofrecen para todos los eventos. Un poller y una tarea de reconciliación siguen siendo necesarios, incluso en una arquitectura orientada a eventos.
El cron no debería contener lógica de negocio. Lanza jobs idempotentes, protegidos contra ejecuciones concurrentes y observables en el mismo sistema que los demás tratamientos.
Una cadencia inicial razonable puede ser:
| Cadencia indicativa | Control | Resultado esperado |
|---|---|---|
| Cada 1 a 5 minutos | Ventas y reservas sin webhook | Cerrar rápidamente los demás canales |
| Cada 10 a 15 minutos | Estados y cantidades de los anuncios activos | Detectar divergencias de stock |
| Cada hora | Jobs bloqueados, reintentos y reservas expiradas | Reparar o alertar |
| Cada noche | Reconciliación exhaustiva | Encontrar anuncios huérfanos y ventas perdidas |
| Cada mañana | Síntesis operativa | Dar al equipo sus prioridades de revisión |
Estas frecuencias deben respetar los límites y las condiciones de uso de cada plataforma.
Un dashboard orientado a anomalías
Un buen dashboard no se limita a mostrar el número de anuncios. Debe responder rápido a cuatro preguntas: qué poseemos, dónde está publicado, qué está derivando y qué acción humana se requiere.
Las métricas más útiles son:
- objetos disponibles, reservados, vendidos y en revisión;
- anuncios activos por proveedor;
- anuncios remotos huérfanos o sin objeto canónico;
- divergencias de precio, cantidad o estado;
- latencia entre una venta y la retirada de los demás canales;
- antigüedad del evento inbox/outbox más viejo sin tratar;
- tasa de fallos y número de reintentos por adaptador;
- tasa de fotos no asignadas y grupos enviados a revisión;
- coste medio de las llamadas de IA por objeto ingerido;
- volumen de ventas y margen por canal.
Las alertas pueden clasificarse por impacto:
- crítica: colisión de venta, objeto vendido todavía activo en otro sitio;
- alta: evento de venta no tratado, retirada remota fallida;
- media: anuncio desincronizado, job bloqueado, reserva expirada;
- informativa: aumento de la tasa de revisión o deriva del coste de ingesta.
Cada alerta debe apuntar al objeto, los anuncios afectados, la última transición exitosa y la acción recomendada. Una alerta sin contexto solo desplaza el trabajo de diagnóstico hacia el operador.
Desplegar esta evolución sin reescribir el pipeline
La migración puede seguir siendo incremental:
- Introducir el modelo canónico. Persistir objetos y unidades de stock antes de crear los borradores del proveedor ya soportado.
- Encapsular el primer proveedor. Convertir la integración existente en adaptador y almacenar sistemáticamente los identificadores remotos.
- Añadir outbox, inbox y jobs de reconciliación. Hacer idempotentes los reintentos antes de aumentar el número de canales.
- Conectar un segundo distribuidor. Empezar por los borradores, luego la publicación y por último los retornos de venta.
- Automatizar el cierre multicanal. Medir la latencia, probar las colisiones y conservar un modo de resolución humana.
- Construir alertas y dashboard. Alimentarlos desde los mismos eventos y estados, no desde contadores paralelos.
El pipeline de imágenes no necesita conocer eBay ni Leboncoin. Produce un objeto canónico y medios validados. Los adaptadores no deben saber cómo se agruparon las fotos. Esa frontera permite hacer evolucionar los modelos de IA, la interfaz y los proveedores de forma independiente.
Conclusión
Pasar de unas fotos a una distribución multicanal no se reduce a añadir un bucle sobre una lista de API. Obliga a decidir dónde vive la verdad, quién posee el stock y cómo cada transición puede reproducirse, auditarse o compensarse.
El pipeline robusto se construye, por tanto, en dos niveles:
- convertir entradas visuales inciertas en objetos canónicos verificados;
- proyectar esos objetos hacia varios canales sin cederles el control del ciclo de vida.
La IA acelera la identificación, la descripción y la fijación de precios. La base canónica, las transiciones transaccionales, los adaptadores idempotentes y la reconciliación protegen la explotación. Es esa combinación -y no el modelo por sí solo- la que convierte una automatización convincente en un verdadero sistema de inventario.