"Build RAG chatbot trong 15 phút" - và 6 tháng tiếp theo để nó ngừng nói dối - <Cảnh báo bài dài nhé> =))
Tutorial nào trên mạng cũng hứa vậy. Và họ không sai: 15 phút là ra demo thật.
Nhét PDF vào vector database → similarity search → lấy top 5 đoạn văn → đưa vào prompt → xong. Demo chạy mượt, sếp vỗ tay, chụp màn hình đăng khoe.
Rồi user thật bước vào:
Upload một hợp đồng scan* - hệ thống đọc được... 0 ký tự. Vector store trống trơn nhưng vẫn báo "Indexed thành công" 🙃
Gõ "NĐ 13"* - nhưng tài liệu ghi "Nghị định 13/2023/NĐ-CP". Vector search: không tìm thấy.
Hỏi một câu không có trong tài liệu - AI tự tin bịa* một câu trả lời nghe cực kỳ thuyết phục.
Mình đang ở tháng thứ N của cái "15 phút" đó - khi build Mochi Flow, platform quản trị tài liệu và ứng dụng AI Agent tích hợp RAG mình đang phát triển. Chia sẻ lại 5 bài học xươ.ng m.áu:
1️⃣ Ingestion - trận chiến bắt đầu từ TRƯỚC khi AI xuất hiện
Tutorial mặc định tài liệu của bạn là file text sạch đẹp. Thực tế ở doanh nghiệp Việt Nam: công văn scan có mộc đỏ đè lên chữ, hợp đồng chụp bằng điện thoại hơi nghiêng, báo cáo PDF layout 2 cột, bảng số liệu nằm trong... ảnh.
Những gì mình phải xử lý trong Mochi Flow:
→ PDF không phải là MỘT loại file. Có PDF text, PDF scan, và loại lai - vài trang có chữ, vài trang là ảnh. Giải pháp: phân tích text density từng trang (đếm mật độ ký tự) rồi phân loại: trang text → extract thẳng, trang scan → đẩy qua OCR, trang lai → chạy cả hai rồi merge. Bỏ bước này là dính ngay ca "index thành công 0 ký tự" ở trên.
→ OCR truyền thống chết với tài liệu Việt. Dấu tiếng Việt nhận sai (ơ thành o là nghĩa khác luôn), bảng biểu vỡ cấu trúc hàng-cột, mộc đỏ và chữ ký đè lên text. Đây là lúc Vision model vào việc: thay vì "nhận diện ký tự", nó đọc cả trang như con người - hiểu layout, đọc được bảng, tự bỏ qua con mộc. Chất lượng khác hẳn OCR cổ điển.
→ Nhưng không phải tài liệu nào cũng được phép gửi ra ngoài. Hợp đồng mật mà đẩy lên API bên thứ ba là vi phạm chính sách dữ liệu. Nên mình build OCR pool 2 chế độ: tài liệu confidential → chạy Vision model local/self-hosted, tài liệu thường → gọi API vision cho nhanh và rẻ. Kèm cost tracking từng trang - vì Vision model không miễn phí, đốt vô tội vạ là hết budget.
Bài học: ingestion bẩn thì retrieval xịn mấy cũng vô nghĩa. Rác vào từ cửa, không phải từ model.
2️⃣ Retrieval chuẩn không phải là một câu lệnh search — nó là một pipeline
Kiến trúc mình đang chạy trong Mochi Flow, mỗi tầng sinh ra để vá một lỗi có thật:
→ Chunking đúng trước đã. Cắt tài liệu sai là mọi thứ phía sau vô nghĩa. Hợp đồng, slide, báo cáo tài chính - không thể cắt cùng một kiểu. Mình phải làm 6 chiến lược (semantic, parent-child, hierarchical...) tùy loại tài liệu.
→ Hybrid Search, không phải vector thuần. BM25 (keyword) bắt được "NĐ 13", dense vector bắt được ngữ nghĩa, rồi RRF fusion trộn 2 kết quả. Thiếu 1 trong 2 là hụt.
→ Reranking. Top-5 của vector search thường dính 2-3 đoạn lạc đề. Thêm tầng rerank chấm điểm lại trước khi đưa vào prompt — chất lượng câu trả lời nhảy vọt.
→ Query classification. Câu tra cứu, câu so sánh, câu tổng hợp cần chiến lược retrieval khác nhau. Hệ thống phải phân loại intent trước rồi mới đi tìm.
→ Multi-hop reasoning. Câu hỏi phức tạp thì tự động tách thành các sub-query nhỏ, trả lời từng phần rồi tổng hợp.
→ Self-reflection. Retrieval kém thì AI phải tự viết lại query và tìm lại, không được trả lời liều.
→ Grounding verification. Tầng cuối: dùng model NLI kiểm tra từng câu AI trả lời có thật sự nằm trong tài liệu không, tô màu độ tin cậy cho user thấy.
Bài học: 80% chất lượng RAG nằm ở ingestion + retrieval, không phải ở model. Đổi lên model xịn hơn không cứu được dữ liệu đầu vào tồi. Garbage in - confident garbage out.
3️⃣ Chọn model: không có "model tốt nhất" - chỉ có model đúng việc cho từng tầng
Sai lầm phổ biến: nghĩ RAG = 1 con LLM to. Thực tế một câu hỏi trong Mochi Flow đi qua 4-5 model khác nhau, mỗi tầng một con chuyên dụng:
→ Embedding: model local qua Ollama hoặc embedding API của cloud provider - tùy chế độ triển khai. Bài học đắt giá nhất: đổi embedding model giữa chừng = re-index lại toàn bộ kho tài liệu (vector dimension khác nhau là không query được). Chọn kỹ từ đầu, đây là quyết định khó đảo ngược nhất trong cả kiến trúc.
→ Reranking - chạy 2 tầng để tiết kiệm: tầng lọc thô dùng bi-encoder multilingual nhỏ (MiniLM) quét nhanh hàng trăm ứng viên, tầng tinh mới dùng cross-encoder (Qwen3-Reranker 0.6B) chấm kỹ top còn lại. Cross-encoder chính xác nhưng chậm - cho nó chấm cả trăm đoạn là user ngồi đợi mòn mỏi.
→ Grounding (NLI): mình chọn mDeBERTa-v3 multilingual - không phải vì nó "xịn nhất", mà vì nó là model hiếm hoi có số đo cụ thể trên tiếng Việt và rất nhẹ, chạy local thoải mái. Và vẫn đang tìm model xịn hơn thay thế.
→ Generation (LLM): tầng DUY NHẤT cần model to - và phải hot-swap được: dữ liệu mật chạy LLM local qua Ollama, việc thường gọi cloud (Gemini/GPT/Claude) cho nhanh. User không cần biết, hệ thống tự route.
→ OCR/Vision: pool 2 chế độ như phần 1.
Nguyên tắc rút ra: model nhỏ chuyên dụng ở mọi tầng giữa, model to chỉ đứng ở tầng cuối. Tầng nào cũng gọi LLM to thì mỗi câu hỏi vừa chậm vừa đốt tiền gấp 10 - mà chất lượng không hơn.
Và nỗi đau mang tên tiếng Việt 🇻🇳
Mọi benchmark bạn đọc trên mạng đều đo bằng tiếng Anh. Đem về chạy tiếng Việt là lộ ra ngay:
→ BM25 mặc định tách từ theo khoảng trắng - với tiếng Anh thì ổn, nhưng tiếng Việt "khách hàng", "học máy" là MỘT từ gồm hai âm tiết. Tách rời ra là ra nghĩa khác, ranking sai hết. Mình phải thêm tầng word segmentation (underthesea, fallback sang pyvi) trước khi cho BM25 chạy.
→ Model nào cũng phải hỏi lại một câu: "có multilingual không, và có SỐ ĐO trên tiếng Việt không?" Embedding, reranker, NLI - mỗi lựa chọn trong Mochi Flow đều lọc qua câu hỏi đó. Thậm chí reranker mình để config được: corpus thuần Việt thì swap sang model reranker chuyên tiếng Việt.
→ OCR tiếng Việt có dấu - như phần 1, Qwen OCR, VietOCR và Vision model đã cứu.
Bài học: stack RAG chuẩn quốc tế chưa chắc chạy tốt ở Việt Nam. Tiếng Việt không khó - nhưng nó hay bị bỏ quên.
4️⃣ RAG không chỉ là "AI search tài liệu"
Trả lời câu hỏi mới là mức 1. Mức 2 là Agentic RAG: gắn kho tri thức vào AI Agent.
Agent kiểu ReAct tự lập kế hoạch: câu này cần tìm ở collection nào, cần search mấy vòng, thiếu dữ liệu thì gọi tool gì. Trong Mochi Flow, mình gắn RAG vào workflow engine — và lúc đó tri thức bắt đầu hành động:
* Đọc tài liệu → tự sinh báo cáo, slides mindmap, quiz
* Nhận câu hỏi từ Telegram/Slack/Zalo → tra kho tri thức → trả lời khách kèm nguồn
Hành động rủi ro (gửi mail, cam kết với khách) → dừng lại chờ người duyệt (human-in-the-loop*)
Search là tính năng. Agent + tri thức + quy trình mới là giải pháp.
5️⃣ Doanh nghiệp không mua "AI thông minh" — họ mua Giải pháp toàn diện
Đây là phần các tutorial không dạy, nhưng chiếm nhiều thời gian build nhất. Mỗi pain point thật = một thứ phải build:
→ "Nhân viên không tìm được tài liệu, toàn hỏi nhau qua Zalo" Chat thẳng với kho tri thức, mỗi câu trả lời kèm citation trỏ đúng trang nguồn.
→ "Sợ AI bịa, lỡ hứa sai với khách thì ai chịu?" Grounding verification + nguyên tắc sắt: không thấy trong tài liệu thì nói không biết, không sáng tác.
→ "Kế toán không được đọc tài liệu của HR" Phân quyền RBAC đến từng kho tài liệu, user isolation tuyệt đối, tài liệu gắn nhãn mật riêng.
→ "Ai đã hỏi gì, AI đã trả lời gì - kiểm được không?" Audit log chống sửa đổi, đủ chuẩn mang đi compliance.
→ "Tài liệu nằm rải rác Drive, SharePoint, phần mềm kế toán" Connectors đồng bộ thẳng về kho tri thức, tự re-index khi file thay đổi.
Làm xong hết mới ngộ ra: phần "AI thông minh" là phần dễ nhất. Phần khó là biến nó thành thứ doanh nghiệp dám giao việc.
Đó là những gì mình học được sau nhiều tháng build Mochi Flow. Series này mình sẽ bóc dần từng tầng — tầng nào anh em muốn nghe sâu trước: ingestion/OCR, chọn model, hybrid search hay agentic RAG?
Còn ai đang build RAG production rồi: cái gì làm bạn mất ngủ nhất - tài liệu scan, retrieval, tiếng Việt hay hallucination? 👇
P/S: Đừng vội đổ lỗi cho model. Model chỉ tóm tắt lại những gì bạn đưa cho nó. Đưa rác - nhận lại rác, kèm thái độ cực kỳ tự tin.