Vector DB 7 — Retrieval cho RAG
Retrieval — trái tim của RAG
LLM 4 — RAG đã dựng bức tranh tổng thể của Retrieval-Augmented Generation: thay vì để mô hình ngôn ngữ trả lời bằng trí nhớ tham số (dễ bịa, không cập nhật, không trích nguồn), ta tìm các đoạn tài liệu liên quan rồi nhồi vào prompt làm ngữ cảnh để mô hình trả lời dựa trên đó. Bài này không lặp lại toàn bộ RAG — nó phóng to đúng một mắt xích: retrieval, tức phần "R" lấy tri thức. Đây cũng là phần mà cả loạt Vector DB (chunking, embedding, ANN, hybrid) hội tụ vào một đường ống thực chiến.
Vì sao tách riêng retrieval để mổ xẻ? Vì trong thực tế, 90% chất lượng RAG được quyết ở retrieval, không phải ở mô hình sinh. Một LLM rất mạnh nhưng được cấp ngữ cảnh sai/thiếu vẫn trả lời sai. Ngược lại, một LLM trung bình được cấp đúng đoạn quy định cần thiết vẫn trả lời chính xác. Nguyên tắc thép: rác vào → rác ra (garbage in, garbage out).
Retrieval chống ảo giác thế nào?
Ảo giác (hallucination) là khi mô hình tự tin bịa ra thông tin không có thật. Với ngân hàng, một câu trả lời bịa về điều kiện giải ngân hay hạn mức là rủi ro nghiệp vụ và pháp lý thật sự. RAG chống ảo giác bằng cách cung cấp ngữ cảnh đúng rồi ràng buộc mô hình chỉ được trả lời dựa trên ngữ cảnh đó ("chỉ dùng thông tin trong tài liệu dưới đây; nếu không có, hãy nói không tìm thấy"). Nhưng cơ chế này chỉ hiệu quả khi retrieval thực sự lấy được đoạn đúng. Nếu retrieval trả về đoạn sai, ta không chống được ảo giác — ta chỉ chuyển từ "bịa từ trí nhớ" sang "trả lời sai dựa trên tài liệu không liên quan", thậm chí còn nguy hiểm hơn vì nghe có vẻ có căn cứ.
Do đó, đầu tư vào retrieval chính là đầu tư vào độ tin cậy của toàn hệ thống.
Hai pipeline: INDEX và QUERY
Một hệ RAG có hai đường ống tách biệt về thời gian:
- INDEX (ingest) — chạy offline/định kỳ: đưa tài liệu vào kho vector.
- QUERY — chạy real-time khi người dùng hỏi: tìm và trả lời.
Pipeline INDEX — đưa tri thức vào kho
-
Load tài liệu. Nguồn ở ngân hàng đa dạng: PDF quy chế, Word quy trình, HTML intranet, thông tư scan. Mỗi định dạng cần parser riêng (PDF scan cần OCR; HTML cần bóc tag, giữ cấu trúc điều–khoản). Parse ẩu (mất bảng, dính header/footer lặp) thì mọi bước sau đều lệch.
-
Chunking. Cắt tài liệu dài thành đoạn vừa với cửa sổ ngữ cảnh của mô hình embedding và đủ nhỏ để một đoạn nói về một ý. Chi tiết ở Vector DB 2 — Embeddings & độ tương đồng: với văn bản pháp lý nên chunk theo cấu trúc điều/khoản kèm overlap nhẹ, không cắt cứng theo số token.
-
Embed. Mỗi chunk qua mô hình embedding → vector (ưu tiên mô hình đa ngôn ngữ mạnh tiếng Việt). Nên embed cả cùng một mô hình với query để hai bên cùng không gian.
-
Lưu vector + metadata. Ghi vào vector store:
(id, vector, content, metadata). Metadata là thứ thường bị xem nhẹ nhưng cực kỳ giá trị:doc_id,doc_title,department,doc_type,effective_date,valid_until,version,page, và đặc biệt danh sách quyền truy cập (ACL). Chính metadata cho phép filter theo quyền và trích nguồn về sau.
Pipeline QUERY — lấy đúng đoạn để trả lời
Đường ống truy vấn có nhiều bước hơn "một câu tìm vector". Thứ tự điển hình:
- (Tuỳ chọn) Query transformation — biến đổi câu hỏi cho dễ tìm.
- Retrieve — hybrid search + filter theo metadata/quyền, lấy một tập ứng viên rộng (top-N, ví dụ 30–50).
- Rerank — xếp lại tập ứng viên bằng mô hình chính xác hơn.
- Chọn top-K — giữ lại K đoạn tốt nhất (ví dụ 4–8).
- Nhồi vào prompt — ghép K đoạn + hướng dẫn + câu hỏi → gửi LLM, kèm chỉ dẫn trích nguồn.
Ta đi sâu từng bước.
Query transformation — sửa câu hỏi trước khi tìm
Câu hỏi người dùng thường ngắn, mơ hồ, dùng từ khác tài liệu ("rút tiền không được" vs. "giao dịch bị từ chối do vượt hạn mức"). Query transformation biến câu hỏi thành dạng dễ khớp hơn:
- Rewrite (viết lại): dùng LLM chuẩn hoá câu hỏi rối rắm thành câu rõ ràng, đủ ngữ cảnh (đặc biệt trong hội thoại nhiều lượt: "còn phí thì sao?" → "phí chuyển khoản liên ngân hàng là bao nhiêu?").
- Expand (mở rộng): thêm từ đồng nghĩa/thuật ngữ ("KH" → "khách hàng", "OTP" → "mã xác thực một lần") để tăng khả năng khớp lexical.
- HyDE (Hypothetical Document Embeddings): cho LLM sinh một câu trả lời giả định, rồi embed câu trả lời đó thay vì câu hỏi — vì đoạn trả lời giả gần tài liệu thật hơn một câu hỏi ngắn. Hữu ích khi query và document lệch phong cách nhiều.
- Multi-query: sinh nhiều biến thể câu hỏi, retrieve từng biến thể rồi hợp nhất (union + dedup). Tăng recall khi có nhiều cách diễn đạt.
Đánh đổi: mỗi phép transformation thêm latency và chi phí gọi LLM. Hệ latency-nhạy thường chỉ rewrite nhẹ; HyDE/multi-query để dành cho câu khó.
Retrieve + rerank — hai tầng lọc
Tầng 1: Retrieve (recall cao, rẻ)
Bước retrieve dùng hybrid search — kết hợp tìm ngữ nghĩa (dense vector) với tìm từ khoá (sparse/BM25) — để vừa bắt được ý nghĩa vừa không bỏ sót mã số, tên riêng, thuật ngữ chính xác. Chi tiết ở Vector DB 6 — Hybrid Search. Đồng thời filter theo metadata ngay trong lúc tìm: phòng ban, loại tài liệu, còn hiệu lực (valid_until >= today), và quyền của người dùng.
Mục tiêu của tầng này là recall cao: lấy rộng (top-N = 30–50) để không bỏ sót đoạn đúng. Chấp nhận có nhiễu, vì tầng sau sẽ lọc.
Tầng 2: Rerank (precision cao)
Reranker là mô hình cross-encoder: thay vì so hai vector tính sẵn (bi-encoder, nhanh nhưng thô), nó đọc đồng thời câu hỏi và từng đoạn ứng viên để chấm điểm chính xác hơn nhiều. Đắt hơn nên chỉ chạy trên top-N ứng viên (không phải cả kho). Sau rerank có thứ hạng đáng tin để chọn top-K (4–8 đoạn) nhồi vào prompt.
# Minh hoạ (KHÔNG phải SQL chạy được) — pipeline query RAG với rerank + filter quyền
candidates = store.hybrid_search(
query_vector=embed(query),
query_text=query, # cho nhánh BM25
top_n=40,
filter={ # filter tại chỗ (vec-06)
"must": [
{"department": user.department},
{"valid_until >=": today()},
{"acl_roles has_any": user.roles}, # PHÂN QUYỀN
]
},
)
ranked = reranker.score(query, candidates) # cross-encoder chấm lại
top_k = ranked[:6] # chọn 6 đoạn tốt nhất
prompt = build_prompt(query, top_k) # nhồi context + yêu cầu trích nguồn
answer = llm.generate(prompt)
Kỹ thuật retrieval nâng cao
Cắt–embed–tìm cơ bản có một mâu thuẫn cố hữu: chunk nhỏ tìm chính xác hơn (một ý → một vector) nhưng chunk lớn cho LLM ngữ cảnh đầy đủ hơn để trả lời. Các kỹ thuật sau giải quyết mâu thuẫn này.
Parent-child / small-to-big
Embed và tìm bằng chunk nhỏ (câu hoặc đoạn ngắn — khớp chính xác), nhưng khi trả về thì trả chunk cha lớn hơn (cả điều/khoản chứa nó) làm context cho LLM. Cách làm: mỗi child lưu parent_id; sau khi retrieve các child, gom các parent tương ứng (dedup) rồi đưa parent vào prompt. Được lợi cả hai đầu: precision khi tìm, đầy đủ khi trả lời.
Sentence-window
Biến thể của small-to-big: index theo từng câu, nhưng khi một câu trúng thì trả về cửa sổ vài câu xung quanh (trước/sau) để LLM có ngữ cảnh liền mạch. Hợp với tài liệu văn xuôi liền mạch hơn là văn bản điều–khoản rời rạc.
Contextual retrieval
Trước khi embed, thêm vào mỗi chunk một câu ngữ cảnh mô tả nó thuộc tài liệu/mục nào (ví dụ: prepend "Trích Quy chế tín dụng 2025, Chương III, Điều 12 về điều kiện giải ngân: ..."). Nhờ đó một chunk rời rạc ("...phải có tài sản bảo đảm...") vẫn mang đủ ngữ cảnh để khớp đúng câu hỏi và không bị hiểu lệch. Đơn giản nhưng cải thiện retrieval rõ rệt với tài liệu nhiều tham chiếu chéo.
Metadata-aware & multi-hop
- Metadata-aware retrieval: khai thác metadata không chỉ để filter mà để định tuyến. Ví dụ câu hỏi có nhắc "thông tư NHNN 2024" → ưu tiên/giới hạn tìm trong
doc_type = 'thong_tu'năm 2024. - Multi-hop: câu hỏi cần ghép nhiều tài liệu ("chính sách phí X thay đổi thế nào so với quy định cũ?") → retrieve nhiều lượt, dùng kết quả lượt đầu để tạo truy vấn lượt sau. Mạnh nhưng tốn latency; thường triển khai dạng agent (xem LLM 4 — RAG).
| Kỹ thuật | Giải quyết | Đánh đổi |
|---|---|---|
| Parent-child / small-to-big | Precision tìm vs. đủ ngữ cảnh | Lưu thêm quan hệ cha–con |
| Sentence-window | Ngữ cảnh liền mạch quanh câu trúng | Hợp văn xuôi, kém với điều–khoản |
| Contextual retrieval | Chunk rời rạc thiếu ngữ cảnh | Tốn bước tiền xử lý (đôi khi gọi LLM) |
| Metadata-aware | Định tuyến, thu hẹp phạm vi | Cần metadata sạch, đầy đủ |
| Multi-hop | Câu hỏi cần ghép nhiều tài liệu | Latency & chi phí cao |
Kiểm soát số lượng & độ dài context
Nhồi nhiều đoạn hơn không đồng nghĩa trả lời tốt hơn. Ba lý do phải kiểm soát:
- Giới hạn token: prompt có trần cửa sổ ngữ cảnh; vượt là lỗi hoặc bị cắt.
- Chi phí & latency: token đầu vào tính tiền và làm chậm; nhồi thừa 40 đoạn khi cần 5 là lãng phí.
- "Lost in the middle": mô hình chú ý kém tới thông tin nằm giữa một context dài. Nhồi quá nhiều đoạn làm loãng, đoạn đúng bị chìm.
Chiến lược: sau rerank chọn top-K nhỏ (thường 4–8), đặt các đoạn điểm cao ở đầu và cuối prompt, và đặt ngân sách token cho phần context. Nếu tổng vượt ngân sách thì cắt bớt đoạn điểm thấp thay vì cắt cụt giữa đoạn. Ít mà đúng luôn thắng nhiều mà loãng.
Đánh giá retrieval cho RAG
Không đo thì không cải thiện được. RAG có hai lớp chỉ số: retrieval (lấy đúng đoạn không?) và generation (trả lời có bám tài liệu không?). Bộ khung phổ biến là RAGAS (RAG Assessment):
| Chỉ số | Đo cái gì | Ý nghĩa |
|---|---|---|
| Context precision | Trong K đoạn lấy về, bao nhiêu thực sự liên quan | Ít nhiễu → context sạch |
| Context recall | Thông tin cần để trả lời có nằm trong context không | Không bỏ sót đoạn đúng |
| Faithfulness | Câu trả lời có được suy ra từ context không (không bịa) | Chống ảo giác |
| Groundedness / Answer relevancy | Câu trả lời có bám context và đúng trọng tâm câu hỏi | Trả lời có căn cứ, đúng ý |
Cách đánh giá thực tế: dựng một bộ câu hỏi vàng (golden set) — vài trăm câu hỏi nghiệp vụ kèm đoạn tài liệu đúng và câu trả lời chuẩn do chuyên gia nghiệp vụ duyệt. Chạy pipeline, đo các chỉ số trên, rồi tinh chỉnh (chunk size, top-N/K, có/không rerank, có/không HyDE) theo số liệu chứ không theo cảm tính. Faithfulness/groundedness thường được chấm bằng LLM-as-judge — cần kiểm định lại judge trên tập nhỏ để tin được.
Trích dẫn nguồn (citation)
Với ngân hàng, câu trả lời không được là "hộp đen". Mỗi câu trả lời phải dẫn nguồn: tài liệu nào, điều/khoản/trang nào — để người dùng tự kiểm chứng và để lưu vết audit. Cơ chế:
- Khi retrieve, giữ metadata
doc_title,page,articletheo mỗi đoạn. - Trong prompt, gắn nhãn mỗi đoạn (
[1],[2]...) và yêu cầu LLM trích số nhãn khi dùng. - Ở tầng ứng dụng, map nhãn về metadata → hiển thị "Nguồn: Quy chế tín dụng 2025, Điều 12, tr.7" kèm link mở tài liệu gốc.
Citation biến RAG từ "tin lời máy" thành "tra được nguồn" — điều kiện gần như bắt buộc để đưa trợ lý vào môi trường tuân thủ.
Cập nhật tài liệu & phân quyền
Cập nhật tài liệu: quy định thay đổi liên tục. Pipeline index phải xử lý thêm/sửa/xoá: khi một tài liệu ra bản mới, cần re-chunk + re-embed và upsert theo doc_id (ghi đè chunk cũ), đồng thời xoá chunk của bản hết hiệu lực hoặc đánh dấu valid_until. Nếu bỏ sót, hệ sẽ trả về quy định đã hết hiệu lực — sai lầm nghiêm trọng với ngân hàng. Nên gắn version và effective_date, và mặc định chỉ tìm trong tài liệu còn hiệu lực.
Phân quyền tài liệu (document-level access control): không phải nhân viên nào cũng được xem mọi tài liệu — tài liệu nhân sự, hồ sơ tín dụng cá nhân, quy định nội bộ theo phòng. Retrieval bắt buộc filter theo quyền của người dùng đang hỏi, chỉ trả về đoạn họ được phép xem. Cách làm: lưu acl_roles/acl_groups trong metadata mỗi chunk, và luôn áp filter quyền tại tầng retrieve (không chỉ dựa vào việc "hy vọng LLM không lộ"). Đây là ranh giới an ninh thật — xem Governance — Kiểm soát truy cập. Nguyên tắc: lọc quyền ở retrieval, không ở generation — LLM không được thấy đoạn mà người dùng không có quyền, ngay từ đầu.
Use case thực tế
Bối cảnh: NCB triển khai trợ lý tra cứu quy định/chính sách nội bộ cho ~2.000 nhân viên. Kho: 8.400 tài liệu (quy chế, quy trình, thông tư, biểu phí) → 1,3 triệu chunk trong pgvector (xem Vector DB 4). Yêu cầu cứng: trích nguồn mọi câu trả lời và phân quyền theo phòng ban/vai trò.
Phiên bản 1 (ngây thơ): dense top-5, không rerank, không filter quyền. Đo trên golden set 300 câu: context recall ~0,71, faithfulness ~0,78. Ba vấn đề: (1) bỏ sót đoạn khi câu hỏi có mã số/tên riêng; (2) đôi khi trả về quy định đã hết hiệu lực; (3) một lần lộ đoạn tài liệu nhân sự cho người không có quyền — sự cố nghiêm trọng.
Cải tiến pipeline:
- Hybrid + filter hiệu lực + ACL: hybrid search (dense + BM25) bắt được mã số; filter
valid_until >= todaybỏ tài liệu hết hạn; filteracl_roleschặn truy cập trái phép ngay ở retrieve. - top-N=40 → rerank cross-encoder → top-K=6: nâng precision, giảm nhiễu.
- Parent-child: embed chunk câu, trả về cả điều/khoản làm context.
- Contextual retrieval: prepend tiêu đề tài liệu + số điều vào mỗi chunk trước khi embed.
- Citation: mỗi câu trả lời kèm "Nguồn: <tài liệu>, Điều <n>, tr.<p>" + link.
Kết quả (đo trên cùng golden set): context recall 0,71 → 0,93, context precision 0,64 → 0,88, faithfulness 0,78 → 0,95. Không còn trả về quy định hết hiệu lực; không còn rò rỉ tài liệu ngoài quyền (kiểm thử red-team riêng cho ACL). Latency p95 ~380ms (thêm ~120ms do rerank) — chấp nhận được cho tra cứu nội bộ. Nhân viên tin dùng hơn hẳn vì mọi câu đều tra được nguồn.
Ghi nhớ
- Retrieval quyết định chất lượng RAG — rác vào thì rác ra. RAG chống ảo giác chỉ khi retrieval lấy đúng đoạn; đoạn sai còn nguy hiểm hơn vì nghe có căn cứ.
- Hai pipeline tách biệt: INDEX (load → chunk → embed → lưu vector + metadata + ACL) và QUERY (transform → retrieve → rerank → top-K → prompt + citation).
- Query transformation: rewrite, expand, HyDE, multi-query — tăng recall, đổi lại latency/chi phí; dùng có chọn lọc.
- Hai tầng lọc: retrieve hybrid+filter (recall cao, top-N rộng) → rerank cross-encoder (precision cao) → chọn top-K nhỏ.
- Kỹ thuật nâng cao: parent-child/small-to-big (embed nhỏ, trả context lớn), sentence-window, contextual retrieval, metadata-aware, multi-hop.
- Kiểm soát context: top-K nhỏ (4–8), đặt ngân sách token, tránh "lost in the middle" — ít mà đúng hơn nhiều mà loãng.
- Đánh giá RAGAS trên golden set: context precision/recall (retrieval), faithfulness/groundedness (generation) — tinh chỉnh theo số liệu.
- Ngân hàng: bắt buộc citation (tra được nguồn, audit) và phân quyền ở tầng retrieve (lọc ACL trước khi LLM thấy, không chỉ ở generation); xử lý cập nhật/hết hiệu lực tài liệu để không trả quy định cũ.
Bài viết liên quan
Phân biệt AI/ML/DL, các kiểu học máy, quy trình ML end-to-end và thuật ngữ nền tảng.
Chất lượng dữ liệu, xử lý thiếu/ngoại lai, mã hoá, chuẩn hoá, tạo đặc trưng và tránh data leakage.
Mô hình ngôn ngữ lớn hoạt động ra sao, token, context window, tham số sinh, và kỹ thuật viết prompt.
Retrieval-Augmented Generation, vector database, tool calling, agent và MCP để xây ứng dụng LLM thực tế.