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ý:

Bảng biểu là điểm yếu kinh điển Một bảng giá bị trích thành chuỗi số rời rạc sẽ vô nghĩa với cả embedding lẫn LLM. Chuyển bảng sang Markdown hoặc thành từng dòng mô tả (“Gói Pro: 299.000đ/tháng, 20 người dùng”) trước khi chunk.

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ỏ

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ượcCách làmHợp vớiLưu ý
Kích thước cố địnhCắt mỗi N token, chồng lấn M tokenĐiểm khởi đầu, văn bản đồng nhấtDễ 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ướcHầu hết tài liệu văn xuôiMặc định tốt, phổ biến trong các thư viện
Theo cấu trúcTách theo heading, mục, điều khoảnQuy chế, tài liệu kỹ thuật, Markdown/HTMLMụ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ạnhVăn bản dài, chuyển chủ đề liên tụcTố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ơnCần vừa tìm chính xác vừa đủ ngữ cảnhLư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

Đừng đoán — đo Chuẩn bị 30–50 câu hỏi có đáp án biết trước, thử 2–3 cấu hình chunk và so recall@k (xem Đánh giá chất lượng). Con số tốt nhất phụ thuộc vào dữ liệu của bạn. Hãy thử ngay trong phòng thí nghiệm: kéo kích thước chunk và xem đoạn đúng lên hay xuống hạng.

Tự kiểm tra

Vì sao nên thêm tiêu đề tài liệu vào nội dung chunk trước khi embed? Vì nhiều đoạn chỉ có nghĩa trong ngữ cảnh mục chứa nó. Tiêu đề giúp vector mang đúng chủ đề, tăng khả năng được tìm thấy, và giúp LLM hiểu đoạn khi đọc.
Chunk 2.000 token có vấn đề gì so với 300 token? Vector bị loãng nhiều ý nên kém khớp với câu hỏi cụ thể; tốn ngữ cảnh và chi phí; trích dẫn kém chính xác. Đổi lại ít bị mất ngữ cảnh — vì thế cần đo, hoặc dùng cha–con.
Metadata allowed_groups dùng để làm gì và nên áp dụng ở bước nào? Để phân quyền: lọc ngay trong truy vấn tới vector store, trước khi đoạn nào được đưa vào prompt. Lọc sau khi sinh câu trả lời là quá muộn.