Hầu hết các hướng dẫn về RAG đều kết thúc ở mức notebook. Họ tải lên một vài tệp PDF được trau chuốt, chia nhỏ văn bản sau mỗi một nghìn ký tự, nhồi các đoạn văn đó vào một cơ sở dữ liệu vector, và gọi đó là một kiến trúc. Vào một chiều thứ Sáu, bản demo đó chạy hoàn hảo. Nhưng khi đưa vào vận hành thực tế (production), chính quy trình đó lại âm thầm trở thành một gánh nặng.

Điểm nghẽn thực sự trong một hệ thống truy xuất hiếm khi nằm ở mô hình hay câu lệnh (prompt). Đó chính là quá trình nạp dữ liệu (ingestion). Một pipeline RAG chỉ có thể truy xuất những gì nó được cung cấp, và nếu nguồn cấp dữ liệu bị nhiễu, lỗi thời hoặc không đầy đủ, mô hình sẽ đưa ra những câu trả lời sai lệch một cách đầy tự tin. Khi người dùng phàn nàn rằng bot đang "ảo giác" (hallucinate), lỗi thường nằm ở thượng nguồn, trong một pipeline dữ liệu mà không ai giám sát chặt chẽ.

Bẫy trên bảng trắng

Các sơ đồ kiến trúc khiến việc nạp dữ liệu trông như một mũi tên duy nhất có nhãn “Documents → Vector DB”. Thực tế thì hỗn loạn hơn nhiều. Các hệ thống nguồn thay đổi mà không báo trước. Bố cục HTML được thiết kế lại. Các URL chuyển hướng đến các trang đích chung chung. Các framework JavaScript thay đổi nội dung sau phản hồi HTTP ban đầu. Coi việc nạp dữ liệu là một tác vụ thiết lập một lần duy nhất là sai lầm đầu tiên. Đó là một vấn đề kỹ thuật dữ liệu (data engineering) liên tục, xứng đáng được xử lý nghiêm ngặt như bất kỳ pipeline ETL nào.

Tại sao các lỗi RAG thường là lỗi do nguồn cấp dữ liệu

Hãy tưởng tượng thế này: một người dùng hỏi trợ lý nội bộ của bạn về chính sách hoàn tiền hiện tại. Mô hình lấy ra đoạn văn bản hàng đầu từ kho vector và tuyên bố thời hạn là 30 ngày. Trong khi chính sách thực tế đã đổi thành 60 ngày từ quý trước. LLM không hề tự bịa ra câu trả lời sai. Nó chỉ tin vào đầu vào kém chất lượng. Lớp truy xuất đã cung cấp một trang cũ, và vì bản nhúng (embedding) trông có vẻ đủ gần về mặt ngữ nghĩa, mô hình đã coi đó là sự thật khách quan.

Mô hình này lặp đi lặp lại liên tục. Các đội ngũ tốn hàng giờ để tinh chỉnh tham số temperature và top-k trong khi kho dữ liệu (corpus) của họ đầy rẫy các chân trang điều hướng, các thông cáo báo chí trùng lặp và các đoạn văn bản bị cắt đôi các bảng biểu. Trước khi bạn tối ưu hóa việc tạo văn bản (generation), hãy kiểm tra lại những gì hệ thống của bạn được phép biết.

Bảy cái bẫy phá hủy quá trình nạp dữ liệu

1. Lần chạy đầu tiên là một lời nói dối

Một dấu tích xanh trong lần thu thập (crawl) đầu tiên hầu như không có ý nghĩa gì. Dữ liệu thực tế luôn biến động. Các trang tài liệu được cấu trúc lại, các liên kết vĩnh viễn (permalinks) của blog bị hỏng, và các sơ đồ trang web (sitemaps) âm thầm mất đi các phần. Nếu bạn chỉ xác nhận rằng pipeline đã hoàn thành mà không gặp lỗi, bạn đang "bay trong mù lòa". Bạn cần xác thực đầu ra. Hãy kiểm tra xem các tài liệu mong đợi có hiện diện hay không, cấu trúc của chúng có còn được phân tích (parse) đúng không, và tổng lượng văn bản có bị sụt giảm không vì một nguồn dữ liệu nào đó đã quyết định thay đổi cách phân trang kết quả.

2. Thu thập dữ liệu (Crawling) không phải là nạp dữ liệu (Ingestion)

Lấy dữ liệu HTML là phần dễ dàng nhất. Một lần crawl thô sẽ thu thập mọi thứ: biểu ngữ cookie, thanh bên "Các bài viết liên quan", các khối quảng cáo và thông báo bản quyền ở chân trang. Nếu bạn chia nhỏ (chunk) mã HTML thô đó một cách ngây thơ, mỗi mảnh văn bản sẽ mang theo cả những đoạn của menu điều hướng. Khi người dùng hỏi về giới hạn tốc độ API (API rate limits), bộ truy xuất có thể đưa ra một đoạn văn bản mà 40% là các liên kết ở thanh bên. Việc trích xuất sạch (clean extraction) là cực kỳ quan trọng. Bạn cần xác định vùng nội dung chính, loại bỏ các phần thừa (boilerplate) và xóa các thành phần lặp lại trên mọi trang. Nếu không, bạn không phải đang xây dựng một cơ sở tri thức, mà là đang xây dựng một công cụ tìm kiếm cho các thành phần giao diện web.

3. Chia nhỏ dữ liệu (Chunking) làm mất ý nghĩa

Chia nhỏ theo kích thước cố định (fixed-size chunking) là mặc định trong hầu hết các hướng dẫn nhanh, và nó rất nguy hiểm. Nếu bạn chia nhỏ một tài liệu thuần túy dựa trên số lượng ký tự, bạn sẽ cắt đôi các bảng, tách rời bước 4 và bước 5 trong một quy trình đánh số, và khiến các dấu đầu dòng bị tách khỏi tiêu đề của chúng. Một đoạn văn bản chỉ chứa nửa sau của một bảng giá là vô dụng về mặt ngữ nghĩa. Chia nhỏ dựa trên cấu trúc (structure-aware chunking) sẽ tôn trọng định dạng ban đầu. Hãy phân tích phân cấp tiêu đề. Giữ nguyên các bảng nếu có thể. Chia nhỏ tại ranh giới các đoạn văn dưới cùng một thẻ H2 hoặc H3. Giữ các danh sách trong cùng một đoạn nếu chúng đủ ngắn. Mục tiêu không phải là các khối có kích thước đồng đều, mà là các đơn vị ý nghĩa mạch lạc.

4. Vấn đề về độ tươi mới (Freshness)

Việc chụp một bản snapshot tĩnh của một wiki nội bộ thì khá đơn giản. Việc liên tục thu thập dữ liệu (ingesting) từ web trực tiếp mới là điều khó khăn. Bạn cần biết khi nào một trang được thu thập lần cuối, liệu nó có thay đổi kể từ đó hay không, và thông tin đó còn hiệu lực trong bao lâu. Dữ liệu lỗi thời không phải lúc nào cũng có nghĩa là một ngày tháng cũ kỹ hiển thị rõ ràng. Đôi khi một trang cập nhật nội dung văn bản nhưng vẫn giữ nguyên URL, vì vậy hệ thống của bạn sẽ không bao giờ nhận ra nếu không có việc băm nội dung (content hashing). Hãy xây dựng các quy tắc làm mới rõ ràng dựa trên mức độ biến động của nguồn dữ liệu. Một nguồn cấp dữ liệu tài chính có thể cần kiểm tra hàng giờ. Một trang giới thiệu công ty có thể chỉ cần kiểm tra hàng quý. Hãy ghi lại các mốc thời gian (timestamps) và thiết lập các giới hạn thời gian tồn tại (time-to-live), đặc biệt nếu lĩnh vực của bạn liên quan đến các hướng dẫn được quản lý chặt chẽ hoặc quan trọng về an toàn, nơi các sự thật cũ có thể gây ra tác hại thực sự.

5. Ô nhiễm trùng lặp (Duplicate Pollution)

Các trang web luôn đầy rẫy sự lặp lại. Cùng một mô tả sản phẩm xuất hiện trên trang danh mục, trang sản phẩm và trang đích quảng bá. Cùng một thông cáo báo chí tồn tại dưới các đường dẫn /news/, /press/, và /blog/. Tìm kiếm vector không tự động loại bỏ trùng lặp. Nếu có mười đoạn (chunks) gần như giống hệt nhau nằm trong cơ sở dữ liệu của bạn, chúng có thể lấn át các kết quả đa dạng và có liên quan trong quá trình truy xuất top-k. Bạn cần theo dõi nguồn chuẩn (canonical tracking) hoặc khử trùng lặp nội dung trước khi nhúng (embedding). Nếu hai đoạn nói cùng một điều, hãy giữ lại nguồn chính thống và loại bỏ các bản sao. Bộ truy xuất (retriever) của bạn có số lượng vị trí giới hạn. Đừng để chúng bị lãng phí.

6. Thiếu Metadata

Một cơ sở dữ liệu vector không có metadata chỉ là một công cụ tìm kiếm văn bản dày đặc mà không có trí nhớ về ngữ cảnh. Việc truy xuất thông minh phụ thuộc vào các tín hiệu lọc và xếp hạng mà các bản nhúng (embeddings) thô không thể cung cấp. Hãy lưu trữ URL nguồn, ngày thu thập, danh mục tài liệu và số phiên bản. Nếu bạn thu thập tài liệu API, việc quản lý phiên bản là thiết yếu. Nếu không có nó, một truy vấn có thể trộn lẫn các thông số kỹ thuật của v1 và v2 vào cùng một câu trả lời. Nếu bạn thu thập các chính sách nhân sự, việc gắn thẻ theo khu vực hoặc phòng ban sẽ cho phép bạn lọc kết quả trước khi chúng đến được mô hình. Metadata biến một đống dữ liệu văn bản thô thành một hệ thống kiến thức được chọn lọc.

7. Lỗ hổng JavaScript

Các trang web hiện đại không gửi nội dung của chúng ngay trong gói HTML đầu tiên. Chúng gửi một khung xương (skeleton) và làm đầy nó (hydrate) bằng các lệnh gọi JavaScript. Một yêu cầu HTTP cơ bản có thể sẽ không thấy gì ngoài một biểu tượng đang tải (loading spinner) và một khung bố cục (layout shell). Nếu đường ống (pipeline) của bạn không thể thực thi JavaScript, bạn sẽ thu thập các trang trống hoặc các mảnh vụn không đầy đủ mà không bao giờ nhận ra có điều gì đó không ổn. Sử dụng trình duyệt không giao diện (headless browser) giúp giải quyết vấn đề hiển thị nhưng lại nảy sinh các vấn đề mới: sử dụng bộ nhớ nặng hơn, thông lượng chậm hơn và các rào cản phát hiện bot. Hãy cân nhắc các sự đánh đổi một cách thận trọng, nhưng đừng lầm tưởng rằng một lệnh curl đơn giản là đủ cho mọi nguồn dữ liệu.

Danh sách kiểm tra thu thập dữ liệu thực tế

Nếu bạn đang xây dựng hoặc xem xét một nguồn cấp dữ liệu RAG, hãy bắt đầu tại đây:

  • Kiểm tra phạm vi nguồn và phân trang. Một sitemap có thể chỉ liệt kê mười bài viết đầu tiên trong một danh mục. Hãy thu thập sâu hơn và xác minh rằng nội dung được phân trang hoặc tải động thực sự được thu thập.
  • Loại bỏ nội dung rập khuôn (boilerplate) trước khi chia nhỏ (chunking). Loại bỏ thanh điều hướng, quảng cáo, chân trang và các tuyên bố miễn trừ trách nhiệm pháp lý lặp đi lặp lại. Nếu một cụm từ xuất hiện trên mọi trang, đó là nhiễu.
  • Sử dụng phương pháp chia nhỏ có nhận biết cấu trúc. Tôn trọng các tiêu đề, danh sách dấu đầu dòng và bảng. Chia nhỏ dựa trên các ranh giới ngữ nghĩa, không phải dựa trên số lượng ký tự.
  • Đính kèm metadata phong phú. Bao gồm URL, ngày thu thập, danh mục nội dung và phiên bản. Hãy làm cho các trường này có thể lọc được trong các truy vấn truy xuất của bạn.
  • Thiết lập tần suất làm mới dựa trên mức độ biến động của dữ liệu. Các nguồn thay đổi nhiều cần được thu thập lại thường xuyên. Các kho lưu trữ tĩnh thì không cần.
  • Giám sát kho ngữ liệu (corpus), không chỉ trạng thái công việc. Một đường ống có thể kết thúc với mã lỗi bằng 0 trong khi vẫn tạo ra dữ liệu rác. Hãy kiểm tra định kỳ các mẫu đoạn (chunks) đã lưu để phát hiện sự sai lệch (drift) và kiểm tra chất lượng.
  • Xác định các quy tắc cho việc quản lý phiên bản và xóa. Khi một trang nguồn bị xóa, hãy xóa các đoạn của nó. Khi nó được cập nhật, hãy ghi đè hoặc tạo phiên bản mới cho chúng. Dữ liệu mồ côi (orphaned data) là một "kẻ giết người thầm lặng".

Sự thật phũ phàng về Embeddings

Không có mô hình nhúng nào, dù tiên tiến đến đâu, có thể sửa chữa một tài liệu bị thiếu. Nó không thể đoán được một trang đã được cập nhật vào tuần trước nếu nguồn cấp dữ liệu của bạn vẫn giữ bản sao của năm ngoái. Nó không thể suy luận ngữ cảnh của một hàng trong bảng bị tách rời khỏi tiêu đề do ranh giới chia nhỏ (chunk boundary) không tốt. Các bản nhúng nén ý nghĩa, nhưng chúng không tạo ra ý nghĩa ở nơi mà lớp thu thập dữ liệu đã thất bại trong việc bảo tồn nó.

Chất lượng truy xuất bắt đầu từ lớp thu thập dữ liệu. Lớp đó quyết định liệu hệ thống RAG của bạn là một công cụ hữu ích hay chỉ là một kẻ nói dối đầy tự tin với một cơ sở dữ liệu vector đứng sau.

Bài học thực tế rút ra

Đừng chỉ đánh giá chất lượng nạp dữ liệu (ingestion health) thông qua các dashboard của pipeline. Các job chạy thành công và log sạch không đảm bảo rằng bạn có một kho ngữ liệu (corpus) chất lượng. Hãy mở cơ sở dữ liệu và đọc trực tiếp các chunk thực tế mà người dùng sẽ truy xuất. Nếu văn bản chứa đầy các thông báo bản quyền, các bảng bị vỡ, hay các trang chính sách đã lỗi thời, thì vấn đề không nằm ở LLM. Hãy xử lý nguồn dữ liệu trước. Mọi thứ khác chỉ là đang tinh chỉnh trên một đống rác.