Debug bằng AI hiệu quả nhất khi bạn đưa cho model những gì bạn sẽ đưa cho một đồng nghiệp ngồi cạnh: toàn bộ lỗi, đoạn code gây ra nó, điều bạn mong đợi, điều đã xảy ra thay vào đó, và những gì bạn đã loại trừ. Model không thể chạy chương trình hay xem dữ liệu của bạn, nên mọi thứ nó biết về bug đều đến từ những gì bạn dán vào. Thói quen hữu ích nhất là hỏi nguyên nhân trước khi hỏi cách sửa.
Dán mọi thứ, không chỉ thông báo lỗi
Hai tab dưới đây mô tả cùng một bug. Một script đọc tệp CSV xuất từ Excel và bị crash với KeyError: 'email', dù tệp rõ ràng có cột email.
KeyError nghĩa là từ điển không có khóa nào tên 'email'. Bạn có thể tránh lỗi bằng .get(), phương thức này trả về None thay vì báo lỗi:
email = row.get("email")
Hoặc kiểm tra trước:
if "email" in row:
email = row["email"]
Câu trả lời đầu tiên đúng về việc KeyError là gì, nhưng lời khuyên của nó lại gây hại trong trường hợp này. Với row.get("email"), crash biến mất, và mọi email đều thành None. Model trả lời cho phiên bản phổ biến nhất của lỗi vì prompt chỉ mô tả có vậy.
Bản báo cáo đầy đủ hiệu quả nhờ hai dòng. "Chỉ cột đầu tiên bị lỗi" và "lưu từ Excel dưới dạng CSV UTF-8" là những manh mối dẫn tới byte order mark, và không manh mối nào nằm trong thông báo lỗi. Bạn hiếm khi biết chi tiết nào là manh mối, nên hãy dán tất cả.
Năm điều cần đưa vào
- Lỗi đầy đủ. Toàn bộ traceback hoặc đầu ra console, không phải lời diễn đạt lại. Số dòng và chuỗi lời gọi cho thấy lỗi bắt đầu từ đâu, thường cách nơi nó lộ ra vài tầng.
- Đoạn code gây ra lỗi. Hàm bị lỗi và đoạn code gọi tới nó. Nếu thông báo lỗi nhắc tới một dòng, hãy bảo đảm dòng đó có trong phần bạn dán.
- Mong đợi so với thực tế. Mỗi thứ một câu. Với những bug không có thông báo lỗi nào (đầu ra sai, trang trắng, truy vấn chậm), đây chính là toàn bộ báo cáo bug.
- Môi trường của bạn. Phiên bản ngôn ngữ, hệ điều hành, phiên bản framework và thư viện khi chúng có thể ảnh hưởng. Bug về mã hóa ký tự, đường dẫn và ngày giờ thường phụ thuộc vào nền tảng.
- Những gì bạn đã thử. Điều này ngăn model đề xuất lại chúng, và kết quả của mỗi lần thử là bằng chứng. "Bỏ phần email đi thì chạy được" đã thu hẹp bug CSV về một cột.
Hỏi nguyên nhân trước khi hỏi cách sửa
Yêu cầu sửa lỗi mời model thay đổi code cho đến khi lỗi biến mất. Yêu cầu tìm nguyên nhân bắt nó suy luận vì sao lỗi xảy ra, và cách sửa sẽ đi ra từ đó. Hãy yêu cầu một danh sách ngắn các nguyên nhân được xếp hạng, kèm cách kiểm tra từng nguyên nhân, rồi tự chạy các bước kiểm tra. Mẫu dưới đây làm đúng việc đó:
TypeError: Cannot read properties of undefined (reading 'map')function UserList() {
const [users, setUsers] = useState();
useEffect(() => {
fetch("/api/users")
.then((res) => res.json())
.then((data) => setUsers(data));
}, []);
return (
<ul>
{users.map((u) => (
<li key={u.id}>{u.name}</li>
))}
</ul>
);
}- Component render trước khi có dữ liệu.
useState()bắt đầu vớiundefined, và React render component một lần trước khi effect chạy, nênusers.mapđược gọi trênundefined. Riêng điều đó đã tạo ra đúng lỗi của bạn, ở mọi lần tải. Kiểm tra: thêmconsole.log(users)ngay trướcreturn. Nếu nó in raundefinedvà lỗi xuất hiện ngay sau đó, thì đây chính là nguyên nhân. - API trả về thứ khác ngoài một mảng. Nếu nó trả về một đối tượng như
{ users: [...] }, lỗi sẽ khác (users.map is not a function), nên đây không phải lỗi bạn đã dán. Nó vẫn có thể là vấn đề tiếp theo sau khi sửa nguyên nhân 1. Kiểm tra:console.log(data)bên trong.thenthứ hai. - Request thất bại. Chưa liên quan: vì lần render đầu tiên đã ném lỗi, React không bao giờ chạy effect, nên request thậm chí chưa được gửi đi. Kiểm tra: tab Network không có request
/api/usersnào, điều này cũng xác nhận nguyên nhân 1.
Câu trả lời làm được điều mà kiểu trả lời sửa trước bỏ qua: nó loại trừ nguyên nhân vì lỗi mà chúng gây ra khác với lỗi bạn đã dán, hoặc vì code không bao giờ chạy tới chỗ chúng có thể xảy ra. Đó cũng là lý do văn bản chính xác của lỗi lại quan trọng. Yêu cầu model liệt kê nguyên nhân trước khi trả lời là một dạng nhẹ của chain-of-thought prompting: phần suy luận đến trước và kết luận dựa trên nó.
Tạo một bản tái hiện tối thiểu
Bản tái hiện tối thiểu (minimal reproduction) là chương trình nhỏ nhất vẫn còn cho thấy bug: dữ liệu viết cứng thay vì gọi cơ sở dữ liệu, một hàm thay vì cả module. Việc dựng nó thường giúp bạn tìm ra bug trước khi kịp hỏi ai, vì mỗi phần bạn bỏ đi hoặc vẫn giữ nguyên bug (phần đó không liên quan), hoặc làm bug biến mất (phần đó có liên quan). Khi vẫn chưa tìm ra, bản tái hiện chính là prompt lý tưởng: đủ ngắn để model đọc từng dòng, và không có code thừa có thể khiến nó đuổi theo sai vấn đề.
Nếu bug phụ thuộc vào dữ liệu, hãy kèm vài hàng gây ra nó. Model có thể suy luận về [{"id": 1, "name": null}]; nó không thể suy luận về "một số hàng trên production".
Khi các cách sửa không còn hiệu quả
Nếu cách sửa thứ ba của model thất bại theo đúng cách cũ, yêu cầu sửa lần thứ tư khó mà khá hơn. Có hai việc giúp ích hơn:
- Đưa cho nó bằng chứng mới. Thêm một dòng print hoặc log cho thấy giá trị thực tế tại điểm lỗi, chạy nó và dán đầu ra vào. Bằng chứng mâu thuẫn với giả thuyết của model là con đường nhanh nhất tới một giả thuyết tốt hơn.
- Bắt đầu một cuộc trò chuyện mới. Những chuỗi debug dài chất đầy các giả thuyết đã bỏ và các phiên bản code cũ, và model có thể cứ tiếp tục xây trên chúng. Một cuộc chat mới với code hiện tại, lỗi, bằng chứng và một dòng nói "đã loại trừ: X và Y" thường tiến xa hơn chỉ trong một câu trả lời.
Hãy cẩn thận với những câu trả lời tự tin về cách một thư viện hoạt động. Model có thể mô tả một tùy chọn hoặc một hàm không hề tồn tại; xem ảo giác AI để biết cách kiểm tra. Khi code không hỏng nhưng bạn không hiểu vì sao nó chạy như vậy, một prompt để giải thích code là công cụ phù hợp hơn.
Câu hỏi thường gặp
Làm sao nhờ ChatGPT hoặc Claude sửa code?
Dán toàn bộ thông báo lỗi và đoạn code gây ra nó, rồi thêm ba dòng ngắn: bạn mong đợi gì, điều gì đã xảy ra thay vào đó, và bạn đã thử những gì. Hỏi nguyên nhân khả dĩ nhất và cách xác nhận nó trước khi hỏi cách sửa. Một câu "sửa cái này" trống trơn sẽ nhận về cách sửa cho phiên bản phổ biến nhất của lỗi, có thể không phải lỗi của bạn.
Có nên dán cả dự án vào AI không?
Không. Hãy dán hàm nơi lỗi xảy ra, đoạn code gọi tới nó và một mẫu dữ liệu mà nó nhận. Tốt hơn nữa, hãy rút gọn vấn đề thành chương trình nhỏ nhất vẫn còn tái hiện được lỗi. Các tệp không liên quan làm câu trả lời chậm hơn và cho model thêm chỗ để đi tìm một vấn đề không hề tồn tại.
Vì sao cách sửa của AI làm lỗi biến mất nhưng chương trình vẫn không chạy đúng?
Cách sửa đó chỉ xử lý triệu chứng. Ví dụ, thay row["email"] bằng row.get("email") sẽ chặn được KeyError, nhưng nếu khóa bị thiếu do một bug ở phía trước, thì giờ mọi email đều âm thầm thành None. Hỏi nguyên nhân trước, kèm cách xác nhận nó, giúp tránh những cách sửa chỉ che giấu vấn đề.
Nếu AI cứ đề xuất những cách sửa không hiệu quả thì sao?
Ngừng hỏi cách sửa và đưa cho nó bằng chứng. Báo lại mỗi lần thử đã thay đổi gì, thêm một dòng print hoặc log cho thấy giá trị thực tế, rồi dán đầu ra đó vào. Nếu cuộc trò chuyện đã dài, hãy bắt đầu cuộc mới với một bản tóm tắt gọn: code, lỗi, bằng chứng và những cách sửa đã bị loại.