Chia nhỏ văn bản cho embedding và RAG — cắt theo token

Chia văn bản dài thành các đoạn đo bằng token có chồng lặp, phục vụ embedding và RAG. Cắt tại ranh giới câu và đoạn, không bao giờ giữa từ, và hiện rõ từng chỗ cắt.

Chia nhỏ văn bản cho embedding: chỗ đặt nhát cắt quyết định thứ bạn tìm lại được

Hệ thống truy xuất hỏng một cách lặng lẽ. Bạn nhúng cả kho tài liệu, dựng tìm kiếm, đặt câu hỏi, rồi nhận về thứ gần đúng chủ đề nhưng không trả lời được câu hỏi. Phản xạ đầu tiên là đổ lỗi cho mô hình embedding hoặc cho độ đo tương đồng. Nhưng phần lớn trường hợp, vấn đề đã xảy ra trước cả hai thứ đó: văn bản bị cắt thành những mảnh mà mỗi mảnh không chứa trọn một ý, và không thuật toán xếp hạng khéo léo nào cứu lại được ý nghĩa đã bị phá hủy ngay lúc cắt.

Công cụ này chia văn bản thành các khối bị giới hạn theo token, có phần chồng lấn, và cắt tại ranh giới có ý nghĩa nhất còn dùng được thay vì tại một vị trí cố định. Nó cho bạn thấy từng ranh giới, bởi một nhát cắt tồi chỉ lộ ra khi đọc chỗ khối này kết thúc và khối kia bắt đầu. Mọi thứ chạy trong tab trình duyệt này.

Cắt theo ranh giới, kiểu đệ quy

Cách ngây thơ là cứ N token cắt một nhát. Nhanh, và sai, bởi ranh giới token chẳng liên quan gì tới ranh giới ngữ nghĩa — bạn sẽ cắt giữa câu, đôi khi giữa một từ, và vector thu được mô tả một mẩu chữ không ai từng viết ra.

Cách làm ở đây đi xuống theo một cái thang các dấu phân tách, dùng dấu đầu tiên tạo ra được những mảnh đủ nhỏ để vừa. Theo thứ tự: trước một tiêu đề Markdown, rồi dòng trống, rồi bất kỳ ký tự xuống dòng nào, rồi ranh giới câu, rồi ranh giới mệnh đề tại dấu phẩy và dấu chấm phẩy, rồi khoảng trắng giữa các từ. Chỉ khi không còn thứ nào trong số đó — một bundle đã nén, một từ khổng lồ duy nhất — nó mới lùi về cắt theo chỉ số token.

Hiệu quả thực tế là cấu trúc được giữ lại chừng nào văn bản còn có cấu trúc. Tài liệu có tiêu đề thì cắt theo mục. Văn xuôi cắt theo đoạn, hoặc theo câu khi một đoạn quá dài. Một bức tường chữ không ngắt thì cắt theo từ. Mỗi khối kết thúc ở chỗ một con người cũng sẽ kết thúc, trừ khi văn bản không cho cơ hội nào như thế.

Phần chồng lấn thực ra để làm gì

Chồng lấn nghĩa là lặp lại phần đuôi của khối này ở đầu khối kế tiếp. Nó tồn tại vì một dữ kiện và thứ mà dữ kiện đó nhắc tới thường nằm hai bên một nhát cắt. “Đợt migration chạy suốt đêm. Nó hỏng ở bảng thứ ba.” Cắt giữa hai câu đó thì khối sau chứa một sự cố không có chủ ngữ, còn khối trước chứa một đợt migration không có kết cục. Cả hai đều không truy xuất ra được gì hữu ích. Có chồng lấn, khối thứ hai mang theo tiền ngữ.

Cái giá là trùng lặp: các token chồng lấn bị lưu, bị nhúng và bị tìm kiếm nhiều hơn một lần. Mười đến mười lăm phần trăm kích thước khối là mức thỏa hiệp thông thường, và đó cũng là chỗ các thiết lập sẵn đứng. Bảng thống kê báo chính xác phần chồng lấn đã thêm bao nhiêu token, nên sự đánh đổi là một con số chứ không phải một cảm giác.

Có một cái chốt đáng biết: nếu phần chồng lấn bằng hoặc lớn hơn kích thước khối thì mỗi khối sẽ bắt đầu bằng đúng những gì khối trước kết thúc, và quá trình không bao giờ tiến lên. Trường hợp đó bị kẹp lại thay vì để nó treo.

Chọn kích thước khối

Kích thước khối là đánh đổi giữa độ chính xác và ngữ cảnh, và không có câu trả lời đúng cho mọi trường hợp:

  • Khối nhỏ, quanh 256 token, cho kết quả khớp sắc nét. Một vector trải trên hai ba câu bị chi phối bởi đúng những gì các câu đó nói. Hợp với hỏi đáp trên tài liệu tra cứu dày đặc thông tin. Rủi ro là những khối khớp rất tốt nhưng thiếu ngữ cảnh xung quanh để thực sự trả lời.
  • Khối vừa, quanh 512, là mặc định phổ biến và là chỗ phần lớn mô hình embedding thấy dễ chịu nhất. Đại khái bằng một đoạn văn có sức nặng.
  • Khối lớn, từ 1024 trở lên, mang nhiều ngữ cảnh hơn cho mỗi lần trúng nhưng làm loãng vector. Một khối phủ bốn chủ đề nằm ở trung bình của cả bốn và không khớp mạnh với chủ đề nào.
  • Khối rất lớn, vài nghìn token, thôi làm đơn vị truy xuất mà trở thành đơn vị phân trang — để đẩy một tài liệu dài qua mô hình theo từng lượt nối tiếp chứ không phải để tìm kiếm trong đó.

Bốn chiến lược chia nhỏ văn bản

Chia thông minh chính là cái thang đệ quy mô tả ở trên, và hợp với phần lớn văn xuôi. Theo đoạn văn gom trọn từng đoạn và không bao giờ xẻ đôi một đoạn, hợp với những văn bản mà đoạn văn vốn đã là đơn vị của một ý và bạn thà chấp nhận khối to nhỏ không đều còn hơn một đoạn bị cắt. Theo tiêu đề Markdown cho mỗi mục một khối, lựa chọn tự nhiên cho tài liệu kỹ thuật nơi tiêu đề gọi đúng tên thứ nằm bên dưới nó. Theo dòng gom trọn từng dòng, dành cho log, bản trích CSV và danh sách nơi mỗi dòng là độc lập.

Ở mọi chế độ, đơn vị nào vẫn vượt giới hạn đều được bộ chia đệ quy rút nhỏ trước khi đóng gói, nên việc chọn chiến lược không bao giờ sinh ra một khối quá khổ.

Đếm token, và con số đó có nghĩa gì

Token được đếm bằng đúng bộ tokenizer mà trang này dùng để đo kích thước kho mã, thuộc họ mà các mô hình OpenAI sử dụng. Con số cho Claude và Gemini lệch đôi chút — từ vựng khác nhau thì cùng một đoạn chữ bị cắt khác nhau — nhưng bám nhau đủ sát để đo một khối so với một giới hạn. Nếu ngân sách của bạn eo hẹp, hãy chừa dư ra thay vì coi con số là tuyệt đối.

Cũng nên nhớ rằng giới hạn công bố của một mô hình embedding là điểm cắt cứng chứ không phải mục tiêu để nhắm tới. Vượt qua nó thì phần đuôi bị bỏ đi trong im lặng — một kiểu hỏng không sinh ra lỗi nào và làm chất lượng truy xuất tệ đi theo cách thật sự khó chẩn đoán.

Kết quả đưa thẳng vào chỗ khác được

Ba kiểu tải xuống. Văn bản thuần với tiêu đề --- Chunk n/N --- tùy chọn, để đọc và kiểm tra. JSON dưới dạng mảng các đối tượng có chỉ số, số token và nội dung, cho một script nuốt trọn cả mớ. JSONL mỗi dòng một đối tượng, đúng thứ mà quy trình nạp theo luồng và phần lớn endpoint nhúng theo lô mong đợi, và là thứ bạn cần khi kho tài liệu quá lớn để giữ trong bộ nhớ dưới dạng một mảng đã phân tích.

Việc này ghép rất tự nhiên với phần còn lại của trang: chuyển một kho mã, một PDF, một trang web hay một bản ghi thành văn bản trước, rồi chia nhỏ kết quả ở đây. Không có gì được tải lên ở bước nào cả.