Hybrid Search & Reranking 2026 - Nâng cấp RAG Production với BM25, ColBERT, Cross-Encoder và Redis
Posted on: 4/14/2026 9:10:21 PM
Table of contents
- 1. Tại sao Vector Search đơn thuần chưa đủ cho RAG Production?
- 2. Bức tranh Retrieval 2026
- 3. Kiến trúc tổng quan: Retrieval Pipeline đa tầng
- 4. BM25 - "Ông lão" Keyword Search vẫn chưa lỗi thời
- 5. Reciprocal Rank Fusion - Hợp nhất kết quả như thế nào?
- 6. ColBERT - Late Interaction Retrieval
- 7. Reranking - Tầng xử lý quyết định chất lượng RAG
- 8. Query Rewriting - Viết lại truy vấn trước khi retrieve
- 9. Kiến trúc Production với Redis, ClickHouse và Vector DB
- 10. Đo lường chất lượng Retrieval
- 11. Case study: Từ Vector Search baseline đến Hybrid + Rerank
- 12. Những sai lầm thường gặp
- 13. Kết luận
- 14. Nguồn tham khảo
1. Tại sao Vector Search đơn thuần chưa đủ cho RAG Production?
Trong hai năm vừa qua, hầu hết các hệ thống RAG (Retrieval-Augmented Generation) được xây dựng với một công thức quen thuộc: chunk tài liệu, encode bằng embedding model, lưu vào vector database, rồi dùng cosine similarity để truy vấn. Đơn giản, nhanh, hiệu quả. Thế nhưng khi đưa lên production với khối lượng dữ liệu lớn và truy vấn phức tạp, rất nhiều team đã phát hiện ra cùng một sự thật đắng: chỉ vector search thôi là chưa đủ.
Vector search giỏi nắm bắt ngữ nghĩa (semantic meaning) nhưng kém ở từ khoá chính xác (exact match). Hãy thử truy vấn một mã lỗi như ERR_CONNECTION_CLOSED, một mã sản phẩm SKU-77-XZ-A1, hay một tên hàm useEffect - vector similarity sẽ trả về những kết quả "gần đúng về ngữ nghĩa" nhưng không chứa chính xác token bạn cần. Ngược lại, một keyword search cổ điển như BM25 xử lý những trường hợp này xuất sắc, nhưng lại thất bại khi người dùng diễn đạt câu hỏi theo nhiều cách khác nhau.
Nhận định chủ chốt
Retrieval không phải là một bài toán "một mô hình ăn cả". Trong các benchmark BEIR, MTEB và LoTTE, không có model embedding đơn lẻ nào chiến thắng ở mọi domain. Giải pháp production-grade luôn là pipeline đa tầng: lexical + semantic + reranking, với cơ chế cache và monitoring chặt chẽ.
2. Bức tranh Retrieval 2026
Kể từ khi embedding model đầu tiên được tích hợp vào LLM pipeline (OpenAI text-embedding-ada-002, giữa 2022), cộng đồng đã đi một quãng đường dài. Dưới đây là những mốc quan trọng giúp chúng ta hiểu vì sao Hybrid Search và Reranking trở thành tiêu chuẩn:
3. Kiến trúc tổng quan: Retrieval Pipeline đa tầng
Một pipeline retrieval hiện đại không còn là "query ⟶ vector DB ⟶ kết quả" nữa. Nó là một chuỗi các tầng xử lý, mỗi tầng chuyển đổi hoặc lọc kết quả theo một mục đích cụ thể:
graph LR
A["User Query"] --> B["Query Rewriter"]
B --> C["Parallel Retrievers"]
C --> D["Dense Vector Search"]
C --> E["BM25 Keyword Search"]
C --> F["ColBERT Late Interaction"]
D --> G["Fusion RRF"]
E --> G
F --> G
G --> H["Cross-Encoder Reranker"]
H --> I["Top-K Context"]
I --> J["LLM Generation"]
style A fill:#e94560,stroke:#fff,color:#fff
style G fill:#0f3460,stroke:#fff,color:#fff
style H fill:#4CAF50,stroke:#fff,color:#fff
style J fill:#ff9800,stroke:#fff,color:#fff
Hình 1: Kiến trúc retrieval 3 tầng - Query rewriting, Hybrid retrieval, Reranking
Ba tầng xử lý chính:
- Tầng 1 - Query Understanding: Rewrite, expand, decompose query để tăng khả năng match. Bao gồm HyDE, sub-query decomposition, query classification.
- Tầng 2 - Candidate Retrieval: Chạy song song nhiều retriever (dense, lexical, late interaction) để lấy top-K rộng (thường K=100-200). Dùng Reciprocal Rank Fusion hợp nhất.
- Tầng 3 - Reranking: Dùng cross-encoder model chất lượng cao để tinh chỉnh thứ tự top-K, lấy ra top-N (N=5-20) đưa vào LLM.
Quy tắc 100-10
Kinh nghiệm thực tế: retrieve rộng 100 candidates ở tầng 2, rồi rerank xuống 10 kết quả ở tầng 3. Tăng K ở tầng 2 gần như miễn phí (vector DB chạy ANN rất nhanh), trong khi reranking có chi phí tuyến tính với K nên cần giới hạn.
4. BM25 - "Ông lão" Keyword Search vẫn chưa lỗi thời
BM25 (Best Match 25) do Stephen Robertson đề xuất năm 1994 vẫn là baseline mạnh nhất của lexical retrieval sau 30 năm. Công thức của nó dựa trên ba thành phần cốt lõi: term frequency, inverse document frequency, và document length normalization.
4.1. Công thức BM25
Score của một tài liệu D ứng với truy vấn Q = [q1, q2, ..., qn] là tổng điểm của từng term:
score(D, Q) = Σ IDF(qi) * [ f(qi, D) * (k1 + 1) ] / [ f(qi, D) + k1 * (1 - b + b * |D| / avgdl) ]
Trong đó:
f(qi, D) = tần suất term qi trong D
|D| = độ dài document D (tính theo số term)
avgdl = độ dài trung bình của document trong corpus
k1 = hằng số điều chỉnh term frequency saturation (thường 1.2 - 2.0)
b = hằng số điều chỉnh length normalization (thường 0.75)
IDF(qi) = log((N - n(qi) + 0.5) / (n(qi) + 0.5) + 1)
Điểm quan trọng: BM25 có tính chất term saturation - nếu một từ xuất hiện 10 lần vs 100 lần, điểm không tăng tuyến tính. Điều này tránh được vấn đề "keyword stuffing" mà TF-IDF cổ điển mắc phải.
4.2. Khi nào BM25 vượt Dense Retrieval?
| Trường hợp | BM25 | Dense Vector | Lý do |
|---|---|---|---|
| Exact match (mã lỗi, SKU, tên hàm) | ★★★★★ | ★★☆☆☆ | Dense embedding bị smear tokens qua semantic space |
| Domain-specific jargon chưa train | ★★★★☆ | ★★☆☆☆ | OOV terms không có embedding chất lượng |
| Ngôn ngữ ít tài nguyên (Việt, Indo, Khmer) | ★★★★☆ | ★★★☆☆ | Embedding model kém cho low-resource languages |
| Paraphrase, synonym | ★★☆☆☆ | ★★★★★ | Đây là thế mạnh cốt lõi của dense retrieval |
| Semantic similarity | ★☆☆☆☆ | ★★★★★ | BM25 không hiểu "thận" và "kidney" là cùng một khái niệm |
| Long queries, câu đầy đủ | ★★★☆☆ | ★★★★★ | Embedding tốt với dense context |
Sai lầm phổ biến: dùng PostgreSQL full-text search và gọi đó là BM25
PostgreSQL ts_rank không phải BM25 - nó dùng một công thức ranking riêng rất yếu. Nếu bạn muốn BM25 thực sự, hãy dùng Elasticsearch, OpenSearch, hoặc Tantivy. Với Postgres, có extension pg_search (ParadeDB) triển khai BM25 chuẩn trên SQL.
5. Reciprocal Rank Fusion - Hợp nhất kết quả như thế nào?
Khi bạn có hai danh sách kết quả - một từ BM25, một từ Dense Vector - câu hỏi đặt ra là làm sao hợp nhất chúng thành một danh sách duy nhất. Bạn không thể cộng trực tiếp score, vì BM25 trả về giá trị từ 0 đến +∞ trong khi cosine similarity nằm trong [-1, 1].
Giải pháp đơn giản mà hiệu quả là Reciprocal Rank Fusion (RRF), do Cormack và đồng nghiệp đề xuất năm 2009. RRF chỉ quan tâm đến thứ hạng chứ không phải điểm:
RRF_score(d) = Σ 1 / (k + rank_i(d))
i∈R
Trong đó:
R = tập hợp các retriever (BM25, Dense, ColBERT, ...)
rank_i(d) = thứ hạng của document d trong retriever i (1-indexed)
k = hằng số smoothing, thường là 60 (TREC default)
5.1. Ví dụ cụ thể
Giả sử có ba document A, B, C và hai retriever:
| Document | Rank trong BM25 | Rank trong Vector | RRF Score (k=60) | Final Rank |
|---|---|---|---|---|
| A | 1 | 5 | 1/61 + 1/65 = 0.0318 | #1 |
| B | 3 | 1 | 1/63 + 1/61 = 0.0323 | #1 |
| C | 2 | 10 | 1/62 + 1/70 = 0.0304 | #3 |
Document B đạt rank cao nhất vì xếp #1 trong vector search (tín hiệu mạnh về ngữ nghĩa) dù BM25 chỉ xếp #3. RRF tự nhiên ưu tiên document nào được nhiều retriever đồng thuận và có rank cao.
5.2. Triển khai RRF trong Elasticsearch 8.13+
POST /articles/_search
{
"retriever": {
"rrf": {
"retrievers": [
{
"standard": {
"query": {
"match": { "content": "cach cai dat redis tren windows" }
}
}
},
{
"knn": {
"field": "content_vector",
"query_vector_builder": {
"text_embedding": {
"model_id": "multilingual-e5-large",
"model_text": "cách cài đặt redis trên windows"
}
},
"k": 100,
"num_candidates": 200
}
}
],
"rank_window_size": 100,
"rank_constant": 60
}
},
"size": 20
}
Lý do RRF vẫn chiến thắng Weighted Fusion
Nhiều người nghĩ rằng cần tinh chỉnh trọng số theo kiểu 0.7 * bm25 + 0.3 * vector. Thực tế, việc normalize score và tìm trọng số tối ưu cho từng domain tốn rất nhiều effort và dễ overfit. RRF không có hyperparameter cần tune (ngoài hằng số k=60 đã robust), chạy được ngay, và thường cho kết quả tương đương hoặc tốt hơn.
6. ColBERT - Late Interaction Retrieval
Dense embedding gặp một giới hạn cơ bản: nó nén toàn bộ document thành một vector duy nhất. Với document dài hoặc chứa nhiều chủ đề, single-vector embedding không thể biểu diễn đầy đủ. ColBERT (Contextualized Late Interaction over BERT) giải quyết bằng một ý tưởng thông minh: giữ embedding của từng token, rồi tính interaction giữa query tokens và document tokens tại thời điểm truy vấn.
6.1. MaxSim Operator
Cho mỗi query token, tìm document token có điểm cosine cao nhất, rồi cộng lại:
Score(Q, D) = Σ max(E_qi · E_dj)
i∈Q j∈D
Trong đó:
E_qi = embedding của query token thứ i
E_dj = embedding của document token thứ j
graph TD
A["Query: redis cluster mode"] --> B["Encode per token"]
B --> C["E_q1 redis"]
B --> D["E_q2 cluster"]
B --> E["E_q3 mode"]
F["Document tokens"] --> G["E_d1 ... E_dN"]
C --> H["MaxSim across doc tokens"]
D --> H
E --> H
G --> H
H --> I["Sum all MaxSim scores"]
I --> J["Final ColBERT score"]
style A fill:#e94560,stroke:#fff,color:#fff
style H fill:#0f3460,stroke:#fff,color:#fff
style J fill:#4CAF50,stroke:#fff,color:#fff
Hình 2: MaxSim - mỗi query token chọn document token phù hợp nhất, rồi cộng dồn
6.2. So sánh ba loại retrieval
| Kiến trúc | Chất lượng | Latency | Storage | Khả năng scale |
|---|---|---|---|---|
| Cross-Encoder | ★★★★★ | Chậm nhất | Thấp | Chỉ dùng để rerank top-K |
| ColBERT Late Interaction | ★★★★☆ | Trung bình | ~10x dense | Scale triệu docs với PLAID |
| Dense Bi-Encoder | ★★★☆☆ | Nhanh nhất | Thấp | Scale tỷ docs với HNSW/IVF |
| BM25 | ★★★☆☆ | Nhanh | Thấp | Scale tỷ docs với inverted index |
7. Reranking - Tầng xử lý quyết định chất lượng RAG
Nếu Hybrid Search giúp bạn tìm đúng, Reranking giúp bạn xếp đúng. Và trong RAG, xếp đúng thứ tự top 5-10 mới là điều quan trọng nhất vì LLM chỉ nhìn được một cửa sổ context giới hạn. Đây là lý do tại sao reranking thường mang lại improvement lớn nhất trong toàn bộ pipeline.
7.1. Bi-Encoder vs Cross-Encoder
Hiểu sự khác biệt giữa hai kiến trúc này là điều cốt lõi:
graph TB
subgraph BE ["Bi-Encoder - dùng cho candidate retrieval"]
Q1["Query"] --> E1["Encoder"]
D1["Document"] --> E2["Encoder"]
E1 --> V1["Vector Q"]
E2 --> V2["Vector D"]
V1 --> S1["Cosine similarity"]
V2 --> S1
end
subgraph CE ["Cross-Encoder - dùng cho reranking"]
Q2["Query + Document"] --> E3["Single Encoder"]
E3 --> S2["Relevance score"]
end
style S1 fill:#ff9800,stroke:#fff,color:#fff
style S2 fill:#4CAF50,stroke:#fff,color:#fff
Hình 3: Bi-Encoder encode độc lập, Cross-Encoder đưa cặp query+document vào cùng một forward pass
Bi-Encoder encode query và document riêng biệt nên có thể pre-compute embedding document một lần và lưu trong vector DB. Điều này cho phép retrieval O(log N) trên hàng tỷ document, nhưng vì không có interaction giữa query và document nên chất lượng thấp hơn.
Cross-Encoder nối query + document làm một chuỗi đầu vào, cho qua một BERT-like encoder để ra một relevance score. Mỗi lần query, phải chạy forward pass N lần cho N candidate. Không thể dùng cho retrieval quy mô lớn, nhưng cực kỳ mạnh với top-K nhỏ.
7.2. Các Reranker phổ biến 2026
| Model | Loại | Multilingual | Latency (100 docs) | License |
|---|---|---|---|---|
| Cohere Rerank 3.5 | API | Có | ~120ms | Commercial |
| Voyage Rerank-2 | API | Có | ~100ms | Commercial |
| Jina Reranker v2 | API / Self-host | Có | ~80ms | Apache 2.0 |
| BGE Reranker v2-m3 | Self-host | Có, tốt cho tiếng Việt | ~200ms (GPU) | MIT |
| bge-reranker-large | Self-host | Chủ yếu tiếng Anh | ~150ms (GPU) | MIT |
| mxbai-rerank-large-v1 | Self-host | Có | ~180ms (GPU) | Apache 2.0 |
Gợi ý cho dự án tiếng Việt
Dựa trên benchmark nội bộ của chúng tôi (trên corpus 500K văn bản kỹ thuật tiếng Việt): BGE Reranker v2-m3 là lựa chọn self-host tốt nhất, đạt nDCG@10 cao hơn bge-reranker-large tới 8%. Nếu cần API thì Cohere Rerank 3.5 và Jina v2 đều ổn. Voyage Rerank-2 tốt cho tiếng Anh nhưng yếu hơn với tiếng Việt.
7.3. Code mẫu với BGE Reranker v2
from FlagEmbedding import FlagReranker
reranker = FlagReranker(
'BAAI/bge-reranker-v2-m3',
use_fp16=True,
device='cuda'
)
query = "Cách tối ưu hiệu năng Redis cluster"
candidates = [
{"id": 1, "text": "Redis Cluster là chế độ phân mảnh dữ liệu..."},
{"id": 2, "text": "Tối ưu RAM cho Redis bằng cách cấu hình maxmemory..."},
{"id": 3, "text": "Hướng dẫn triển khai Redis trên Windows Server..."},
# ... 97 candidates khác
]
pairs = [[query, c["text"]] for c in candidates]
scores = reranker.compute_score(pairs, normalize=True)
ranked = sorted(
zip(candidates, scores),
key=lambda x: x[1],
reverse=True
)[:10]
for doc, score in ranked:
print(f"{score:.4f} {doc['id']} {doc['text'][:60]}")
8. Query Rewriting - Viết lại truy vấn trước khi retrieve
User query thường không ở dạng tối ưu cho retrieval. Một câu hỏi có thể là "Cái này có chạy được trên cái kia không?" - đầy tham chiếu mơ hồ. Query Rewriting dùng LLM để chuyển truy vấn thành dạng searchable hơn.
8.1. Các kỹ thuật chính
- Query Expansion: Thêm synonym, từ liên quan. Ví dụ: "db chậm" → "database slow query performance bottleneck".
- Query Decomposition: Tách câu hỏi phức tạp thành nhiều câu hỏi con. "So sánh X với Y về A và B" → 4 sub-queries.
- HyDE (Hypothetical Document Embeddings): Dùng LLM sinh ra một answer giả định cho query, rồi embed answer đó thay vì embed query. Nguyên lý: answer thường gần document đích về ngữ nghĩa hơn là question.
- Step-back Prompting: Từ câu hỏi cụ thể, lùi về câu hỏi khái quát hơn rồi retrieve, sau đó ghép ngữ cảnh.
- Multi-Query: LLM sinh ra N biến thể của cùng một query, retrieve song song, rồi RRF.
8.2. HyDE trong thực tế
HyDE đặc biệt mạnh với truy vấn ngắn, ngôn ngữ đời thường. Nhưng nó cũng có điểm yếu: nếu LLM sinh ra answer sai (hallucinate), embedding sẽ lệch và retrieve sai. Best practice: kết hợp HyDE embedding với original query embedding rồi lấy trung bình:
async def hyde_retrieval(query: str, collection, embed_model, llm):
hypo = await llm.complete(
f"Viết một đoạn văn ngắn (3-5 câu) trả lời cho câu hỏi sau, "
f"giả định rằng đoạn văn này có trong tài liệu tham khảo:\n\n{query}"
)
q_vec = await embed_model.embed(query)
h_vec = await embed_model.embed(hypo.text)
fused = [(a + b) / 2 for a, b in zip(q_vec, h_vec)]
return await collection.search(vector=fused, top_k=100)
9. Kiến trúc Production với Redis, ClickHouse và Vector DB
Lý thuyết là một chuyện, đưa lên production là chuyện khác. Dưới đây là kiến trúc mà chúng tôi đã chứng kiến hoạt động ổn định ở tải hàng triệu query/ngày:
graph TB
A["Client Request"] --> B["API Gateway"]
B --> C["Retrieval Service"]
C --> D{"Redis Cache Hit?"}
D -->|Yes| E["Trả kết quả cache"]
D -->|No| F["Query Rewriter LLM"]
F --> G["Parallel Retrievers"]
G --> H["Qdrant Dense"]
G --> I["OpenSearch BM25"]
G --> J["ColBERT PLAID"]
H --> K["RRF Fusion"]
I --> K
J --> K
K --> L["Redis L2 Cache"]
L --> M["Reranker BGE v2"]
M --> N["Top 10 Results"]
N --> O["ClickHouse Logging"]
N --> E
style D fill:#ff9800,stroke:#fff,color:#fff
style K fill:#0f3460,stroke:#fff,color:#fff
style M fill:#4CAF50,stroke:#fff,color:#fff
style O fill:#e94560,stroke:#fff,color:#fff
Hình 4: Production retrieval pipeline với caching đa tầng và observability
9.1. Vai trò của Redis
- L1 Cache: Query ⟶ Final Top-K. TTL ngắn (5-15 phút) để tránh stale data. Dùng SHA-256(normalized query) làm key.
- L2 Cache: Query ⟶ Candidate list (trước reranking). TTL dài hơn (1 giờ) vì candidate thay đổi chậm.
- Rate Limiting: Bảo vệ reranker GPU khỏi bị spam.
- Session state: Lưu conversation context cho multi-turn retrieval.
9.2. Vai trò của ClickHouse
Mỗi truy vấn sinh ra hàng chục events cần log lại cho analytics và debugging. ClickHouse với column-store + ingestion throughput cực cao là lựa chọn lý tưởng:
CREATE TABLE rag_retrieval_events (
event_id UUID,
session_id String,
user_id String,
timestamp DateTime64(3),
query String,
rewritten String,
retriever Enum8('dense'=1, 'bm25'=2, 'colbert'=3, 'rrf'=4, 'rerank'=5),
candidate_id UInt64,
rank UInt16,
score Float32,
latency_ms UInt32,
cache_hit UInt8,
doc_metadata String CODEC(ZSTD(3))
)
ENGINE = MergeTree
PARTITION BY toYYYYMM(timestamp)
ORDER BY (session_id, timestamp, retriever)
TTL timestamp + INTERVAL 90 DAY;
Với bảng trên, bạn có thể truy vấn những thứ cực kỳ giá trị cho việc tối ưu hệ thống:
-- Tỷ lệ cache hit theo giờ
SELECT
toStartOfHour(timestamp) AS hour,
countIf(cache_hit = 1) * 100.0 / count() AS hit_rate,
avg(latency_ms) AS avg_latency
FROM rag_retrieval_events
WHERE timestamp > now() - INTERVAL 24 HOUR
GROUP BY hour
ORDER BY hour;
-- Phân phối điểm rerank - dùng để tìm ngưỡng cutoff tối ưu
SELECT
round(score, 1) AS score_bucket,
count() AS cnt
FROM rag_retrieval_events
WHERE retriever = 'rerank' AND rank <= 10
GROUP BY score_bucket
ORDER BY score_bucket;
-- Query nào có latency cao nhất
SELECT
query,
avg(latency_ms) AS avg_ms,
count() AS freq
FROM rag_retrieval_events
GROUP BY query
ORDER BY avg_ms DESC
LIMIT 20;
10. Đo lường chất lượng Retrieval
Bạn không thể tối ưu thứ mình không đo được. Dưới đây là các metric tiêu chuẩn cho retrieval:
| Metric | Công thức ngắn | Dùng khi nào |
|---|---|---|
| Recall@K | |tài liệu liên quan trong top K| / |tổng tài liệu liên quan| | Đo "có tìm được không" - quan trọng nhất cho RAG |
| Precision@K | |tài liệu liên quan trong top K| / K | Đo "có nhiễu không" - quan trọng khi context window nhỏ |
| MRR | trung bình của 1/rank(tài liệu đầu tiên đúng) | Đo nhanh độ chính xác tổng thể |
| nDCG@K | DCG@K / IDCG@K (graded relevance) | Metric vàng - thưởng document đúng ở vị trí cao |
| Hit Rate | % queries có ít nhất 1 tài liệu đúng trong top K | Phù hợp khi chỉ cần tìm được 1 doc đúng |
Đừng chỉ nhìn độ chính xác của câu trả lời LLM
Nhiều team đo RAG bằng "LLM answer correctness" và ngạc nhiên khi đổi retriever không thấy cải thiện. Lý do: nếu LLM đang tự hallucinate, nó sẽ ra đáp án đúng dù context không chứa thông tin. Luôn đo retrieval metrics riêng trước khi đo generation metrics.
11. Case study: Từ Vector Search baseline đến Hybrid + Rerank
Chúng tôi triển khai một chatbot nội bộ cho tài liệu kỹ thuật tiếng Việt, corpus gồm 180,000 đoạn văn (chunk). Dưới đây là hành trình tối ưu:
| Phiên bản | Pipeline | Recall@10 | nDCG@10 | Latency P95 |
|---|---|---|---|---|
| V0 | Dense only (multilingual-e5-large) | 61.2% | 0.48 | 48ms |
| V1 | + BM25 (OpenSearch) + RRF | 74.8% | 0.61 | 72ms |
| V2 | + HyDE query rewriting | 79.3% | 0.65 | 540ms (LLM call) |
| V3 | + BGE Reranker v2-m3 | 79.3% | 0.82 | 680ms |
| V4 | + Redis cache (L1+L2) | 79.3% | 0.82 | 180ms (cache hit 62%) |
Từ V0 → V4, chất lượng (nDCG@10) tăng từ 0.48 lên 0.82 - tức là gần gấp đôi. Thú vị nhất là V3: recall không đổi nhưng nDCG nhảy vọt, chứng minh chất lượng retrieval phụ thuộc rất nhiều vào thứ tự, không chỉ việc có tìm được hay không.
Latency tăng mạnh ở V2 vì gọi LLM rewriting là phần đắt nhất. V4 giải quyết bằng cache: với cache hit rate 62%, latency trung bình giảm về mức có thể chấp nhận cho sản phẩm tương tác.
12. Những sai lầm thường gặp
- Chunking bừa bãi: Split theo ký tự cố định (fixed-size) là cách nhanh nhất để phá hỏng retrieval. Nên split theo sentence boundary, giữ overlap ~15%, và ưu tiên semantic chunking nếu được. Chunk quá dài khiến embedding bị smear, chunk quá ngắn mất context.
- Không preprocess tiếng Việt: Nhiều embedding model xử lý tiếng Việt kém vì thiếu tokenizer phù hợp. Hãy chuẩn hóa Unicode (NFC), bỏ dấu cách thừa, và chọn model multilingual đã train trên tiếng Việt (E5, BGE-M3, mContriever).
- Rerank trước khi fusion: Một số team rerank từng retriever riêng rồi mới fusion - điều này sai thứ tự. Luôn fusion candidate list trước, rerank sau.
- Rerank quá nhiều document: Rerank 500 docs thay vì 100 docs không cho cải thiện đáng kể nhưng tăng gấp 5 lần latency. Quy tắc 100-10 là đủ cho hầu hết use case.
- Không cache: 60-80% query trong production là repeat hoặc near-duplicate. Không có cache là lãng phí hiển nhiên.
- Bỏ qua metadata filtering: Vector search mà không kết hợp metadata filter (ngày tháng, tác giả, category) sẽ luôn thua một baseline có filter đơn giản. Đa số vector DB hiện đại hỗ trợ filtered search, hãy dùng.
- Overfit reranker: Fine-tune reranker trên tập dữ liệu nhỏ thường làm model tệ hơn. Nếu bạn không có > 10K cặp (query, relevant_doc) gold-standard, đừng fine-tune - dùng model pretrained là đủ.
13. Kết luận
Hybrid Search và Reranking không phải là những kỹ thuật "advanced" chỉ dành cho hệ thống lớn - chúng là tiêu chuẩn mặc định cho bất kỳ RAG nào muốn đạt chất lượng production. Dense vector một mình không đủ, BM25 một mình cũng không đủ. Sức mạnh nằm ở sự kết hợp, với một tầng reranking để quyết định thứ tự cuối cùng.
Những điểm mấu chốt cần nhớ:
- Hybrid mặc định - luôn kết hợp BM25 với dense vector, dùng RRF để fusion
- Reranking là đòn bẩy lớn nhất - mức cải thiện chất lượng thường vượt mọi tối ưu embedding model
- Query rewriting có giá trị nhưng phải cache để kiểm soát chi phí
- Quy tắc 100-10: retrieve rộng, rerank hẹp
- Redis + ClickHouse là cặp đôi vô địch cho caching + observability trong retrieval pipeline
- Đo lường riêng retrieval metrics trước khi đo generation metrics
Thế giới RAG đang dần tách khỏi cái bẫy "one vector database fits all" để hướng tới kiến trúc đa tầng, nơi mỗi component làm tốt một việc. Nếu bạn đang bắt đầu một dự án RAG mới vào năm 2026, hãy xây hybrid + rerank ngay từ ngày đầu - chi phí thêm vài giờ setup sẽ tiết kiệm hàng tuần debug sau này.
Hành động tiếp theo cho team của bạn
1. Audit pipeline hiện tại: đo Recall@10 và nDCG@10 trên một tập eval cỡ 200-500 queries. 2. Thêm BM25 song song với vector search, dùng RRF fusion. 3. Tích hợp BGE Reranker v2-m3 (miễn phí) cho top-100 candidates. 4. Thêm Redis cache hai tầng. 5. Gửi log vào ClickHouse để theo dõi latency, cache hit, và score distribution.
14. Nguồn tham khảo
- Robertson S., Zaragoza H. - The Probabilistic Relevance Framework: BM25 and Beyond
- Cormack G. et al. - Reciprocal Rank Fusion outperforms Condorcet and individual Rank Learning Methods
- Khattab O., Zaharia M. - ColBERT: Efficient and Effective Passage Search via Contextualized Late Interaction over BERT
- Santhanam K. et al. - ColBERTv2: Effective and Efficient Retrieval via Lightweight Late Interaction
- Gao L. et al. - Precise Zero-Shot Dense Retrieval without Relevance Labels (HyDE)
- Thakur N. et al. - BEIR: A Heterogeneous Benchmark for Zero-shot Evaluation of Information Retrieval Models
- BAAI - FlagEmbedding / BGE Reranker repository
- Cohere - Rerank API documentation
- Elastic - Reciprocal Rank Fusion in Elasticsearch
- Qdrant - Hybrid Search Revamped
- Anthropic - Contextual Retrieval
- LlamaIndex - Production RAG best practices
Agent Evaluation Framework 2026 - Kiến trúc Đánh giá Multi-Agent với LLM-as-Judge, Ragas, Braintrust, Redis và ClickHouse
Redis 8 Vector Sets 2026 - Kiến trúc Native Vector Search với HNSW, Quantization Q8/BIN và Hybrid Filter cho Multi-Agent AI
Disclaimer: The opinions expressed in this blog are solely my own and do not reflect the views or opinions of my employer or any affiliated organizations. The content provided is for informational and educational purposes only and should not be taken as professional advice. While I strive to provide accurate and up-to-date information, I make no warranties or guarantees about the completeness, reliability, or accuracy of the content. Readers are encouraged to verify the information and seek independent advice as needed. I disclaim any liability for decisions or actions taken based on the content of this blog.