Mở đầu: Khi Chatbot Không Còn "Nhìn" Được Sản Phẩm
Tôi đã làm việc với rất nhiều đội ngũ phát triển chatbot và hệ thống hỏi đáp tự động trong suốt 5 năm qua. Và một trong những vấn đề phổ biến nhất mà tôi gặp phải là: các hệ thống RAG (Retrieval-Augmented Generation) truyền thống chỉ hoạt động tốt với văn bản. Khi người dùng hỏi về một chiếc áo màu xanh có hoạ tiết hoa, chatbot không thể "nhìn" thấy sản phẩm đó trong cơ sở dữ liệu hình ảnh của doanh nghiệp. Bài viết này sẽ hướng dẫn bạn xây dựng một kiến trúc Multi-Modal RAG hoàn chỉnh, kết hợp khả năng truy xuất và hiểu cả hình ảnh lẫn văn bản — và tất cả đều có thể triển khai dễ dàng với HolySheep AI.Case Study: Startup E-Commerce Ở TP.HCM Giải 70% Chi Phí AI
Bối cảnh: Một nền tảng thương mại điện tử tại TP.HCM với hơn 50,000 sản phẩm, trong đó 40% là sản phẩm thời trang với hình ảnh đặc thù (màu sắc, kiểu dáng, hoạ tiết). Đội ngũ muốn xây dựng chatbot tư vấn sản phẩm thông minh có thể "nhìn" ảnh và trả lời câu hỏi như "Cho tôi xem những áo sơ mi trắng có viền xanh".
Điểm đau với nhà cung cấp cũ: Họ đã sử dụng một nhà cung cấp API AI quốc tế với độ trễ trung bình 1.2 giây cho mỗi truy vấn multi-modal, chi phí $4,200/tháng cho 8 triệu token hình ảnh và văn bản. Đội ngũ kỹ thuật phải duy trì 3 service riêng biệt: embedding service, vector database, và LLM inference — mỗi service lại có config và monitoring riêng.
Lý do chọn HolySheep: Sau khi benchmark, họ nhận thấy HolySheep cung cấp endpoint multi-modal duy nhất xử lý cả embedding lẫn inference, với độ trễ thực tế dưới 180ms (so với 1.2 giây trước đó). Tỷ giá tính theo ¥1 = $1 giúp họ tiết kiệm 85% chi phí vận hành.
Các bước di chuyển cụ thể:
- Bước 1 - Đổi base_url: Thay thế api.openai.com/v1 bằng
https://api.holysheep.ai/v1trong toàn bộ config - Bước 2 - Xoay API key: Tạo API key mới từ HolySheep dashboard và cập nhật vào environment variables
- Bước 3 - Canary deploy: Triển khai 10% traffic sang HolySheep trong tuần đầu, theo dõi error rate và latency, sau đó tăng dần lên 100%
- Bước 4 - Optimize chunking: Điều chỉnh chunk size từ 512 tokens xuống 256 tokens cho hình ảnh sản phẩm để cải thiện retrieval precision
Kết quả sau 30 ngày go-live:
- Độ trễ trung bình: 1,200ms → 180ms (giảm 85%)
- Chi phí hàng tháng: $4,200 → $680 (tiết kiệm 84%)
- Tỷ lệ CSAT (customer satisfaction): tăng từ 72% lên 89%
- Conversion rate từ chatbot tư vấn: tăng 23%
Kiến Trúc Multi-Modal RAG: Tổng Quan Hệ Thống
Sơ đồ luồng dữ liệu
┌─────────────────────────────────────────────────────────────────────┐
│ MULTI-MODAL RAG ARCHITECTURE │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ ┌──────────────────┐ ┌──────────────────────┐ │
│ │ INPUT │───▶│ IMAGE PROCESS │───▶│ TEXT EXTRACTION │ │
│ │ (Mixed) │ │ & OCR (nếu cần)│ │ from Images │ │
│ └──────────┘ └──────────────────┘ └──────────────────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ HOLYSHEEP API (base_url + key) │ │
│ │ │ │
│ │ • Vision Embedding ( CLIP-like ) │ │
│ │ • Text Embedding ( multilingual ) │ │
│ │ • LLM Inference ( context-aware ) │ │
│ │ │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ VECTOR DATABASE (Hybrid Search) │ │
│ │ │ │
│ │ • Image vectors + Text vectors │ │
│ │ • Cross-modal similarity │ │
│ │ • Metadata filtering │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ RESPONSE GENERATION │ │
│ │ │ │
│ │ • Retrieved context (images + text) │ │
│ │ • RAG prompt with vision capabilities │ │
│ │ • Final answer with referenced images │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
Cài Đặt Môi Trường Và Dependencies
Trước tiên, hãy thiết lập môi trường Python với các thư viện cần thiết cho multi-modal RAG:
# Tạo virtual environment và cài đặt dependencies
python -m venv venv_multimodal_rag
source venv_multimodal_rag/bin/activate # Linux/Mac
venv_multimodal_rag\Scripts\activate # Windows
Cài đặt các thư viện cần thiết
pip install requests pillow python-dotenv qdrant-client
pip install numpy scikit-learn # cho vector operations
pip install fastapi uvicorn # cho API server (optional)
Kiểm tra cài đặt
python -c "import requests, PIL, qdrant_client; print('Setup thanh cong!')"
# config.py - Cấu hình HolySheep API
import os
from dotenv import load_dotenv
load_dotenv()
Cấu hình HolySheep - LUÔN LUÔN sử dụng base_url này
BASE_URL = "https://api.holysheep.ai/v1"
API_KEY = os.getenv("HOLYSHEEP_API_KEY", "YOUR_HOLYSHEEP_API_KEY")
Cấu hình Vector Database (ví dụ Qdrant)
QDRANT_HOST = os.getenv("QDRANT_HOST", "localhost")
QDRANT_PORT = int(os.getenv("QDRANT_PORT", "6333"))
COLLECTION_NAME = "multimodal_products"
Cấu hình RAG
EMBEDDING_MODEL = "clip-vit-base-patch32"
LLM_MODEL = "gpt-4.1" # Hoặc deepseek-v3.2, claude-sonnet-4.5
CHUNK_SIZE = 256
RETRIEVAL_TOP_K = 5
print(f"Configuration loaded:")
print(f" Base URL: {BASE_URL}")
print(f" LLM Model: {LLM_MODEL}")
print(f" Embedding Model: {EMBEDDING_MODEL}")
Module 1: Image Embedding Với HolySheep Vision API
HolySheep cung cấp endpoint vision mạnh mẽ cho phép embedding hình ảnh trực tiếp. Dưới đây là implementation chi tiết:
import base64
import requests
from PIL import Image
from io import BytesIO
from typing import List, Dict, Optional
import time
class HolySheepVisionClient:
"""Client cho HolySheep Vision API - Multi-modal embedding"""
def __init__(self, api_key: str, base_url: str = "https://api.holysheep.ai/v1"):
self.api_key = api_key
self.base_url = base_url
self.session = requests.Session()
self.session.headers.update({
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
})
def encode_image_to_base64(self, image_source: str) -> str:
"""
Chuyển đổi image từ URL hoặc local path thành base64
Args:
image_source: URL hoặc đường dẫn local tới hình ảnh
Returns:
Chuỗi base64 của hình ảnh
"""
if image_source.startswith(('http://', 'https://')):
response = requests.get(image_source)
response.raise_for_status()
image_data = response.content
else:
with open(image_source, 'rb') as f:
image_data = f.read()
return base64.b64encode(image_data).decode('utf-8')
def get_image_embedding(self, image_source: str) -> List[float]:
"""
Lấy embedding vector cho một hình ảnh
Args:
image_source: URL hoặc đường dẫn local
Returns:
List[float] - embedding vector (1536 dimensions với CLIP)
"""
# Encode image
image_base64 = self.encode_image_to_base64(image_source)
# Gọi HolySheep Vision API
# Lưu ý: Sử dụng endpoint /embeddings với model vision
payload = {
"model": "clip-vit-base-patch32",
"input": {
"type": "image",
"content": image_base64
}
}
start_time = time.time()
response = self.session.post(
f"{self.base_url}/embeddings",
json=payload,
timeout=30
)
latency_ms = (time.time() - start_time) * 1000
if response.status_code != 200:
raise Exception(f"API Error: {response.status_code} - {response.text}")
result = response.json()
embedding = result['data'][0]['embedding']
print(f"✓ Image embedding retrieved in {latency_ms:.1f}ms")
return embedding
def batch_get_embeddings(self, image_sources: List[str]) -> List[List[float]]:
"""
Lấy embeddings cho nhiều hình ảnh cùng lúc
Args:
image_sources: Danh sách URL hoặc đường dẫn
Returns:
List[List[float]] - danh sách embedding vectors
"""
embeddings = []
# Xử lý batch với HolySheep để tối ưu chi phí
for source in image_sources:
try:
embedding = self.get_image_embedding(source)
embeddings.append(embedding)
except Exception as e:
print(f"⚠ Lỗi khi xử lý {source}: {e}")
embeddings.append(None)
return embeddings
Ví dụ sử dụng
if __name__ == "__main__":
client = HolySheepVisionClient(
api_key="YOUR_HOLYSHEEP_API_KEY",
base_url="https://api.holysheep.ai/v1"
)
# Test với một hình ảnh mẫu
test_image = "https://example.com/product-sample.jpg"
try:
embedding = client.get_image_embedding(test_image)
print(f"Embedding dimension: {len(embedding)}")
except Exception as e:
print(f"Kiểm tra thất bại: {e}")
Module 2: Text Embedding Đa Ngôn Ngữ
HolySheep hỗ trợ text embedding với nhiều ngôn ngữ, bao gồm tiếng Việt, tiếng Trung, tiếng Anh — hoàn hảo cho các ứng dụng thương mại điện tử đa quốc gia:
import requests
from typing import List, Dict, Union
import time
class HolySheepTextClient:
"""Client cho HolySheep Text Embedding API - Hỗ trợ đa ngôn ngữ"""
def __init__(self, api_key: str, base_url: str = "https://api.holysheep.ai/v1"):
self.api_key = api_key
self.base_url = base_url
self.session = requests.Session()
self.session.headers.update({
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
})
def get_text_embedding(
self,
text: str,
model: str = "text-embedding-3-large"
) -> List[float]:
"""
Lấy embedding vector cho văn bản
Args:
text: Văn bản cần embedding
model: Model embedding (mặc định: text-embedding-3-large)
Returns:
List[float] - embedding vector
"""
payload = {
"model": model,
"input": text
}
start_time = time.time()
response = self.session.post(
f"{self.base_url}/embeddings",
json=payload,
timeout=30
)
latency_ms = (time.time() - start_time) * 1000
if response.status_code != 200:
raise Exception(f"API Error: {response.status_code} - {response.text}")
result = response.json()
embedding = result['data'][0]['embedding']
print(f"✓ Text embedding retrieved in {latency_ms:.1f}ms")
return embedding
def get_texts_embeddings(
self,
texts: List[str],
model: str = "text-embedding-3-large"
) -> List[List[float]]:
"""
Batch embedding cho nhiều văn bản
Args:
texts: Danh sách văn bản
model: Model embedding
Returns:
List[List[float]] - danh sách embedding vectors
"""
payload = {
"model": model,
"input": texts
}
start_time = time.time()
response = self.session.post(
f"{self.base_url}/embeddings",
json=payload,
timeout=60
)
latency_ms = (time.time() - start_time) * 1000
if response.status_code != 200:
raise Exception(f"API Error: {response.status_code} - {response.text}")
result = response.json()
embeddings = [item['embedding'] for item in result['data']]
print(f"✓ Batch embedding ({len(texts)} texts) retrieved in {latency_ms:.1f}ms")
return embeddings
def cosine_similarity(self, vec1: List[float], vec2: List[float]) -> float:
"""Tính cosine similarity giữa 2 vectors"""
import numpy as np
v1 = np.array(vec1)
v2 = np.array(vec2)
return np.dot(v1, v2) / (np.linalg.norm(v1) * np.linalg.norm(v2))
Ví dụ sử dụng
if __name__ == "__main__":
client = HolySheepTextClient(
api_key="YOUR_HOLYSHEEP_API_KEY",
base_url="https://api.holysheep.ai/v1"
)
# Test với tiếng Việt
test_texts = [
"áo sơ mi nam trắng cổ điển",
"quần jeans nam rách gối",
"giày thể thao nữ màu hồng",
"áo phông unisex form rộng"
]
embeddings = client.get_texts_embeddings(test_texts)
print(f"Embedding dimensions: {len(embeddings[0])}")
# Test similarity
sim = client.cosine_similarity(embeddings[0], embeddings[3])
print(f"Similarity giữa 'áo sơ mi' và 'áo phông': {sim:.4f}")
Module 3: Hybrid Search Với Qdrant Vector Database
Để kết hợp tìm kiếm hình ảnh và văn bản, tôi khuyên sử dụng Qdrant — một vector database mã nguồn mở với khả năng hybrid search tuyệt vời:
from qdrant_client import QdrantClient
from qdrant_client.models import Distance, VectorParams, PointStruct, Filter
from qdrant_client.models import MatchAny, MatchValue
from typing import List, Dict, Tuple, Optional
import numpy as np
class HybridSearchEngine:
"""
Hybrid Search Engine kết hợp image và text embeddings
"""
def __init__(
self,
host: str = "localhost",
port: int = 6333,
collection_name: str = "multimodal_products"
):
self.client = QdrantClient(host=host, port=port)
self.collection_name = collection_name
self.vector_dim = 1536 # CLIP + Text-embedding-3-large dimension
def create_collection(self, recreate: bool = False):
"""
Tạo collection cho multi-modal vectors
Args:
recreate: Nếu True, xóa collection cũ và tạo mới
"""
if recreate:
self.client.delete_collection(collection_name=self.collection_name)
print(f"✓ Deleted existing collection '{self.collection_name}'")
# Kiểm tra xem collection đã tồn tại chưa
collections = self.client.get_collections().collections
collection_names = [c.name for c in collections]
if self.collection_name not in collection_names:
self.client.create_collection(
collection_name=self.collection_name,
vectors_config=VectorParams(
size=self.vector_dim,
distance=Distance.COSINE
)
)
print(f"✓ Created collection '{self.collection_name}'")
else:
print(f"Collection '{self.collection_name}' already exists")
def insert_product(
self,
product_id: str,
image_url: str,
product_name: str,
description: str,
category: str,
image_embedding: List[float],
text_embedding: List[float]
) -> str:
"""
Chèn một sản phẩm vào database với cả image và text embeddings
Args:
product_id: ID duy nhất của sản phẩm
image_url: URL hình ảnh sản phẩm
product_name: Tên sản phẩm
description: Mô tả sản phẩm
category: Danh mục sản phẩm
image_embedding: Vector embedding từ hình ảnh
text_embedding: Vector embedding từ text
Returns:
str: ID của point được chèn
"""
# Combine image và text embeddings (có thể weighted)
combined_embedding = np.mean([image_embedding, text_embedding], axis=0)
combined_embedding = combined_embedding.tolist()
point = PointStruct(
id=product_id,
vector=combined_embedding,
payload={
"image_url": image_url,
"product_name": product_name,
"description": description,
"category": category,
"image_embedding_raw": image_embedding, # Lưu riêng để search cross-modal
"text_embedding_raw": text_embedding
}
)
operation_info = self.client.upsert(
collection_name=self.collection_name,
points=[point]
)
return operation_info
def search_by_text(
self,
query_embedding: List[float],
top_k: int = 5,
category_filter: Optional[str] = None
) -> List[Dict]:
"""
Tìm kiếm sản phẩm bằng text embedding
Args:
query_embedding: Query vector
top_k: Số lượng kết quả
category_filter: Lọc theo danh mục
Returns:
List[Dict]: Danh sách sản phẩm phù hợp
"""
search_filter = None
if category_filter:
search_filter = Filter(
must=[MatchValue(key="category", value=category_filter)]
)
results = self.client.search(
collection_name=self.collection_name,
query_vector=query_embedding,
limit=top_k,
query_filter=search_filter,
with_payload=True
)
return [
{
"id": hit.id,
"score": hit.score,
"product_name": hit.payload["product_name"],
"description": hit.payload["description"],
"category": hit.payload["category"],
"image_url": hit.payload["image_url"]
}
for hit in results
]
def search_by_image(
self,
query_embedding: List[float],
top_k: int = 5
) -> List[Dict]:
"""
Tìm kiếm sản phẩm bằng image embedding (cross-modal retrieval)
"""
results = self.client.search(
collection_name=self.collection_name,
query_vector=query_embedding,
limit=top_k,
with_payload=True
)
return [
{
"id": hit.id,
"score": hit.score,
"product_name": hit.payload["product_name"],
"description": hit.payload["description"],
"category": hit.payload["category"],
"image_url": hit.payload["image_url"]
}
for hit in results
]
def hybrid_search(
self,
text_query: str,
image_url: Optional[str],
text_embedding: List[float],
image_embedding: Optional[List[float]],
top_k: int = 5,
text_weight: float = 0.6,
image_weight: float = 0.4
) -> List[Dict]:
"""
Hybrid search kết hợp cả text và image queries
Args:
text_query: Query text
image_url: URL hình ảnh (optional)
text_embedding: Text embedding vector
image_embedding: Image embedding vector (nếu có)
top_k: Số kết quả
text_weight: Trọng số cho text (0.0 - 1.0)
image_weight: Trọng số cho image (0.0 - 1.0)
Returns:
List[Dict]: Kết quả hybrid search
"""
if image_embedding is None:
# Chỉ search bằng text
return self.search_by_text(text_embedding, top_k)
# Normalize weights
total_weight = text_weight + image_weight
text_weight /= total_weight
image_weight /= total_weight
# Combine vectors
combined_vector = np.add(
np.multiply(text_embedding, text_weight),
np.multiply(image_embedding, image_weight)
).tolist()
results = self.client.search(
collection_name=self.collection_name,
query_vector=combined_vector,
limit=top_k,
with_payload=True
)
return [
{
"id": hit.id,
"score": hit.score,
"product_name": hit.payload["product_name"],
"description": hit.payload["description"],
"category": hit.payload["category"],
"image_url": hit.payload["image_url"]
}
for hit in results
]
Ví dụ sử dụng
if __name__ == "__main__":
search_engine = HybridSearchEngine(
host="localhost",
port=6333,
collection_name="multimodal_products"
)
# Tạo collection
search_engine.create_collection(recreate=False)
print("✓ Hybrid Search Engine initialized")
Module 4: RAG Inference Với HolySheep LLM
Bây giờ, hãy xây dựng module RAG inference sử dụng HolySheep LLM để generate câu trả lời dựa trên context đã retrieve:
import requests
from typing import List, Dict, Optional
import json
import time
class MultiModalRAG:
"""
Multi-Modal RAG System sử dụng HolySheep LLM
Hỗ trợ cả text và image context
"""
def __init__(
self,
api_key: str,
base_url: str = "https://api.holysheep.ai/v1",
model: str = "gpt-4.1"
):
self.api_key = api_key
self.base_url = base_url
self.model = model
self.session = requests.Session()
self.session.headers.update({
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
})
def build_rag_prompt(
self,
query: str,
retrieved_products: List[Dict],
include_images: bool = True
) -> List[Dict]:
"""
Xây dựng prompt cho RAG với multi-modal context
Args:
query: Câu hỏi của user
retrieved_products: Kết quả từ hybrid search
include_images: Có bao gồm hình ảnh trong context không
Returns:
List[Dict]: Messages format cho LLM
"""
# System prompt
system_message = {
"role": "system",
"content": """Bạn là trợ lý tư vấn sản phẩm thông minh cho cửa hàng thương mại điện tử.
Nhiệm vụ của bạn:
1. Phân tích câu hỏi của khách hàng để hiểu nhu cầu
2. Đề xuất sản phẩm phù hợp từ danh sách được cung cấp
3. Giải thích tại sao sản phẩm đó phù hợp với yêu cầu
4. Trả lời bằng tiếng Việt, thân thiện và chuyên nghiệp
LUÔN bao gồm:
- Tên sản phẩm và hình ảnh (nếu có)
- Mô tả ngắn gọn đặc điểm nổi bật
- Lý do sản phẩm phù hợp với nhu cầu
Nếu không tìm thấy sản phẩm phù hợp, hãy gợi ý các sản phẩm tương tự hoặc hỏi thêm thông tin."""
}
# Build context từ retrieved products
context_parts = []
for idx, product in enumerate(retrieved_products, 1):
context_parts.append(f"""
Sản phẩm #{idx}:
- Tên: {product.get('product_name', 'N/A')}
- Mô tả: {product.get('description', 'N/A')}
- Danh mục: {product.get('category', 'N/A')}
- Hình ảnh: {product.get('image_url', 'N/A')}
- Độ phù hợp: {product.get('score', 0):.2%}""")
context_text = "\n".join(context_parts)
# User message với context
user_message = {
"role": "user",
"content": f"""Khách hàng hỏi: "{query}"
Danh sách sản phẩm liên quan được tìm thấy:
{context_text}
Hãy tư vấn sản phẩm phù hợp nhất cho khách hàng."""
}
return [system_message, user_message]
def generate_response(
self,
query: str,
retrieved_products: List[Dict],
temperature: float = 0.7,
max_tokens: int = 1000
) -> Dict:
"""
Generate câu trả lời từ RAG context
Args:
query: Câu hỏi của user
retrieved_products: Kết quả hybrid search
temperature: Creativity level (0.0 - 1.0)
max_tokens: Số token tối đa cho response
Returns:
Dict với response và metadata
"""
messages = self.build_rag_prompt(query, retrieved_products)
payload = {
"model": self.model,
"messages": messages,
"temperature": temperature,
"max_tokens": max_tokens
}
start_time = time.time()
response = self.session.post(
f"{self.base_url}/chat/completions",
json=payload,
timeout=60
)
latency_ms = (time.time() - start_time) * 1000
if response.status_code != 200:
raise Exception(f"API Error: {response.status_code} - {response.text}")
result = response.json()