Phàn nàn lớn nhất về code AI sinh ra là nó trông đúng nhưng không hoạt động đúng. Hàm được viết ra mà không có code path nào gọi đến. Test được viết nhưng test sai thứ. Edge case được xử lý trong comment nhưng không phải trong code.
Tôi xây agent QA chuyên biệt để bắt những vấn đề này. Nó chạy chậm hơn sinh code thô 10 lần. Nó reject nhiều plan hơn là approve ngay lần đầu. Và nó đã loại bỏ bug production khỏi workflow agentic của tôi.
Đây là cách nó hoạt động.
Vấn Đề: Tại Sao Code AI Trông Đúng Nhưng Không Phải
Khi coding agent tạo ra output, bạn đang thấy kết quả của pattern completion theo xác suất. Model đã thấy hàng triệu file code. Nó biết code đúng trông như thế nào. Nó có thể tái tạo pattern một cách thuyết phục.
Nhưng nhận dạng pattern không phải hiểu biết. Agent không biết rằng userService.fetchById() được gọi từ 3 nơi và cả 3 caller đều kỳ vọng kiểu trả về User | null. Nó thấy function signature và viết thứ trông có vẻ đúng.
Ba failure mode tôi thấy nhiều nhất:
1. Hàm Mồ Côi
Agent viết hàm hoàn chỉnh, có documentation tốt. Compile được. Có đúng kiểu. Không ai gọi nó.
Điều này xảy ra khi agent implement tính năng “bottom up” — viết implementation trước, rồi quên wire nó vào application flow. Code tồn tại, test pass trên chính hàm đó, và tính năng đơn giản không xuất hiện trên UI.
Ví dụ thực tế từ CubLearn:
// Agent viet ham hoan chinh nay
export function getYLEExercisesByLevel(level: 'starters' | 'movers' | 'flyers') {
return PRONUNCIATION_EXERCISES.filter(ex => ex.id.startsWith(`en-yle-${level}`));
}
// Nhung khong ai goi no -- game component van dung array hardcode
// Bai tap moi vo hinh voi nguoi dung
2. Test Chi Happy-Path
Agent viết test. Test pass. Test chỉ cover trường hợp mọi thứ đều ổn.
// Test cua agent
it('nen lay lesson theo id', async () => {
const lesson = await getLessonById('yle-starters-numbers');
expect(lesson.title).toBe('So dem va dem so');
});
// Test con thieu:
// - Neu id khong ton tai?
// - Neu id la chuoi rong?
// - Neu database khong truy cap duoc?
// - Neu lesson khong co tu vung?
Happy path test cho 100% line coverage trên nhánh thành công. Zero coverage trên nhánh thất bại chắc chắn sẽ xảy ra trong production.
3. Stub Duoc Ship
Token exhaustion giữa task. Agent viết:
async function syncToCloudflare(posts: BlogPost[]): Promise<void> {
// TODO: implement chunked upload for large post arrays
// For now, handle single posts only
if (posts.length === 1) {
await uploadSingle(posts[0]);
}
}
Comment ghi “tạm thời.” Nó được ship. Sáu tháng sau, ai đó thử sync 50 bài viết và không có gì xảy ra.
Phuong Phap Luan Cua Agent QA
Agent QA của tôi chạy 4 pass xác minh theo trình tự. Mỗi pass có thể reject công việc và gửi lại để sửa.
Pass 1: Kiem Tra Plan
Trước khi code nào được viết, bản kế hoạch được review.
Checklist:
- Mỗi file thay đổi được liệt kê với line range cụ thể
- Mỗi hàm mới được đặt tên rõ ràng (không có “thêm helper function khi cần”)
- Type interface cho data structure mới được chỉ định
- Danh sách “không được chạm vào” rõ ràng
- Đánh giá rủi ro bao gồm cái gì có thể bị phá
- Mọi dependency bên ngoài được xác định (APIs, env vars, thư viện bên thứ ba)
Trigger reject: Bất kỳ mục nào mơ hồ, thiếu, hoặc dùng ngôn ngữ như “xử lý phù hợp” hay “các thay đổi cần thiết.”
Reject thực tế từ session gần đây:
PLAN BI REJECT -- Pass 1
Muc 3: "Cap nhat pipeline render blog theo yeu cau"
Day khong phai ke hoach. "Theo yeu cau" khong phai la duong dan file.
Liet ke moi file trong rendering pipeline va chi dinh chinh xac
dong nao se thay doi va tai sao.
Muc 7: Thieu -- dieu gi xay ra khi upload Cloudflare R2
that bai? Plan khong co error handling spec.
Day la loi hard fail hay skip im lang?
Tra lai voi duong dan file cu the va error handling spec.
Pass 2: Kiem Tra Wiring
Sau khi code được sinh, agent QA trace mỗi hàm mới từ definition đến call site.
Câu hỏi đơn giản: Code này có thể reach được không?
Kiem tra wiring: getYLEExercisesByLevel()
Definition: packages/domain/src/rules/pronunciation.ts:147
Call site ky vong: game component render bai luyen phat am
Ket qua tim kiem:
- apps/web/src/app/[locale]/games/pronunciation/page.tsx -- KHONG TIM THAY
- apps/web/src/components/PronunciationGame.tsx -- KHONG TIM THAY
- apps/web/src/data/english-lessons.ts -- KHONG TIM THAY
Ket qua: HAM MO COI -- khong the goi tu bat ky code path nao
Trang thai: FAIL -- them call site hoac xoa ham
Kiem tra nay mot minh da bat duoc 6 ham mo coi trong cac du an cua toi trong mot thang qua.
Pass 3: Kiem Tra Test Coverage
Voi moi ham moi hoac behavior thay doi, agent QA xac minh:
- Test happy path ton tai — truong hop thanh cong duoc test
- Test failure path ton tai — it nhat: input rong, null/undefined, network fail, kieu bat ngo
- Integration test ton tai — ham hoat dong khi duoc goi tu call site thuc su (khong chi isolation)
- Khong co dead test — test tham chieu ham thuc su ton tai
Nguong coverage: toi thieu 80% line coverage, 100% branch coverage tren cac nhanh quan trong (auth, data mutation, external API).
Kiem tra test: thay doi tts.ts
Ham them/thay doi: 4
- sha256Hex() -- 0 test -- FAIL
- langCodeFromVoice() -- 0 test -- FAIL
- isChirp3HD() -- 0 test -- WARN (pure function, rui ro thap)
- synthesizeChunk() -- 1 test (chi happy path) -- WARN
Truong hop test bi thieu:
- sha256Hex('') -- hanh vi chuoi rong
- langCodeFromVoice('invalid') -- ten voice bi loi
- synthesizeChunk() khi Google API tra ve 429 (rate limit)
- synthesizeChunk() khi audioContent la base64 bi hong
Them test hoac giai thich tai sao bo qua cac truong hop nay la chap nhan duoc.
Pass 4: Tuan Thu Convention
Agent QA chay checklist coding convention cua chung toi:
Cho TypeScript:
- Khong co type
any(dungunknownvoi type guard) - Khong co non-null assertion (
!) tren data ben ngoai - Tat ca async function co try/catch hoac error propagation ro rang
- Khong co
console.logtrong production code - Tat ca interface duoc export co JSDoc documentation
Cho Markdown (bai blog):
- Khong co em-dash (U+2014) — dung
-- - Khong co smart quote — dung
"dau thang" - Noi dung tieng Viet co day du dau (kiem tra bang dem ky tu non-ASCII)
- YAML frontmatter co day du truong bat buoc
Cho Cloudflare Workers:
- Khong co
Date.now()(dungperformance.now()) - Tat ca
Responseobject bao gom CORS header ctx.waitUntil()duoc dung cho background task khong blocking
Kiem tra cuoi cung chinh la thu da bat duoc loi Cloudflare Workers 1101 som trong workflow cua toi. Em-dash va smart quote gay ra loi im lang trong CF Workers. Kiem tra convention gio chay tren moi file markdown.
Dau Tu 10x Thoi Gian: Co Dang Khong?
Agent QA chay cham hon sinh code tho 10 lan. Task toan 5 phut cho coding agent ton 50 phut voi QA review.
Co dang khong?
De toi so sanh 2 thang du lieu tu chinh cac du an cua minh:
Truoc agent QA (thang 6-7/2025):
- 23 lan deploy
- 8 bug production tim thay sau deploy
- 3 lan phai rollback
- Thoi gian trung binh fix bug sau deploy: 2.5 gio
Sau agent QA (thang 8-9/2025):
- 31 lan deploy
- 1 bug production tim thay sau deploy (CORS header tren endpoint moi — bi bat boi smoke test, khong phai QA)
- 0 lan rollback
- Thoi gian fix: 15 phut (smoke test bat ngay lap tuc)
Agent QA ton kem 10x moi task. No tiet kiem khoang 20 gio debug sau-deploy trong 2 thang.
Quan trong hon: no thay doi ban chat cong viec cua toi. Toi ngung la debugger. Toi tro thanh nguoi viet specification va reviewer.
Bắt Đầu: Tự Xây Agent QA
Nếu muốn implement pattern này, hãy bắt đầu đơn giản:
Agent QA tối giản:
- Sau mỗi lần sinh code, prompt agent: “Liệt kê mọi hàm mới trong diff này. Với mỗi hàm, tìm tất cả nơi trong codebase gọi nó. Trả về mọi hàm không có call site nào.”
- Review danh sách. Xóa hoặc wire in mọi hàm mồ côi.
Một kiểm tra duy nhất đó — wiring verification — sẽ bắt được nhiều bug hơn bất kỳ bước đơn lẻ nào khác.
Thêm kiểm tra test tiếp theo. Rồi convention check. Build up từng bước.
Phương pháp luận quan trọng hơn implementation. Bắt đầu xác minh, lặp lại dựa trên những gì bạn tìm thấy.
Day la Phan 3 cua series “Xay Bang Agentic Engineering”. Truoc: Plan Mode hoac That Bai Tiep theo: Debug Dua Tren Bang Chung — Cho Agent Mat De Nhin