Quản lý AI agent giống quản lý một team offshore ở một điểm quan trọng: dặn bằng lời không đủ. Sau 6 tháng làm site này với AI agent, tôi chuyển những lỗi có thể mô tả thành điều kiện kiểm tra tự động, rồi vẫn đọc lại thay đổi và nghiệm thu trên môi trường thật.
TL;DR (Executive Summary)
- Bài toán: Tôi ước lượng AI agent viết khoảng 90% code cho site 3 ngôn ngữ. Luật ghi trong tài liệu vẫn bị vi phạm; có lỗi tồn tại trên production 25 ngày trong khi local chạy đúng.
- Giải pháp: Tôi dùng 4 tầng kiểm soát: luật chữ, cổng build, Stop hook cho Claude Code và kiểm trên môi trường thật.
- Kết quả: Sau 6 lỗi từng lọt lên production, tôi bổ sung các bước kiểm tra tương ứng. Chưa có số đo tỉ lệ lỗi trước và sau.
Bối cảnh: một người, một site, phần lớn code do AI viết
Site nguyenchau.dev chạy 3 ngôn ngữ (Việt, Anh, Nhật) trên Cloudflare Pages. Từ 18/03 đến 25/09/2026, repo có 1.279 commit. Git ghi nhận 694 commit có dòng đồng tác giả Claude. Tính cả Codex, tôi ước lượng mình chỉ tự viết khoảng 10% code; số commit có đồng tác giả không đo được tỉ lệ code do AI viết.
Lý do rất thực tế: tôi không còn thời gian code như trước. Nhưng tôi cũng không giao hết. Tôi giữ ba việc:
- Đặt mục tiêu, và viết nó ra để agent đọc được.
- Cấp đủ nguồn thông tin để agent suy luận: dữ liệu, bối cảnh, quyết định cũ và lý do.
- Nghiệm thu: đọc lại commit, kiểm trên môi trường thật.
Phần còn lại, gồm viết code, sắp xếp thông tin, sửa câu chữ, tôi thường giao cho agent. Cách phân việc này gần với vai trò Delivery Manager trong một team offshore, và nhiều bài học dưới đây tôi từng gặp khi làm việc với con người.
Agent làm đúng tài liệu, nên tài liệu phải đúng mục tiêu
Khi tôi còn định hướng site thành một kênh tìm khách, tài liệu cho agent ghi rằng mọi trang nội dung cần có khối giới thiệu tác giả kèm lời mời liên hệ. Agent thêm khối này vào cụm /learn ba lần (29/07, 04/08 và 10/08/2026). Lần nào cũng có lý do nghe rất hợp lý: tăng tín hiệu E-E-A-T, thêm điểm chạm chuyển đổi.
Trong ba lần đó, agent làm theo mục tiêu cũ trong tài liệu. Khi tôi đổi quyết định, trước là gỡ khối tác giả ở /learn vì lý do riêng tư, sau là bỏ hướng biến site thành phễu tìm khách, agent không tự biết. Việc đầu tiên tôi phải làm là viết lại tài liệu, kèm lịch sử ba lần bị thêm lại và lý do vì sao không thêm nữa.
Với team offshore cũng vậy: team làm đúng spec, spec lỗi thời thì team sai một cách rất chăm chỉ. Tôi đã viết về việc chuyển tri thức ngầm thành luật tường minh trong bài vì sao "chất lượng Nhật" khó tái hiện ở team offshore VN. Với AI agent, điều đó còn khắt khe hơn: quyết định mới phải được đưa vào nguồn thông tin mà agent đọc được.
6 lỗi đã lọt lên production và bước kiểm tôi thêm sau đó
Tài liệu đúng vẫn chưa đủ. Đây là những lỗi đã thực sự lên production, vì sao tôi không phát hiện sớm hơn, và bước kiểm tra tôi thêm sau đó:
| Lỗi trên production | Vì sao không ai thấy sớm | Cổng chặn tái phát |
|---|---|---|
| 34 URL tiếng Anh/Nhật của 17 trang bị chuyển hướng về bản tiếng Việt suốt 25 ngày (08/08–02/09/2026) | Lỗi sinh ra từ bản vá cho một lỗi khác; môi trường local không tái hiện được | Validator khoá bảng quyết định ngôn ngữ + kiểm thẳng production sau deploy |
| Chatbot gửi câu trả lời của khách về email của tôi, trong khi khung chat ghi "lưu tạm trên thiết bị" | Chatbot không có test nào; lộ ra khi tôi review | Validator kiểm 5 luật hành vi + test cho chính validator |
| Structured data bản tiếng Anh/Nhật mô tả tác giả khác bản tiếng Việt: mất chứng chỉ, sai chức danh, sai URL (3 lần) | Bản tĩnh vẫn đúng nên kiểm tra cũ luôn báo xanh; lỗi nằm ở đoạn JS sửa schema lúc đổi ngôn ngữ | Validator đọc chính đoạn JS đó |
| FAQ khai trong structured data nhưng không hiện trên trang | Structured data không hiện ra, nhìn trang bằng mắt không thể thấy thiếu | Validator so FAQ hiển thị với FAQ trong schema |
| Trang chủ: crawler đọc một câu, người dùng thấy một câu khác | Hai bản HTML và JS chạy song song; luật đã ghi trong README vẫn bị vi phạm | Validator so từng câu giữa hai bản |
| Số điện thoại viết hai kiểu cho cùng một doanh nghiệp trong JSON-LD | Thông tin này được viết tay ở 20 file | Validator so với một nguồn chuẩn duy nhất |
Hiện có 10 validator chạy trong lệnh build, trước bước đóng gói giao diện. Khi một validator phát hiện vi phạm, build dừng và bản đó không được deploy. Các validator chỉ kiểm được những điều kiện đã định nghĩa.
Lỗi chatbot là lỗi tôi ngại kể nhất. Dữ liệu gửi đi không kèm thông tin liên hệ nào, nhưng giao diện đã nói với khách một điều và hệ thống làm điều ngược lại. Tôi phát hiện khi review ngày 05/09/2026, vá trong ngày, và từ đó chatbot không gửi gì nếu khách không chủ động để lại liên hệ.
Lỗi 34 URL: vì sao "local chạy đúng" không chứng minh được gì
Lỗi này đáng kể chi tiết vì nó cho thấy giới hạn của cả agent lẫn test.
Ngày 08/08, agent audit site và tìm ra một lỗi thật: nhiều trang chỉ có bản tiếng Việt vẫn trả về nội dung khi bị gọi bằng URL tiếng Anh, tạo ra hàng chục URL trùng nội dung. Bản vá của agent là đọc nội dung trang, xem trang có khai bản dịch không, không có thì chuyển hướng về bản tiếng Việt.
Bản vá đúng về logic. Nhưng trên Cloudflare, với file tĩnh, lớp middleware không đọc được nội dung trang dưới dạng văn bản. Kết quả là mọi trang tĩnh đều bị kết luận "không có bản dịch", kể cả 17 trang có bản dịch thật. Còn ở môi trường giả lập local, nội dung vẫn đọc được bình thường, nên mọi kiểm tra đều xanh.
Tôi phát hiện khi đọc lại các commit gần nhất, thấy phần xử lý URL ngôn ngữ có gì đó không ổn. Tôi kiểm ở local: chạy đúng. Kiểm trên production: sai. Tôi đưa bằng chứng đó cho agent, và agent sửa theo hướng quyết định bằng dữ liệu có sẵn lúc build thay vì đọc nội dung trang lúc chạy.
Có ba bài học ở đây:
- Bản vá của agent cho lỗi A có thể đẻ ra lỗi B lớn hơn. Agent giải rất tốt vấn đề trước mặt, nhưng không tự nhìn được hệ quả ở chỗ nó không được cho xem.
- Test xanh ở local chỉ chứng minh code chạy ở local. Từ đó, sau mỗi deploy đụng tới định tuyến, tôi kiểm thẳng trên production. Với team offshore, đây là lý do UAT phải làm trên môi trường của khách.
- Lớp phát hiện cuối cùng vẫn là người đọc lại. Không cổng nào bắt được lỗi này trước khi nó xảy ra, vì chưa ai từng gặp nó.
Khi nào lời dặn phải thành cổng chặn?
README của repo có hẳn một mục "Common AI Mistakes — DON'T DO THIS". Mục 4b ghi lỗi sửa bản JSX mà quên bản HTML, kèm ghi chú lỗi này đã xảy ra ở 3 trang landing. Ngày 06/08/2026, nó lại xuất hiện ở form trang chủ. Cùng ngày, luật đó được chuyển thành validator.
Commit tạo Stop hook cho cụm /learn có một câu tôi thấy tóm được ý chính: biến luật trong CLAUDE.md từ request thành guarantee. Trong Claude Code, Stop hook chạy khi agent định kết thúc lượt làm việc. Nếu nội dung /learn đã bị sửa mà validator còn lỗi, hook chặn lượt kết thúc và trả lỗi để agent sửa tiếp. Với team người, nó tương đương việc không cho báo "xong" khi chưa qua Definition of Done. Cách cấu hình hook tôi ghi lại ở note permission và hooks của Claude Code.
Cùng nguyên tắc này, tôi đã áp vào một dự án thật. Trước khi học kỹ về bản chất của model, trong một hệ thống SaaS cho khách Nhật tôi phụ trách, 7 trên 10 tính năng AI dựa hoàn toàn vào prompt, tức là dựa vào việc model có suy luận đúng và có nghe lời hay không. Ví dụ đơn giản nhất: nội dung gửi cho khách người Nhật bắt buộc phải có kính ngữ 様. Prompt đã ràng buộc, model vẫn có lúc bỏ sót.
Sau đó tôi đề xuất khách refactor. Code dùng thông tin khách hàng để chọn kính ngữ, chẳng hạn thêm 様 theo quy tắc áp dụng cho khách Nhật. Prompt chỉ còn giữ phần thật sự cần suy luận. Còn nhiều trường hợp tương tự, nhưng vì NDA tôi chỉ kể ví dụ đơn giản nhất. Nguyên tắc tôi rút ra: việc nào có quy tắc xác định được thì đừng giao cho trí nhớ của model.
"Cổng chặn", theo cách tôi dùng từ này, là một kiểm tra tự động mà vi phạm thì việc không đi tiếp được: build gãy, merge bị chặn, agent không kết thúc được lượt. Nó khác checklist ở chỗ không ai "quên chạy" nó được.
4 tầng kiểm soát AI agent
| Tầng | Ở site này | Ở team offshore |
|---|---|---|
| 1. Luật chữ | CLAUDE.md, README, tài liệu SEO | Spec, coding convention |
| 2. Cổng build | 10 validator, vi phạm thì không deploy | CI gate chặn merge |
| 3. Chặn trong phiên | Stop hook trong Claude Code: chặn kết thúc lượt khi validator /learn còn lỗi | Không cho báo "xong" khi chưa qua DoD |
| 4. Kiểm trên môi trường thật | Kiểm production sau deploy | UAT trên môi trường khách |
Mỗi tầng chặn một loại lỗi và có giới hạn riêng:
- Luật chữ giữ mục tiêu, ngoại lệ và lý do của quyết định cũ, nhưng có thể bị bỏ qua hoặc bị hợp lý hoá.
- Cổng build chặn các vi phạm thuộc điều kiện đã định nghĩa, nhưng không bao quát mọi cách phát sinh lỗi.
- Chặn trong phiên trả lỗi để agent sửa trước khi đưa người review. Ở site này Stop hook mới phủ cụm /learn trong Claude Code.
- Kiểm trên môi trường thật bắt được lỗi chỉ xuất hiện ở production, nhưng tốn công người và không tự động.
Sau một lỗi lọt lên production, tôi xem phần nào có thể biến thành điều kiện kiểm trong build. Tầng luật chữ vẫn giữ mục tiêu và lý do của quyết định, những thứ cổng tự động không diễn đạt được.
Cổng tốt kiểm hành vi, không kiểm câu chữ
Dựng cổng sai thì cổng thành gánh nặng. Ba nguyên tắc tôi giữ:
- Kiểm hành vi, không kiểm câu chữ. Validator của chatbot chạy engine thật trên câu thật, rồi kiểm các bất biến như "khách chưa để lại liên hệ thì không gửi gì". Nó không kiểm chữ trên nút bấm. Test kiểm câu chữ sẽ gãy mỗi lần sửa copy, và dạy người sửa test thay vì đọc test.
- Kiểm tính nhất quán, không đòi đầy đủ. Validator thông tin doanh nghiệp không bắt 20 trang khai đủ mọi trường. Trang nào khai trường nào thì trường đó phải khớp nguồn chuẩn.
- Cổng cũng phải được kiểm. Validator của chatbot có một bộ test riêng, cố tình cài lại từng lỗi cũ để chắc chắn validator vẫn báo đỏ. Cổng không bao giờ đỏ có thể là cổng đã hỏng.
Giới hạn và cái giá
Vẽ ra một quy trình gọn gàng mà giấu cái giá thì cũng là nói quá. Đây là những chỗ thật:
- Cổng chỉ bắt được điều kiện đã thiết kế để kiểm. Lỗi 34 URL là tôi đọc thay đổi rồi kiểm trên production mới thấy. Tôi vẫn cần bước review của con người.
- Mỗi cổng là một chi phí bảo trì. Một câu FAQ ở trang giới thiệu giờ sống ở 4 chỗ. Validator giữ 4 chỗ đó khớp nhau, nhưng sửa một câu vẫn là sửa bốn chỗ.
- Có cổng chỉ là kiểm tra tĩnh trên source code, không phải render thật. Validator schema không bắt được một số cách gán gián tiếp. Đủ cho các lỗi đã gặp, không đủ cho mọi lỗi.
- Tôi chưa có số đo trước và sau. Tôi không đếm số lỗi mỗi tháng trước khi dựng cổng, nên không nói "giảm X% lỗi". Với 6 lỗi trong bảng, tôi đã bổ sung các kiểm tra tương ứng; điều đó không chứng minh mọi biến thể của chúng đều bị chặn.
- Quy mô là một người, một site. Sau 6 tháng tôi chưa gặp cổng nào chặn oan đến mức phải gỡ, nhưng một team 30 người sẽ có ma sát khác. Bản thiết kế cho team offshore tôi viết riêng ở bài AI Quality Gate cho code AI offshore. Bài đó là thiết kế chưa đo, còn bài này là phiên bản đã chạy thật ở quy mô nhỏ nhất.
AI agent không làm vai trò Delivery Manager biến mất, nó dời vai trò đó sang chỗ khác. Thay vì review từng dòng code, việc của tôi là viết mục tiêu cho đúng, biết lỗi nào phải thành cổng, và vẫn đọc lại commit. Kỹ năng đọc và nghiệm thu đầu ra của AI chính là lớp tôi mô tả trong bài từ BrSE đến AI Bridge SE. Còn nếu bạn đang cân nhắc đưa AI vào quy trình vận hành, bài toán thường nằm ở việc tách phần cần suy luận khỏi phần có đúng một đáp án. Tôi viết thêm về hướng đó ở trang AI automation cho SME.
Câu hỏi thường gặp
Có nên để AI agent viết phần lớn code của một hệ thống production không?
Được, nếu người giao việc giữ lại ba việc: đặt mục tiêu rõ, cấp đủ nguồn thông tin để agent suy luận, và nghiệm thu đầu ra. Ở site nguyenchau.dev tôi ước lượng khoảng 90% code do AI agent viết. Tôi đọc lại thay đổi, dùng cổng kiểm tra tự động trong build và nghiệm thu trên môi trường thật.
Vì sao viết luật trong CLAUDE.md hay trong prompt là chưa đủ?
Luật viết bằng chữ là một yêu cầu, không phải một bảo đảm. Model có thể bỏ qua hoặc tự tìm lý do hợp lý để làm khác. Ở site này, README ghi rõ "sửa bản JSX mà quên bản HTML" là lỗi AI hay mắc, lỗi đó vẫn xảy ra. Khi chuyển thành validator chạy trong build, vi phạm làm gãy deploy thay vì lên production.
Việc gì nên giao cho prompt, việc gì nên viết thành code?
Việc nào có quy tắc xác định được thì viết thành code, ví dụ chọn kính ngữ theo thông tin khách hàng. Prompt giữ phần thật sự cần suy luận. Để model tự nhớ một quy tắc cố định là đặt chất lượng vào thứ khó kiểm soát.