Đây là một debug session tôi đã chứng kiến năm ngoái.
Developer nhận ra dark mode của blog bị nhấp nháy khi load trang. Họ nhờ Claude fix. Claude đọc CSS. Nó tìm được nguyên nhân có vẻ hợp lý. Nó thay đổi. Developer refresh. Vẫn nhấp nháy. Claude đoán thêm lần nữa. Refresh. Vẫn nhấp nháy. Lần thứ ba. Refresh. Nhấp nháy biến mất — nhưng giờ có một flash trắng thay thế.
Sau 45 phút và 6 lần thử, developer tự tìm ra nguyên nhân: một cuộc gọi localStorage.getItem() đang xảy ra sau lần paint đầu tiên, gây ra double-render. Bản fix là 3 dòng code.
Tại sao agent thất bại? Không phải model. Claude Opus có thể suy luận về race condition. Vấn đề là agent đang đoán mò mà không có bằng chứng. Nó không thể thấy trang đã render. Nó không thể thấy browser console. Nó không thể quan sát hành vi paint thực tế.
Nó đang debug trong bóng tối.
Những Điểm Mù Của Agent
Coding agent hoạt động trong terminal có ba khoảng trống tầm nhìn cơ bản:
Điểm Mù 1: Giao Diện Đã Render
Agent có thể đọc file HTML. Nó không thể thấy browser render gì từ HTML đó.
Khoảng cách lớn hơn về trường. Browser không chỉ parse HTML — nó tính toán CSS specificity, áp dụng animation, evaluate JavaScript thay đổi DOM, xử lý z-index stacking. Một element trong source HTML có thể vô hình vì element khác có z-index: 9999 phủ lên trên. Agent không thể biết mà không nhìn thấy render.
Chụp màn hình giúp nhưng không giải quyết hoàn toàn. Screenshot cho agent hình ảnh nhưng không có element ID, computed style, ARIA role. “Nút trông có vẻ sai” là dạng vấn đề khác để debug so với “pointer-events: none vì ancestor có overflow: hidden với z-index sai.”
Điểm Mù 2: Runtime Logs
Khi code chạy trong production, agent không thể thấy điều gì đang xảy ra trừ khi bạn kết nối log cho nó.
Một Cloudflare Worker thất bại im lặng sau 10ms vì lỗi encoding ký tự — agent không thể biết. Nó có thể đọc source code. Nó không thể quan sát việc thực thi.
Đây là lý do lỗi Cloudflare Workers 1101 khó debug mà không có bằng chứng. Worker đang nhận markdown, xử lý nó, và sau đó — tại một điểm nào đó — thất bại. Lỗi xuất hiện trong dashboard Cloudflare nhưng không phải trong source code.
Điểm Mù 3: Cấu Trúc Ngữ Nghĩa
Ngay cả trong code mà nó có thể đọc, agent có hiểu biết ngữ nghĩa hạn chế. Nó đọc văn bản. Nó không hiểu ý nghĩa.
Bộ Công Cụ Evidence-Based
Giải pháp là cho agent bằng chứng — quan sát thực tế của hệ thống — không chỉ là source code.
Đây là các công cụ tôi dùng:
Chrome DevTools MCP
Google ra mắt Chrome DevTools MCP vào ngày 22/9/2025. Nó cho AI coding agent truy cập trực tiếp vào Chrome DevTools Protocol.
Ý nghĩa thực tế: agent giờ có thể quan sát trình duyệt thay vì đoán về nó.
Agent có thể làm gì với DevTools MCP:
DOM inspection:
agent: "z-index tính toán của element #modal-overlay là gì?"
devtools: "Element #modal-overlay: z-index tính toán là 'auto' (không phải 9999 như trong source).
Parent .modal-wrapper có overflow: hidden đặt lại stacking context.
Fix: chuyển modal-overlay ra ngoài .modal-wrapper trong DOM."
Style debugging:
agent: "Tại sao dark mode toggle không áp dụng class dark?"
devtools: "Event listener trên #dark-toggle đang fire đúng.
document.documentElement.classList.add('dark') ĐANG được gọi.
Nhưng MutationObserver trên .theme-wrapper đang xóa class 'dark' 12ms sau.
Stack trace: theme.js:47 -> initThemeObserver -> removeAttribute"
Tôi dùng Chrome DevTools MCP để debug reading mode của blog. Highlight sync bị lệch một đoạn. Với DevTools, agent có thể quan sát giá trị scrollY, bounding box của từng đoạn, và trạng thái CSS highlight đồng thời. Fix trong 8 phút. Không có DevTools: ước tính hơn 2 giờ đoán mò.
Docker Logs MCP
Cho debug phía server, Docker Logs MCP cho agent truy cập stdout/stderr của container theo thời gian thực.
Ví dụ thực tế: Nâng cấp TTS caching đêm qua. Sau khi deploy, tôi muốn xác minh Cloudflare Cache API đang thực sự cache. Agent:
- Gửi request TTS cho cụm từ thử nghiệm
- Kiểm tra header
X-TTS-Cache(MISS lần đầu) - Gửi cùng request đó lần nữa
- Kiểm tra header (HIT — chứng minh edge cache hoạt động)
- Kiểm tra Wrangler logs để xác nhận function không chạy lần thứ hai
Không có log access, chúng tôi phải tin vào header. Với log, chúng tôi xác nhận cache hoạt động ở cấp độ hạ tầng.
Serena MCP — Hiểu Ngữ Nghĩa Code
Serena là MCP server mã nguồn mở cung cấp hiểu biết ngữ nghĩa cấp IDE về code. Nó không phải text search — mà là symbol resolution thực sự.
Điều này thay đổi gì:
Không có Serena:
agent: grep tìm "getLessonById"
kết quả: 23 match trên 12 file
agent: đọc tất cả 12 file
agent: hết context
agent: đoán giáo dục về match nào liên quan
Với Serena:
agent: "Tìm tất cả caller của getLessonById và kiểu trả về kỳ vọng"
serena: "getLessonById được gọi từ 3 nơi:
1. apps/web/src/app/[locale]/lessons/[id]/page.tsx:47
-- kỳ vọng: Lesson | null (xử lý null với redirect)
2. apps/web/src/components/LessonCard.tsx:23
-- kỳ vọng: Lesson (không có null check -- có thể crash nếu thiếu lesson)
3. apps/web/src/api/lesson-export.ts:89
-- kỳ vọng: Lesson[] (SAI KIỂU -- sẽ thất bại im lặng)"
Serena tìm ra type mismatch trong lesson-export.ts mà grep không bao giờ tìm được — vì nó hiểu kiểu, không chỉ văn bản.
GitNexus — Đồ Thị Tri Thức Codebase
GitNexus index repository thành đồ thị tri thức: node là code entity (hàm, class, interface), edge là quan hệ (gọi, import, kế thừa).
Use case chính: phân tích bán kính ảnh hưởng. Trước khi thay đổi hàm chia sẻ, agent hỏi GitNexus: “Nếu tôi thay đổi kiểu trả về của synthesizeChunk(), thứ gì bị hỏng?”
GitNexus bán kính ảnh hưởng: synthesizeChunk()
Caller trực tiếp: onRequestPost() trong tts.ts
Caller gián tiếp: không có (edge function, không có import)
Tác động: THẤP -- thay đổi bị gói gọn trong file này.
An toàn để sửa kiểu trả về.
Một Phiên Debug Evidence-Based Đầy Đủ
Đây là debug session với đầy đủ bằng chứng.
Vấn đề: TTS bài blog tiếng Việt đang phát tiếng Anh dù đang ở đường dẫn /vi/.
Không có bằng chứng:
- Agent sẽ tìm kiếm code phát hiện ngôn ngữ
- Tìm thấy nhiều ứng viên
- Đoán cái nào sai
- Thay đổi
- Hy vọng nó hoạt động
Với bằng chứng:
-
DevTools MCP: Agent kiểm tra giá trị
document.documentElement.langtrên trang VI- Kết quả:
"en"— thuộc tính lang là tiếng Anh dù URL là VI
- Kết quả:
-
Điều tra source: Agent đọc
BaseLayout.astro- Tìm thấy:
lang={locale}được đặt đúng — HTML element sẽ cólang="vi"
- Tìm thấy:
-
DevTools lần nữa: Agent kiểm tra xem trang đã hoàn tất hydrate chưa
- Tìm thấy:
langban đầu là"en"rồi đổi thành"vi"sau hydration — race condition
- Tìm thấy:
-
Nguyên nhân gốc:
TTSPlayer.astrokhởi tạo trước Astro View Transitions framework cập nhật thuộc tínhlang -
Fix: Đọc
document.documentElement.langtại thời điểm TTS được trigger (người dùng click play) thay vì lúc khởi tạo -
Xác nhận: DevTools xác nhận
langlà"vi"lúc click play — fix chính xác
Tổng thời gian: 11 phút. Không đoán mò. Mỗi bước có căn cứ trong quan sát thực tế.
Nguyên Tắc: Đo Lường, Rồi Mới Fix
Quy tắc cho debug dựa trên bằng chứng rất đơn giản:
Trước khi đề xuất fix, quan sát vấn đề.
- Giá trị thực tế của trạng thái có vẻ sai là gì?
- Giá trị đó đang được đặt ở đâu?
- Nó nên được đặt là gì?
- Khi nào giá trị sai xuất hiện?
Bốn câu hỏi. Bốn quan sát. Một bản fix.
Agent trả lời bốn câu hỏi này bằng đo lường thực tế sẽ fix bug chính xác ngay lần đầu. Agent đoán mò sẽ cần sau lần.
Hãy cho agent mắt để nhìn. Công cụ tồn tại. Hãy dùng chúng.
Đây là Phần 4 của series “Xây Bằng Agentic Engineering”. Trước: Agent QA Tiếp theo: Từ Agent Đến Production — Pipeline Deploy Đầy Đủ