AI Hallucination — Tại sao LLM "bịa" và 6 tầng phòng thủ cho dev
Vì sao hallucination là bản chất của next-token prediction chứ không phải bug: 4 cơ chế, taxonomy 6 dạng dev gặp, mô hình phòng thủ Swiss-cheese 6 tầng, vụ thật (luật sư, Air Canada, slopsquatting), và cân phòng thủ theo rủi ro.
Có một sự thật khó chịu bạn phải nắm trước khi giao việc cho LLM mỗi ngày: model không biết khi nào nó đang sai. Nó nói câu bịa với đúng giọng tự tin như câu đúng. Con người còn có cảm giác “cái này mình không chắc”; LLM thì không.
Đó là hallucination. Và đây là điểm mấu chốt của cả bài: nó không phải bug của ChatGPT, Claude hay Gemini sẽ được “fix ở version sau”. Nó là hệ quả toán học tất yếu của cách mọi LLM hiện tại vận hành. Hiểu được vì sao, bạn sẽ ngừng chờ phép màu và bắt đầu xây hàng rào.
Ý tưởng lớn nhất: LLM không phải cỗ máy tra cứu sự thật. Nó là cỗ máy đoán chuỗi token có khả năng nhất. “Đúng sự thật” chỉ là tác dụng phụ thường gặp, không phải mục tiêu thiết kế. Khi không có dữ liệu, nó vẫn đoán — và một câu bịa trôi chảy luôn “có khả năng” hơn một câu “tôi không biết”.
1. Hallucination là gì — định nghĩa chính xác
Hallucination = output nghe hợp lý nhưng không có căn cứ trong training data hoặc trong context bạn cung cấp.
Từ khoá là “hợp lý” (plausible). Output bịa không phải chữ ngẫu nhiên vô nghĩa — nó trông đúng đến mức bạn dễ không nhận ra:
// Hỏi: 'list user từ Supabase, limit 10, mới nhất trước'
const { data } = await supabase
.from('users')
.select('*')
.limit(10)
.orderBy('created_at', 'desc'); // ← Supabase KHÔNG có method này
Method đúng là .order('created_at', { ascending: false }). Model trộn
syntax từ Prisma/Mongoose/SQL vì tất cả “nghe giống ORM”. Code đẹp,
TypeScript loose vẫn pass, runtime mới nổ.
Ẩn dụ: hallucination giống một diễn viên ứng tác (improv) không bao giờ thoát vai. Bạn đưa tình huống nào, anh ta cũng diễn tiếp trơn tru, đầy tự tin — kể cả khi anh ta chẳng biết gì về chủ đề. Mục tiêu của anh ta là “giữ mạch diễn”, không phải “nói đúng”.
Hiểu lầm thường gặp: “model bịa vì nó kém / chưa đủ thông minh”. Sai. Ngay cả model giỏi nhất cũng bịa, vì bịa không đến từ thiếu năng lực mà từ chính cơ chế next-token (Phần 2). Model giỏi hơn chỉ bịa ít thường xuyên hơn và tinh vi hơn — đôi khi nguy hiểm hơn.
2. Vì sao hallucination là điều bắt buộc phải xảy ra
Nghe cực đoan, nhưng đúng về mặt cơ chế. Mọi LLM chạy theo một vòng lặp duy nhất (xem Tokenization & Sampling):
Input: chuỗi token đã có
│
▼
Model: tính phân phối xác suất p(token kế tiếp | context)
│ — MỌI token trong từ điển đều có xác suất > 0
▼
Chọn: token xác suất cao nhất (hoặc sample từ phân phối)
│
▼
Lặp lại cho token tiếp theo
Đọc kỹ dòng “mọi token đều có xác suất > 0”. Nó kéo theo ba hệ quả:
- Không có “token bị cấm” — không tồn tại cơ chế chặn cứng theo logic.
- Model không suy luận “câu này không thể chứa từ X”. Nó chỉ biết pattern từ training.
- Khi gặp câu hỏi nó chưa từng thấy dữ liệu, nó không im lặng — nó đắp pattern gần nhất.
Ví dụ kinh điển:
Hỏi: "Signature của hook useDeepMemo trong React 19 là gì?"
useDeepMemo không tồn tại. Nhưng model đã thấy hàng triệu ví dụ
useMemo(() => value, deps). Pattern đó cực mạnh → nó dựng ra một
signature “kiểu useMemo” mang tên useDeepMemo. Xác suất của output
trôi chảy này cao hơn xác suất của “Tôi không biết”.
Vì sao “I don’t know” lại khó: muốn model biết từ chối, phải dạy nó nhận diện và chấp nhận sự bất định (calibration, abstention learning) — một trong những bài toán khó nhất của AI hiện tại. Có tiến bộ, nhưng còn xa hoàn hảo.
Kết luận của phần này: còn next-token prediction, còn hallucination. Nó là feature của kiến trúc, không phải bug của sản phẩm.
3. Bốn cơ chế gây hallucination phổ biến
Hiểu cơ chế giúp bạn đoán trước khi nào rủi ro tăng vọt.
3.1. Knowledge cutoff — dữ liệu training có hạn dùng
Library/framework ra sau ngày cutoff → model buộc phải đoán.
- React 19 (12/2024), Astro 5 đổi content collections, Next.js App Router đổi 3 lần trong 18 tháng.
- Ecosystem càng “nhanh”, rủi ro càng cao.
Ẩn dụ: như hỏi đường một người vừa rời thành phố 2 năm trước. Họ trả lời tự tin theo bản đồ cũ — và dẫn bạn vào con đường giờ đã thành công trường.
3.2. Pattern bleeding — trộn lẫn các framework giống nhau
# Hỏi Django query → model trộn với SQLAlchemy/Prisma
qs = User.objects.where(age__gt=18).order_by('name').limit(10)
# Django đúng: .filter(...), và phải slice [:10] thay cho .limit(10)
3.3. Confabulation dưới áp lực — “muốn giúp” hơn “biết thật”
Bạn: "Code này có bug? [paste]"
AI: "Có, dòng 5 thiếu null check." ← đoán theo pattern phổ biến
Bạn: "Sửa rồi vẫn lỗi."
AI: "À, có thể do dòng 12..." ← đoán tiếp, không dựa hiểu biết thật
Mỗi lần bạn nói “vẫn lỗi”, model dựng giả thuyết mới — không phải vì nó hiểu nguyên nhân, mà vì nó phải nói gì đó.
3.4. Context corruption — “lost in the middle”
[100KB đầu context: convention dự án, "không dùng axios"]
...
[câu hỏi cuối: "gọi API thế nào?"]
→ AI: const res = await axios.get('/api/...') ← đi ngược convention
Không phải AI cố tình. Nó bỏ sót thông tin nằm giữa context dài — một hiện tượng có nghiên cứu chính thức (“Lost in the Middle”).
4. Taxonomy — 6 dạng hallucination dev gặp hằng ngày
| Dạng | Mô tả | Ví dụ |
|---|---|---|
| 1. Method/API không tồn tại | Tên nghe đúng, thật thì khác | array.shuffle(), el.getTextContent() |
| 2. Import sai chỗ | Package/path hợp lý nhưng sai | import { useDebounce } from 'react' |
| 3. Config option bịa | Nghe kỹ thuật, không có trong schema | "strictOptionalProperties": "safe" |
| 4. Type signature sai | Số/loại tham số/trả về sai | const [s, set, reset] = useState(0) |
| 5. Behavior sai (nguy hiểm nhất) | Code chạy nhưng hành vi ngầm sai | ”Array.sort() không mutate” (sai — nó mutate) |
| 6. URL/reference ảo | Link đúng format, 404 khi click | react.dev/reference/hooks/useDeepMemo |
Dạng 5 đáng sợ nhất vì nó không crash. Code chạy, test sơ sài pass, nhưng mental model của bạn bị nhiễm độc — và bug lan sang chỗ khác. Type-check không bắt được; chỉ test hành vi mới bắt (Phần 5).
5. Mô hình phòng thủ 6 tầng — “Swiss cheese”
Không một tầng nào bắt hết hallucination. Triết lý đúng là mô hình phô mai Thuỵ Sĩ (Swiss cheese model, James Reason — vốn dùng trong an toàn hàng không và y tế): mỗi tầng là một lát phô mai có lỗ; xếp nhiều lát lại thì lỗ hiếm khi thẳng hàng, nên lỗi khó xuyên qua hết.
┌──────────────┐
│ 6. Human │ Review PR/commit; "tribal context"
│ review │
┌───┴──────────────┴───┐
│ 5. Observability │ Feature flag, canary, Sentry
┌───┴──────────────────────┴───┐
│ 4. Integration + E2E │ Chạy thật → bắt config/URL/DB sai
┌───┴──────────────────────────────┴───┐
│ 3. Unit test │ Bắt behavior sai (Dạng 5)
┌───┴──────────────────────────────────────┴───┐
│ 2. Lint + static analysis │ Import/style/unused
┌───┴──────────────────────────────────────────────┴───┐
│ 1. Type-check (TypeScript strict, mypy...) │ Method tồn tại? Signature đúng?
└──────────────────────────────────────────────────────┘
↑ rẻ nhất, nhanh nhất, bắt được nhiều nhất — đặt lên đầu
Nguyên tắc xếp tầng: lỗi nên bị chặn ở tầng rẻ và sớm nhất có thể. Đừng để một method-không-tồn-tại lọt tới tận production rồi mới phát hiện qua Sentry.
| Tầng | Bắt được gì | Bỏ lọt gì |
|---|---|---|
| 1. Type-check | Method/signature/import sai (Dạng 1,2,4) | Behavior sai, config runtime |
| 2. Lint | Import thừa, any, @ts-ignore lén | Logic |
| 3. Unit test | Behavior sai (Dạng 5) | Tích hợp thật, config |
| 4. Integration/E2E | Config bịa, URL 404, query sai (Dạng 3,6) | Lỗi chỉ xảy ra ở scale/production |
| 5. Observability | Lỗi production, hồi quy | Lỗi “đúng kỹ thuật, sai ý đồ” |
| 6. Human review | ”Tribal context” (quyết định team, data distribution) | Cái con người cũng bỏ sót |
Hai tầng đáng nhấn:
- Tầng 1 — Type-check: bắt buộc strict mode +
noUncheckedIndexedAccess+exactOptionalPropertyTypes. Không strict →anyngầm → AI bịa method mà TS im lặng. - Tầng 3 — Unit test cho behavior:
test('sortAsc trả về bản sao đã sắp, KHÔNG mutate input', () => {
const original = [3, 1, 2];
const sorted = sortAsc(original);
expect(sorted).toEqual([1, 2, 3]);
expect(original).toEqual([3, 1, 2]); // ← bắt đúng Dạng 5
});
6. Điều này đã gây thiệt hại thật ngoài đời
Hallucination không phải rủi ro lý thuyết. Vài vụ được ghi nhận công khai:
| Vụ việc | Chuyện gì xảy ra | Bài học |
|---|---|---|
| Mata v. Avianca (2023, New York) | Luật sư nộp hồ sơ với các án lệ do ChatGPT bịa hoàn toàn; toà phát hiện và phạt | Output AI là giả thuyết, không phải nguồn dẫn |
| Air Canada chatbot (2024) | Chatbot tự “chế” chính sách hoàn tiền; toà buộc hãng phải chịu trách nhiệm | Bạn chịu trách nhiệm cho cái AI nói thay bạn |
| Google Bard demo (2023) | Trả lời sai về kính viễn vọng James Webb ngay trong video ra mắt | Sai tự tin xảy ra cả ở demo có chuẩn bị |
| Package hallucination / “slopsquatting” | LLM gợi ý tên package npm/pip không tồn tại; kẻ xấu đăng ký đúng tên đó để cài malware | Hallucination thành lỗ hổng supply-chain |
Điểm rút ra cho dev: dạng nguy hiểm nhất không phải code crash — mà là output trôi chảy, đáng tin, và sai, được một con người mệt mỏi bấm “accept”.
7. Khi nào AI bịa ít hơn / nhiều hơn
| AI ít hallucinate khi… | AI dễ hallucinate khi… |
|---|---|
| Context cụ thể: attach file thật, dán docs, link ticket | Context mơ hồ (“fix this”) |
| Có tool/MCP đọc docs real-time (vd Context7) | Library mới/non-mainstream/internal |
| Dùng thinking model (bảo thủ hơn, dám nói “chưa chắc”) | Task mở (“design architecture for…”) |
| Grounding qua RAG tốt → nhìn nguồn thật | Hội thoại dài, không có vòng phản hồi/verify |
| Task định nghĩa rõ (“refactor theo 3 rule này”) | Yêu cầu chung chung (“improve this code”) |
Hai kỹ thuật ép AI tự soi lại
1. Self-verification (Socratic) — bắt AI xác thực chính output của nó:
"Soi lại đoạn trên. Xác nhận từng method/import có thật trong
library X phiên bản Y.Z không. Nếu không chắc, ghi 'chưa chắc'."
Khoảng 60–70% trường hợp AI tự phát hiện và sửa — không phải vì nó “nhớ ra”, mà vì câu hỏi kích hoạt pattern kiểm tra thay vì pattern sinh code. Tốn ~5 giây, vài trăm token. Không phải thuốc tiên, nhưng là một lát phô mai miễn phí.
2. Chống “You’re absolutely right” — model hay đồng ý với giả thuyết sai của bạn vì nó được reward khi bạn vui:
Bạn: "Tôi nghĩ bug do useEffect chạy 2 lần trong strict mode."
AI: "You're absolutely right!..." ← có thể đang xuôi theo bạn
Cách chặn: đừng đưa kết luận sớm — chỉ mô tả triệu chứng. Khi AI đồng ý, thách thức: “Chứng minh bằng một test case reproduce đi.” Nếu không reproduce được → giả thuyết sai (dù cả hai đang gật gù). Đây là lý do minimal reproduction quan trọng: nó khách quan hoá tranh luận.
Key Takeaways
- Hallucination là feature của kiến trúc, không phải bug. Next-token prediction luôn đoán, kể cả khi không có dữ liệu — không version nào “fix” hết được.
- Output AI = giả thuyết, không phải sự thật. Verify trước khi commit. Sự tự tin của model nói lên độ trôi chảy, không phải độ đúng.
- Dạng nguy hiểm nhất là “behavior sai” (Dạng 5): không crash, lọt type-check, đầu độc mental model. Chỉ test hành vi mới bắt được.
- Phòng thủ theo mô hình Swiss cheese 6 tầng: type-check → lint → unit → integration/E2E → observability → human review. Lỗi nên bị chặn ở tầng sớm và rẻ nhất.
- Context cụ thể giảm tần suất bịa ~10×. Mơ hồ + library mới = vùng rủi ro cao nhất.
- Vụ thật đã có thiệt hại (luật sư bị phạt, Air Canada thua kiện, slopsquatting) — đây là rủi ro vận hành, không phải lý thuyết.
Common Mistakes
- Tin vào giọng tự tin của AI. Tin test + type + review, không tin ngữ điệu.
- “Accept all” mà không đọc từng dòng diff. Đây chính là lỗ để hallucination xuyên qua tầng cuối.
- Tắt strict mode / xài
any/@ts-ignoređể “cho nó chạy” — tự tay khoét thủng tầng phòng thủ rẻ nhất. - Đưa giả thuyết sai rồi hỏi AI xác nhận → nhận về confabulation đồng thuận. Hãy mô tả triệu chứng, bắt AI tự đề xuất + chứng minh.
- Tin URL/citation do AI tạo mà không click kiểm tra — chúng đúng format nhưng thường ảo.
- Copy tên package AI gợi ý mà không kiểm tra trên registry — rủi ro slopsquatting.
When Should You Use This?
Câu hỏi đúng không phải “có dùng AI không” mà là “hàng rào dày bao nhiêu cho từng mức rủi ro”. Cân lượng phòng thủ theo mức nguy hiểm:
| Mức rủi ro | Loại code | Phòng thủ tối thiểu |
|---|---|---|
| 🔴 Cực cao | Security, crypto, auth | Test với test vector chuẩn (RFC) + security review người thật + audit trail. Không trust AI trực tiếp |
| 🔴 Cao | Payment, tính tiền, thuế | Unit test biên (decimal, rounding) + human review bắt buộc |
| 🟡 Trung bình | Logic nghiệp vụ core | Unit + integration test, review bình thường |
| 🟡 Thấp–TB | Hành vi UI | E2E/component test |
| 🟢 Thấp | Style, format, comment | Lint + type-check là đủ |
- Dùng AI thoải mái cho boilerplate, refactor có ràng buộc rõ, vùng có type + test mạnh.
- Dùng AI thận trọng (verify nặng) cho security/payment, library mới, task mở, hoặc code không có test bao quanh.
Quick Start Guide
Bước đầu tiên để bắt đầu phòng thủ hôm nay:
- Bật tầng 1 (rẻ nhất, mạnh nhất) — siết TypeScript:
// tsconfig.json
{
"compilerOptions": {
"strict": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true
}
}
- Thêm một verification prompt vào thói quen sau mỗi đoạn code AI sinh:
"Liệt kê mọi method, import, và config option bạn vừa dùng.
Với từng cái, xác nhận nó có thật trong <library@version>.
Cái nào không chắc, đánh dấu 'CẦN KIỂM TRA'."
- Viết test hành vi cho thứ AI tạo — đặc biệt với hàm có hành vi ngầm (mutate, async, edge case). Đây là lưới bắt Dạng 5.
- Không bao giờ “accept all” cho code rủi ro cao — đọc từng dòng diff; mang “tribal context” của team vào review.
- Đầu tư vào bộ kỹ năng verify — nó tăng giá trị theo thời gian: model mạnh hơn vẫn bịa (coverage gap, internal code, ngôn ngữ mơ hồ), chỉ là tinh vi hơn.
Dev làm việc tốt với AI không phải dev “tin AI nhiều hơn”. Ngược lại — họ hoài nghi có phương pháp hơn.
Đọc thêm trong series
- Tokens & Pricing — chi phí thật của AI coding
- Working with Coding Agents
- Finding and Fixing Bugs with AI
- Reviewing and Testing Code with AI
Reference
- Google Research — “Lost in the Middle” (arxiv.org/abs/2307.03172)
- Mata v. Avianca, Inc. (S.D.N.Y. 2023) — chế tài cho citation bịa bởi ChatGPT
- Moffatt v. Air Canada (BC Civil Resolution Tribunal, 2024) — trách nhiệm cho chatbot
- Anthropic — Calibration & uncertainty papers
- Cursor Learn (cursor.com/learn)