Skip to Content
✨ AI Engineering🔍 RAG SystemAdvanced TechniquesHybrid Search

Hybrid Search

Khái niệm cơ bản

Tưởng tượng bạn đang tìm sách trong thư viện…

  • Keyword Search: Tìm đúng tên sách → Miss sách có tên khác nhưng cùng nội dung
  • Semantic Search: Tìm theo chủ đề → Có thể miss sách có tên chính xác bạn cần
  • Hybrid Search: Cả hai! → Không bỏ sót

Vấn đề với Semantic Search thuần

User: "Thông số iPhone 15 Pro Max" ❌ Vector Search có thể trả về: - "So sánh các flagship 2024" (liên quan chung) - "Camera smartphone cao cấp" (gần về ý nghĩa) ✅ Keyword Search trả về chính xác: - "iPhone 15 Pro Max: Thông số kỹ thuật chi tiết"

Semantic Search yếu với:

  • Tên riêng (iPhone, Tesla, Nguyễn Văn A)
  • Mã sản phẩm (SKU-12345, INV-2024-001)
  • Từ viết tắt (OT, WFH, B2B)

User Query ┌─────────┴─────────┐ ▼ ▼ [BM25/Sparse] [Dense Vector] │ │ Keyword results Semantic results │ │ └─────────┬─────────┘ [RRF Fusion] Final Rankings

BM25 (Spare Vector)

Thuật toán classic, đếm tần suất từ (TF-IDF cải tiến):

  • TF: Term Frequency - từ xuất hiện bao nhiêu lần
  • IDF: Inverse Document Frequency - từ hiếm có trọng số cao hơn

Dense Vector (Embeddings)

Sử dụng embedding model để tìm theo ý nghĩa.


Reciprocal Rank Fusion (RRF)

Công thức hợp nhất kết quả:

RRF(d) = Σ 1/(k + rank(d))

Trong đó:

  • R = tập hợp các rankings (BM25, Dense)
  • r(d) = rank của document d trong ranking r
  • k = hằng số (thường = 60)

Ví dụ tính tay

BM25 Results: Doc A (rank 1), Doc B (rank 2), Doc C (rank 5) Dense Results: Doc B (rank 1), Doc C (rank 2), Doc A (rank 8)

RRF(A) = 1/(60+1) + 1/(60+8) = 0.0164 + 0.0147 = 0.0311 RRF(B) = 1/(60+2) + 1/(60+1) = 0.0161 + 0.0164 = 0.0325 ← Winner! RRF(C) = 1/(60+5) + 1/(60+2) = 0.0154 + 0.0161 = 0.0315

Kết quả cuối: B > C > A (Doc B thắng vì đều tốt ở cả hai)


Code: Hybrid Search với Weaviate

import weaviate client = weaviate.connect_to_local() # Hybrid query với alpha (cân bằng sparse vs dense) response = client.query.get("Document", ["content", "title"]).with_hybrid( query="iPhone 15 Pro Max specifications", alpha=0.5, # 0.5 = 50% keyword, 50% semantic fusion_type="ranked" # Dùng RRF ).with_limit(10).do() # alpha = 1.0 → Pure semantic # alpha = 0.0 → Pure keyword # alpha = 0.5 → Balanced (recommended)

Code: Hybrid Search với Pinecone

from pinecone import Pinecone pc = Pinecone(api_key="xxx") index = pc.Index("my-index") # Hybrid query response = index.query( vector=embed("iPhone 15 Pro Max specs"), sparse_values={ "indices": [1, 2, 3], # Token IDs "values": [0.5, 0.3, 0.2] # BM25 weights }, top_k=10, include_metadata=True )

So sánh: Khi nào dùng gì?

Loại QueryKeyword OnlySemantic OnlyHybrid
”iPhone 15 Pro Max”✅ Best⚠️ May miss
“Điện thoại camera tốt”❌ Miss✅ Best
“JIRA-2024-001”✅ Best❌ Miss
“Làm sao tối ưu hiệu suất”❌ Miss✅ Best

💡 Rule of thumb: Luôn dùng Hybrid nếu database support!


Bài tập thực hành

Mục tiêu

Implement RRF từ đầu để hiểu cách hoạt động.

Code

def reciprocal_rank_fusion(rankings: list[list[str]], k: int = 60) -> dict: """ rankings: list of ranked document lists Example: [["A", "B", "C"], ["B", "C", "A"]] """ scores = {} for ranking in rankings: for rank, doc_id in enumerate(ranking, start=1): if doc_id not in scores: scores[doc_id] = 0 scores[doc_id] += 1 / (k + rank) # Sort by score descending return dict(sorted(scores.items(), key=lambda x: x[1], reverse=True)) # Test bm25_results = ["doc_a", "doc_b", "doc_c", "doc_d"] dense_results = ["doc_b", "doc_c", "doc_a", "doc_e"] fused = reciprocal_rank_fusion([bm25_results, dense_results]) print(fused) # Expected: doc_b > doc_c > doc_a > doc_d > doc_e

Thử thách

Thêm weight cho mỗi ranking (ví dụ: BM25 weight 0.3, Dense weight 0.7).


Tóm tắt

Khái niệmÝ nghĩa
Hybrid SearchKết hợp keyword + semantic
BM25Sparse vector, đếm từ
RRFThuật toán hợp nhất rankings
AlphaTỷ lệ cân bằng (0.5 recommended)

Bài tiếp theo: Reranking - Lọc tinh kết quả.

Last updated on