Milvus
Zilliz
  • Home
  • Blog
  • Cree una cadena de ventas de bestsellers a imágenes para el comercio electrónico con Nano Banana 2 + Milvus + Qwen 3.5

Cree una cadena de ventas de bestsellers a imágenes para el comercio electrónico con Nano Banana 2 + Milvus + Qwen 3.5

  • Tutorials
March 03, 2026
Lumina Wang

Si construyes herramientas de IA para vendedores de comercio electrónico, probablemente hayas escuchado esta petición miles de veces: "Tengo un nuevo producto. Dame una imagen promocional que parezca que pertenece a una lista de los más vendidos. Sin fotógrafo, sin estudio, y que sea barata".

Ese es el problema en una frase. Los vendedores tienen fotos planas y un catálogo de bestsellers que ya convierten. Quieren tender un puente entre ambos con IA, rápido y a escala.

Cuando Google lanzó Nano Banana 2 (Gemini 3.1 Flash Image) el 26 de febrero de 2026, lo probamos el mismo día y lo integramos en nuestra canalización de recuperación existente basada en Milvus. El resultado: el coste total de generación de imágenes se redujo a aproximadamente un tercio de lo que se gastaba antes, y el rendimiento se duplicó. La reducción del precio por imagen (aproximadamente un 50% más barato que Nano Banana Pro) explica parte de ello, pero el mayor ahorro se debe a la eliminación total de los ciclos de reprocesamiento.

En este artículo se explican los aciertos de Nano Banana 2 en el comercio electrónico, las deficiencias que aún persisten y se ofrece un tutorial práctico sobre el proceso completo: Búsqueda híbrida Milvus para encontrar bestsellers visualmente similares, Qwen 3.5 para el análisis de estilo y Nano Banana 2 para la generación final.

Novedades de Nano Banana 2

Nano Banana 2 (Gemini 3.1 Flash Image) se lanzó el 26 de febrero de 2026. Lleva la mayoría de las capacidades de Nano Banana Pro a la arquitectura Flash, lo que significa una generación más rápida a un precio más bajo. Estas son las principales mejoras:

  • Calidad de nivel profesional a velocidad Flash. Nano Banana 2 ofrece un conocimiento, un razonamiento y una fidelidad visual de primera clase que antes eran exclusivos de Pro, pero con la latencia y el rendimiento de Flash.
  • Salida de 512px a 4K. Cuatro niveles de resolución (512px, 1K, 2K, 4K) con soporte nativo. El nivel de 512px es nuevo y exclusivo de Nano Banana 2.
  • 14 relaciones de aspecto. Añade 4:1, 1:4, 8:1 y 1:8 al conjunto existente (1:1, 2:3, 3:2, 3:4, 4:3, 4:5, 5:4, 9:16, 16:9, 21:9).
  • Hasta 14 imágenes de referencia. Mantiene el parecido de hasta 5 caracteres y la fidelidad de hasta 14 objetos en un único flujo de trabajo.
  • Renderizado de texto mejorado. Genera texto en imagen legible y preciso en varios idiomas, con soporte para traducción y localización en una única generación.
  • Base de búsqueda de imágenes. Utiliza datos web en tiempo real e imágenes de Google Search para generar representaciones más precisas de temas del mundo real.
  • ~50% más barato por imagen. A 1K de resolución: 0 ,067 frente a Pro's0 ,134.

Un caso de uso divertido de Nano Banano 2: Generar un panorama basado en la localización a partir de una simple captura de pantalla de Google Maps

Dada una captura de pantalla de Google Maps y una indicación de estilo, el modelo reconoce el contexto geográfico y genera un panorama que conserva las relaciones espaciales correctas. Resulta útil para producir creatividades publicitarias orientadas a una región (el telón de fondo de una cafetería parisina, un paisaje urbano de Tokio) sin necesidad de recurrir a fotografías de archivo.

Para conocer el conjunto completo de funciones, consulta el blog de anuncios de Google y la documentación para desarrolladores.

¿Qué significa esta actualización de Nano Banana para el comercio electrónico?

El comercio electrónico es uno de los sectores que más imágenes utiliza. Listados de productos, anuncios en marketplaces, creatividades para redes sociales, campañas de banners, escaparates localizados: cada canal exige un flujo constante de activos visuales, cada uno con sus propias especificaciones.

Los requisitos básicos para la generación de imágenes con IA en el comercio electrónico se reducen a:

  • Mantener los costes bajos: el coste por imagen tiene que funcionar a escala de catálogo.
  • Coincidir con el aspecto de los productos más vendidos: las nuevas imágenes deben estar en consonancia con el estilo visual de los anuncios que ya tienen éxito.
  • Evitar las infracciones: no copiar las imágenes de la competencia ni reutilizar activos protegidos.

Además, los vendedores transfronterizos necesitan:

  • Compatibilidad con formatos multiplataforma: diferentes relaciones de aspecto y especificaciones para mercados, anuncios y escaparates.
  • Renderizado de texto multilingüe: texto en imagen limpio y preciso en varios idiomas.

Nano Banana 2 está cerca de marcar todas las casillas. Las secciones siguientes desglosan lo que significa cada mejora en la práctica: dónde resuelve directamente un punto débil del comercio electrónico, dónde se queda corto y cuál es el impacto real en los costes.

Reducción de hasta un 60% de los costes de producción

Con una resolución de 1K, Nano Banana 2 cuesta 0 ,,067 por imagen frente a los0 ,134, lo que supone una reducción directa del 50%. Pero el precio por imagen es sólo la mitad de la historia. Lo que solía acabar con los presupuestos de los usuarios era el retrabajo. Cada mercado impone sus propias especificaciones de imagen (1:1 para Amazon, 3:4 para los escaparates de Shopify, ultrawide para los anuncios de banner), y producir cada variante significaba una pasada de generación separada con sus propios modos de fallo.

Nano Banana 2 reduce esos pasos adicionales a uno solo.

  • Cuatro niveles de resolución nativa.

  • 512px (0,045 $)

  • 1K ($0.067)

  • 2K ($0.101)

  • 4K ($0.151).

El nivel de 512px es nuevo y exclusivo de Nano Banana 2. Ahora los usuarios pueden generar borradores de 512px de bajo coste para su iteración y generar el activo final a 2K o 4K sin necesidad de un paso de escalado adicional.

  • 14 relaciones de aspecto soportadas en total. He aquí algunos ejemplos:

  • 4:1

  • 1:4

  • 8:1

  • 1:8

Estas nuevas proporciones ultraanchas y ultraaltas se unen al conjunto ya existente. Una sesión de generación puede producir varios formatos como: Imagen principal de Amazon (1:1), Héroe de escaparate (3:4) y Banner publicitario (ultra-ancho u otras proporciones).

Para estas 4 proporciones no es necesario recortar, ni rellenar, ni volver a preguntar. Las 10 relaciones de aspecto restantes se incluyen en el conjunto completo, lo que hace que el proceso sea más flexible en las distintas plataformas.

Sólo el ahorro de un 50% por imagen reduciría la factura a la mitad. La eliminación de la repetición del trabajo en todas las resoluciones y relaciones de aspecto es lo que ha reducido el coste total a aproximadamente un tercio de lo que se gastaba antes.

Soporte de hasta 14 imágenes de referencia con estilo Bestseller

De todas las actualizaciones de Nano Banana 2, la mezcla multirreferencia es la que más ha afectado a nuestro proceso Milvus. Nano Banana 2 acepta hasta 14 imágenes de referencia en una sola solicitud, manteniendo:

  • Semejanza de caracteres para un máximo de 5 caracteres
  • Fidelidad a los objetos para un máximo de 14 objetos

En la práctica, recuperamos varias imágenes de superventas de Milvus, las pasamos como referencias y la imagen generada heredó su composición de escena, iluminación, pose y colocación de accesorios. No fue necesario recurrir a la ingeniería para reconstruir a mano esos patrones.

Los modelos anteriores sólo admitían una o dos referencias, lo que obligaba a los usuarios a elegir un único éxito de ventas para imitarlo. Con 14 ranuras de referencia, podíamos combinar las características de varios de los listados con mejores resultados y dejar que el modelo sintetizara un estilo compuesto. Esta es la capacidad que hace posible el proceso basado en la recuperación que se describe en el tutorial siguiente.

Produzca imágenes de primera calidad listas para el mercado sin los costes de producción ni la logística tradicionales

Para obtener una generación de imágenes coherente y fiable, evite volcar todos sus requisitos en una única solicitud. Un enfoque más fiable es trabajar por etapas: generar primero el fondo, luego el modelo por separado y, por último, componerlos juntos.

Hemos probado la generación de fondos en los tres modelos de Nano Banana con la misma imagen: un horizonte de Shanghái ultrapanorámico 4:1 en un día lluvioso visto a través de una ventana, con la Torre de la Perla Oriental visible. Esta imagen pone a prueba la composición, el detalle arquitectónico y el fotorrealismo en una sola pasada.

Nano Banana original vs. Nano Banana Pro vs. Nano Banana 2

  • Nano Banana original. Textura de lluvia natural con una distribución de gotas creíble, pero detalles de los edificios demasiado suavizados. La Torre de la Perla Oriental apenas era reconocible y la resolución no cumplía los requisitos de producción.
  • Nano Banana Pro. Atmósfera cinematográfica: la cálida iluminación interior contrastaba con la fría lluvia de forma convincente. Sin embargo, se omitió por completo el marco de la ventana, aplanando la sensación de profundidad de la imagen. Utilizable como imagen de apoyo, no como protagonista.
  • Nano Banana 2. Renderizada la escena completa. El marco de la ventana en primer plano creaba profundidad. La Torre de la Perla Oriental aparece claramente detallada. Aparecen barcos en el río Huangpu. La iluminación por capas distinguía la calidez interior del cielo nublado exterior. Las texturas de la lluvia y las manchas de agua eran casi fotográficas, y la relación 4:1 ultrawide mantenía la perspectiva correcta con sólo una pequeña distorsión en el borde izquierdo de la ventana.

Para la mayoría de las tareas de generación de fondos en la fotografía de productos, la Nano Banana 2 se puede utilizar sin necesidad de postprocesado.

Renderizado limpio de texto en imagen en todos los idiomas

Etiquetas de precios, banners promocionales y textos multilingües son inevitables en las imágenes de comercio electrónico, e históricamente han sido un punto de ruptura para la generación de IA. Nano Banana 2 los maneja significativamente mejor, soportando el renderizado de texto en la imagen a través de múltiples idiomas con traducción y localización en una sola generación.

Renderizado de texto estándar. En nuestras pruebas, la salida de texto no contenía errores en ninguno de los formatos de comercio electrónico que probamos: etiquetas de precios, breves eslóganes de marketing y descripciones bilingües de productos.

Continuación de la escritura a mano. Dado que el comercio electrónico requiere a menudo elementos escritos a mano, como etiquetas de precios y tarjetas personalizadas, probamos si los modelos podían igualar un estilo manuscrito existente y ampliarlo; en concreto, igualar una lista de tareas manuscrita y añadir 5 nuevos elementos con el mismo estilo. Resultados de los tres modelos:

  • Nano Banana original. Números de secuencia repetidos, estructura mal entendida.
  • Nano Banana Pro. Estructura correcta, pero mala reproducción del estilo de letra.
  • Nano Banana 2. Sin errores. El grosor de los trazos y el estilo de las letras coinciden lo suficiente como para no distinguirse de la fuente.

Sin embargo, la propia documentación de Google señala que Nano Banana 2 "aún puede tener problemas con la ortografía precisa y los detalles finos en las imágenes". Nuestros resultados fueron limpios en todos los formatos que probamos, pero cualquier flujo de trabajo de producción debería incluir un paso de verificación del texto antes de publicarlo.

Tutorial paso a paso: Construir una cadena de superventas a imágenes con Milvus, Qwen 3.5 y Nano Banana 2

Antes de empezar: Arquitectura y configuración del modelo

Para evitar la aleatoriedad de la generación de una sola promesa, dividimos el proceso en tres etapas controlables: recuperar lo que ya funciona con la búsqueda híbrida Milvus, analizar por qué funciona con Qwen 3.5 y, a continuación, generar la imagen final con esas restricciones incorporadas con Nano Banana 2.

Una breve introducción a cada herramienta si no has trabajado con ellas antes:

  • Milvus: la base de datos vectorial de código abierto más adoptada. Almacena tu catálogo de productos como vectores y ejecuta búsquedas híbridas (filtros densos + dispersos + escalares) para encontrar las imágenes más vendidas y similares a un nuevo producto.
  • Qwen 3.5: un popular LLM multimodal. Toma las imágenes recuperadas de los bestsellers y extrae los patrones visuales que hay detrás de ellas (disposición de la escena, iluminación, pose, estado de ánimo) en un mensaje de estilo estructurado.
  • Nano Banana 2: modelo de generación de imágenes de Google (Gemini 3.1 Flash Image). Toma tres datos de entrada: la composición plana del nuevo producto, una referencia de un bestseller y la solicitud de estilo de Qwen 3.5. Produce la foto promocional final.

La lógica que subyace a esta arquitectura parte de una observación: el activo visual más valioso de cualquier catálogo de comercio electrónico es la biblioteca de imágenes de bestsellers que ya se han convertido. Las poses, las composiciones y la iluminación de esas fotos se han perfeccionado a través del gasto publicitario real. Recuperar esos patrones directamente es mucho más rápido que aplicarles ingeniería inversa mediante la escritura de avisos, y ese paso de recuperación es exactamente lo que gestiona una base de datos vectorial.

Este es el flujo completo. Llamamos a cada modelo a través de la API de OpenRouter, por lo que no hay necesidad de una GPU local ni de descargar los pesos de los modelos.

New product flat-lay
│
│── Embed → Llama Nemotron Embed VL 1B v2
│
│── Search → Milvus hybrid search
│   ├── Dense vectors (visual similarity)
│   ├── Sparse vectors (keyword matching)
│   └── Scalar filters (category + sales volume)
│
│── Analyze → Qwen 3.5 extracts style from retrieved bestsellers
│   └── scene, lighting, pose, mood → style prompt
│
└── Generate → Nano Banana 2
    ├── Inputs: new product + bestseller reference + style prompt
    └── Output: promotional photo

Nos apoyamos en tres capacidades de Milvus para hacer funcionar la etapa de recuperación:

  1. Búsqueda híbrida densa + dispersa. Ejecutamos incrustaciones de imágenes y vectores TF-IDF de texto como consultas paralelas y, a continuación, fusionamos los dos conjuntos de resultados con RRF (Reciprocal Rank Fusion).
  2. Filtrado por campos escalares. Filtramos por campos de metadatos como categoría y recuento_ventas antes de la comparación vectorial, para que los resultados sólo incluyan productos relevantes y de alto rendimiento.
  3. Esquema de campos múltiples. Almacenamos vectores densos, vectores dispersos y metadatos escalares en una única colección Milvus, lo que mantiene toda la lógica de recuperación en una sola consulta en lugar de dispersa en varios sistemas.

Preparación de datos

Catálogo histórico de productos

Comenzamos con dos activos: una carpeta images/ de fotos de productos existentes y un archivo products.csv que contiene sus metadatos.

images/
├── SKU001.jpg
├── SKU002.jpg
├── ...
└── SKU040.jpg

products.csv fields: product_id, image_path, category, color, style, season, sales_count, description, price

Datos de nuevos productos

Para los productos de los que queremos generar imágenes promocionales, preparamos una estructura paralela: una carpeta new_products/ y new_products.csv.

new_products/
├── NEW001.jpg    # Blue knit cardigan + grey tulle skirt set
├── NEW002.jpg    # Light green floral ruffle maxi dress
├── NEW003.jpg    # Camel turtleneck knit dress
└── NEW004.jpg    # Dark grey ethnic-style cowl neck top dress

new_products.csv fields: new_id, image_path, category, style, season, prompt_hint

Paso 1: Instalar dependencias

!pip install pymilvus openai requests pillow scikit-learn tqdm

Paso 2: Importar módulos y configuraciones

import os, io, base64, csv, time
import requests as req
import numpy as np
from PIL import Image
from tqdm.notebook import tqdm
from sklearn.feature_extraction.text import TfidfVectorizer
from IPython.display import display

from openai import OpenAI from pymilvus import MilvusClient, DataType, AnnSearchRequest, RRFRanker

Configurar todos los modelos y rutas:

# -- Config --
OPENROUTER_API_KEY = os.environ.get(
    "OPENROUTER_API_KEY",
    "<YOUR_OPENROUTER_API_KEY>",
)

# Models (all via OpenRouter, no local download needed) EMBED_MODEL = “nvidia/llama-nemotron-embed-vl-1b-v2” # free, image+text → 2048d EMBED_DIM = 2048 LLM_MODEL = “qwen/qwen3.5-397b-a17b” # style analysis IMAGE_GEN_MODEL = “google/gemini-3.1-flash-image-preview” # Nano Banana 2

# Milvus MILVUS_URI = “./milvus_fashion.db” COLLECTION = “fashion_products” TOP_K = 3

# Paths IMAGE_DIR = “./images” NEW_PRODUCT_DIR = “./new_products” PRODUCT_CSV = “./products.csv” NEW_PRODUCT_CSV = “./new_products.csv”

# OpenRouter client (shared for LLM + image gen) llm = OpenAI(api_key=OPENROUTER_API_KEY, base_url=“https://openrouter.ai/api/v1”)

print(“Config loaded. All models via OpenRouter API.”)

Funciones de ayuda

Estas funciones de ayuda se encargan de la codificación de imágenes, las llamadas a la API y el análisis sintáctico de las respuestas:

  • image_to_uri(): Convierte una imagen PIL en un URI de datos base64 para el transporte de la API.
  • get_image_embeddings(): Codifica por lotes imágenes en vectores de 2048 dimensiones a través de la API de incrustación de OpenRouter.
  • get_text_embedding(): Codifica texto en el mismo espacio vectorial de 2048 dimensiones.
  • sparse_to_dict(): Convierte una fila de matriz dispersa scipy en el formato {index: value} que Milvus espera para vectores dispersos.
  • extract_images(): Extrae las imágenes generadas de la respuesta de la API de Nano Banana 2.
# -- Utility functions --

def image_to_uri(img, max_size=1024): “""Convert PIL Image to base64 data URI.""” img = img.copy() w, h = img.size if max(w, h) > max_size: r = max_size / max(w, h) img = img.resize((int(w * r), int(h * r)), Image.LANCZOS) buf = io.BytesIO() img.save(buf, format=“JPEG”, quality=85) return f"data:image/jpeg;base64,{base64.b64encode(buf.getvalue()).decode()}"

def get_image_embeddings(images, batch_size=5): “""Encode images via OpenRouter embedding API.""” all_embs = [] for i in tqdm(range(0, len(images), batch_size), desc=“Encoding images”): batch = images[i : i + batch_size] inputs = [ {“content”: [{“type”: “image_url”, “image_url”: {“url”: image_to_uri(img, max_size=512)}}]} for img in batch ] resp = req.post( “https://openrouter.ai/api/v1/embeddings”, headers={“Authorization”: f"Bearer {OPENROUTER_API_KEY}"}, json={“model”: EMBED_MODEL, “input”: inputs}, timeout=120, ) data = resp.json() if “data” not in data: print(f"API error: {data}") continue for item in sorted(data[“data”], key=lambda x: x[“index”]): all_embs.append(item[“embedding”]) time.sleep(0.5) # rate limit friendly return np.array(all_embs, dtype=np.float32)

def get_text_embedding(text): “""Encode text via OpenRouter embedding API.""” resp = req.post( “https://openrouter.ai/api/v1/embeddings”, headers={“Authorization”: f"Bearer {OPENROUTER_API_KEY}"}, json={“model”: EMBED_MODEL, “input”: text}, timeout=60, ) return np.array(resp.json()[“data”][0][“embedding”], dtype=np.float32)

def sparse_to_dict(sparse_row): “""Convert scipy sparse row to Milvus sparse vector format {index: value}.""” coo = sparse_row.tocoo() return {int(i): float(v) for i, v in zip(coo.col, coo.data)}

def extract_images(response): “""Extract generated images from OpenRouter response.""” images = [] raw = response.model_dump() msg = raw[“choices”][0][“message”] # Method 1: images field (OpenRouter extension) if “images” in msg and msg[“images”]: for img_data in msg[“images”]: url = img_data[“image_url”][“url”] b64 = url.split(“,”, 1)[1] images.append(Image.open(io.BytesIO(base64.b64decode(b64)))) # Method 2: inline base64 in content parts if not images and isinstance(msg.get(“content”), list): for part in msg[“content”]: if isinstance(part, dict) and part.get(“type”) == “image_url”: url = part[“image_url”][“url”] if url.startswith(“data:image”): b64 = url.split(“,”, 1)[1] images.append(Image.open(io.BytesIO(base64.b64decode(b64)))) return images

print(“Utility functions ready.”)

Paso 3: Cargar el catálogo de productos

Lee products.csv y carga las imágenes de producto correspondientes:

with open(PRODUCT_CSV, newline="", encoding="utf-8") as f:
    products = list(csv.DictReader(f))

product_images = [] for p in products: img = Image.open(os.path.join(IMAGE_DIR, p[“image_path”])).convert(“RGB”) product_images.append(img)

print(f"Loaded {len(products)} products.") for i in range(3): p = products[i] print(f"{p[‘product_id’]} | {p[‘category’]} | {p[‘color’]} | {p[‘style’]} | sales: {p[‘sales_count’]}") display(product_images[i].resize((180, int(180 * product_images[i].height / product_images[i].width))))

Ejemplo de salida:

Paso 4: Generar incrustaciones

La búsqueda híbrida requiere dos tipos de vectores para cada producto.

4.1 Vectores densos: incrustación de imágenes

El modelo nvidia/llama-nemotron-embed-vl-1b-v2 codifica la imagen de cada producto en un vector denso de 2048 dimensiones. Dado que este modelo admite entradas tanto de imágenes como de texto en un espacio vectorial compartido, las mismas incrustaciones funcionan para la recuperación de imagen a imagen y de texto a imagen.

# Dense embeddings: image → 2048-dim vector via OpenRouter API
dense_vectors = get_image_embeddings(product_images, batch_size=5)
print(f"Dense vectors: {dense_vectors.shape}  (products x {EMBED_DIM}d)")

Resultados:

Dense vectors: (40, 2048)  (products x 2048d)

4,2 Vectores dispersos: Incrustación de texto TF-IDF

Las descripciones de texto de los productos se codifican en vectores dispersos mediante el vectorizador TF-IDF de scikit-learn. Estos vectores capturan las coincidencias a nivel de palabra clave que los vectores densos pueden pasar por alto.

# Sparse embeddings: TF-IDF on product descriptions
descriptions = [p["description"] for p in products]
tfidf = TfidfVectorizer(stop_words="english", max_features=500)
tfidf_matrix = tfidf.fit_transform(descriptions)

sparse_vectors = [sparse_to_dict(tfidf_matrix[i]) for i in range(len(products))] print(f"Sparse vectors: {len(sparse_vectors)} products, vocab size: {len(tfidf.vocabulary_)}") print(f"Sample sparse vector (SKU001): {len(sparse_vectors[0])} non-zero terms")

Resultados:

Sparse vectors: 40 products, vocab size: 179
Sample sparse vector (SKU001): 11 non-zero terms

¿Por qué ambos tipos de vectores? Los vectores densos y dispersos se complementan. Los vectores densos captan la similitud visual: paleta de colores, silueta de la prenda, estilo general. Los vectores dispersos captan la semántica de las palabras clave: términos como "floral", "midi" o "gasa" que indican atributos del producto. La combinación de ambos produce una calidad de recuperación significativamente mejor que cualquiera de los dos enfoques por separado.

Paso 5: Crear una colección Milvus con un esquema híbrido

Este paso crea una única colección Milvus que almacena vectores densos, vectores dispersos y campos de metadatos escalares juntos. Este esquema unificado es lo que permite la búsqueda híbrida en una única consulta.

CampoTipoFinalidad
vector_densoFLOAT_VECTOR (2048d)Incrustación de imagen, similitud COSINE
vector_esparcidoSPARSE_FLOAT_VECTORVector disperso TF-IDF, producto interno
categoríaVARCHAREtiqueta de categoría para filtrado
recuento_de_ventasINT64Volumen histórico de ventas para filtrar
color, estilo, temporadaVARCHAREtiquetas de metadatos adicionales
precioFLOATPrecio del producto
milvus_client = MilvusClient(uri=MILVUS_URI)

if milvus_client.has_collection(COLLECTION): milvus_client.drop_collection(COLLECTION)

schema = milvus_client.create_schema(auto_id=True, enable_dynamic_field=True) schema.add_field(“id”, DataType.INT64, is_primary=True) schema.add_field(“product_id”, DataType.VARCHAR, max_length=20) schema.add_field(“category”, DataType.VARCHAR, max_length=50) schema.add_field(“color”, DataType.VARCHAR, max_length=50) schema.add_field(“style”, DataType.VARCHAR, max_length=50) schema.add_field(“season”, DataType.VARCHAR, max_length=50) schema.add_field(“sales_count”, DataType.INT64) schema.add_field(“description”, DataType.VARCHAR, max_length=500) schema.add_field(“price”, DataType.FLOAT) schema.add_field(“dense_vector”, DataType.FLOAT_VECTOR, dim=EMBED_DIM) schema.add_field(“sparse_vector”, DataType.SPARSE_FLOAT_VECTOR)

index_params = milvus_client.prepare_index_params() index_params.add_index(field_name=“dense_vector”, index_type=“FLAT”, metric_type=“COSINE”) index_params.add_index(field_name=“sparse_vector”, index_type=“SPARSE_INVERTED_INDEX”, metric_type=“IP”)

milvus_client.create_collection(COLLECTION, schema=schema, index_params=index_params) print(f"Milvus collection '{COLLECTION}' created with hybrid schema.")

Inserta los datos del producto:

# Insert all products
rows = []
for i, p in enumerate(products):
    rows.append({
        "product_id": p["product_id"],
        "category": p["category"],
        "color": p["color"],
        "style": p["style"],
        "season": p["season"],
        "sales_count": int(p["sales_count"]),
        "description": p["description"],
        "price": float(p["price"]),
        "dense_vector": dense_vectors[i].tolist(),
        "sparse_vector": sparse_vectors[i],
    })

milvus_client.insert(COLLECTION, rows) stats = milvus_client.get_collection_stats(COLLECTION) print(f"Inserted {stats[‘row_count’]} products into Milvus.")

Salida:

Inserted 40 products into Milvus.

Paso 6: Búsqueda híbrida para encontrar superventas similares

Este es el paso central de la recuperación. Para cada nuevo producto, el proceso ejecuta tres operaciones simultáneamente:

  1. Búsqueda densa: encuentra productos con imágenes visualmente similares.
  2. Búsqueda dispersa: busca productos con palabras clave de texto coincidentes mediante TF-IDF.
  3. Filtrado escalar: restringe los resultados a la misma categoría y a productos con sales_count > 1500.
  4. RRF reranking: fusiona las listas de resultados densas y dispersas mediante Reciprocal Rank Fusion.

Carga el nuevo producto:

# Load new products
with open(NEW_PRODUCT_CSV, newline="", encoding="utf-8") as f:
    new_products = list(csv.DictReader(f))

# Pick the first new product for demo new_prod = new_products[0] new_img = Image.open(os.path.join(NEW_PRODUCT_DIR, new_prod[“image_path”])).convert(“RGB”)

print(f"New product: {new_prod[‘new_id’]}") print(f"Category: {new_prod[‘category’]} | Style: {new_prod[‘style’]} | Season: {new_prod[‘season’]}") print(f"Prompt hint: {new_prod[‘prompt_hint’]}") display(new_img.resize((300, int(300 * new_img.height / new_img.width))))

Salida:

Codifica el nuevo producto:

# Encode new product
# Dense: image embedding via API
query_dense = get_image_embeddings([new_img], batch_size=1)[0]

# Sparse: TF-IDF from text query query_text = f"{new_prod[‘category’]} {new_prod[‘style’]} {new_prod[‘season’]} {new_prod[‘prompt_hint’]}" query_sparse = sparse_to_dict(tfidf.transform([query_text])[0])

# Scalar filter filter_expr = f’category == "{new_prod[“category”]}" and sales_count > 1500’

print(f"Dense query: {query_dense.shape}") print(f"Sparse query: {len(query_sparse)} non-zero terms") print(f"Filter: {filter_expr}")

Salida:

Dense query: (2048,)
Sparse query: 6 non-zero terms
Filter: category == "midi_dress" and sales_count > 1500

Ejecutar búsqueda híbrida

Las llamadas clave de la API aquí:

  • AnnSearchRequest crea solicitudes de búsqueda separadas para los campos vectoriales densos y dispersos.
  • expr=filter_expr aplica el filtrado escalar dentro de cada solicitud de búsqueda.
  • RRFRanker(k=60) fusiona las dos listas de resultados utilizando el algoritmo Reciprocal Rank Fusion.
  • hybrid_search ejecuta ambas peticiones y devuelve los resultados fusionados y reordenados.
# Hybrid search: dense + sparse + scalar filter + RRF reranking
dense_req = AnnSearchRequest(
    data=[query_dense.tolist()],
    anns_field="dense_vector",
    param={"metric_type": "COSINE"},
    limit=20,
    expr=filter_expr,
)
sparse_req = AnnSearchRequest(
    data=[query_sparse],
    anns_field="sparse_vector",
    param={"metric_type": "IP"},
    limit=20,
    expr=filter_expr,
)

results = milvus_client.hybrid_search( collection_name=COLLECTION, reqs=[dense_req, sparse_req], ranker=RRFRanker(k=60), limit=TOP_K, output_fields=[“product_id”, “category”, “color”, “style”, “season”, “sales_count”, “description”, “price”], )

# Display retrieved bestsellers retrieved_products = [] retrieved_images = [] print(f"Top-{TOP_K} similar bestsellers:\n") for hit in results[0]: entity = hit[“entity”] pid = entity[“product_id”] img = Image.open(os.path.join(IMAGE_DIR, f"{pid}.jpg")).convert(“RGB”) retrieved_products.append(entity) retrieved_images.append(img) print(f"{pid} | {entity[‘category’]} | {entity[‘color’]} | {entity[‘style’]} " f"| sales: {entity[‘sales_count’]} | ${entity[‘price’]:.1f} | score: {hit[‘distance’]:.4f}") print(f" {entity[‘description’]}") display(img.resize((250, int(250 * img.height / img.width)))) print()

Salida: los 3 bestsellers más similares, ordenados por puntuación fusionada.

Paso 7: Analizar el estilo de los bestsellers con Qwen 3.5

Introducimos las imágenes recuperadas en Qwen 3.5 y le pedimos que extraiga su ADN visual común: composición de la escena, iluminación, pose de la modelo y estado de ánimo general. A partir de este análisis, obtenemos una única generación de imágenes lista para enviar a Nano Banana 2.

content = [
    {"type": "image_url", "image_url": {"url": image_to_uri(img)}}
    for img in retrieved_images
]
content.append({
    "type": "text",
    "text": (
        "These are our top-selling fashion product photos.\n\n"
        "Analyze their common visual style in these dimensions:\n"
        "1. Scene / background setting\n"
        "2. Lighting and color tone\n"
        "3. Model pose and framing\n"
        "4. Overall mood and aesthetic\n\n"
        "Then, based on this analysis, write ONE concise image generation prompt "
        "(under 100 words) that captures this style. The prompt should describe "
        "a scene for a model wearing a new clothing item. "
        "Output ONLY the prompt, nothing else."
    ),
})

response = llm.chat.completions.create( model=LLM_MODEL, messages=[{“role”: “user”, “content”: content}], max_tokens=512, temperature=0.7, ) style_prompt = response.choices[0].message.content.strip() print(“Style prompt from Qwen3.5:\n”) print(style_prompt)

Ejemplo de resultado:

Style prompt from Qwen3.5:

Professional full-body fashion photograph of a model wearing a stylish new dress. Bright, soft high-key lighting that illuminates the subject evenly. Clean, uncluttered background, either stark white or a softly blurred bright outdoor setting. The model stands in a relaxed, natural pose to showcase the garment’s silhouette and drape. Sharp focus, vibrant colors, fresh and elegant commercial aesthetic.

Paso 8: Generación de la imagen promocional con Nano Banana 2

Introducimos tres datos en Nano Banana 2: la foto plana del nuevo producto, la imagen del bestseller mejor clasificado y el mensaje de estilo que extrajimos en el paso anterior. El modelo los compone en una foto promocional que combina la nueva prenda con un estilo visual probado.

gen_prompt = (
    f"I have a new clothing product (Image 1: flat-lay photo) and a reference "
    f"promotional photo from our bestselling catalog (Image 2).\n\n"
    f"Generate a professional e-commerce promotional photograph of a female model "
    f"wearing the clothing from Image 1.\n\n"
    f"Style guidance: {style_prompt}\n\n"
    f"Scene hint: {new_prod['prompt_hint']}\n\n"
    f"Requirements:\n"
    f"- Full body shot, photorealistic, high quality\n"
    f"- The clothing should match Image 1 exactly\n"
    f"- The photo style and mood should match Image 2"
)

gen_content = [ {“type”: “image_url”, “image_url”: {“url”: image_to_uri(new_img)}}, {“type”: “image_url”, “image_url”: {“url”: image_to_uri(retrieved_images[0])}}, {“type”: “text”, “text”: gen_prompt}, ]

print(“Generating promotional photo with Nano Banana 2…”) gen_response = llm.chat.completions.create( model=IMAGE_GEN_MODEL, messages=[{“role”: “user”, “content”: gen_content}], extra_body={ “modalities”: [“text”, “image”], “image_config”: {“aspect_ratio”: “3:4”, “image_size”: “2K”}, }, ) print(“Done!”)

Parámetros clave de la llamada a la API de Nano Banana 2:

  • modalidades: ["text", "image"]: declara que la respuesta debe incluir una imagen.
  • image_config.aspect_ratio: controla la relación de aspecto de salida (3:4 funciona bien para retratos/moda).
  • image_config.image_size: establece la resolución. Nano Banana 2 soporta desde 512px hasta 4K.

Extrae la imagen generada:

generated_images = extract_images(gen_response)

text_content = gen_response.choices[0].message.content if text_content: print(f"Model response: {text_content[:300]}\n")

if generated_images: for i, img in enumerate(generated_images): print(f"— Generated promo photo {i+1} —") display(img) img.save(f"promo_{new_prod[‘new_id’]}{i+1}.png") print(f"Saved: promo{new_prod[‘new_id’]}_{i+1}.png") else: print(“No image generated. Raw response:”) print(gen_response.model_dump())

Salida:

Paso 9: Comparación lado a lado

El resultado da en el clavo: la iluminación es suave y uniforme, la pose de la modelo parece natural y el ambiente coincide con la referencia del bestseller.

En lo que falla es en la mezcla de prendas. El cárdigan parece pegado a la modelo en lugar de llevado, y la etiqueta blanca del escote se transparenta. La generación de una sola pasada tiene dificultades con este tipo de integración detallada de la ropa en el cuerpo, por lo que en el resumen se explican las soluciones.

Paso 10. Generación de lotes para todos los productos nuevos Generación de lotes para todos los productos nuevos

Envolvemos todo el proceso en una única función y la ejecutamos en el resto de nuevos productos. El código del lote se omite aquí por brevedad; póngase en contacto con nosotros si necesita la implementación completa.

Dos cosas destacan en los resultados por lotes. Las indicaciones de estilo que recibimos de Qwen 3.5 se ajustan significativamente a cada producto: un vestido de verano y un jersey de invierno reciben descripciones de escena realmente diferentes adaptadas a la estación, el caso de uso y los accesorios. Las imágenes que obtenemos de Nano Banana 2, a su vez, están a la altura de la fotografía de estudio real en cuanto a iluminación, textura y composición.

Conclusión

En este artículo, hemos tratado lo que Nano Banana 2 aporta a la generación de imágenes de comercio electrónico, lo hemos comparado con el Nano Banana original y el Pro en tareas de producción reales, y hemos explicado cómo crear una cadena de producción de bestsellers a imágenes con Milvus, Qwen 3.5 y Nano Banana 2. Esta cadena de producción tiene cuatro ventajas prácticas.

Este pipeline tiene cuatro ventajas prácticas:

  • Coste controlado, presupuestos predecibles. El modelo de incrustación (Llama Nemotron Embed VL 1B v2) es gratuito en OpenRouter. Nano Banana 2 cuesta aproximadamente la mitad por imagen que Pro, y la salida multiformato nativa elimina los ciclos de retoque que solían duplicar o triplicar la factura efectiva. Para los equipos de comercio electrónico que gestionan miles de referencias por temporada, esta previsibilidad significa que la producción de imágenes se adapta al catálogo en lugar de desbordar el presupuesto.
  • Automatización de principio a fin, tiempo de publicación más rápido. El flujo desde la foto plana del producto hasta la imagen promocional acabada se realiza sin intervención manual. Un nuevo producto puede pasar de la foto de almacén a la imagen de anuncio lista para el mercado en cuestión de minutos en lugar de días, lo que es más importante durante las temporadas altas, cuando la rotación del catálogo es mayor.
  • No requiere GPU local, menor barrera de entrada. Todos los modelos se ejecutan a través de la API OpenRouter. Un equipo sin infraestructura de ML ni personal de ingeniería dedicado puede ejecutar este proceso desde un portátil. No hay nada que aprovisionar, nada que mantener ni ninguna inversión inicial en hardware.
  • Mayor precisión de recuperación, mayor coherencia de marca. Milvus combina el filtrado denso, disperso y escalar en una sola consulta, superando sistemáticamente los enfoques de un solo vector para la correspondencia de productos. En la práctica, esto significa que las imágenes generadas heredan de forma más fiable el lenguaje visual establecido de su marca: la iluminación, la composición y el estilo que sus bestsellers existentes ya han demostrado que convierten. El resultado se ve como si perteneciera a su tienda, no como arte genérico de archivo de IA.

También hay limitaciones sobre las que vale la pena ser franco:

  • Mezcla de ropa y cuerpo. La generación de una sola pasada puede hacer que la ropa parezca compuesta en lugar de desgastada. Los detalles finos, como los accesorios pequeños, a veces se difuminan. Solución: generar por etapas (primero el fondo, luego la pose del modelo y después la composición). Este enfoque multipase da a cada paso un alcance más estrecho y mejora significativamente la calidad de la mezcla.
  • Fidelidad de detalle en casos extremos. Los accesorios, los patrones y los diseños con mucho texto pueden perder nitidez. Solución: añada restricciones explícitas a la solicitud de generación ("la ropa se ajusta de forma natural al cuerpo, sin etiquetas expuestas, sin elementos adicionales, los detalles del producto son nítidos"). Si la calidad sigue siendo baja en un producto concreto, cambie a Nano Banana Pro para la versión final.

Milvus es la base de datos vectorial de código abierto que alimenta el paso de búsqueda híbrida, y si quieres echar un vistazo o probar a intercambiar tus propias fotos de producto, elinicio rápido de te llevará unos diez minutos. Tenemos una comunidad muy activa en Discord y Slack, y nos encantaría ver lo que la gente construye con esto. Y si acabas comparando Nano Banana 2 con un producto vertical diferente o con un catálogo mayor, ¡comparte los resultados! Nos encantaría conocerlos.

Seguir leyendo

    Try Managed Milvus for Free

    Zilliz Cloud is hassle-free, powered by Milvus and 10x faster.

    Get Started

    Like the article? Spread the word

    Sigue Leyendo