Mục lục
- Mục tiêu bài học
- Vì sao retrieve thuần chưa đủ
- Pattern 2-stage retrieval
- Bi-encoder vs Cross-encoder
- Vì sao cross-encoder chính xác hơn
- Trade-off speed và scale
- Workflow re-rank
- Các reranker phổ biến 2024-2026
- Code — Cohere Rerank API
- Code — BGE Reranker local
- Full pipeline ChromaDB + reranker
- Trade-off K khởi đầu
- Latency thực tế
- Cost ước tính
- Reranker training ngắn
- Khi nào dùng reranker
- Khi nào KHÔNG cần reranker
- LLM-as-Reranker
- Listwise vs Pointwise
- Hybrid retrieval + rerank
- HyDE — pre-retrieval enhancement
- Query rewriting / expansion
- RAG pipeline evolution
- Đánh giá impact của reranker
- Pitfall hay gặp
- Bài tập
Mục tiêu bài học
Sau bài này, bạn cần:
- Giải thích được pattern 2-stage retrieval và lý do dùng (balance speed và quality).
- Phân biệt bi-encoder và cross-encoder về kiến trúc, output, khả năng pre-compute.
- Biết tại sao cross-encoder chính xác hơn nhưng không scale được trên toàn corpus.
- Liệt kê được các reranker phổ biến: Cohere Rerank, BGE Reranker, Jina Reranker, MS MARCO MiniLM, Voyage Rerank, mxbai-rerank.
- Viết được code rerank với Cohere API và BGE local (
sentence-transformersCrossEncoder). - Ghép được full pipeline: ChromaDB top-50 → BGE Reranker top-5 → LLM.
- Quyết được khi nào reranker đáng thêm vào pipeline và khi nào không.
- Hiểu các kỹ thuật pre-retrieval bổ sung: HyDE, multi-query, query rewriting.
Bài này nằm ở cuối Module 6, ngay trước capstone QA system PDF. Reranker là "improvement step" rõ rệt nhất bạn có thể thêm vào một Naive RAG để cải thiện chất lượng.
Vì sao retrieve thuần chưa đủ
Bài 39 đã chỉ ra: vector DB cho query một danh sách top-K chunk gần nhất theo cosine/dot. Trong thực tế, top-K này thường có vài vấn đề:
- Embedding model "trung bình" hóa ngữ nghĩa cả câu — query và doc có thể gần nhau về chủ đề nhưng doc không thật sự trả lời được câu hỏi.
- Top-1 không phải lúc nào cũng là câu trả lời tốt nhất; doc liên quan thật có thể nằm ở vị trí 7 hoặc 15.
- HNSW/IVF là approximate search — vị trí xếp hạng trong top-K có nhiễu so với brute-force exact.
- Hybrid (vector + BM25) merge bằng RRF cho thứ tự "hợp lý" nhưng không tối ưu cho relevance.
Giải pháp: lấy K rộng hơn ta cần (ví dụ 50-100), rồi chấm lại điểm từng cặp (query, doc) bằng model mạnh hơn. Đó là re-ranking.
Pattern 2-stage retrieval
STAGE 1 — Retrieval (recall, broad)
─────────────────────────────────────────────────────────
Query → bi-encoder embed → vector DB (top-K)
K thường 50-100. Mục tiêu: bao đủ doc liên quan.
Tốc độ: vài chục ms cho corpus triệu chunk.
STAGE 2 — Reranking (precision, narrow)
─────────────────────────────────────────────────────────
(Query, doc_i) → cross-encoder → score_i, với i = 1..K
Sort theo score → top-N (N thường 5-10).
Tốc độ: 50-500 ms cho K=50 (CPU/GPU).
Tên gọi quen thuộc: retrieve-then-rerank, hoặc recall-then-precision. Stage 1 tối đa hóa recall (kéo về càng nhiều doc liên quan càng tốt). Stage 2 tối đa hóa precision (chọn ra cái thật sự đúng).
Lý do tách thành 2 stage thay vì dùng model mạnh cho mọi cặp: model mạnh chậm; chạy nó cho toàn corpus (hàng triệu chunk) bất khả thi. Stage 1 nhanh nhưng kém chính xác làm nhiệm vụ "lọc thô"; stage 2 chậm nhưng chính xác chỉ phải xử lý K candidate.
Bi-encoder vs Cross-encoder
BI-ENCODER (dual encoder)
─────────────────────────────────────────────────────────
query → Encoder → vec_q ┐
├─→ cosine(vec_q, vec_d)
doc → Encoder → vec_d ┘
• Hai input encode ĐỘC LẬP.
• vec_d pre-compute được, lưu vector DB.
• Query time: chỉ encode query + 1 phép similarity.
CROSS-ENCODER
─────────────────────────────────────────────────────────
[CLS] query [SEP] doc [SEP] → Encoder → score
• Query và doc concat lại, đưa CÙNG vào model.
• Attention chạy GIỮA query và doc.
• Output: 1 scalar = relevance score.
• KHÔNG pre-compute được — query mới đến mới chạy.
Bi-encoder là kiến trúc của embedding model bạn đã dùng ở Bài 36 (sentence-transformers, BGE-M3, OpenAI text-embedding-3-*). Cross-encoder là kiến trúc của reranker. Hai dòng model song song trong literature thường gọi là dense retrieval (bi-encoder) và cross-attention re-ranker (cross-encoder).
Bản chất paper gốc: Reimers & Gurevych 2019 (Sentence-BERT, arXiv 1908.10084) giới thiệu cách tách BERT thành bi-encoder để pre-compute vector — chính là embedding model hôm nay.
Vì sao cross-encoder chính xác hơn
Bi-encoder produce hai vector độc lập, mỗi vector "nén" một câu thành vài trăm chiều. Khi tính cosine, mọi thông tin tương tác chi tiết giữa query và doc đã mất.
Cross-encoder cho query và doc cùng đi qua model với full attention: mỗi token của query có thể "nhìn" mọi token của doc và ngược lại. Model học được:
- Từ nào của query khớp đoạn nào của doc.
- Doc có thật sự trả lời câu hỏi không, hay chỉ chung chủ đề.
- Phủ định, mâu thuẫn, ngữ cảnh phụ thuộc lẫn nhau.
Kết quả: trên các benchmark IR (MS MARCO, BEIR), cross-encoder thường outperform bi-encoder thuần 5-20% về NDCG@10 / MRR. Đổi lại tốc độ chậm hơn nhiều lần.
Tham khảo: Nogueira & Cho 2019 (Passage Re-ranking with BERT, arXiv 1901.04085) là paper sớm chỉ ra cross-encoder rerank cải thiện rõ rệt baseline BM25 trên MS MARCO.
Trade-off speed và scale
Trục Bi-encoder Cross-encoder
─────────────────────────────────────────────────────────────────
Pre-compute doc Có (lưu vec DB) Không
Query time Encode 1 query + Encode K cặp
1 phép similarity (query, doc_i)
song song hoặc batch
Scale với corpus N Tốt (HNSW ~ log N) O(N) per query
→ không scale toàn corpus
Độ chính xác (NDCG) Trung bình Cao (+5–20%)
Latency (CPU, 1 cặp) < 10 ms ~10-30 ms / cặp
Latency K=50 (GPU) ~30 ms (search) ~50-200 ms (batch)
Cross-encoder chạy 1 forward pass cho mỗi cặp (query, doc). Với K=50, mỗi query cần 50 forward pass — chấp nhận được. Với N = 1M doc trong corpus, 1M forward pass mỗi query — không khả thi.
Đây chính là lý do không dùng cross-encoder thay vector DB, mà chỉ dùng nó sau stage 1 cho top-K candidate.
Workflow re-rank
Query
│
│ (bi-encoder)
▼
Vector DB ──── top-50 candidate ────┐
│
│ (cross-encoder, batch)
▼
Score cho từng cặp (query, doc_i)
│
│ sort desc
▼
Top-5 cho LLM
│
▼
Augmented prompt → LLM → Answer
Thực hiện đúng yêu cầu hai điểm:
- Batch cross-encoder. Gọi
predict([[q, d1], [q, d2], ...])một lần, không loop từng cặp gọi model riêng — chậm gấp K lần. - Giữ metadata (source, page, id) song song với chunk text, để sau khi rerank vẫn có nguồn cite cho LLM.
Các reranker phổ biến 2024-2026
Reranker Loại Đặc điểm
────────────────────────────────────────────────────────────────────
Cohere Rerank v3 API Multilingual, prod-ready, $2/1K
search. Model rerank-multilingual-v3.0.
BGE Reranker (BAAI) OSS bge-reranker-base/large/v2-m3.
MS MARCO + multilingual. HF.
Jina Reranker OSS / API jina-reranker-v2-base-multilingual.
Hỗ trợ 100+ ngôn ngữ.
MS MARCO MiniLM OSS cross-encoder/ms-marco-MiniLM-L-6-v2.
Lightweight (~80MB), tiếng Anh.
Voyage Rerank API voyage-rerank-1, focus domain tech /
legal / finance.
mxbai-rerank OSS mixedbread-ai/mxbai-rerank-large-v1.
Top MTEB rerank board cuối 2024.
Quy tắc chọn:
- Prototype tiếng Anh, không muốn host → Cohere Rerank API.
- Tiếng Việt / đa ngôn ngữ, on-prem → BGE Reranker v2-m3 hoặc Jina v2 multilingual.
- CPU only, latency thấp → MS MARCO MiniLM.
- Chất lượng cao nhất hiện tại (OSS) → mxbai-rerank-large hoặc BGE Reranker v2-m3.
Embedding model và reranker không cần "match"; quy tắc thực tế là dùng embedding multilingual tốt cho stage 1, reranker multilingual tốt cho stage 2.
Code — Cohere Rerank API
pip install cohere
import cohere
import os
co = cohere.Client(os.environ["COHERE_API_KEY"])
query = "RAG là gì?"
documents = [
"RAG kết hợp LLM với knowledge base ngoài.",
"Python là ngôn ngữ phổ biến cho AI.",
"Vector database lưu embedding và hỗ trợ similarity search.",
"Retrieval-Augmented Generation truy xuất doc trước khi generate.",
]
results = co.rerank(
query=query,
documents=documents,
top_n=3,
model="rerank-multilingual-v3.0",
)
for r in results.results:
print(r.index, round(r.relevance_score, 3), documents[r.index][:60])
Output ví dụ (số có thể khác):
3 0.972 Retrieval-Augmented Generation truy xuất doc trước khi generate.
0 0.951 RAG kết hợp LLM với knowledge base ngoài.
2 0.412 Vector database lưu embedding và hỗ trợ similarity search.
Các điểm cần lưu ý:
relevance_scorechuẩn hoá ~[0, 1] — có thể đặt threshold để loại doc dưới 0.3.top_nchỉ điều khiển số kết quả trả về; Cohere vẫn rerank toàn bộdocuments.- Cohere giới hạn ~1000 doc / call và 4096 token / doc.
rerank-multilingual-v3.0hỗ trợ tốt tiếng Việt; bản tiếng Anh dùngrerank-english-v3.0.
Code — BGE Reranker local
pip install sentence-transformers
from sentence_transformers import CrossEncoder
model = CrossEncoder("BAAI/bge-reranker-base")
query = "RAG là gì?"
docs = [
"RAG kết hợp LLM với knowledge base ngoài.",
"Python là ngôn ngữ phổ biến cho AI.",
"Vector database lưu embedding và hỗ trợ similarity search.",
"Retrieval-Augmented Generation truy xuất doc trước khi generate.",
]
pairs = [[query, d] for d in docs]
scores = model.predict(pairs) # numpy array, mỗi phần tử = score
ranked = sorted(zip(docs, scores), key=lambda x: -x[1])
for d, s in ranked[:3]:
print(round(float(s), 3), d[:60])
Đặc điểm:
model.predictchạy batch tự động — gọi 1 lần cho cả danh sách pair.- Score là logit (chưa sigmoid). Để chuẩn ~[0, 1], áp
1 / (1 + np.exp(-scores)); nhưng cho việc rerank, chỉ cần thứ tự nên không cần normalize. - Có thể bật GPU bằng
CrossEncoder(..., device="cuda"). - BGE Reranker v2-m3 (
BAAI/bge-reranker-v2-m3) chất lượng cao hơn cho đa ngôn ngữ, nặng hơn (~568M params).
Full pipeline ChromaDB + reranker
import chromadb
from sentence_transformers import SentenceTransformer, CrossEncoder
from openai import OpenAI
embedder = SentenceTransformer("BAAI/bge-m3")
reranker = CrossEncoder("BAAI/bge-reranker-base")
llm = OpenAI()
client = chromadb.PersistentClient(path="./chroma_db")
col = client.get_collection("docs")
def rag_with_rerank(query: str, k_retrieve: int = 50, k_final: int = 5):
# Stage 1 — retrieve top-K
q_vec = embedder.encode([query]).tolist()
res = col.query(query_embeddings=q_vec, n_results=k_retrieve)
candidates = res["documents"][0]
metas = res["metadatas"][0]
# Stage 2 — rerank
pairs = [[query, d] for d in candidates]
scores = reranker.predict(pairs)
top = sorted(
zip(candidates, metas, scores), key=lambda x: -x[2]
)[:k_final]
# Build prompt
context = "\n".join(
f"[{i+1}] {d}" for i, (d, _, _) in enumerate(top)
)
prompt = (
"Trả lời dựa trên CONTEXT. Nếu không đủ, nói \"không có thông tin\".\n\n"
f"CONTEXT:\n{context}\n\nQUESTION: {query}\nANSWER:"
)
resp = llm.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": prompt}],
)
return resp.choices[0].message.content, top
answer, sources = rag_with_rerank("RAG là gì?")
print(answer)
for d, m, s in sources:
print(f" score={float(s):.3f} src={m.get('source')}")
So với pipeline naive ở Bài 35 (chỉ n_results=3 rồi đưa thẳng cho LLM), pipeline này thêm một step rerank với chi phí thêm vài chục đến vài trăm ms. Trên domain có nhiều chunk gần nhau về chủ đề, top-5 sau rerank thường khác đáng kể top-5 từ vector DB.
Trade-off K khởi đầu
K (top-K stage 1) Recall Rerank latency Note
─────────────────────────────────────────────────────────
K = 10 Thấp Rất nhanh Có thể miss doc liên quan
K = 20-30 Trung Nhanh Baseline tiết kiệm
K = 50 Tốt ~80-200 ms Sweet spot phổ biến
K = 100 Rất tốt ~150-400 ms Khi corpus nhiễu, cần recall cao
K = 200+ Tiệm cận Chậm Hiếm khi đáng
Quy tắc thực tế:
- Bắt đầu K = 50, đo recall@K bằng test set Q&A.
- Nếu doc liên quan đã nằm trong top-50 stage 1 → reranker làm phần còn lại.
- Nếu recall@50 thấp (~< 80%) → vấn đề ở stage 1 (embedding model, chunking, hybrid), không phải ở reranker.
Tăng K không "free": cost rerank tuyến tính với K. K = 100 cost rerank gấp đôi K = 50.
Latency thực tế
Reranker Hardware K=50 latency
─────────────────────────────────────────────────────────────
ms-marco-MiniLM-L-6-v2 CPU ~80 ms
ms-marco-MiniLM-L-6-v2 GPU (T4) ~20 ms
BGE Reranker base CPU ~250 ms
BGE Reranker base GPU (T4) ~50 ms
BGE Reranker v2-m3 (large) GPU (T4) ~150 ms
Cohere Rerank API Cloud ~150-300 ms (network)
Con số tham khảo; phụ thuộc batch size, độ dài chunk, GPU. Với pipeline RAG tổng end-to-end thường 1-3s (LLM call dominant), thêm 100-300 ms rerank chấp nhận được cho hầu hết app.
Tối ưu nếu cần:
- Quantize reranker (INT8) — giảm 2-4 lần latency.
- Dùng
onnxruntime/ctranslate2cho CPU serving. - Cache score cho cặp (query phổ biến, doc) bằng Redis.
Cost ước tính
Option Cost ước tính
─────────────────────────────────────────────────────────
Cohere Rerank API ~$2 / 1000 search (1 search = 1 query, K doc bất kỳ)
→ 100K query/tháng ≈ $200
Voyage Rerank API ~$0.05 / 1M token (giá năm 2026, kiểm tra lại)
Self-host BGE $0 per call, ~$100-500/tháng cho GPU server (T4/L4)
Self-host MiniLM $0 per call, chạy CPU cũng đủ cho ~100 QPS
Reranker là cost mới thêm vào RAG, không "free". Đánh giá đáng tiền hay không:
- Đo improvement quality (Recall@5, MRR, hoặc LLM-as-Judge faithfulness).
- Nếu cải thiện 3-10% trên metric quan trọng → thường đáng cho domain quan trọng.
- Nếu cải thiện < 1% → bỏ rerank, đầu tư vào chunking / embedding.
Reranker training ngắn
Reranker pre-train sẵn (BGE, MiniLM) đủ tốt cho phần lớn case. Khi cần fine-tune trên domain riêng (legal Việt, medical, code repo nội bộ):
- Dữ liệu: triplet (query, positive_doc, negative_doc). Positive = doc thực sự liên quan; negative = doc gần nhưng không trả lời.
- Loss: BCE (binary cross-entropy) hoặc margin ranking.
- Tool:
sentence-transformersCrossEncoder.fit()hoặc trainer của BGE. - Lượng data: vài nghìn triplet đã đủ cải thiện đo đếm được; production thường vài chục nghìn.
from sentence_transformers import CrossEncoder, InputExample
from torch.utils.data import DataLoader
model = CrossEncoder("BAAI/bge-reranker-base", num_labels=1)
train_samples = [
InputExample(texts=[query, pos], label=1.0),
InputExample(texts=[query, neg], label=0.0),
# ... thousands more
]
loader = DataLoader(train_samples, batch_size=16, shuffle=True)
model.fit(train_dataloader=loader, epochs=1, warmup_steps=100,
output_path="./my-reranker")
Khó nhất không phải code mà là thu thập triplet sạch — đặc biệt là hard negative (doc gần nhưng không phải answer). Một cách phổ biến: dùng bi-encoder top-K, lấy doc rank thấp làm hard negative.
Khi nào dùng reranker
- Quality quan trọng hơn vài trăm ms latency: legal, medical, finance, compliance.
- Top-K vector thuần đo được precision kém — top-5 hay có 1-2 doc nhiễu.
- Corpus có nhiều doc chung chủ đề, khác chi tiết (FAQ với câu hỏi tương tự nhau).
- Đã có hybrid (vector + BM25) và muốn merge kết quả thông minh hơn RRF.
- Có budget thêm vài trăm ms + cost (Cohere API hoặc GPU host).
Trong stack production phổ biến 2024-2026, reranker gần như là default. Ngoại lệ là app cần latency < 500ms tổng hoặc cost cực thấp.
Khi nào KHÔNG cần reranker
- Top-K retrieval đã đủ tốt — đo trên test set thấy Recall@5 đã ≥ 95%.
- Latency critical (chat realtime, voice agent): mỗi 100 ms quan trọng.
- Cost sensitive — workload triệu query, mỗi $0.002 thêm vào là số lớn.
- Corpus nhỏ vài trăm chunk: bi-encoder top-5 thường đã chính xác.
- Đang ở giai đoạn prototype, chưa có golden set để đo improvement.
Quyết định cuối cùng nên đến từ test set có nhãn chứ không phải intuition. A/B test: pipeline có rerank vs không, đo Recall@5, MRR, faithfulness. Chỉ giữ rerank nếu improvement > chi phí.
LLM-as-Reranker
Thay vì cross-encoder chuyên dụng, dùng một LLM (GPT-4o, Claude Sonnet 4) chấm relevance từng doc:
prompt = f"""Đánh giá mức độ liên quan của DOC với QUERY trên thang 0-10.
Chỉ trả về số.
QUERY: {query}
DOC: {doc}
SCORE:"""
resp = llm.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": prompt}],
max_tokens=4,
)
score = int(resp.choices[0].message.content.strip())
Đặc điểm:
- Chất lượng có thể vượt cross-encoder OSS trên domain phức tạp (đa ngôn ngữ, suy luận).
- Đắt: K = 50 → 50 LLM call mỗi query, latency hàng giây, cost $0.05-0.5.
- Phù hợp cho query rất quan trọng (legal precedent, medical decision) hoặc cho offline eval.
- Có thể giảm cost bằng listwise: cho LLM xem cả K doc một lần và yêu cầu rank (xem bước 19).
Tham khảo: Sun et al. 2023 (Is ChatGPT Good at Search?, arXiv 2304.09542) — đánh giá GPT-3.5/4 làm reranker, cho thấy đạt hoặc vượt cross-encoder trên một số benchmark.
Listwise vs Pointwise
Pointwise reranking
─────────────────────────────────────────────────────────
Mỗi cặp (query, doc_i) chấm điểm độc lập.
Sort theo điểm. Cross-encoder cổ điển = pointwise.
Ưu: batch dễ, scale tốt theo K.
Nhược: không thấy được "doc nào tốt hơn doc nào".
Listwise reranking
─────────────────────────────────────────────────────────
Đưa cả K doc vào model cùng lúc, model trả ra thứ tự.
LLM list-rerank: prompt "rank các doc sau theo relevance".
Ưu: thấy bức tranh toàn cảnh, có thể so sánh chéo.
Nhược: token cost cao, khó với K lớn (LLM context).
Pairwise reranking ở giữa: so sánh từng cặp doc (doc_i vs doc_j), tốn O(K²) — ít dùng production.
Thực tế phổ biến nhất vẫn là pointwise cross-encoder. Listwise LLM dùng khi quality quan trọng tột bậc và budget cho phép.
Hybrid retrieval + rerank
Stack "modern" cho production RAG là kết hợp cả hybrid retrieval (Bài 35 bước 17) và reranking:
┌─ Vector retrieve (top-50) ─┐
Query ──────────┤ ├─→ Union (RRF)
└─ BM25 retrieve (top-50) ─┘ → top-100
│
│ (cross-encoder)
▼
Rerank → top-5
│
▼
LLM → Answer
Lý do gộp:
- BM25 bắt keyword cụ thể (mã sản phẩm, tên function) tốt hơn vector.
- Vector bắt diễn giải / paraphrase tốt hơn BM25.
- RRF gộp nhanh, không tối ưu relevance — rerank đứng cuối thay vai trò "final ranking".
Đây là pattern của Elasticsearch + dense vector, Qdrant hybrid, Weaviate hybrid. LangChain có EnsembleRetriever + ContextualCompressionRetriever (wrap reranker).
HyDE — pre-retrieval enhancement
HyDE (Hypothetical Document Embeddings, Gao et al. 2022, arXiv 2212.10496) là kỹ thuật pre-retrieval: cải thiện retrieve thay vì rerank sau.
Vấn đề:
Câu hỏi và câu trả lời nằm ở "không gian khác".
Embed query rồi search có thể không gần doc đích.
HyDE:
1. LLM sinh ra một câu trả lời GIẢ ĐỊNH cho query
(có thể sai, nhưng có dạng văn bản giống câu trả lời thật).
2. Embed câu trả lời giả này, không phải query gốc.
3. Search vector DB với embedding này.
4. Doc thực sự liên quan thường gần "câu trả lời giả" hơn query.
def hyde_retrieve(query, k=10):
hypo = llm.chat.completions.create(
model="gpt-4o-mini",
messages=[{
"role": "user",
"content": f"Viết một đoạn trả lời ngắn (3-4 câu) cho câu hỏi: {query}",
}],
).choices[0].message.content
hypo_vec = embedder.encode([hypo]).tolist()
return col.query(query_embeddings=hypo_vec, n_results=k)
HyDE thường cải thiện retrieve 5-15% trên benchmark, đặc biệt với query ngắn / mơ hồ. Cost thêm 1 LLM call nhỏ.
HyDE và reranking không loại nhau: HyDE cải thiện stage 1, reranker cải thiện stage 2; có thể dùng cả hai.
Query rewriting / expansion
- Query rewriting — LLM viết lại câu hỏi cho rõ hơn, thêm context, sửa ambiguity. Ví dụ "ngày mai họp lúc mấy giờ" → "Cuộc họp ngày 26/05/2026 bắt đầu lúc mấy giờ".
- Multi-query — LLM sinh N paraphrase của query, retrieve từng cái, union kết quả (rồi rerank).
- Step-back prompting — LLM sinh câu hỏi tổng quát hơn để retrieve doc nền tảng, sau đó retrieve doc cụ thể cho câu hỏi gốc.
def multi_query(query, n=3):
prompt = (
f"Sinh {n} cách diễn đạt khác của câu hỏi sau, mỗi dòng một câu:\n"
f"{query}"
)
out = llm.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": prompt}],
).choices[0].message.content
return [query] + [line for line in out.split("\n") if line.strip()]
Mỗi query con retrieve top-K, union với dedup, rồi cho qua reranker. Tốn thêm N call vector DB + 1 call LLM cho sinh paraphrase, đổi lại recall cao hơn cho query ngắn / chỉ một từ.
RAG pipeline evolution
Phiên bản Pipeline
─────────────────────────────────────────────────────────
1. Naive RAG query → embed → vector top-K → LLM
2. + Rerank query → embed → vector top-50 →
rerank → top-5 → LLM
3. + Hybrid query → (BM25 + vector) → fusion top-100 →
rerank → top-5 → LLM
4. + Query rewrite query → LLM rewrite (or HyDE / multi-query)
→ (BM25 + vector) → fusion top-100 →
rerank → top-5 → LLM
5. + Agentic LLM agent tự quyết: cần retrieve không?
query nào? Có cần round thứ 2 không?
Quy tắc: chỉ thêm step khi đo được lợi ích. Bắt đầu naive, thêm rerank trước (impact cao, dễ làm), sau đó hybrid, query rewrite. Agentic thường để cho task phức tạp, đa hop.
Bài 41 capstone (QA system PDF) sẽ kết hợp ChromaDB + reranker = pipeline phiên bản 2.
Đánh giá impact của reranker
Quy trình quyết định reranker có đáng dùng không:
- Build test set 50-100 query có nhãn doc liên quan (ground truth).
- Đo Recall@5 và MRR cho pipeline không rerank.
- Đo Recall@5 và MRR cho pipeline có rerank (cùng test set).
- Tính improvement: ví dụ Recall@5 từ 0.72 → 0.86 = +14 điểm.
- Quy ra business impact: với 100K query/tháng, 14% query thêm được trả lời đúng = X user happy hơn / Y ticket support giảm.
- So với cost thêm (cohere $200/tháng hoặc GPU $300/tháng) → quyết.
def recall_at_k(retrieved_ids, gold_ids, k=5):
top_k = retrieved_ids[:k]
return len(set(top_k) & set(gold_ids)) / max(1, len(gold_ids))
def mrr(retrieved_ids, gold_ids):
for i, doc_id in enumerate(retrieved_ids):
if doc_id in gold_ids:
return 1.0 / (i + 1)
return 0.0
Bài 58 (Module 9) sẽ deep-dive metric RAG với RAGAS.
Pitfall hay gặp
- Loop từng cặp gọi reranker — quên batch, chậm gấp K lần. Luôn dùng
model.predict(pairs)hoặc tương đương. - K khởi đầu quá nhỏ — chỉ rerank top-5 thì reranker không có gì để "fix"; phải lấy top-30/50 mới có không gian để sắp lại.
- Mất metadata sau rerank — sort score nhưng không sort song song metadata → không cite nguồn đúng.
- Reranker mismatch ngôn ngữ — dùng
ms-marco-MiniLM(English-only) cho corpus tiếng Việt → score gần random. - Chunk quá dài — reranker thường giới hạn 512 token / cặp; chunk dài hơn bị truncate, mất thông tin cuối doc.
- Quên đo — thêm reranker mà không đo improvement → có thể đang trả tiền cho không gì.
- Overcomplicate — thêm rerank, HyDE, multi-query, query rewrite cùng lúc, không biết cái nào đóng góp bao nhiêu. Thêm từng cái và đo.
- Cohere rate limit — quên handle 429 / retry với backoff khi gọi API.
Bài tập
- Chạy lại pipeline RAG từ Bài 38/39 trên một corpus quen thuộc (note Obsidian, PDF cá nhân, blog cũ). Lấy top-5 từ vector DB cho 5 câu hỏi và ghi lại.
- Thêm BGE Reranker (
BAAI/bge-reranker-base) vào pipeline với K stage 1 = 50. So sánh top-5 trước và sau rerank cho cùng 5 câu hỏi. Note câu hỏi nào thay đổi đáng kể, câu nào không đổi. - Thử cả Cohere Rerank
rerank-multilingual-v3.0và so sánh với BGE Reranker base. Note thời gian chạy, score, top-5. - Cài HyDE: LLM (GPT-4o-mini) sinh câu trả lời giả 3-4 câu, embed nó thay vì query gốc, retrieve top-10. So sánh top-10 với cách dùng query gốc. Có khác đáng kể không?
- Xây test set 20 query có nhãn doc liên quan (do bạn tự đánh dấu). Đo Recall@5 và MRR cho 4 pipeline: (a) naive, (b) + rerank, (c) + HyDE, (d) + cả HyDE và rerank. Vẽ bảng so sánh.
- (Tùy chọn) Thử LLM-as-Reranker: chấm relevance 0-10 cho từng cặp (query, doc) bằng GPT-4o-mini. So với BGE Reranker về chất lượng và cost.
- Reimers & Gurevych — Sentence-BERT: Sentence Embeddings using Siamese BERT-Networks (arXiv 1908.10084)
- Nogueira & Cho — Passage Re-ranking with BERT (arXiv 1901.04085)
- Gao et al. — Precise Zero-Shot Dense Retrieval without Relevance Labels (HyDE, arXiv 2212.10496)
- Sun et al. — Is ChatGPT Good at Search? Investigating Large Language Models as Re-Ranking Agents (arXiv 2304.09542)
- Gao et al. — Retrieval-Augmented Generation for Large Language Models: A Survey (arXiv 2312.10997)
- Cohere — Rerank overview
- Cohere — Rerank API reference
- BAAI/bge-reranker-base — Hugging Face
- BAAI/bge-reranker-v2-m3 — Multilingual reranker
- Jina Reranker v2 — Multilingual
- cross-encoder/ms-marco-MiniLM-L-6-v2 — MS MARCO MiniLM
- mixedbread-ai/mxbai-rerank-large-v1
- sentence-transformers — Cross-Encoder usage
- LangChain — Contextual compression and reranking
- LlamaIndex — Node Postprocessors (reranking)
