Nano Banana Multimodal RAG System: Creating Enterprise-Level AI Applications with Milvus

Nano Banana Multimodal RAG System: Creating Enterprise-Level AI Applications with Milvus

Nano Banana Multimodal RAG: A New Force in AI Beyond Image Generation

Recently, Nano Banana has surged in popularity on social media, and its rise is no coincidence! You may have seen the images it generates or even experienced this tool yourself — as the latest image generation model, it can astonishingly and rapidly convert plain text into collectible-quality photos of figurines.

Simply input a command like “Swap Elon’s hat and skirt,” and about 16 seconds later, you will receive a photo-realistic result: the shirt neatly tucked in, colors naturally blended, accessories perfectly matched, all without manual editing and no delays.

I couldn’t help but test it myself, and the prompt I provided was:

“Using the Nano Banana model, create a commercial figurine of a 1/7 scale illustration character in a realistic style and scene. Place the figurine on a computer desk with a round transparent acrylic base (no text on the base); the computer screen should display the ZBrush modeling process of the figurine; next to the screen, place a toy packaging box that matches the original image of the figurine, in line with Bandai’s style.”

The final generated effect amazed me — it looked like a mass-produced prototype just taken off the exhibition stand, with detail and texture far exceeding expectations.

Clearly, various teams have already discovered the significant application value of Nano Banana. One of our clients (a mobile platform specializing in capsule toys and dress-up games) is developing a new feature: allowing players to upload personal photos and instantly dress their virtual avatars with in-game accessories through AI. Additionally, some e-commerce brands are trying a “one-shot, multiple uses” model: just take a single basic model image, and AI can generate countless combinations of clothing and hairstyles without needing to shoot 20 times in a studio, greatly reducing operational costs.

But the problem is: relying solely on image generation cannot solve all needs. Such systems also require intelligent retrieval capabilities — the ability to instantly find matching clothing, props, and visual elements from a vast unstructured media library. Without intelligent retrieval, the generation model is like “groping in the dark.” What enterprises truly need is a multimodal RAG system: letting Nano Banana handle creative generation while a powerful vector database manages contextual retrieval.

This is where Milvus comes into play. As an open-source vector database, Milvus can index and efficiently search billions of embedded vectors (including images, text, audio, and more). When used in conjunction with Nano Banana, it can become the core framework of an enterprise-level multimodal RAG process, supporting the complete link of “retrieve – match – generate.”

In the subsequent content of this article, we will gradually explain how to build an enterprise-level multimodal RAG system by combining Nano Banana with Milvus and analyze why this combination can unleash the next wave of AI application trends.

1. Building a Text-to-Image Retrieval Engine: Solving the Media Library “Search Dilemma”

For rapidly developing consumer brands, game studios, and media companies, the bottleneck of AI image generation is not the model itself, but rather the data chaos problem.

These companies’ archives are filled with unstructured data, including product photos, character design materials, promotional video clips, and clothing renderings. When you need to find “the red cloak from last season’s spring/summer collection,” traditional keyword searches often fall short — this is a common pain point faced by the industry today.

The solution is clear: build a text-to-image retrieval system.

The specific implementation ideas are as follows:

  1. Use the CLIP model to convert text and image data into vectors (i.e., the “embedding” process);
  2. Store these vectors in Milvus (Milvus is an open-source vector database designed for similarity search);
  3. When a user inputs a text description (e.g., “a red silk cloak with gold trim”), the system queries the Milvus database and returns the top 3 images with the highest semantic similarity.

This solution is not only fast and highly scalable but also transforms a chaotic media library into a structured, precisely queryable resource pool.

1.1 Specific Implementation Steps: Code and Comments (English)

Step 1: Install Dependencies

# Install necessary dependenciespip install --upgrade pymilvus pillow matplotlib

Step 2: Import Required Libraries

import os<br />import clip<br />import torch<br />from PIL import Image # For image processing<br />import matplotlib.pyplot as plt # For image visualization<br />from pymilvus import MilvusClient # Milvus client<br />from glob import glob # For finding specified format files<br />import math # For mathematical calculations<br />print("All libraries have been successfully imported!")

Step 3: Initialize Milvus Client

# Initialize Milvus client (ensure Milvus service is running)milvus_client = MilvusClient(uri="http://localhost:19530", token="root:Miluvs")<br />print("Milvus client has been successfully initialized!")

Step 4: Load CLIP Model

# Load CLIP model (for text and image vector conversion)model_name = "ViT-B/32" # Choose CLIP model version<br /># Automatically determine the running device: prefer GPU (cuda), if not, use CPU<br />device = "cuda" if torch.cuda.is_available() else "cpu"<br /># Load model and preprocessing function<br />model, preprocess = clip.load(model_name, device=device)<br /># Set model to evaluation mode (turn off random behaviors during training, such as dropout)<br />model.eval()<br /># Print model loading information to confirm loading status<br />print(f"CLIP model '{model_name}' has been successfully loaded, running on: {device}")<br />print(f"Model input resolution: {model.visual.input_resolution}")<br />print(f"Context length: {model.context_length}")<br />print(f"Vocabulary size: {model.vocab_size}")

Model Loading Output:

CLIP model `ViT-B/32` loaded successfully, running on: cpu Model input resolution: 224<br />Context length: 77<br />Vocabulary size: 49,408

Step 5: Define Feature Extraction Functions (Image to Vector, Text to Vector)

def encode_image(image_path):
“””
Convert image to a normalized feature vector (embedding vector)
Parameters: image_path – image file path
Returns: normalized image feature vector (in list form), returns None if processing fails
“””
try:
# 1. Read image and perform preprocessing (e.g., Resize, normalization, etc., standard operations required by CLIP model)
image = preprocess(Image.open(image_path)).unsqueeze(0).to(device)
# 2. Disable gradient calculation (to avoid unnecessary memory usage, only for inference)
with torch.no_grad():
# 3. Generate image feature vector
image_features = model.encode_image(image)
# 4. Normalize vector (ensure vector length is 1 for subsequent cosine similarity calculation)
image_features /= image_features.norm(dim=-1, keepdim=True)
# 5. Convert tensor to list and return (remove dimension of size 1, transfer to CPU for subsequent storage)
return image_features.squeeze().cpu().tolist()
except Exception as e:
# Capture exceptions during processing (e.g., file not found, format error, etc.)
print(f”Error processing image {image_path}: {e}”)
return None
def encode_text(text):
“””
Convert text to a normalized feature vector (embedding vector)
Parameters: text – input text (query statement or description)
Returns: normalized text feature vector (in list form)
“””
# 1. Tokenize text (text preprocessing format required by CLIP model)
text_tokens = clip.tokenize([text]).to(device)
# 2. Disable gradient calculation
with torch.no_grad():
# 3. Generate text feature vector
text_features = model.encode_text(text_tokens)
# 4. Normalize vector
text_features /= text_features.norm(dim=-1, keepdim=True)
# 5. Convert to list and return
return text_features.squeeze().cpu().tolist()
print(“Feature extraction functions have been successfully defined!”)

Step 6: Create Milvus Collection (Similar to a “Table” in a Database)

# Define collection name (can be modified according to business scenario, such as “Clothing Material Library” or “Character Design Library”)collection_name = "production_image_collection"<br /># If the collection already exists, delete it first (to avoid conflicts from duplicate creation)<br />if milvus_client.has_collection(collection_name):<br /> milvus_client.drop_collection(collection_name)<br /> print(f"Deleted existing collection: {collection_name}")<br /># Create new collection<br />milvus_client.create_collection(<br /> collection_name=collection_name,<br /> dimension=512, # Vector dimension (fixed at 512 for CLIP ViT-B/32 model)<br /> auto_id=True, # Automatically generate primary key ID (no manual specification required)<br /> enable_dynamic_field=True, # Enable dynamic fields (support flexible addition of fields later, such as image source, shooting time, etc.)<br /> metric_type="COSINE" # Similarity calculation method (cosine similarity, suitable for high-dimensional vector matching)<br />)<br /># Print collection information to confirm successful creation<br />print(f"Collection '{collection_name}' has been successfully created!")<br />print(f"Collection details: {milvus_client.describe_collection(collection_name)}")

Collection Creation Success Output:

Existing collection deleted: production_image_collectionCollection 'production_image_collection' created successfully!<br />Collection info: {'collection_name': 'production_image_collection', 'auto_id': True, 'num_shards': 1, 'description': '', 'fields': [{'field_id': 100, 'name': 'id', 'description': '', 'type': , 'params': {}, 'auto_id': True, 'is_primary': True}, {'field_id': 101, 'name': 'vector', 'description': '', 'type': , 'params': {'dim': 512}}, {'field_id': 102, 'name': 'function': [], 'aliases': [], 'collection_id': 460508990706033544, 'consistency_level': 2, 'properties': {}, 'num_partitions': 1, 'enable_dynamic_field': True, 'created_timestamp': 460511723827494913, 'updated_timestamp': 460511723827494913}

Step 7: Process Images and Insert into Milvus Collection

# Set image folder path (replace with your local image storage path)image_dir = "./production_image"<br /># For storing data to be inserted into Milvus (each element is a dictionary of image information)<br />raw_data = []<br /># Define supported image formats (cover common formats to avoid omissions)<br />image_extensions = ['*.jpg', '*.jpeg', '*.png', '*.JPEG', '*.JPG', '*.PNG']<br /># Store paths of all image files<br />image_paths = []<br /># Iterate through all formats to collect image paths<br />for ext in image_extensions:<br /> image_paths.extend(glob(os.path.join(image_dir, ext)))<br />print(f"Found {len(image_paths)} images in {image_dir} folder")<br /># Process images and generate embedding vectors<br />successful_count = 0 # Record the number of successfully processed images<br />for i, image_path in enumerate(image_paths):<br /> # Print processing progress (enhance user experience, facilitate tracking progress)<br /> print(f"Processing progress: {i + 1}/{len(image_paths)} - {os.path.basename(image_path)}")<br /> # Generate image embedding vector<br /> image_embedding = encode_image(image_path)<br /> # If vector generation is successful, construct data dictionary and add to list<br /> if image_embedding is not None:<br /> image_dict = {"vector": image_embedding, # Image embedding vector (core field)<br /> "filepath": image_path, # Full path of the image file (for later loading)<br /> "filename": os.path.basename(image_path)} # Image file name (for display)<br /> raw_data.append(image_dict)<br /> successful_count += 1<br />print(f"Successfully processed {successful_count} images")

Image Processing Progress Output Example:

Found 50 images in ./production_imageProcessing progress: 1/50 - download (5).jpeg<br />Processing progress: 2/50 - images (2).jpeg<br />Processing progress: 3/50 - download (23).jpeg<br />Processing progress: 4/50 - download.jpeg<br />Processing progress: 5/50 - images (14).jpeg<br />Processing progress: 6/50 - images (16).jpeg<br />…<br />Processing progress: 44/50 - download (10).jpeg<br />Processing progress: 45/50 - images (18).jpeg<br />Processing progress: 46/50 - download (9).jpeg<br />Processing progress: 47/50 - download (12).jpeg<br />Processing progress: 48/50 - images (1).jpeg<br />Processing progress: 49/50 - download.png<br />Processing progress: 50/50 - images.png<br />Successfully processed 50 images

Step 8: Insert Processed Data into Milvus Collection

# If there is successfully processed data, perform the insert operationif raw_data:<br /> print("Inserting data into Milvus collection...")<br /> # Call insert interface to write data into Milvus<br /> insert_result = milvus_client.insert(collection_name=collection_name, data=raw_data)<br /> # Print insert result to confirm success<br /> print(f"Successfully inserted {insert_result['insert_count']} image data into Milvus")<br /> print(f"Example IDs of inserted data: {insert_result['ids'][:5]}...")<br />else:<br /> # If no valid data, prompt user to check image path or format<br /> print("No successfully processed image data to insert, please check the image folder path or image format")

Step 9: Define Search and Visualization Functions (Implementing “Text-to-Image” and Result Display)

def search_images_by_text(query_text, top_k=3):
“””
Search for similar images in Milvus based on text query
Parameters:
query_text – text query statement (e.g., “gold watch”)
top_k – number of similar images to return (default returns top 3)
Returns: list of search results (including information and similarity scores of similar images)
“””
print(f”Search query: ‘{query_text}'”)
# 1. Convert text query to embedding vector
query_embedding = encode_text(query_text)
# 2. Perform similarity search in Milvus
search_results = milvus_client.search(collection_name=collection_name,
data=[query_embedding],
limit=top_k,
output_fields=[“filepath”, “filename”])
# Return the first query result (since this is a single query, take index 0)
return search_results[0]
def visualize_search_results(query_text, results):
“””
Visualize the results of text search (display images, filenames, and similarity scores)
Parameters:
query_text – text query statement (for title display)
results – list of search results (return value of search_images_by_text function)
“””
num_images = len(results)
# If no matching results, directly prompt
if num_images == 0:
print(“No matching images found”)
return
# 1. Create subplots (1 row num_images columns, control the size of the graph)
fig, axes = plt.subplots(1, num_images, figsize=(5 * num_images, 5))
# Set total title (display query statement and return quantity)
fig.suptitle(f’Search Results: “{query_text}” (Top {num_images})’, fontsize=16, fontweight=’bold’)
# 2. Handle single image scenario (avoid subplot dimension errors)
if num_images == 1:
axes = [axes]
# 3. Iterate through results, load and display each image
for i, result in enumerate(results):
try:
# Get image path, filename, and similarity score
img_path = result[‘entity’][‘filepath’]
filename = result[‘entity’][‘filename’]
score = result[‘distance’] # Cosine similarity score (the closer to 1, the higher the similarity)
# Load image and display in subplot
img = Image.open(img_path)
axes[i].imshow(img)
# Set subplot title (including filename and similarity, keeping 3 decimal places)
axes[i].set_title(f”{filename}\nSimilarity: {score:.3f}”, fontsize=10)
# Hide axes (to avoid interference with image display)
axes[i].axis(‘off’)
# Print result details in console (for text record)
print(f”{i + 1}. File: {filename}, Similarity score: {score:.4f}”)
except Exception as e:
# If loading image fails, display error message in subplot
axes[i].text(0.5, 0.5, f’Failed to load image\n{str(e)}’, ha=’center’, va=’center’, transform=axes[i].transAxes)
axes[i].axis(‘off’)
# Adjust subplot spacing to avoid title overlap
plt.tight_layout()
# Show images (pop up window in Jupyter Notebook or local environment)
plt.show()
print(“Search and visualization functions have been successfully defined!”)

Step 10: Execute Text-to-Image Search (Test Functionality)

# Example 1: Search for “gold watch”query1 = "a gold watch"<br />results1 = search_images_by_text(query1, top_k=3)<br /># Visualize search results<br />visualize_search_results(query1, results1)

Search Execution Output:

Search query: ‘a gold watch’1. File: images (19).jpeg, Similarity score: 0.2934<br />2. File: download (26).jpeg, Similarity score: 0.3073<br />3. File: images (17).jpeg, Similarity score: 0.2717

(Note: After executing the code, an image window will pop up displaying the above 3 similar images and their similarity scores.)

2. Integrating Nano Banana to Generate Brand Promotional Images: From “Retrieval” to “Creation”

Now, our text-to-image retrieval system is fully compatible with Milvus. Next, we will integrate Nano Banana to generate new brand promotional content based on the retrieved materials.

2.1 Specific Implementation Steps: Code and Comments (English)

Step 1: Install Google SDK (Dependency for Nano Banana)

# Install Google Generative AI SDK (for calling Nano Banana related interfaces)pip install google-generativeai<br /># Install requests library (for network requests, such as image downloads)<br />pip install requests<br />print("Google Generative AI SDK installation completed!")

Step 2: Configure Gemini API (Required to Connect to Nano Banana)

import google.generativeai as genai # Import Google Generative AI library<br />from PIL import Image # For image reading<br />from io import BytesIO # For processing image byte streams<br /># Configure API key (replace with your personal/business API key obtained from Google Cloud Platform)<br />genai.configure(api_key="")

Step 3: Generate New Promotional Images Based on Retrieved Materials

# Define generation prompt (must align with brand needs, clarifying style, scene, and elements)prompt = ("A European male model in a suit wearing a gold watch, overall style in line with high-end business brand tone, background is a minimalist white studio scene.")<br /># Read the retrieved reference image (using the previously searched "gold watch" image as a reference to ensure element consistency)<br /># Note: Replace with the actual image path (can be obtained from the filepath field in search_images_by_text results)<br />image = Image.open("/path/to/image/watch.jpg")<br /># Initialize the generation model (specifying the version of the Gemini model that Nano Banana depends on)<br />model = genai.GenerativeModel('gemini-2.5-flash-image-preview')<br /># Call the generation interface: pass in the prompt and reference image to generate new content<br />response = model.generate_content([prompt, image])<br /># Process generation results (extract text description or save the generated image)<br />for part in response.candidates[0].content.parts:<br /> # If the result contains text (e.g., generation description), print the text<br /> if part.text is not None:<br /> print(part.text)<br /> # If the result contains an image (core output), save and display the image<br /> elif part.inline_data is not None:<br /> # Convert byte stream to image object<br /> generated_image = Image.open(BytesIO(part.inline_data.data))<br /> # Save the generated image locally (can modify save path and filename)<br /> generated_image.save("generated_brand_image.png")<br /> # Open the image to view the generation effect<br /> generated_image.show()

3. Transforming the Development Workflow: From “Static Management” to “Dynamic Generation”

For developers, the integration of Milvus and Nano Banana fundamentally changes the way content generation projects are handled. You no longer need to manage a massive static material library or rely on expensive creative teams; instead, you have a dynamic system — capable of real-time retrieval and generation of content required for applications, greatly enhancing development efficiency and flexibility.

Let’s look at a recent client case: a brand launched several new products but chose to completely skip the traditional photography process. Through the integrated system we built, they combined their existing product database with Nano Banana’s generation capabilities, generating a complete set of promotional images in just one day, reducing costs by 70%.

Example prompt they used:

“A model wearing the brand’s latest summer dress in a beach scene, with soft sunlight, background featuring coconut trees and blue sea, overall tone in line with the brand’s summer visual style.”

When it comes to creating complex and variable content (which typically requires coordination among photographers, models, and set designers, consuming time and effort), the advantages of this system become even more apparent: Milvus is responsible for accurately retrieving the required resources (such as clothing styles and scene elements), while Nano Banana handles creative generation, allowing you to programmatically create complex scenes that meet specific needs.

For example, a prompt for generating promotional images for a jewelry brand:

“A model leaning against a blue convertible sports car, wearing a backless black dress, adorned with the brand’s diamond necklace and blue gemstone watch, stepping in silver high heels, holding the brand’s Labubu series pendant, with the background being a seaside road at dusk, with warm and soft lighting.”

For developers in the gaming or collectibles field, this system opens up new possibilities for rapid prototyping and concept validation: without spending weeks on 3D modeling to validate concept feasibility, you can now directly generate realistic product visualizations that include packaging, scene backgrounds, and even manufacturing processes.

For example, a prompt for generating prototype images for a figurine development team:

“Using the Nano Banana model, create a commercial figurine of a 1/7 scale illustration character in a realistic style and scene. Place the figurine on a computer desk with a round transparent acrylic base (no text on the base); the computer screen should display the ZBrush modeling process of the figurine; next to the screen, place a toy packaging box that matches the original image of the figurine, in line with Bandai’s style, with the overall scene styled as a designer’s studio.”

4. Conclusion: The Leap from “Creative Toy” to “Enterprise-Level System”

From a technical perspective, Nano Banana is far from a “novel toy” — it possesses the operability required for production environments, which is crucial for developers. Its core advantages lie in consistency and controllability: it can reduce the interference of edge cases on application logic, ensuring that the generated results meet expectations. Moreover, its ability to control details (such as maintaining brand color consistency, generating lighting and reflections that conform to physical laws, and ensuring visual uniformity across multiple formats) also addresses key pain points in automated processes.

And the real “magic” lies in combining it with the Milvus vector database: Milvus is not just a tool for storing embedded vectors, but also an intelligent asset manager — capable of filtering out the most relevant historical content, providing precise contextual guidance for the generation model. This brings three core values:

  1. Faster generation speed: The model has precise contextual references, eliminating the need to “create from scratch,” reducing generation time by 40%;
  2. Higher application consistency: Ensures that all generated content aligns with brand style or business specifications, avoiding visual confusion;
  3. Automated style control: No manual review required, the system can automatically execute brand visual guidelines, reducing operational costs.

In short, Milvus upgrades Nano Banana from a “creative tool” to a scalable enterprise-level system, suitable for various industries such as e-commerce, gaming, media, and advertising.

Of course, no system is perfect: complex multi-step instructions may still deviate, and lighting physical effects may sometimes exceed expectations. Our proven reliable solution is: supplement text prompts with reference images stored in Milvus. This can provide the model with richer foundational information, making the generation results more predictable while shortening iteration cycles. With this configuration, you can not only experiment with the capabilities of multimodal RAG but also confidently deploy it in production environments to drive business growth.

Leave a Comment