Costruire una pipeline da bestseller a immagine per l'e-commerce con Nano Banana 2 + Milvus + Qwen 3.5
Se costruite strumenti di intelligenza artificiale per i venditori di e-commerce, probabilmente avete sentito questa richiesta migliaia di volte: "Ho un nuovo prodotto. Datemi un'immagine promozionale che sembri appartenere a un bestseller. Senza fotografo, senza studio, e che sia economica".
Questo è il problema in una frase. I venditori dispongono di foto a posa piatta e di un catalogo di bestseller che già convertono. Vogliono creare un ponte tra le due cose con l'intelligenza artificiale, in modo rapido e su scala.
Quando Google ha rilasciato Nano Banana 2 (Gemini 3.1 Flash Image) il 26 febbraio 2026, l'abbiamo testata il giorno stesso e integrata nella nostra pipeline di recupero esistente basata su Milvus. Il risultato: il costo totale di generazione delle immagini è sceso a circa un terzo di quanto speso in precedenza e il throughput è raddoppiato. La riduzione del prezzo per immagine (circa il 50% in meno rispetto a Nano Banana Pro) spiega in parte questo risultato, ma il risparmio maggiore deriva dall'eliminazione totale dei cicli di rielaborazione.
Questo articolo illustra i vantaggi di Nano Banana 2 per l'e-commerce, i punti deboli e un'esercitazione pratica per la pipeline completa: La ricerca ibrida di Milvus per trovare bestseller visivamente simili, Qwen 3.5 per l'analisi dello stile e Nano Banana 2 per la generazione finale.
Cosa c'è di nuovo in Nano Banana 2?
Nano Banana 2 (Gemini 3.1 Flash Image) è stato lanciato il 26 febbraio 2026. Porta la maggior parte delle funzionalità di Nano Banana Pro sull'architettura Flash, il che significa una generazione più veloce a un prezzo inferiore. Ecco i principali aggiornamenti:
- Qualità di livello professionale alla velocità di Flash. Nano Banana 2 offre una conoscenza, un ragionamento e una fedeltà visiva di livello mondiale, in precedenza esclusiva di Pro, ma con la latenza e il throughput di Flash.
- Uscita da 512px a 4K. Quattro livelli di risoluzione (512px, 1K, 2K, 4K) con supporto nativo. Il livello da 512px è nuovo ed esclusivo di Nano Banana 2.
- 14 rapporti di aspetto. Aggiunge 4:1, 1:4, 8:1 e 1:8 al set esistente (1:1, 2:3, 3:2, 3:4, 4:3, 4:5, 5:4, 9:16, 16:9, 21:9).
- Fino a 14 immagini di riferimento. Mantiene la somiglianza dei personaggi fino a 5 e la fedeltà degli oggetti fino a 14 in un unico flusso di lavoro.
- Rendering del testo migliorato. Genera un testo leggibile e accurato in più lingue, con supporto per la traduzione e la localizzazione in un'unica generazione.
- Ricerca per immagini. Utilizza dati web in tempo reale e immagini provenienti da Google Search per generare rappresentazioni più accurate di soggetti reali.
- ~50% in meno per immagine. Alla risoluzione di 1K: 0 ,134.
Un caso d'uso divertente di Nano Banano 2: generare un panorama consapevole della posizione in base a una semplice schermata di Google Map
Dato uno screenshot di Google Maps e una richiesta di stile, il modello riconosce il contesto geografico e genera un panorama che conserva le corrette relazioni spaziali. Utile per produrre creatività pubblicitarie mirate alla regione (sfondo di un caffè parigino, paesaggio stradale di Tokyo) senza dover ricorrere a fotografie di stock.
Per l'elenco completo delle funzioni, consultare il blog di annuncio di Google e la documentazione per gli sviluppatori.
Cosa significa questo aggiornamento di Nano Banana per il commercio elettronico?
Il commercio elettronico è uno dei settori a più alta intensità di immagini. Inserzioni di prodotti, annunci sul mercato, creazioni sociali, campagne banner, vetrine localizzate: ogni canale richiede un flusso costante di risorse visive, ognuna con le proprie specifiche.
I requisiti fondamentali per la generazione di immagini AI nell'e-commerce si riducono a:
- Mantenere i costi bassi - il costo per immagine deve funzionare su scala di catalogo.
- Abbinare l'aspetto dei bestseller comprovati: le nuove immagini devono allinearsi allo stile visivo delle inserzioni che già convertono.
- Evitare le violazioni: non copiare le creazioni dei concorrenti o riutilizzare beni protetti.
Inoltre, i venditori transfrontalieri hanno bisogno di:
- Supporto di formati multipiattaforma - diversi rapporti di aspetto e specifiche per marketplace, annunci e vetrine.
- Rendering del testo multilingue: testo dell'immagine pulito e accurato in più lingue.
Nano Banana 2 è in grado di soddisfare tutte le esigenze. Le sezioni che seguono illustrano il significato pratico di ciascun aggiornamento: dove risolve direttamente un problema di e-commerce, dove non lo risolve e qual è l'impatto effettivo sui costi.
Riduzione dei costi di produzione fino al 60%
Alla risoluzione di 1K, Nano Banana 2 costa 0 ,134, con un taglio del 50%. Ma il prezzo per immagine è solo metà della storia. Ciò che uccideva i budget degli utenti era la rielaborazione. Ogni marketplace impone le proprie specifiche di immagine (1:1 per Amazon, 3:4 per le vetrine di Shopify, ultrawide per i banner pubblicitari) e la produzione di ogni variante comportava un passaggio di generazione separato con le proprie modalità di fallimento.
Nano Banana 2 riduce questi passaggi extra in uno solo.
Quattro livelli di risoluzione nativa.
512px ($0,045)
1K ($0.067)
2K ($0.101)
4K ($0.151).
Il livello di 512px è nuovo ed esclusivo di Nano Banana 2. Gli utenti possono ora generare bozze a 512px a basso costo per l'iterazione e produrre l'asset finale a 2K o 4K senza una fase di upscaling separata.
14 rapporti di aspetto supportati in totale. Ecco alcuni esempi:
4:1
1:4
8:1
1:8
Questi nuovi rapporti ultra-wide e ultra-tall si aggiungono alla serie esistente. Una sessione di generazione può produrre vari formati come: Immagine principale di Amazon (1:1), Eroe della vetrina (3:4) e Banner pubblicitario (ultra-wide o altri rapporti).
Per questi 4 rapporti non è necessario alcun ritaglio, padding o ri-prompting. I restanti 10 rapporti di aspetto sono inclusi nel set completo, rendendo il processo più flessibile su diverse piattaforme.
Il risparmio di circa il 50% per immagine da solo dimezzerebbe il conto. L'eliminazione della rielaborazione delle risoluzioni e dei rapporti di aspetto ha fatto scendere il costo totale a circa un terzo di quanto si spendeva prima.
Supporto fino a 14 immagini di riferimento con lo stile Bestseller
Di tutti gli aggiornamenti di Nano Banana 2, il blending multi-referenza è quello che ha avuto il maggiore impatto sulla nostra pipeline Milvus. Nano Banana 2 accetta fino a 14 immagini di riferimento in una singola richiesta, mantenendo:
- Somiglianza dei caratteri per un massimo di 5 personaggi
- Fedeltà dell'oggetto per un massimo di 14 oggetti
In pratica, abbiamo recuperato più immagini di bestseller da Milvus, le abbiamo passate come riferimenti e l'immagine generata ha ereditato la composizione della scena, l'illuminazione, la posa e il posizionamento degli oggetti. Non è stata necessaria alcuna ingegnerizzazione per ricostruire questi modelli a mano.
I modelli precedenti supportavano solo uno o due riferimenti, costringendo gli utenti a scegliere un singolo bestseller da imitare. Con 14 slot di riferimento, abbiamo potuto mescolare le caratteristiche di più inserzioni con le migliori performance e lasciare che il modello sintetizzasse uno stile composito. È questa la capacità che rende possibile la pipeline basata sul reperimento, descritta nell'esercitazione che segue.
Produrre immagini di qualità, pronte per il commercio, senza costi di produzione o logistica tradizionali
Per una generazione di immagini coerente e affidabile, evitate di scaricare tutti i requisiti in un unico prompt. Un approccio più affidabile è quello di lavorare per fasi: generare prima lo sfondo, poi il modello separatamente e infine comporli insieme.
Abbiamo testato la generazione dello sfondo su tutti e tre i modelli Nano Banana con lo stesso prompt: uno skyline ultrawide 4:1 di Shanghai in un giorno di pioggia visto attraverso una finestra, con la Oriental Pearl Tower visibile. Questa richiesta mette alla prova la composizione, i dettagli architettonici e il fotorealismo in un unico passaggio.
Nano Banana originale vs. Nano Banana Pro vs. Nano Banana 2
- Nano Banana originale. Texture naturale della pioggia con distribuzione credibile delle gocce, ma dettagli degli edifici eccessivamente smussati. L'Oriental Pearl Tower era a malapena riconoscibile e la risoluzione era inferiore ai requisiti di produzione.
- Nano Banana Pro. Atmosfera cinematografica: l'illuminazione calda degli interni si contrappone alla pioggia fredda in modo convincente. Tuttavia, ha omesso completamente la cornice della finestra, appiattendo il senso di profondità dell'immagine. Utilizzabile come immagine di supporto, non come protagonista.
- Nano Banana 2. Rendering dell'intera scena. La cornice della finestra in primo piano creava profondità. La Torre della Perla Orientale era chiaramente dettagliata. Le navi appaiono sul fiume Huangpu. L'illuminazione stratificata distingueva il calore degli interni dal cielo coperto degli esterni. Le texture della pioggia e delle macchie d'acqua erano quasi fotografiche e il rapporto ultrawide 4:1 manteneva la prospettiva corretta con solo una piccola distorsione sul bordo sinistro della finestra.
Per la maggior parte delle attività di generazione dello sfondo nella fotografia di prodotto, abbiamo trovato l'output di Nano Banana 2 utilizzabile senza post-elaborazione.
Rendering pulito del testo nell'immagine in tutte le lingue
Cartellini dei prezzi, banner promozionali e testi multilingue sono inevitabili nelle immagini di e-commerce e storicamente sono stati un punto di rottura per la generazione di AI. Nano Banana 2 li gestisce in modo decisamente migliore, supportando il rendering del testo nell'immagine in più lingue con traduzione e localizzazione in un'unica generazione.
Rendering del testo standard. Nei nostri test, la resa del testo è stata priva di errori in tutti i formati di e-commerce che abbiamo provato: etichette dei prezzi, brevi tagline di marketing e descrizioni bilingue dei prodotti.
Continuazione della scrittura a mano. Poiché l'e-commerce richiede spesso elementi scritti a mano, come cartellini dei prezzi e biglietti personalizzati, abbiamo testato se i modelli fossero in grado di riprodurre uno stile di scrittura esistente e di estenderlo, in particolare riproducendo un elenco di cose da fare scritto a mano e aggiungendo 5 nuove voci nello stesso stile. Risultati dei tre modelli:
- Nano Banana originale. Numeri di sequenza ripetuti, struttura incompresa.
- Nano Banana Pro. Layout corretto, ma scarsa riproduzione dello stile dei caratteri.
- Nano Banana 2. Zero errori. Corrisponde al peso del tratto e allo stile della forma delle lettere tanto da essere indistinguibile dalla fonte.
Tuttavia, nella documentazione di Google si legge che Nano Banana 2 "può ancora avere problemi con l'ortografia accurata e con i dettagli delle immagini". I nostri risultati sono stati puliti in tutti i formati testati, ma qualsiasi flusso di lavoro di produzione dovrebbe includere una fase di verifica del testo prima della pubblicazione.
Esercitazione passo dopo passo: Costruire una pipeline da bestseller a immagine con Milvus, Qwen 3.5 e Nano Banana 2
Prima di iniziare: Architettura e configurazione del modello
Per evitare l'aleatorietà della generazione con un solo prompt, abbiamo diviso il processo in tre fasi controllabili: recuperare ciò che già funziona con la ricerca ibrida di Milvus, analizzare il motivo per cui funziona con Qwen 3.5, quindi generare l'immagine finale con questi vincoli incorporati con Nano Banana 2.
Una breve introduzione a ogni strumento se non avete mai lavorato con loro:
- Milvus: il database vettoriale open-source più diffuso. Memorizza il catalogo dei prodotti come vettori ed esegue una ricerca ibrida (filtri densi + sparsi + scalari) per trovare le immagini dei bestseller più simili a un nuovo prodotto.
- Qwen 3.5: un popolare LLM multimodale. Prende le immagini dei bestseller recuperate ed estrae i modelli visivi alla base di esse (disposizione della scena, illuminazione, posa, umore) in un prompt di stile strutturato.
- Nano Banana 2: modello di generazione di immagini da Google (Gemini 3.1 Flash Image). Riceve tre input: il flat-lay di un nuovo prodotto, un bestseller di riferimento e la richiesta di stile di Qwen 3.5. Produce la foto promozionale finale.
La logica alla base di questa architettura parte da un'osservazione: la risorsa visiva più preziosa di qualsiasi catalogo di e-commerce è la libreria di immagini bestseller già convertite. Le pose, le composizioni e l'illuminazione di queste foto sono state perfezionate attraverso una spesa pubblicitaria reale. Recuperare direttamente questi schemi è un ordine di grandezza più veloce rispetto alla loro reingegnerizzazione attraverso la scrittura di prompt, e questa fase di recupero è esattamente quella che gestisce un database vettoriale.
Ecco il flusso completo. Richiamiamo ogni modello attraverso l'API di OpenRouter, quindi non c'è bisogno di una GPU locale e non ci sono pesi del modello da scaricare.
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
Per far funzionare la fase di recupero ci affidiamo a tre funzionalità di Milvus:
- Ricerca ibrida densa e rada. Eseguiamo le incorporazioni delle immagini e i vettori TF-IDF del testo come query parallele, quindi uniamo i due gruppi di risultati con il reranking RRF (Reciprocal Rank Fusion).
- Filtraggio dei campi scalari. Prima di confrontare i vettori, filtriamo i campi dei metadati, come la categoria e il numero di vendite, in modo che i risultati includano solo i prodotti rilevanti e performanti.
- Schema a più campi. Memorizziamo i vettori densi, i vettori radi e i metadati scalari in un'unica raccolta Milvus, in modo da mantenere l'intera logica di reperimento in un'unica query anziché sparpagliarla in più sistemi.
Preparazione dei dati
Catalogo storico dei prodotti
Partiamo da due asset: una cartella/immagini di foto di prodotti esistenti e un file products.csv contenente i loro metadati.
images/
├── SKU001.jpg
├── SKU002.jpg
├── ...
└── SKU040.jpg
products.csv fields:
product_id, image_path, category, color, style, season, sales_count, description, price
Dati dei nuovi prodotti
Per i prodotti per i quali vogliamo generare immagini promozionali, prepariamo una struttura parallela: una cartella new_products/ e 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
Passo 1: Installare le dipendenze
!pip install pymilvus openai requests pillow scikit-learn tqdm
Passo 2: Importare moduli e configurazioni
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
Configurare tutti i modelli e i percorsi:
# -- 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.”)
Funzioni di utilità
Queste funzioni di aiuto gestiscono la codifica delle immagini, le chiamate API e l'analisi delle risposte:
- image_to_uri(): Converte un'immagine PIL in un URI di dati base64 per il trasporto API.
- get_image_embeddings(): Codifica le immagini in vettori a 2048 dimensioni tramite l'API OpenRouter Embedding.
- get_text_embedding(): Codifica il testo nello stesso spazio vettoriale a 2048 dimensioni.
- sparse_to_dict(): Converte una riga di matrice sparsa scipy nel formato {index: value} che Milvus si aspetta per i vettori sparsi.
- extract_images(): Estrae le immagini generate dalla risposta API di 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.”)
Passo 3: caricare il catalogo dei prodotti
Leggere il file products.csv e caricare le immagini dei prodotti corrispondenti:
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))))
Esempio di output:
Passo 4: Generare le incorporazioni
La ricerca ibrida richiede due tipi di vettori per ogni prodotto.
4.1 Vettori densi: incorporazioni di immagini
Il modello nvidia/llama-nemotron-embed-vl-1b-v2 codifica l'immagine di ogni prodotto in un vettore denso di 2048 dimensioni. Poiché questo modello supporta input sia di immagine che di testo in uno spazio vettoriale condiviso, le stesse incorporazioni funzionano per il recupero da immagine a immagine e da testo a immagine.
# 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)")
Output:
Dense vectors: (40, 2048) (products x 2048d)
4,2 vettori sparsi: Incorporazione del testo TF-IDF
Le descrizioni testuali dei prodotti sono codificate in vettori sparsi utilizzando il vettorizzatore TF-IDF di scikit-learn. Questi catturano la corrispondenza a livello di parola chiave che i vettori densi possono perdere.
# 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")
Output:
Sparse vectors: 40 products, vocab size: 179
Sample sparse vector (SKU001): 11 non-zero terms
Perché entrambi i tipi di vettori? I vettori densi e sparsi si completano a vicenda. I vettori densi catturano la somiglianza visiva: palette di colori, silhouette dell'abito, stile generale. I vettori sparsi catturano la semantica delle parole chiave: termini come "floreale", "midi" o "chiffon" che segnalano gli attributi del prodotto. La combinazione di entrambi produce una qualità di reperimento significativamente migliore rispetto a uno dei due approcci da solo.
Passo 5: Creare una collezione Milvus con uno schema ibrido
Questo passo crea una singola raccolta Milvus che memorizza insieme vettori densi, vettori radi e campi di metadati scalari. Questo schema unificato consente la ricerca ibrida in un'unica query.
| Campo | Tipo | Scopo |
|---|---|---|
| vettore_denso | VETTORE FLOTTANTE (2048d) | Incorporamento dell'immagine, somiglianza COSINE |
| vettore_sparso | SPARSE_FLOAT_VECTOR | Vettore sparse TF-IDF, prodotto interno |
| categoria | VARCHAR | Etichetta di categoria per il filtraggio |
| numero_di_vendite | INT64 | Volume storico delle vendite per il filtraggio |
| colore, stile, stagione | VARCHAR | Etichette di metadati aggiuntive |
| prezzo | FLOTTANTE | Prezzo del prodotto |
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.")
Inserire i dati del prodotto:
# 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.")
Output:
Inserted 40 products into Milvus.
Fase 6: Ricerca ibrida per trovare prodotti più venduti simili
Questa è la fase centrale del reperimento. Per ogni nuovo prodotto, la pipeline esegue contemporaneamente tre operazioni:
- Ricerca densa: trova i prodotti con immagini visivamente simili.
- Ricerca sparsa: trova i prodotti con parole chiave di testo corrispondenti tramite TF-IDF.
- Filtro scalare: limita i risultati alla stessa categoria e ai prodotti con numero di vendite > 1500.
- RRF reranking: unisce gli elenchi di risultati densi e radi utilizzando la Reciprocal Rank Fusion.
Carica il nuovo prodotto:
# 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))))
Output:
Codifica il nuovo prodotto:
# 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}")
Uscita:
Dense query: (2048,)
Sparse query: 6 non-zero terms
Filter: category == "midi_dress" and sales_count > 1500
Eseguire la ricerca ibrida
Le chiamate API chiave sono le seguenti:
- AnnSearchRequest crea richieste di ricerca separate per i campi vettoriali densi e radi.
- expr=filter_expr applica un filtro scalare a ciascuna richiesta di ricerca.
- RRFRanker(k=60) fonde i due elenchi di risultati classificati utilizzando l'algoritmo Reciprocal Rank Fusion.
- hybrid_search esegue entrambe le richieste e restituisce i risultati uniti e riclassificati.
# 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()
Output: i 3 bestseller più simili, classificati in base al punteggio fuso.
Fase 7: Analisi dello stile dei bestseller con Qwen 3.5
Inseriamo le immagini dei bestseller recuperate in Qwen 3.5 e gli chiediamo di estrarre il loro DNA visivo condiviso: composizione della scena, impostazione dell'illuminazione, posa della modella e stato d'animo generale. Da questa analisi, otteniamo un prompt di singola generazione pronto per essere consegnato 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)
Esempio di output:
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.
Passo 8: Generare l'immagine promozionale con Nano Banana 2
A Nano Banana 2 passiamo tre input: la foto flat-lay del nuovo prodotto, l'immagine del bestseller più venduto e la richiesta di stile estratta nel passaggio precedente. Il modello li compone in una foto promozionale che abbina il nuovo capo a uno stile visivo collaudato.
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!”)
Parametri chiave della chiamata API Nano Banana 2:
- modalità: ["text", "image"]: dichiara che la risposta deve includere un'immagine.
- image_config.aspect_ratio: controlla il rapporto d'aspetto dell'output (3:4 funziona bene per gli scatti di ritratto/moda).
- image_config.image_size: imposta la risoluzione. Nano Banana 2 supporta da 512px a 4K.
Estrarre l'immagine generata:
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())
Output:
Fase 9: confronto laterale
L'output è perfetto: l'illuminazione è morbida e uniforme, la posa della modella è naturale e l'atmosfera corrisponde al bestseller di riferimento.
Il punto debole è la fusione dei capi. Il cardigan sembra incollato sulla modella piuttosto che indossato e l'etichetta bianca della scollatura traspare. La generazione a passaggio singolo ha difficoltà a gestire questo tipo di integrazione fine dei vestiti con il corpo, per cui nel riepilogo si parla di soluzioni.
Fase 10: Generazione di lotti per tutti i nuovi prodotti
L'intera pipeline viene racchiusa in un'unica funzione ed eseguita per i restanti nuovi prodotti. Il codice del batch viene omesso qui per brevità; contattateci se avete bisogno dell'implementazione completa.
Due cose spiccano nei risultati del batch. I suggerimenti di stile che riceviamo da Qwen 3.5 si adattano in modo significativo a ogni prodotto: un abito estivo e una maglia invernale ricevono descrizioni della scena realmente diverse, adattate alla stagione, al caso d'uso e agli accessori. Le immagini ottenute da Nano Banana 2, a loro volta, sono all'altezza di una vera fotografia in studio per quanto riguarda illuminazione, texture e composizione.
Conclusione
In questo articolo abbiamo illustrato le caratteristiche di Nano Banana 2 per la generazione di immagini per l'e-commerce, l'abbiamo confrontato con Nano Banana e Pro originali in attività di produzione reali e abbiamo illustrato come costruire una pipeline di bestseller-immagine con Milvus, Qwen 3.5 e Nano Banana 2. Questa pipeline ha quattro vantaggi pratici: la possibilità di creare un'immagine di qualità e la possibilità di creare un'immagine di qualità.
Questa pipeline presenta quattro vantaggi pratici:
- Costi controllati, budget prevedibili. Il modello di embedding (Llama Nemotron Embed VL 1B v2) è gratuito su OpenRouter. Nano Banana 2 funziona a circa la metà del costo per immagine di Pro, e l'output multiformato nativo elimina i cicli di rilavorazione che prima raddoppiavano o triplicavano il costo effettivo. Per i team di e-commerce che gestiscono migliaia di SKU per stagione, questa prevedibilità significa che la produzione di immagini va di pari passo con il catalogo, invece di sforare il budget.
- Automazione end-to-end, tempi più rapidi per la quotazione. Il flusso che va dalla foto del prodotto in posa piatta all'immagine promozionale finita si svolge senza interventi manuali. Un nuovo prodotto può passare dalla foto di magazzino all'immagine di inserzione pronta per il mercato in pochi minuti anziché in giorni, il che è molto importante durante le stagioni di punta, quando il turnover del catalogo è più elevato.
- Non è necessaria una GPU locale, la barriera all'ingresso è più bassa. Ogni modello viene eseguito tramite l'API di OpenRouter. Un team senza infrastruttura di ML e senza personale tecnico dedicato può eseguire questa pipeline da un laptop. Non c'è nulla da fornire, nulla da mantenere e nessun investimento iniziale in hardware.
- Maggiore precisione di reperimento, maggiore coerenza del marchio. Milvus combina filtri densi, radi e scalari in un'unica query, superando costantemente gli approcci monovettoriali per la corrispondenza dei prodotti. In pratica, questo significa che le immagini generate ereditano in modo più affidabile il linguaggio visivo consolidato del vostro marchio: l'illuminazione, la composizione e lo stile che i vostri bestseller esistenti hanno già dimostrato di saper convertire. Il risultato ha l'aspetto di un prodotto che appartiene al vostro negozio, non di una generica stock art dell'IA.
Ci sono anche delle limitazioni che vale la pena di evidenziare:
- La fusione tra indumenti e corpo. La generazione a passaggio singolo può far sembrare gli abiti composti piuttosto che indossati. I dettagli fini, come i piccoli accessori, a volte si confondono. Soluzione: generare in fasi (prima lo sfondo, poi la posa del modello, quindi il composito). Questo approccio a più passaggi dà a ogni fase un ambito più ristretto e migliora significativamente la qualità della fusione.
- Fedeltà dei dettagli nei casi limite. Gli accessori, i modelli e i layout ricchi di testo possono perdere nitidezza. Soluzione: aggiungere vincoli espliciti alla richiesta di generazione ("gli abiti si adattano naturalmente al corpo, non ci sono etichette esposte, non ci sono elementi extra, i dettagli del prodotto sono nitidi"). Se la qualità è ancora bassa su un prodotto specifico, passare a Nano Banana Pro per la versione finale.
Milvus è il database vettoriale open-source che alimenta la fase di ricerca ibrida; se volete curiosare o provare a inserire le vostre foto di prodotti, ilquickstart dura circa dieci minuti. Abbiamo una comunità piuttosto attiva su Discord e Slack, e ci piacerebbe vedere cosa si costruisce con questo sistema. E se finite per utilizzare Nano Banana 2 con un'altra verticale di prodotti o con un catalogo più ampio, vi preghiamo di condividere i risultati! Ci piacerebbe sentirli.
Continua a leggere
- Nano Banana + Milvus: trasformare l'hype in un RAG multimodale pronto per l'impresa
- Cos'è OpenClaw? Guida completa all'agente AI open source
- Tutorial su OpenClaw: Connettersi a Slack per l'assistente AI locale
- Abbiamo estratto il sistema di memoria di OpenClaw e lo abbiamo reso open source (memsearch)
- Memoria persistente per il codice Claude: memsearch ccplugin
Try Managed Milvus for Free
Zilliz Cloud is hassle-free, powered by Milvus and 10x faster.
Get StartedLike the article? Spread the word



