AI Core cho lập trình viên/Mini-challenge: chọn kiến trúc cho ba bài toán thật
44/54
Bài 44 / 54~20 phútEmbedding, tìm kiếm & RAGMiễn phí lượt xem

Mini-challenge: chọn kiến trúc cho ba bài toán thật

Ba bài toán nghe giống nhau — chatbot policy, tài liệu kỹ thuật, trợ lý tester. Tự chọn prompt-only / RAG / fine-tune; có bài không cần RAG.

TL;DR: Kiến trúc cho một hệ hỏi-đáp trên tài liệu không quyết bởi "có tài liệu hay không", mà bởi ba trục: kho tài liệu có vượt cửa sổ context không, truy vấn là ngôn ngữ tự nhiên hay định danh chính xác, và dữ liệu đổi bao lâu một lần. Bài này cho ba scenario ngân hàng để bạn tự áp ba trục đó — chọn giữa prompt-only, RAG, RAG hybrid và fine-tune — rồi liệt kê rủi ro và ước lượng chi phí, trước khi xem lời giải.

Bạn là tech lead một đội nền tảng nội bộ ngân hàng. Sáng thứ Hai, ba ticket cùng rơi vào backlog, cả ba đều mở đầu bằng đúng một câu: "Mình muốn làm con chatbot AI cho...". Sếp muốn mỗi ticket một đề xuất kiến trúc kèm rủi ro và ước tính chi phí, trước chiều.

Cả ba nghe giống nhau — đều là "hỏi đáp trên tài liệu công ty". Nhưng nếu bạn trả lời cả ba bằng cùng một kiến trúc, ít nhất một cái sẽ hoặc đội chi phí vô ích, hoặc hỏng ở đúng loại câu hỏi quan trọng nhất. Bài này bắt bạn phân biệt chúng.

🎯 Đề bài — ba ticket

Với mỗi ticket, bạn phải trả ba thứ: (1) kiến trúc — prompt-only, RAG, RAG hybrid, hay fine-tune; (2) ước lượng chi phí thô — kho tài liệu rơi vào bậc bao nhiêu token, có vừa context không; (3) rủi ro — hai tới ba rủi ro cụ thể của lựa chọn đó.

Ticket A — Chatbot tra cứu policy nhân sự

  • Kho: ~500 văn bản policy (nghỉ phép, thai sản, công tác phí…), mỗi văn bản 3–8 trang, viết bằng tiếng Việt.
  • Cập nhật: sửa vài lần mỗi năm khi quy định đổi; câu trả lời sai điều khoản là rủi ro tuân thủ.
  • Truy vấn: ngôn ngữ tự nhiên — "nghỉ thai sản được bao nhiêu ngày theo policy 2024?". Người dùng cần biết câu trả lời dựa trên điều khoản nào.

Ticket B — Trợ lý tra cứu tài liệu kỹ thuật

  • Kho: runbook vận hành + wiki kỹ thuật của đội, vài nghìn trang.
  • Cập nhật: wiki đổi liên tục — mỗi lần deploy hoặc sự cố lại có người sửa.
  • Truy vấn: xoay quanh định danh chính xác"lỗi ERR-PAYMENT-4023 xử lý thế nào?", "endpoint /api/v2/transfers nhận field gì?", "bug này fix ở version nào?".

Ticket C — Trợ lý cho tester trong đợt UAT

  • Kho: ~150 test spec của một đợt UAT, mỗi spec trung bình nửa trang, đặc bảng, mã ID và số liệu.
  • Cập nhật: cố định suốt đợt test (2–3 tuần), không sửa giữa chừng.
  • Truy vấn: ngôn ngữ tự nhiên — "test case nào cover luồng chuyển khoản liên ngân hàng?", "spec cho hạn mức giao dịch nằm ở đâu?".

🧭 Bộ công cụ quyết định

Đây là các tiêu chí bạn đã học rải rác trong module, gom lại thành một khung. Khung cho tiêu chí, không cho sẵn đáp án của ba ticket — việc ghép là của bạn.

Kiến trúcChọn khiCái giá phải trả
Prompt-only (nhét thẳng tài liệu vào context)Toàn bộ tài liệu liên quan vừa cửa sổ context và ổn định trong một phiênTrả token cho cả kho mỗi request; kho lớn thì bất khả thi
RAG (retrieve top-k rồi mới sinh)Kho vượt context, hoặc đổi thường xuyên, hoặc cần trích nguồnHạ tầng vector store + một điểm hỏng mới: retrieval
RAG hybrid (keyword + vector)Như RAG, nhưng truy vấn xoay quanh định danh chính xácPhức tạp hơn: hai đường tìm kiếm phải hoà kết quả
Fine-tuneĐổi giọng/định dạng/hành vi đầu raĐắt, phải train lại khi dữ liệu đổi; không nạp được sự kiện đáng tin

Ba câu hỏi lọc, theo thứ tự:

  1. Kho có vượt cửa sổ context không? Ước lượng: (số văn bản × độ dài trung bình) × hệ số token tiếng Việt. Nếu không vượt và dữ liệu tĩnh trong phiên → cân nhắc prompt-only trước khi nghĩ tới RAG.
  2. Truy vấn là ngôn ngữ tự nhiên hay định danh chính xác? Nếu trung tâm là mã/tên/version → semantic thuần sẽ trượt, cần hybrid.
  3. Dữ liệu đổi bao lâu một lần? Đổi thường xuyên nghiêng về RAG (re-index rẻ); fine-tune phải train lại nên gần như luôn thua ở trục này.

Nhớ lại bài tokenization: cùng nội dung, tiếng Việt tốn gấp rưỡi tới hơn gấp đôi token so với tiếng Anh. Khi ước lượng kho tài liệu tiếng Việt có vừa context không, đừng dùng quy tắc "4 ký tự = 1 token" của tiếng Anh — nó cho ra con số lạc quan quá mức.

✍️ Phần việc của bạn

Trước khi cuộn xuống, viết ra — trên giấy hoặc trong đầu, nhưng phải thành câu — cho cả ba ticket:

  • Kiến trúc bạn chọn, và một câu vì sao.
  • Ước lượng thô: kho rơi vào bậc bao nhiêu token, có vừa cửa sổ context của một model chat phổ biến không.
  • Hai tới ba rủi ro cụ thể cho lựa chọn đó.

RAG hỏng theo bốn kiểu có tên chuẩn (benchmark RGB — xem phần Mở rộng): noise robustness (retrieve nhiễu), negative rejection (không biết từ chối khi thiếu dữ liệu), information integration (không gộp được nhiều nguồn), counterfactual robustness (tin tài liệu sai). Bài rag-failure-modes mổ từng cái; ở đây dùng bốn tên này làm danh sách kiểm khi liệt kê rủi ro cho mỗi ticket.

Với mỗi ticket, hãy tự hỏi kiến trúc đơn giản nhất nào đáp ứng đủ ba trục ở trên trước khi mặc định RAG chỉ vì nó là chủ đề của module.

Câu hỏi thứ tư — chẩn đoán hai lỗi: hai hệ đã lên production, mỗi hệ báo một lỗi. Với mỗi lỗi, tự trả lời: hỏng ở bước retrieval hay generation, và ứng với failure mode nào — trước khi xem lời giải.

Cả hai đều là hệ RAG của Ticket A:

  • Lỗi 1: hỏi "nghỉ thai sản theo policy 2024", bot trả lời đúng định dạng nhưng trích policy 2022 (đã hết hiệu lực).
  • Lỗi 2: hỏi về "chế độ nghỉ không lương dài hạn" — một chính sách công ty chưa ban hành. Kho không có đoạn nào khớp, nhưng bot vẫn trả một câu chi tiết, tự tin (kèm số ngày, điều kiện cụ thể) như thể policy đó có thật.

Hai lỗi nghe giống nhau ("bot trả sai") nhưng nằm ở hai bước khác nhau của pipeline. Phân biệt được chúng mới là chẩn đoán.

💡 Gợi ý

Không có con số hay lựa chọn nào ở đây — chỉ là câu hỏi để bạn tự lần ra đáp án. Nếu chưa viết xong phần việc ở trên, quay lại làm trước.

  • Ước lượng token: với mỗi ticket, kho tài liệu nhân ra khoảng bao nhiêu token (nhớ hệ số tiếng Việt)? Con số đó lớn hơn hay nhỏ hơn cửa sổ context của một model chat 2026? Câu trả lời quyết định prompt-only còn khả thi hay không.
  • Loại truy vấn: với mỗi ticket, câu hỏi của người dùng chủ yếu là ngôn ngữ tự nhiên hay xoay quanh một chuỗi định danh chính xác? Bài semantic-vs-keyword nói gì về loại thứ hai?
  • Cập nhật: dữ liệu mỗi ticket đổi bao lâu một lần? Nếu đổi, kiến trúc bạn chọn cập nhật bằng cách nào — re-index, sửa prompt, hay train lại toàn bộ?
  • Phản chứng: với từng ticket, thử hỏi ngược "nếu bỏ retrieval đi thì hỏng chỗ nào?" — và trả lời trung thực.
  • Rủi ro: nhìn lại bốn failure mode ở bài rag-failure-modes — cái nào áp cho đúng kiến trúc bạn vừa chọn cho từng ticket?
  • Chẩn đoán: nhớ lại ở bài rag-failure-modes, "hỏng ở retrieval" khác "hỏng ở generation" thế nào — rồi hỏi mỗi lỗi thuộc bên nào. (Bốn failure mode chỉ mô tả một trong hai bước đó.)

✅ Lời giải

Không có một đáp án "đúng tuyệt đối" — có lập luận vữnglập luận hổng. Dưới đây là lập luận vững cho từng ticket; nếu bạn chọn khác nhưng nêu được đánh đổi tương đương thì vẫn đạt.

Ticket A → RAG (semantic là đủ), kèm trích nguồn

Ước lượng: 500 văn bản × 3–8 trang, nhân hệ số token tiếng Việt, rơi vào bậc vài triệu token — chắc chắn vượt cửa sổ ~200 nghìn token phổ biến, và xấp xỉ hoặc vượt cả cửa sổ 1–2M của những model lớn nhất 2026, nên bắt buộc phải retrieve chứ không nhét thẳng được. Kể cả nếu có model vừa đủ, nhét vài triệu token mỗi request cũng bất khả thi về chi phí lẫn latency. (Cửa sổ context version-sensitive, kiểm lại theo model thật.)

Vì sao RAG: kho vượt context (bắt buộc retrieve), dữ liệu đổi vài lần/năm (RAG cập nhật bằng re-index, không train lại), và cần trích nguồn (RAG trả kèm đoạn gốc để người dùng kiểm điều khoản). Truy vấn là ngôn ngữ tự nhiên nên semantic search hợp — chưa cần hybrid.

Vì sao KHÔNG fine-tune: đây là cái bẫy hay gặp nhất. Fine-tune không nạp được sự kiện đáng tin — model đã fine-tune trên policy vẫn bịa số ngày, tên điều khoản. Kiến thức tuân thủ cần grounding lúc suy luận (đưa đúng đoạn vào context), không phải nhồi vào trọng số. Đây là kết luận có nghiên cứu, không phải ý kiến: fine-tune không giám sát thua RAG ở cả kiến thức cũ lẫn kiến thức mới (Ovadia et al., EMNLP 2024).

Rủi ro:

  • Retrieve sai điều khoản → trả lời sai một cách tự tin (rác vào, rác ra). Với dữ liệu tuân thủ, một câu sai điều khoản là sự cố thật.
  • Không biết từ chối khi không có policy nào khớp — model bịa thay vì nói "không tìm thấy". Đây là negative rejection trong taxonomy RGB.
  • Prompt injection (OWASP LLM01:2025, đứng số 1): nếu một văn bản policy chứa câu chèn lệnh, nội dung đó vào thẳng context — RAG làm tăng bề mặt tấn công vì kéo tài liệu không kiểm soát vào prompt.

Ticket B → RAG hybrid (keyword + vector)

Vì sao hybrid, không vector thuần: đây là cái bẫy trung tâm của bài. Truy vấn xoay quanh định danh chính xácERR-PAYMENT-4023, /api/v2/transfers, số version. Semantic search là điểm mù ở đúng loại này: nó tìm "gần nghĩa", mà một mã lỗi thì không có "gần nghĩa" — chỉ có trùng hoặc không. Vector thuần sẽ trả về runbook nghe giống thay vì đúng mã.

Với mã lỗi, số hoá đơn, tên endpoint, version, keyword/BM25 thắng vì đây là bài toán khớp chuỗi chính xác chứ không phải gần nghĩa — một mã lỗi không có "gần đúng", chỉ trùng hoặc không. Bài semantic-vs-keyword đi sâu vào ranh giới này; hybrid chạy song song hai đường rồi hoà điểm để ăn cả hai.

Ước lượng & cập nhật: vài nghìn trang × hệ số token tiếng Việt rơi vào bậc vài triệu token — vượt context, bắt buộc retrieve. Wiki đổi liên tục → RAG re-index hợp, fine-tune vô lý (train lại mỗi lần đổi là bất khả thi).

Rủi ro:

  • Chọn vector thuần → miss thẳng ở truy vấn quan trọng nhất (đúng mã lỗi). Rủi ro số một chính là chọn sai retrieval.
  • Noise robustness (RGB): retrieve nhầm một runbook gần nghĩa nhưng khác mã → dẫn tester đi sai hướng.
  • Hoà điểm keyword và vector không có công thức phổ quát — phải đo trên truy vấn thật của đội (đúng tinh thần "đo trên dữ liệu của mình" ở bài embedding).

Ticket C → Prompt-only (nhét thẳng vào context) — KHÔNG RAG

Ước lượng: 150 spec × nửa trang, nhân hệ số token tiếng Việt, lại đặc token hơn văn xuôi vì đầy bảng/mã ID/số liệu (bài tokenization đã đo: code và số cắt vụn gần gấp đôi mật độ token) — cộng lại rơi vào bậc ~60–100 nghìn token. Đó vừa cửa sổ context 200 nghìn token trở lên của model chat phổ biến 2026 (version-sensitive, kiểm lại theo model thật). Kho tĩnh suốt đợt UAT.

Vì sao KHÔNG RAG: đây là bẫy ngược — "có tài liệu ⇒ phải RAG" là sai. Khi toàn bộ kho vừa context và không đổi, nhét thẳng tất cả spec vào prompt cho ra kết quả đơn giản hơn và đáng tin hơn: không vector store, không bước retrieve, nên không có điểm hỏng retrieval. Thêm RAG ở đây là thêm hạ tầng và một nguồn lỗi mới để đổi lấy… không gì cả.

Rủi ro & đánh đổi:

  • Lost in the Middle: khi context gần đầy, model dễ bỏ qua thông tin nằm giữa prompt — hiệu năng cao nhất khi thông tin liên quan ở đầu hoặc cuối (Liu et al., TACL 2024). Nếu kho gần chạm trần context, lọc bớt spec không liên quan trước khi nhét.
  • Chi phí token mỗi request: nhét full kho mỗi lần hỏi là tốn. Đây là một trục ngoài khung ba câu hỏi — nếu lưu lượng truy vấn rất cao suốt đợt, chi phí token lặp lại mới là lúc RAG bắt đầu đáng giá (giảm token/request). Nói cách khác, ngay cả khi ba trục chỉ về prompt-only, một trục chi phí thứ tư vẫn có thể lật ngược ở quy mô lớn.

Câu hỏi chẩn đoán → hai lỗi, hai bước khác nhau

Lỗi 1 (Ticket A) → hỏng ở retrieval. Nội dung sai (policy 2022) không phải model bịa — nó nằm sẵn trong đoạn được retrieve rồi đưa vào context; model chỉ trung thành sinh từ cái nó nhận. Vậy lỗi ở retrieval: bước tìm kiếm trả về đúng chủ đề (nghỉ thai sản) nhưng sai ràng buộc chính xác (năm 2024) — đúng biến thể của bẫy Ticket B, "policy 2024" là định danh chính xác mà semantic search bỏ qua. Sửa ở retrieval: lọc metadata theo năm, hoặc hybrid khớp chuỗi version — không phải chỉnh prompt hay đổi model sinh.

Lỗi 2 → hỏng ở generation, đúng tên gọi negative rejection. Đây cũng là hệ RAG, nhưng lỗi không ở retrieval: bước tìm đã làm đúng việc — lục kho và (đúng) không thấy đoạn nào khớp, vì policy đó chưa tồn tại. Lỗi ở generation: khi context không có gì liên quan, model đáng lẽ phải từ chối ("không có policy về việc này") nhưng lại bịa. Đó chính là negative rejection — một trong bốn failure mode RGB, và nằm ở phía sinh. Sửa ở tầng sinh: prompt/ngưỡng buộc model trả "không tìm thấy" khi context truy hồi yếu, chứ không phải retrieve nhiều hơn.

Đối chiếu hai lỗi cho ra nguyên tắc chẩn đoán: cô lập bước hỏng trước — hỏi "đoạn model nhận có SAI/thiếu không (→ retrieval), hay đoạn đúng mà model xử lý sai (→ generation)?". Lỗi 1 sai vì đoạn sai được đưa vào; Lỗi 2 sai vì model xử lý sai một context vốn đúng (rỗng). Và chỉ khi đã chốt "generation" mới bàn tới bốn failure mode — vì cả bốn đều mô tả cách model xử lý context, tức đều ở phía sinh; lỗi thuần retrieval (như Lỗi 1) nằm thượng nguồn, ngoài bốn cái tên đó.

Khung một hình

Ba câu hỏi lọc, áp theo thứ tự từ trên xuống — dừng ở câu đầu tiên cho ra kiến trúc (lần lượt là lời giải Ticket C, B, A):

Cây chọn kiến trúc: kho vừa cửa sổ context và tĩnh thì prompt-only; truy vấn xoay quanh định danh chính xác thì RAG hybrid; còn lại là RAG semantic kèm trích nguồn

Fine-tune không nằm trong cây này: nó đổi giọng/định dạng đầu ra, không phải cách nạp kiến thức — nên gần như không bao giờ là đáp án cho câu hỏi "trả lời đúng theo tài liệu".

🎓 Mở rộng

  • Bốn failure mode là khung chẩn đoán, không phải bằng chứng đo trên tiếng Việt. Taxonomy noise robustness · negative rejection · information integration · counterfactual robustness đến từ benchmark RGB (Chen et al., AAAI 2024) — nhưng RGB đánh giá 6 model trên corpus tiếng Anh và tiếng Trung, không có tiếng Việt. Khung thì chuyển giao được; con số cụ thể thì bạn phải tự đo trên dữ liệu Việt của mình.
  • "RAG giảm hallucination" là giảm, không phải triệt tiêu. RAG ra đời để LLM truy cập tri thức ngoài tham số một cách có kiểm chứng, kèm nguồn gốc cho câu trả lời (Lewis et al., NeurIPS 2020) — giảm bịa đặt là hệ quả được ghi nhận ở các nghiên cứu sau, không phải con số nhà cung cấp hứa. Cơ chế bịa còn lại vẫn có tên: retrieve nhiễu, không biết từ chối, vị trí trong context. Đừng hứa với sếp con số phần trăm — hãy hứa một khung để chẩn đoán khi nó vẫn sai.
  • Ba ticket, ba kiến trúc khác nhau, cùng một mô tả ban đầu. Đó là bài học lớn nhất: câu "làm chatbot hỏi đáp trên tài liệu" chưa đủ để chọn kiến trúc. Ba trục — kích thước so context, loại truy vấn, tần suất cập nhật — mới là thứ quyết định.

✨ Điều bạn vừa làm được

  • Phân biệt ba bài toán nghe giống hệt nhau và ánh xạ mỗi cái sang một kiến trúc khác nhau, có lập luận.
  • Nhận ra khi nào KHÔNG dùng RAG — kỹ năng hiếm hơn và giá trị hơn việc biết dựng RAG.
  • Bác bỏ fine-tune như cách nạp kiến thức, bằng lý do có nguồn chứ không phải cảm tính.
  • Gắn mỗi lựa chọn với nhóm rủi ro có tên riêng, và cô lập được bước hỏng (retrieval vs generation) khi một hệ RAG lên production và vẫn trả lời sai.

Bài tiếp theo: Tổng kết module — cheat sheet & tự đánh giá

Bài này đáng gửi cho bạn học cùng?

Copy link đã gắn nguồn — dán group, chat, hoặc LinkedIn.

Bài này có giúp bạn hiểu bản chất không?

Hỏi đáp về bài này

Chưa có câu hỏi

Đặt câu hỏi

Có gì chưa rõ trong bài? Đặt câu hỏi đầu tiên — câu trả lời từ cộng đồng giúp bạn (và người sau).

Đặt câu hỏi đầu tiên

Bài tiếp theo

Tổng kết: Embedding, tìm kiếm & RAG