Retrieval là bước lấy top-k chunk gần nhất với câu hỏi. Nhưng "gần nhất" không đảm bảo "đúng nhất". Bài này dạy cách retrieval và — quan trọng hơn — cách đo lường chất lượng bằng recall@k để biết pipeline của bạn tốt hay tệ thay vì cảm tính.
Retrieval hoạt động thế nào
- Embed câu hỏi bằng đúng model đã index.
- Tính độ tương đồng (cosine) với mọi vector trong index.
- Trả về k chunk điểm cao nhất (top-k).
def retrieve(query, k=5):
qv = embed([query])[0]
hits = client.search("docs", query_vector=qv.tolist(), limit=k)
return [(h.payload["text"], h.score) for h in hits]
Recall@k là gì
Recall@k = tỉ lệ câu hỏi mà chunk đúng nằm trong top-k kết quả retrieval.
Ví dụ: bạn có 100 câu hỏi, mỗi câu có ít nhất một chunk "vàng" (gold chunk) chứa câu trả lời. Nếu với 82 câu, gold chunk xuất hiện trong top-5 → recall@5 = 82%.
Đây là chỉ số quan trọng nhất vì: nếu chunk đúng không được retrieval kéo ra, LLM giỏi mấy cũng không cứu được. Retrieval là trần chất lượng của cả hệ thống.
Xây tập đánh giá (gold set)
- Thu thập 30–100 câu hỏi thật.
- Với mỗi câu, xác định chunk_id nào chứa câu trả lời đúng (gán tay).
- Lưu thành cặp
(câu hỏi, [id gold chunk]).
gold = [
{"q": "quy đổi phép năm được bao nhiêu ngày?", "gold_ids": [42]},
{"q": "giờ làm việc mấy giờ?", "gold_ids": [7]},
]
Tính recall@k
def recall_at_k(gold, k):
hit = 0
for item in gold:
ids = [h.id for h in client.search(
"docs", query_vector=embed([item["q"]])[0].tolist(), limit=k)]
if any(g in ids for g in item["gold_ids"]):
hit += 1
return hit / len(gold)
for k in [1, 3, 5, 10, 20]:
print(f"recall@{k} = {recall_at_k(gold, k):.2%}")
Đọc kết quả để ra quyết định
- recall@20 cao nhưng recall@5 thấp: chunk đúng CÓ trong index nhưng bị xếp hạng thấp → cần reranking (Bài 5). Đây là dấu hiệu kinh điển.
- recall@20 cũng thấp: chunk đúng gần như không được kéo ra → vấn đề ở chunking hoặc embedding, rerank không cứu được.
- recall@1 ≈ recall@5: retrieval đã rất tốt, có thể giảm k để tiết kiệm.
Chọn k hợp lý
k lớn → recall cao hơn nhưng nhiều nhiễu vào context và tốn token. Chiến lược thực chiến: retrieval với k lớn (ví dụ 20–30) rồi rerank xuống 3–5 để đưa vào LLM. Bài 5 sẽ làm điều này.
Checklist đo lường retrieval
- [ ] Có gold set ≥ 30 câu, gán chunk đúng bằng tay.
- [ ] Tính recall@1, @5, @10, @20 — không chỉ một con số.
- [ ] So sánh recall@5 vs recall@20 để biết cần rerank hay cần sửa chunk.
- [ ] Chạy lại đo mỗi khi đổi chunk size, model, hoặc prompt viết lại query.
- [ ] Lưu kết quả theo phiên bản để thấy tiến bộ.
Sai lầm thường gặp
- Không có gold set: đánh giá RAG bằng "nhìn có vẻ đúng" → không tối ưu được.
- Chỉ nhìn câu trả lời cuối mà không đo retrieval riêng: không biết lỗi ở retrieval hay ở LLM.
- Chọn k quá nhỏ rồi kết luận RAG kém, trong khi chunk đúng nằm ở top-8.
- Nhầm recall với precision: recall@k đo "chunk đúng có được kéo ra không"; precision đo "trong top-k bao nhiêu cái liên quan". Với RAG, recall@k quan trọng hơn vì LLM có thể lọc nhiễu.
- Đổi embedding model mà không đo lại: giả định model mới tốt hơn là sai lầm phổ biến.