Chuẩn bị dữ liệu & chunking
“Rác vào, rác ra” đúng với RAG hơn bất cứ đâu. Cách bạn làm sạch và chia nhỏ tài liệu quyết định hệ thống có tìm được đoạn đúng hay không.
Nạp và làm sạch dữ liệu
Tài liệu thật hiếm khi sạch. Trước khi chia đoạn, hãy xử lý:
- Trích văn bản đúng cấu trúc: giữ tiêu đề, danh sách, bảng. PDF scan cần OCR; PDF nhiều cột dễ bị trộn dòng.
- Loại nhiễu: header/footer lặp mỗi trang, số trang, menu website, cookie banner, chữ ký email.
- Chuẩn hoá: Unicode (tiếng Việt dựng sẵn và tổ hợp trông giống nhau nhưng khác byte — nên đưa về NFC), khoảng trắng, ký tự lạ.
- Khử trùng lặp: nhiều bản của cùng một tài liệu làm kết quả truy xuất bị lặp và lấn chỗ các đoạn khác.
Metadata: thứ bị quên nhiều nhất
Mỗi chunk nên mang theo thông tin về nguồn gốc của nó:
{
"chunk_id": "hr-remote-v2.1#4",
"doc_title": "Quy định làm việc từ xa",
"section": "3. Đối tượng áp dụng",
"source_url": "https://intranet.example/hr/remote",
"version": "2.1",
"updated_at": "2025-03-01",
"department": "HR",
"allowed_groups": ["all-staff"]
}
Metadata phục vụ ba việc: lọc (chỉ tìm trong tài liệu HR, bản mới nhất), phân quyền (người dùng chỉ thấy đoạn thuộc nhóm của họ) và trích dẫn (hiển thị tên tài liệu, mục, đường dẫn).
Vì sao phải chia nhỏ
- Embedding của đoạn quá dài bị “loãng”: một vector phải đại diện cho quá nhiều ý, nên không còn gần với câu hỏi cụ thể nào.
- Giới hạn ngữ cảnh và chi phí: đưa 5 đoạn 400 token rẻ và tập trung hơn nhiều so với 5 tài liệu 20 trang.
- Trích dẫn chính xác: chỉ ra đúng mục, đúng đoạn thay vì “ở đâu đó trong tài liệu này”.
Nhưng đoạn quá ngắn cũng hỏng: câu “Thời hạn là 30 ngày.” đứng một mình không cho biết thời hạn của cái gì.
Các chiến lược chunking
| Chiến lược | Cách làm | Hợp với | Lưu ý |
|---|---|---|---|
| Kích thước cố định | Cắt mỗi N token, chồng lấn M token | Điểm khởi đầu, văn bản đồng nhất | Dễ cắt giữa câu, giữa bảng |
| Đệ quy (recursive) | Thử tách theo đoạn → câu → từ cho tới khi vừa kích thước | Hầu hết tài liệu văn xuôi | Mặc định tốt, phổ biến trong các thư viện |
| Theo cấu trúc | Tách theo heading, mục, điều khoản | Quy chế, tài liệu kỹ thuật, Markdown/HTML | Mục quá dài vẫn cần tách tiếp |
| Ngữ nghĩa (semantic) | Tách ở chỗ embedding của các câu liên tiếp thay đổi mạnh | Văn bản dài, chuyển chủ đề liên tục | Tốn tính toán hơn, cần đo mới biết có lợi |
| Cha–con (small-to-big) | Tìm bằng đoạn nhỏ, đưa vào prompt đoạn cha lớn hơn | Cần vừa tìm chính xác vừa đủ ngữ cảnh | Lưu thêm quan hệ cha–con |
Mẹo quan trọng: gắn ngữ cảnh vào chunk
Trước khi embed, thêm tiêu đề tài liệu và đường dẫn mục vào đầu mỗi chunk. Đoạn “Thời hạn là 30 ngày.” trở thành:
Quy định hoàn ứng chi phí › 4. Thời hạn nộp chứng từ
Thời hạn là 30 ngày kể từ ngày phát sinh chi phí.
Chỉ một dòng tiêu đề nhưng giúp cả tìm kiếm lẫn LLM hiểu đoạn này nói về điều gì.
Minh hoạ chia đoạn có chồng lấn
def chunk_words(text, size=120, overlap=20):
words = text.split()
step = size - overlap
chunks = []
for start in range(0, len(words), step):
piece = words[start:start + size]
if piece:
chunks.append(" ".join(piece))
if start + size >= len(words):
break
return chunks
Phần chồng lấn giữ lại vài câu cuối của đoạn trước ở đầu đoạn sau, để một ý nằm ngay ranh giới vẫn xuất hiện trọn vẹn ở ít nhất một đoạn. Chạy trên tài liệu 1.000 từ, hàm này trả về 10 đoạn: chín đoạn 120 từ và một đoạn cuối 100 từ, hai đoạn liền nhau dùng chung đúng 20 từ, và không từ nào bị bỏ sót. (Thực tế nên đếm bằng token của mô hình embedding thay vì đếm từ.)
Chọn kích thước và overlap
- Điểm khởi đầu hợp lý: 200–500 token mỗi đoạn, chồng lấn khoảng 10–20%.
- Câu hỏi dạng tra cứu dữ kiện (“mức phụ cấp là bao nhiêu?”) thường hợp đoạn nhỏ.
- Câu hỏi cần giải thích (“quy trình phê duyệt diễn ra thế nào?”) thường hợp đoạn lớn hơn hoặc cha–con.
- Không vượt quá giới hạn token đầu vào của mô hình embedding — phần thừa có thể bị cắt âm thầm.