
Pipeline RAG từ PDF: chunk kèm số trang và cổng kiểm
Chunk PDF giữ heading và số trang, rồi dựng cổng kiểm: golden dataset 10-50 dòng, rule check miễn phí và ba metric đo riêng retrieval.
Bạn đổ 300 trang handbook PDF vào vector store, hỏi "nhân viên được bao nhiêu ngày phép", và model trả về một đoạn nằm lọt giữa bảng lương. Không ai biết đoạn đó ở trang nào để kiểm lại, nên cũng không ai dám bật cho người dùng thật. Bài này dựng đúng hai nửa thường bị bỏ qua: chunk PDF giữ được ngữ cảnh, và cổng kiểm đo riêng retrieval với generation trước khi release.
Chunk phải mang theo heading và số trang
Một hướng dẫn mới trên dev.to mở đầu bằng nhận định đáng để lấy làm nguyên tắc thiết kế: "Large language models read Markdown far better than raw PDF text." Tác giả chuyển PDF sang Markdown sạch rồi lấy chunk sẵn sàng cho RAG kèm số trang — lưu ý là bằng API thương mại AnyToMarkdown qua RapidAPI, nên các tham số dưới đây là của dịch vụ đó, không phải chuẩn chung.
Điểm đáng chép lại là hình dạng chunk, không phải nhà cung cấp. Mỗi chunk trong hướng dẫn trông như sau:
{ "index": 1, "content": "...", "tokens": 392,
"headings": ["Employee Handbook", "2. Benefits"],
"pageStart": 2, "pageEnd": 2 }
Tác giả mô tả hành vi chia: "Chunks follow the document's sections: they never start in the middle of a table, and each one carries its heading path, so a chunk about vacation days still says it belongs to Benefits." Đây là tuyên bố của hướng dẫn về dịch vụ họ dùng; nếu bạn tự chunk bằng thư viện khác, hãy coi headings và pageStart là trường bắt buộc.
Tham số gọi trong bài là chunkSize 400, chunkOverlap 40, kèm includeContent: false để bỏ phần Markdown đầy đủ khi bạn chỉ cần chunk. Vài giới hạn vận hành được nêu thẳng: PDF scan thì để ocr ở chế độ auto hoặc ép bằng ocr=force, chỉ cần vài trang thì thêm pages=1-10, và "the free plan accepts 10 MB and 50 pages per request". Với handbook vài trăm trang, giới hạn 50 trang mỗi request quyết định bạn có phải chia job hay không.
Giữ metadata cạnh vector, đừng chỉ nhét text
Bước embed trong hướng dẫn dùng model text-embedding-3-small, và kèm một câu đáng dán lên tường: "Keep headings and pageStart next to each vector. They let your answers cite a section and page." Câu trả lời chỉ kiểm chứng được khi nó chỉ ra mục nào, trang nào.
# ví dụ minh hoạ, không phải code production
texts = [c["content"] for c in chunks]
meta = [(c["headings"], c["pageStart"]) for c in chunks]
# lưu meta cùng vector, cùng index
Golden dataset 10-50 dòng, viết tay
Nửa còn lại là release gate. Một checklist RAG khác, mới đăng, đặt vấn đề đúng chỗ: "Most RAG launch checklists say 'evaluate retrieval' and 'monitor in production'. That is true and hard to act on." Tác giả chạy checklist này trong nền tảng EvaliQA, nên tên setting và ngưỡng mặc định bên dưới là của nền tảng đó; cái đáng mang đi là thứ tự làm, không phải tên nút.
Quy mô được khuyến nghị rất cụ thể: "For a new project that means 10 to 50 rows, written by someone who knows the domain." Mỗi dòng cần input đúng cách người dùng thật gõ, một expected outcome hoặc rubric (với RAG cần expected_output vàng vì metric recall dựa vào nó), metadata tối thiểu gồm category, difficulty và source (hand, production, ticket, incident), kèm một câu lý do tồn tại.
Hai cái bẫy được nêu thẳng: "Do not bulk-generate rows and call the result golden. And never delete a row with source = incident because the bug is fixed. That row is your alarm for the day a prompt tweak or model update brings the bug back." Dòng incident chính là regression test của bạn.
Rule check trước, judge sau
Thứ tự này tiết kiệm tiền thật: "Before you pay for any judge calls, add rule checks. They are free, run in milliseconds and give the same verdict every time." Mỗi rule chấm 1 hoặc 0, nên ngưỡng mặc định 0.5 chỉ có nghĩa là rule đã giữ.
- Non-Empty cho mọi test plan — một turn timeout hoặc chỉ gọi tool có thể trả về rỗng.
- JSON Schema nếu service trả structured output: đặt
required,additionalProperties: falsevà enum để bắt sai shape chứ không chỉ bắt JSON hỏng. - Contains cho câu bắt buộc như disclaimer: ở chế độ
allnó chấm theo tỉ lệ keyword tìm thấy, nên phải đặt ngưỡng 1.0 khi cần đủ hết — "Otherwise 3 of 6 keywords passes at the default 0.5". - Contains ở chế độ
nonelàm guardrail từ cấm, nhưng khớp theo substring: "the keyword cat also matches 'category'". - Length Check với
min_length/max_lengththeo chars hoặc words, bắt hồi quy dài dòng sau khi đổi model. - Regex Match cho định dạng source id (tắt
full_match); với pattern cấm, tài liệu gợi ý negative lookahead dạng(?s)^(?!.*\d{16}).*$, chỉ pass khi không có chuỗi 16 chữ số.
Cảnh báo kèm theo: pattern \d+ sẽ pass bất kỳ câu trả lời nào có chữ số, nên hãy thử regex trên output thật trước khi commit.
Ba metric, và cách đọc khi số xấu
| Metric | Ngưỡng mặc định | Đo gì |
|---|---|---|
| Answer Precision | 0.8 | Đối chiếu nghiêm với expected_output |
| Faithfulness | 0.7 | Mọi claim phải được retrieval_context đỡ |
| Contextual Recall | 0.7 | Retrieval có lấy về đúng thứ câu trả lời vàng cần không |
Giá trị của bộ ba nằm ở chỗ nó tách được hai lỗi hay bị gộp: "If recall is low, no prompt work will fix the answer. If recall is high and precision is low, look at generation." Recall thấp mà ngồi sửa prompt là mất buổi chiều vô ích.
Ba chỉnh sửa còn lại nên áp dụng ngay từ ngày đầu. Nâng Faithfulness lên 0.9 hoặc 0.95 nếu bạn làm y tế, pháp lý hay tài chính, nơi mọi claim không có nguồn đều là bug. Đừng gắn cả sáu metric RAG cùng lúc, vì "the pass-rate signal turns into noise". Và nếu bạn retrieve 20 chunk nhưng chỉ đưa 5 vào model, hãy cap top_k của Contextual Precision ở 5 để metric phản ánh đúng thứ model thật sự nhìn thấy.
Một chi tiết nhỏ mà hỏng là hỏng cả phép đo: pin model làm judge, đừng xoay vòng giữa các lần chạy cần so sánh — "The judge is part of the measurement instrument." Tương tự khi nối endpoint của bạn vào runner: import request từ cURL, HAR hay Postman rồi phải thay câu hỏi đã capture bằng biến {{input}}, nếu không "every row sends the same text and the scores mean nothing".
Làm gì trong tuần này
- Chạy lại pipeline ingest hiện tại và kiểm một chunk ngẫu nhiên: nó có heading path và số trang chưa? Nếu chưa, sửa chỗ đó trước khi đụng tới prompt.
- Viết tay 10 dòng golden đầu tiên cho các luồng nghiệp vụ đã có tài liệu, mỗi dòng kèm source và một câu lý do tồn tại.
- Bật rule check trước, đo một vòng, rồi mới gắn ba metric judge — và ghi lại model judge bạn đã pin để lần sau còn so được.
Không spam, hủy đăng ký bất kỳ lúc nào.
Bài viết liên quan

Chạy Beam 501B: tự host GPU hay gọi qua AI Gateway?
16 thg 10, 2026
Hóa đơn LLM tăng vọt: 4 chỗ rò tiền và số đo cắt token
16 thg 10, 2026