RAG sai vì chunking, không vì ranker: cách đo lại cho đúng
Thí nghiệm đo bằng golden set: recall@1 chỉ 6/10 dù recall@3 đạt 10/10, và chunk theo AST mới cứu được retrieval cho code — không phải reranker.
Bạn chunk tài liệu, embed, cosine search, nhồi top-k vào prompt — demo chạy đúng ngay lần đầu. Rồi bạn trỏ nó vào corpus thật, hỏi một câu thật, và nó trả lời sai rất tự tin. Dưới đây là một thí nghiệm đo từng bước, chỉ ra chỗ hỏng thật và thứ tự sửa.
Nguồn là báo cáo của một kỹ sư tự dựng pipeline RAG không framework trong hai tuần gần đây, đo bằng golden set thay vì cảm giác. Mẫu nhỏ, một người làm — đọc như case study có số, không phải benchmark chuẩn.
Ba kiểu hỏng, ba cách sửa khác nhau
Tác giả cố tình dựng bản RAG thô nhất — cắt mỗi 300 ký tự, embed, cosine search — rồi đi tìm lỗi:
- Chunk cắt giữa từ. Kết quả hạng 1 là đoạn
"dles everything required to run a node, so there are no external runtime depende"— mở đầu giữa chữ "bundles", kết thúc giữa chữ "dependencies". Retrieval xếp đúng chunk, nhưng chunk đó vô dụng khi làm context. - Câu trả lời bị xé lẻ. Với câu hỏi về accumulator sau crash, chuỗi lập luận đầy đủ nằm rải ở ba chunk khác nhau, không chunk nào chứa trọn.
- Nhạy từ vựng. Câu hỏi theo cách người dùng nói tự nhiên ("how to set up?") điểm thấp hơn hẳn câu dùng đúng từ của tài liệu ("how do I install the binary?").
"Cứ dùng embeddings" không xử lý được kiểu nào trong ba kiểu trên.
Đổi metric trước, đừng đổi retriever
Chi tiết đáng copy nhất không phải kỹ thuật retrieval, mà là metric. Metric ban đầu là recall@3 — câu trả lời có nằm trong top 3 không — và mọi cấu hình đều đạt 10/10. Một metric mà cái gì cũng pass thì không nói lên điều gì. Chuyển sang recall@1 cộng average rank, điểm thật hiện ra: 6/10, nhiều câu có mặt nhưng xếp hạng tệ.
Nếu dashboard RAG của bạn đang xanh hết, khả năng cao bạn đang đo recall@k với k quá rộng.
Kích thước chunk là đánh đổi, không có số đúng
Chunk theo biên câu cộng overlap xử lý xong lỗi cắt giữa từ. Nhưng đo trên cùng câu hỏi crash recovery cho thấy giới hạn: chunk nhỏ (300 ký tự) đưa kết luận lên hạng 1 nhưng phần lập luận hỗ trợ rơi xuống hạng 3; chunk lớn (800 ký tự) giữ nguyên câu trả lời nhưng embedding bị loãng nên xếp hạng tệ hơn.
Hybrid search không phải bản nâng cấp miễn phí
Trên tài liệu văn xuôi, hybrid (BM25 + vector, fuse bằng Reciprocal Rank Fusion) chỉ ngang vector thuần: nó kéo câu trả lời crash từ hạng 3 lên hạng 1, nhưng làm tệ các truy vấn thuần ngữ nghĩa vì nhiễu keyword của BM25 làm hỏng phần fusion.
Bức tranh đảo chiều trên code. Khi index một codebase Flask 24 file (~470 chunk), lúc đầu mọi phương pháp đều recall gần bằng 0 — dấu hiệu vấn đề ở thượng nguồn, không ở ranker. Nguyên nhân: chunker viết cho văn xuôi ghép các hàm không liên quan vào cùng một chunk. Sau khi chuyển sang chunk theo AST — một chunk cho mỗi function hoặc class, kèm số dòng chính xác — hybrid search mới thắng rõ ràng, vì lúc này identifier thực sự định nghĩa chunk chứa nó.
Kết luận tác giả rút ra: chất lượng retrieval bị chi phối bởi độ mạch lạc của chunk, không phải sự khéo léo của ranker.
Việc nên làm tuần này
- Dựng golden set và đo recall@1, không phải recall@3.
- Chunk theo biên ngữ nghĩa: câu cho văn xuôi, AST cho code — xong việc này trước khi cân nhắc reranker.
- Coi hybrid search là tuỳ corpus: bật cho corpus lớn nhiều identifier, đo lại trước khi bật cho corpus văn xuôi nhỏ.
- Trả top-k và để model tổng hợp — nhiều câu trả lời vốn nằm ở nhiều chunk.
- Buộc kết quả trả kèm
file:line. Một tài liệu kiến trúc agent self-hosted cũng đặt "a required source is cited" vào danh sách acceptance check.
Không spam, hủy đăng ký bất kỳ lúc nào.

