Nhiều bài toán RAG cần lọc theo metadata kèm tìm kiếm tương đồng: "chỉ lấy tài liệu thuộc sản phẩm X, tiếng Việt, sau 2024". Ba cách, khác nhau ở chỗ lọc trước hay sau khi tìm.
- Post-filter (lọc sau) — tìm ANN trên toàn bộ index lấy top-K rồi mới lọc. Đơn giản, không đụng tới index. Nhược điểm chí mạng: nếu bộ lọc loại phần lớn corpus thì top-K sau khi lọc rỗng hoặc còn quá ít, buộc phải nâng K lên rất cao (lấy 500 để còn 10) nên vừa chậm vừa tốn bộ nhớ. Chỉ hợp khi bộ lọc loại dưới ~20%.
- Pre-filter (lọc trước) — lọc metadata ra tập con rồi mới tìm ANN trong đó. Chính xác, không sót. Nhược điểm ít người lường: nó phá cấu trúc của chỉ mục ANN. HNSW đi theo đồ thị láng giềng; bỏ bớt node làm đồ thị thưa ra và sinh vùng không tới được, khiến recall tụt. Tập con quá nhỏ thì thường phải quay về quét tuần tự. Hợp khi bộ lọc loại trên ~80%.
- Filtered HNSW (lai) — đưa bộ lọc vào trong quá trình duyệt đồ thị: đi tới đâu chỉ xét node thoả điều kiện tới đó, không đủ láng giềng thì mở rộng thêm. Giữ được recall cao mà vẫn lọc đúng, tránh cả hai điểm yếu trên. Đây là cách các vector DB hiện nay dùng — Qdrant gọi là lọc theo payload, Weaviate là filtered vector search, pgvector hỗ trợ từ 0.7.
Chọn thế nào phụ thuộc độ chọn lọc của bộ lọc, và tốt nhất là né hẳn việc phải lọc:
- Ít giá trị phân biệt (vài chục tenant, vài ngôn ngữ) → tách hẳn thành collection/namespace riêng. Truy vấn đúng collection thì khỏi lọc gì cả — nhanh nhất và an toàn nhất, vì dữ liệu các tenant không nằm chung.
- Nhiều giá trị, mỗi truy vấn chỉ lấy một phần rất nhỏ (lọc theo
user_idtrên hàng triệu người dùng) → filtered HNSW kèm chỉ mục metadata. - Lọc theo khoảng (
ngày > X) → độ chọn lọc đổi theo từng truy vấn nên đừng cố định chiến lược; filtered HNSW an toàn hơn. - Nhiều điều kiện AND → độ chọn lọc nhân lên, dễ nhỏ tới mức quét tuần tự lại nhanh hơn.
Việc bắt buộc phải làm: đo lại recall sau khi thêm bộ lọc. Cấu hình sai có thể làm recall tụt hàng chục phần trăm mà hệ vẫn chạy êm, không báo lỗi gì — chỉ là kết quả trả về thiếu mà không ai biết.