Milvus
Zilliz
  • Home
  • Blog
  • Постройте конвейер преобразования бестселлеров в изображения для электронной коммерции с помощью Nano Banana 2 + Milvus + Qwen 3.5

Постройте конвейер преобразования бестселлеров в изображения для электронной коммерции с помощью Nano Banana 2 + Milvus + Qwen 3.5

  • Tutorials
March 03, 2026
Lumina Wang

Если вы создаете инструменты искусственного интеллекта для продавцов электронной коммерции, вы наверняка слышали этот запрос тысячу раз: "У меня новый продукт. Дайте мне рекламное изображение, которое будет выглядеть так, будто ему место в списке бестселлеров. Без фотографа, без студии, и чтобы это было недорого".

Вот в чем проблема в одном предложении. У продавцов есть плоские фотографии и каталог бестселлеров, которые уже конвертируются. Они хотят соединить эти два понятия с помощью искусственного интеллекта, причем быстро и масштабно.

Когда 26 февраля 2026 года Google выпустил Nano Banana 2 (Gemini 3.1 Flash Image), мы протестировали его в тот же день и интегрировали в наш существующий поисковый конвейер на базе Milvus. Результат: общая стоимость генерации изображений снизилась примерно до одной трети от прежних затрат, а пропускная способность удвоилась. Частично это объясняется снижением цены за одно изображение (примерно на 50 % дешевле, чем Nano Banana Pro), но более значительная экономия достигается за счет полного исключения циклов доработки.

В этой статье мы расскажем о том, что в Nano Banana 2 сделано правильно для электронной коммерции, и о том, где она все еще не работает, а также рассмотрим практическое руководство по работе с полным конвейером: Гибридный поиск Milvus для поиска визуально похожих бестселлеров, Qwen 3.5 для анализа стиля и Nano Banana 2 для окончательной генерации.

Что нового в Nano Banana 2?

Nano Banana 2 (флеш-образ Gemini 3.1) был запущен 26 февраля 2026 года. Он переносит большинство возможностей Nano Banana Pro на архитектуру Flash, что означает более быструю генерацию по более низкой цене. Вот ключевые обновления:

  • Качество уровня Pro на скорости Flash. Nano Banana 2 обеспечивает знания, рассуждения и визуальную точность мирового уровня, ранее присущие только Pro, но с задержкой и пропускной способностью Flash.
  • Разрешение от 512px до 4K. Четыре уровня разрешения (512px, 1K, 2K, 4K) с нативной поддержкой. Уровень 512px является новым и уникальным для Nano Banana 2.
  • 14 соотношений сторон. Добавляет 4:1, 1:4, 8:1 и 1:8 к существующему набору (1:1, 2:3, 3:2, 3:4, 4:3, 4:5, 5:4, 9:16, 16:9, 21:9).
  • До 14 опорных изображений. Поддерживает сходство персонажей для 5 персонажей и достоверность объектов для 14 объектов в одном рабочем процессе.
  • Улучшенный рендеринг текста. Генерирует разборчивый и точный текст на изображении на нескольких языках с поддержкой перевода и локализации в рамках одной генерации.
  • Основание для поиска изображений. Использует веб-данные и изображения из поиска Google в режиме реального времени для создания более точных изображений реальных объектов.
  • На ~50 % дешевле на одно изображение. При разрешении 1K: 0,067versusPro′s0,067 против0 0 ,134.

Забавный пример использования Nano Banano 2: создание панорамы с учетом местоположения на основе простого снимка экрана Google Map

Получив скриншот Google Maps и подсказку по стилю, модель распознает географический контекст и генерирует панораму, сохраняющую правильные пространственные отношения. Это полезно для создания рекламных креативов, ориентированных на конкретный регион (фон парижского кафе, пейзаж улицы Токио), без привлечения стоковых фотографий.

Полный набор функций можно найти в блоге Google и в документации для разработчиков.

Что означает обновление Nano Banana для электронной коммерции?

Электронная коммерция - одна из самых требовательных к изображениям отраслей. Листинги товаров, объявления на рынке, социальные креативы, баннерные кампании, локализованные витрины магазинов: каждый канал требует постоянного потока визуальных активов, каждый со своими спецификациями.

Основные требования к искусственному интеллекту для генерации изображений в электронной коммерции сводятся к следующему:

  • Поддерживать низкие затраты - стоимость одного изображения должна работать в масштабах каталога.
  • Соответствие внешнему виду проверенных бестселлеров - новые изображения должны соответствовать визуальному стилю объявлений, которые уже конвертируются.
  • Избегайте нарушений - не копируйте креативы конкурентов и не используйте повторно защищенные активы.

Кроме того, трансграничным продавцам необходимо:

  • Поддержка мультиплатформенных форматов - различные соотношения сторон и спецификации для маркетплейсов, объявлений и витрин.
  • Многоязычный рендеринг текста - чистый и точный текст в изображении на нескольких языках.

Nano Banana 2 подходит практически ко всем пунктам. В следующих разделах мы рассмотрим, что каждое обновление означает на практике: где оно непосредственно решает болевую точку электронной коммерции, где не дотягивает до нее, а также каково реальное влияние на затраты.

Сокращение затрат на генерацию вывода на 60 %

При разрешении 1K стоимость одного изображения вNano Banana 2 составляет 0,067perimageversusPro′s0,067 по сравнению с0 ,134, что составляет прямое 50-процентное сокращение. Но цена за изображение - это только половина истории. Что раньше убивало бюджеты пользователей, так это доработки. Каждый маркетплейс устанавливает свои спецификации изображений (1:1 для Amazon, 3:4 для витрин Shopify, ultrawide для баннерной рекламы), и производство каждого варианта означало отдельную генерацию с собственными режимами отказа.

Nano Banana 2 сводит все эти дополнительные проходы к одному.

  • Четыре уровня нативного разрешения.

  • 512px ($0,045)

  • 1K ($0.067)

  • 2K ($0.101)

  • 4K ($0.151).

Уровень 512px является новым и уникальным для Nano Banana 2. Теперь пользователи могут генерировать недорогие 512-пиксельные черновики для итераций и выводить конечный актив в 2K или 4K без отдельного шага апскейлинга.

  • Всегоподдерживается 14 соотношений сторон. Вот несколько примеров:

  • 4:1

  • 1:4

  • 8:1

  • 1:8

Эти новые сверхширокие и сверхвысокие соотношения присоединяются к уже существующему набору. За один сеанс генерации можно получить различные форматы, такие как: Главное изображение Amazon (1:1), Герой витрины (3:4) и Баннерная реклама (сверхширокая или в других соотношениях).

Для этих 4 соотношений не требуется ни обрезка, ни подгонка, ни перепросмотр. Остальные 10 соотношений сторон включены в полный набор, что делает процесс более гибким для разных платформ.

Одна только экономия ~50 % на каждом изображении уменьшит счет вдвое. Устранение переделок в разных разрешениях и соотношениях сторон позволило снизить общие затраты примерно до одной трети от прежних.

Поддержка до 14 эталонных изображений в стиле бестселлера

Из всех обновлений Nano Banana 2 наибольшее влияние на наш конвейер Milvus оказало смешивание нескольких ссылок. Nano Banana 2 принимает до 14 референсных изображений в одном запросе, поддерживая:

  • Сходство персонажей для 5 персонажей
  • достоверность объектов для 14 объектов.

На практике мы извлекали из Milvus несколько изображений бестселлеров, передавали их в качестве ссылок, и сгенерированное изображение наследовало их композицию сцены, освещение, позирование и расположение реквизита. Восстанавливать эти детали вручную не требовалось.

Предыдущие модели поддерживали только одну или две ссылки, что заставляло пользователей выбирать один бестселлер для подражания. Имея 14 слотов для ссылок, мы могли смешивать характеристики из нескольких наиболее успешных списков и позволять модели синтезировать общий стиль. Именно эта возможность позволила реализовать конвейер, основанный на поиске, как показано в учебном пособии ниже.

Создание визуальных изображений премиум-класса, готовых к коммерческой эксплуатации, без традиционных затрат на производство и логистику

Чтобы добиться стабильной и надежной генерации изображений, не следует сваливать все требования на один запрос. Более надежный подход - работать поэтапно: сначала сгенерировать фон, затем отдельно модель и, наконец, скомпоновать их вместе.

Мы протестировали генерацию фона для всех трех моделей Nano Banana с помощью одного и того же запроса: 4:1 ультраширокий пейзаж Шанхая в дождливый день, виднеющийся из окна, с башней Oriental Pearl Tower. Этот запрос позволяет проверить композицию, архитектурные детали и фотореализм за один проход.

Оригинальный Nano Banana против Nano Banana Pro против Nano Banana 2

  • Оригинальный Nano Banana. Естественная текстура дождя с правдоподобным распределением капель, но слишком сглаженные детали зданий. Башня Oriental Pearl Tower была едва различима, а разрешение не соответствовало требованиям производства.
  • Nano Banana Pro. Кинематографическая атмосфера: теплое освещение интерьера убедительно обыгрывает холодный дождь. Однако при этом полностью отсутствовала оконная рама, что ослабило ощущение глубины изображения. Можно использовать как вспомогательный образ, но не как героя.
  • Nano Banana 2. Рендеринг всей сцены. Оконная рама на переднем плане создала глубину. Восточная жемчужная башня была четко детализирована. На реке Хуанпу появились корабли. Многослойное освещение отличает тепло интерьера от пасмурной погоды снаружи. Текстуры дождя и водяных пятен были почти фотографическими, а ультраширокое соотношение 4:1 обеспечило правильную перспективу с незначительными искажениями у левого края окна.

Для большинства задач по созданию фона при съемке товаров мы нашли выход Nano Banana 2 пригодным без постобработки.

Чистое отображение текста на изображении на разных языках

Ценники, рекламные баннеры и многоязычные тексты неизбежны в изображениях электронной коммерции, и они исторически были точкой разрыва для генерации искусственного интеллекта. Nano Banana 2 справляется с ними значительно лучше, поддерживая рендеринг текста в изображении на нескольких языках с переводом и локализацией за одну генерацию.

Стандартный рендеринг текста. В ходе нашего тестирования текст выводился без ошибок во всех опробованных нами форматах электронной коммерции: на ценниках, в коротких маркетинговых теглайнах и двуязычных описаниях товаров.

Продолжение рукописного текста. Поскольку электронная коммерция часто требует рукописных элементов, таких как ценники и персонализированные карточки, мы проверили, могут ли модели соответствовать существующему рукописному стилю и расширять его - в частности, соответствовать рукописному списку дел и добавлять 5 новых пунктов в том же стиле. Результаты по трем моделям:

  • Оригинальный Nano Banana. Повторяющиеся порядковые номера, непонятная структура.
  • Nano Banana Pro. Правильный макет, но плохое воспроизведение стиля шрифта.
  • Nano Banana 2. Ноль ошибок. Вес штриха и стиль начертания совпадают достаточно близко, чтобы быть неотличимыми от источника.

Однако в собственной документации Google отмечается, что Nano Banana 2 "все еще может испытывать трудности с точным написанием и мелкими деталями на изображениях". Наши результаты были чистыми во всех протестированных форматах, но любой рабочий процесс должен включать этап проверки текста перед публикацией.

Пошаговое руководство: Построение конвейера преобразования бестселлера в изображение с помощью Milvus, Qwen 3.5 и Nano Banana 2

Прежде чем мы начнем: Архитектура и настройка модели

Чтобы избежать случайности при генерации одного запроса, мы разделили процесс на три контролируемых этапа: выясняем, что уже работает с помощью гибридного поиска Milvus, анализируем, почему это работает, с помощью Qwen 3.5, а затем генерируем финальное изображение с учетом этих ограничений с помощью Nano Banana 2.

Кратко о каждом инструменте, если вы не работали с ним раньше:

  • Milvus: самая распространенная база данных векторов с открытым исходным кодом. Хранит каталог товаров в виде векторов и выполняет гибридный поиск (плотный + разреженный + скалярный фильтры), чтобы найти изображения-бестселлеры, наиболее похожие на новый продукт.
  • Qwen 3.5: популярный мультимодальный LLM. Получает изображения бестселлеров и извлекает из них визуальные паттерны (расположение сцены, освещение, позы, настроение) в виде структурированной подсказки по стилю.
  • Nano Banana 2: модель генерации изображений от Google (Gemini 3.1 Flash Image). Принимает три входных сигнала: рекламный ролик нового продукта, ссылку на бестселлер и стилистическую подсказку Qwen 3.5. Выдает финальную рекламную фотографию.

Логика этой архитектуры начинается с одного наблюдения: самый ценный визуальный актив в любом каталоге электронной коммерции - это библиотека изображений бестселлеров, которые уже были преобразованы. Позы, композиции и освещение на этих фотографиях были отработаны в ходе реальных рекламных кампаний. Извлечь эти шаблоны напрямую на порядок быстрее, чем разрабатывать их с помощью подсказок, и именно этот этап извлечения и выполняет векторная база данных.

Вот полный процесс. Мы вызываем каждую модель через API OpenRouter, поэтому нам не требуется локальный GPU и не нужно загружать веса моделей.

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

Для обеспечения работы этапа поиска мы опираемся на три возможности Milvus:

  1. Плотный + разреженный гибридный поиск. Мы выполняем вкрапления изображений и текстовые TF-IDF-векторы в качестве параллельных запросов, а затем объединяем два набора результатов с помощью реранжирования RRF (Reciprocal Rank Fusion).
  2. Фильтрация по скалярным полям. Перед сравнением векторов мы фильтруем метаданные по таким полям, как category и sales_count, поэтому в результаты включаются только релевантные и высокоэффективные товары.
  3. Многополевая схема. Мы храним плотные векторы, разреженные векторы и скалярные метаданные в одной коллекции Milvus, что позволяет хранить всю логику поиска в одном запросе, а не разбрасывать ее по нескольким системам.

Подготовка данных

Исторический каталог продукции

Мы начинаем с двух активов: папки images/ с фотографиями существующих товаров и файла products.csv, содержащего их метаданные.

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

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

Данные о новых продуктах

Для продуктов, для которых мы хотим сгенерировать рекламные изображения, мы подготовим параллельную структуру: папку new_products/ и файл 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

Шаг 1: Установка зависимостей

!pip install pymilvus openai requests pillow scikit-learn tqdm

Шаг 2: Импорт модулей и конфигураций

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

Настройте все модели и пути:

# -- 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.”)

Вспомогательные функции

Эти вспомогательные функции выполняют кодирование изображений, вызовы API и разбор ответов:

  • image_to_uri(): Преобразует изображение PIL в URI данных base64 для передачи через API.
  • get_image_embeddings(): Пакетное кодирование изображений в 2048-мерные векторы с помощью OpenRouter Embedding API.
  • get_text_embedding(): Кодирует текст в то же 2048-мерное векторное пространство.
  • sparse_to_dict(): Преобразует строку разреженной матрицы scipy в формат {index: value}, который Milvus ожидает для разреженных векторов.
  • extract_images(): Извлекает сгенерированные изображения из ответа API 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.”)

Шаг 3: Загрузка каталога товаров

Прочитайте файл products.csv и загрузите соответствующие изображения продуктов:

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))))

Пример вывода:

Шаг 4: Генерация вкраплений

Гибридный поиск требует двух типов векторов для каждого продукта.

4.1 Плотные векторы: вкрапления изображений

Модель nvidia/llama-nemotron-embed-vl-1b-v2 кодирует каждое изображение товара в 2048-мерный плотный вектор. Поскольку эта модель поддерживает как изображения, так и текст в общем векторном пространстве, одни и те же вкрапления работают для поиска по изображениям и по тексту.

# 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)")

Выводы:

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

4,2 разреженных вектора: TF-IDF вкрапления текста

Текстовые описания продуктов кодируются в разреженные векторы с помощью векторизатора TF-IDF от scikit-learn. Они позволяют получить соответствие на уровне ключевых слов, которое плотные векторы могут пропустить.

# 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")

Вывод:

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

Почему оба типа векторов? Плотные и разреженные векторы дополняют друг друга. Плотные векторы фиксируют визуальное сходство: цветовую палитру, силуэт одежды, общий стиль. Разреженные векторы фиксируют семантику ключевых слов: такие термины, как "цветочный", "миди" или "шифон", которые указывают на атрибуты товара. Сочетание обоих подходов дает значительно более высокое качество поиска, чем любой из них в отдельности.

Шаг 5: Создание коллекции Milvus с гибридной схемой

На этом шаге создается единая коллекция Milvus, которая хранит плотные векторы, разреженные векторы и скалярные поля метаданных вместе. Эта единая схема обеспечивает гибридный поиск в одном запросе.

ПолеТипНазначение
плотный_векторПЛОТНЫЙ_ВЕКТОР (2048d)Встраивание изображения, сходство COSINE
разреженный_векторSPARSE_FLOAT_VECTORРазреженный вектор TF-IDF, внутреннее произведение
категорияVARCHARМетка категории для фильтрации
счётчик_продажINT64Исторический объем продаж для фильтрации
цвет, стиль, сезонVARCHARДополнительные метки метаданных
ценаFLOATЦена продукта
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.")

Вставьте данные о товаре:

# 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.")

Выход:

Inserted 40 products into Milvus.

Шаг 6: Гибридный поиск для поиска похожих бестселлеров

Это основной шаг поиска. Для каждого нового товара конвейер выполняет три операции одновременно:

  1. Плотный поиск: находит продукты с визуально похожими вкраплениями изображений.
  2. Разрозненный поиск: находит продукты с совпадающими ключевыми словами в тексте с помощью TF-IDF.
  3. Скалярная фильтрация: ограничивает результаты одной и той же категорией и товарами с sales_count > 1500.
  4. Ранжирование RRF: объединяет плотный и разреженный списки результатов с помощью Reciprocal Rank Fusion.

Загрузка нового продукта:

# 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))))

Выход:

Закодировать новый продукт:

# 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}")

Выход:

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

Выполнить гибридный поиск

Ключевые вызовы API здесь:

  • AnnSearchRequest создает отдельные поисковые запросы для плотных и разреженных векторных полей.
  • expr=filter_expr применяет скалярную фильтрацию в каждом поисковом запросе.
  • RRFRanker(k=60) объединяет два ранжированных списка результатов с помощью алгоритма Reciprocal Rank Fusion.
  • hybrid_search выполняет оба запроса и возвращает объединенные, переранжированные результаты.
# 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()

Выходные данные: 3 наиболее похожих бестселлера, ранжированные по слиянию рангов.

Шаг 7: Анализ стиля бестселлеров с помощью Qwen 3.5

Мы загружаем полученные изображения бестселлеров в Qwen 3.5 и просим его извлечь их общую визуальную ДНК: композицию сцены, освещение, позу модели и общее настроение. В результате анализа мы получаем подсказку одного поколения, готовую к передаче в 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)

Образец выходных данных:

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.

Шаг 8: Генерация рекламного изображения с помощью Nano Banana 2

Мы передаем в Nano Banana 2 три входных сигнала: фотографию нового продукта на плоской поверхности, изображение бестселлера, занимающего первое место в рейтинге, и подсказку о стиле, которую мы извлекли на предыдущем шаге. Модель объединяет их в рекламную фотографию, которая сочетает новый предмет одежды с проверенным визуальным стилем.

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!”)

Ключевые параметры для вызова API Nano Banana 2:

  • modalities: ["текст", "изображение"]: определяет, что ответ должен содержать изображение.
  • image_config.aspect_ratio: управляет соотношением сторон выводимого изображения (3:4 хорошо подходит для портретных/модных снимков).
  • image_config.image_size: задает разрешение. Nano Banana 2 поддерживает разрешение от 512px до 4K.

Извлеките сгенерированное изображение:

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())

Выход:

Шаг 9: Сравнение бок о бок

Выходные данные в общих чертах совпадают: освещение мягкое и ровное, поза модели выглядит естественно, а настроение соответствует бестселлеру.

Где мы видим недостаток, так это в смешивании одежды. Кардиган выглядит наклеенным на модель, а не надетым, а белый ярлык на горловине просвечивает. Однопроходная генерация не справляется с такой тонкой интеграцией одежды с телом, поэтому в кратком обзоре мы рассмотрим обходные пути.

Шаг 10: Пакетная генерация для всех новых продуктов

Мы сворачиваем весь конвейер в одну функцию и запускаем ее для всех оставшихся новых продуктов. Для краткости код пакетной генерации здесь не приводится; если вам нужна полная реализация, обращайтесь.

В результатах пакетной обработки выделяются две вещи. Подсказки по стилю, которые мы получаем от Qwen 3.5, значительно корректируются в зависимости от продукта: летнее платье и зимний трикотаж получают совершенно разные описания сцены в зависимости от сезона, сценария использования и аксессуаров. Изображения, которые мы получаем от Nano Banana 2, в свою очередь, не уступают реальным студийным фотографиям по освещению, текстуре и композиции.

Заключение

В этой статье мы рассказали о том, что нового привнесла Nano Banana 2 в создание изображений для электронной коммерции, сравнили ее с оригинальной Nano Banana и Pro в реальных производственных задачах и рассказали о том, как построить конвейер преобразования бестселлеров в изображения с помощью Milvus, Qwen 3.5 и Nano Banana 2.

Этот конвейер имеет четыре практических преимущества:

  • Контролируемая стоимость, предсказуемые бюджеты. Модель встраивания (Llama Nemotron Embed VL 1B v2) бесплатна на OpenRouter. Nano Banana 2 стоит примерно в два раза дешевле Pro в расчете на одно изображение, а встроенный мультиформатный вывод позволяет избежать циклов доработки, которые раньше удваивали или утраивали эффективный счет. Для команд электронной коммерции, управляющих тысячами SKU в сезон, такая предсказуемость означает, что производство изображений масштабируется вместе с каталогом, а не выбивается из бюджета.
  • Сплошная автоматизация, ускоренное время выхода на рынок. Поток от фотографии товара на плоском слое до готового рекламного изображения проходит без ручного вмешательства. Новый продукт может пройти путь от фотографии со склада до готового к размещению на рынке изображения за несколько минут, а не дней, что особенно важно в пиковые сезоны, когда оборот каталога наиболее высок.
  • Не требуется локальный GPU, более низкий барьер для входа. Каждая модель работает через API OpenRouter. Команда, не имеющая инфраструктуры ML и выделенного штата инженеров, может запустить этот конвейер с ноутбука. Ничего не нужно предоставлять, ничего не нужно обслуживать, и нет никаких предварительных инвестиций в оборудование.
  • Более высокая точность поиска, более высокая согласованность бренда. Milvus сочетает плотную, разреженную и скалярную фильтрацию в одном запросе, неизменно превосходя одновекторные подходы для сопоставления товаров. На практике это означает, что созданные изображения надежнее наследуют устоявшийся визуальный язык вашего бренда: освещение, композицию и стиль, которые уже доказали, что ваши существующие бестселлеры конвертируются. Полученные изображения выглядят так, как будто им самое место в вашем магазине, а не как общие стоковые иллюстрации ИИ.

Есть и ограничения, о которых стоит сказать прямо:

  • Смешивание одежды с телом. При однопроходной генерации одежда может выглядеть скорее композитной, чем ношеной. Мелкие детали, например мелкие аксессуары, иногда размываются. Выход: поэтапная генерация (сначала фон, затем поза модели, затем композиция). Такой многопроходный подход дает более узкий охват каждого этапа и значительно улучшает качество смешивания.
  • Точность детализации в крайних случаях. Аксессуары, узоры и макеты с большим количеством текста могут потерять четкость. Решение: добавьте явные ограничения в подсказку генерации ("одежда естественно сидит на теле, нет открытых ярлыков, нет лишних элементов, детали изделия четкие"). Если качество конкретного продукта по-прежнему оставляет желать лучшего, переключитесь на Nano Banana Pro для окончательной обработки.

Milvus - это векторная база данных с открытым исходным кодом, на основе которой осуществляется гибридный поиск, и если вы хотите поработать с ней или попробовать вставить свои собственные фотографии товаров,быстрый старт займет около десяти минут. У нас довольно активное сообщество на Discord и Slack, и мы с удовольствием посмотрим, что люди создадут с его помощью. И если вы запустите Nano Banana 2 на другой вертикали продуктов или в большем каталоге, пожалуйста, поделитесь результатами! Мы будем рады услышать о них.

Продолжить чтение

    Try Managed Milvus for Free

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

    Get Started

    Like the article? Spread the word

    Продолжить чтение