AI và cái bẫy của sự "lười biếng trí tuệ"

AI và cái bẫy của sự "lười biếng trí tuệ"

Lập trình viên là nghề hiếm hoi coi lười biếng là một đức tính. Larry Wall từng xếp nó đứng đầu trong ba phẩm chất của người viết Perl , trước cả thiếu kiên nhẫn và kiêu ngạo. Cái lười ấy có một nghịch lý nhỏ: muốn lười đúng cách thì phải chịu khó. Bạn phải ngồi viết script, dựng pipeline, đặt alias cho lệnh dài, tức là làm nhiều hơn hôm nay để khỏi lặp lại ngày mai.

AI agent khiến kiểu lười này rẻ đi rất nhiều. Việc trước đây phải tự viết script, giờ chỉ cần mô tả. Tôi dùng agent hằng ngày và không có ý định quay lại.

Nhưng có một kiểu lười khác, âm thầm đi kèm, và nó rẻ đến mức khó nhận ra mình đang trả giá. Đó là giao luôn cho AI cả câu hỏi "thế nào là đúng", rồi nhận output vì nó trông chuyên nghiệp. Tôi gọi đó là lười trí tuệ, và tôi nói được vì chính tôi đã rơi vào.

Hàm chạy được, và làm Supabase từ chối phục vụ

Tôi nhờ AI viết một function lấy dữ liệu từ Supabase cho một danh sách entry. Function chạy được, và tôi nhận.

Đến khi chạy với danh sách đủ dài, Supabase bắt đầu rate limit. Lý do nằm ở cách gọi dữ liệu: function truy vấn một lần cho mỗi entry, thay vì gom lại lấy một lần cho cả nhóm. Từng truy vấn riêng lẻ đều hợp lệ. Chỉ là số lần gọi tăng theo số entry, và tôi đã nhận code mà không đặt câu hỏi về con số đó.

Tôi muốn nói rõ hai điều để khỏi rút sai bài học. Supabase không có lỗi gì, rate limit là việc nó phải làm. AI cũng không "không biết code". Thứ sai nằm ở hình dạng của cả luồng dữ liệu, và đó là thứ tôi chưa nói ra thành tiêu chí, cũng chưa kiểm tra trước khi nhận.

Nói cách khác, tôi đã chấp nhận output bằng tiêu chí "chạy được". Tiêu chí thật của việc này là cách truy cập dữ liệu, và câu hỏi "N entry thì bao nhiêu request" chưa từng được đặt ra.

Bẫy nằm ở chỗ output trông quá đàng hoàng

Code do người tự viết, lúc còn nháp, thường để lộ dấu vết dở dang. Tên biến tạm bợ, chưa bắt lỗi, log in bừa. Vẻ ngoài lộn xộn đó là một tín hiệu: chưa xong, cần xem lại.

Code AI sinh ra thì ngược lại. Nó bước ra đã mặc vest. Docstring đầy đủ, type hint chuẩn, lỗi được bắt và ghi lại đúng kiểu. Mọi tín hiệu bề mặt đều nói "đã xong". Não người quen đọc tín hiệu bề mặt, nên nó dễ tự tắt phần cảnh giác.

Đây là lý do bẫy này không chừa ai. Người mới dễ rơi vào vì chưa biết phải nghi ngờ chỗ nào. Người lâu năm dễ rơi vào vì tin rằng nếu có gì sai, mình sẽ nhìn ra ngay. Niềm tin đó hình thành từ việc đọc code người viết, nơi vẻ ngoài và chất lượng thường đi cùng nhau. Với code AI, hai thứ đó tách rời. Kinh nghiệm cũ cho ta tốc độ đọc, nhưng cũng cho ta một thói quen lướt qua đúng chỗ cần dừng.

Cái lười trí tuệ ở đây rất cụ thể. Câu hỏi "hàm này gọi bao nhiêu request cho N entry" không khó. Nó chỉ không được hỏi, vì output không có dấu hiệu nào gợi rằng cần hỏi.

Đọc hết từng dòng chưa chắc là kiểm tra đủ

Có một lời khuyên hay được đưa ra: không commit code AI nếu chưa hiểu từng dòng. Lời khuyên này đúng, nhưng chưa đủ, và dễ thành một kiểu an toàn giả nếu dừng ở đó.

Khi agent sinh ra mấy trăm dòng, việc đọc từng dòng tốn một lượng chú ý lớn, mà sự chú ý thì có hạn. Ta sẽ đọc kỹ ở đầu, lướt dần về cuối, và thấy yên tâm vì "đã review". Trong chuyện Supabase, cái vòng lặp gọi truy vấn hoàn toàn có thể nhận ra khi đọc, nếu người đọc đang mang sẵn câu hỏi về số lần gọi ra ngoài. Nếu chỉ đọc để xem từng dòng có hợp lý không, nó trôi qua. Đọc kỹ cho ta biết mỗi dòng làm gì. Nó không tự động cho ta biết cả luồng đang làm gì với hệ thống bên ngoài.

Vì vậy, bên cạnh việc đọc, tôi nhìn vào năm chỗ:

  • Giả định. Code này đang mặc định điều gì về dữ liệu, về môi trường, về thứ tự thực hiện. Mặc định nào chưa được nói ra.
  • Luồng dữ liệu. Dữ liệu đi từ đâu đến đâu, qua bao nhiêu lần gọi ra ngoài, lớn lên theo cái gì. Câu "N entry thì bao nhiêu request" thuộc chỗ này.
  • Điều kiện biên. Danh sách rỗng, một phần tử, trùng lặp, thiếu trường, mất kết nối giữa chừng.
  • Thay đổi phạm vi. Agent có chạm vào file, hàm hay cấu hình nào ngoài việc được giao không. Một agent nhiệt tình có thể tiện tay sửa thêm vài thứ, và chỗ "tiện tay" đó hiếm khi được kiểm tra.
  • Kết quả và cách thử. Nó đã chạy gì để kết luận là xong, và tôi có lặp lại được không.

Năm câu này không thay cho việc đọc. Chúng đòi tôi biết mình đang mong cái gì trước khi nhìn output. AI có thể gợi ý tiêu chí cho phần này, và gợi ý thường có ích. Nhưng quyết định tiêu chí nào là tiêu chí thật thì vẫn là việc của người nhận.

Người mới vẫn dùng được, miễn là chia nhỏ và đi tìm

Có một lời khuyên khác cũng hay gặp: chỉ giao AI làm những gì mình đã biết rõ. Nghe chắc chắn, nhưng nếu áp dụng chặt thì người mới không bao giờ được bắt đầu, và chẳng ai học được công nghệ mới bằng AI cả.

Thử một ví dụ giả định. Một người chưa từng dùng Supabase, nhận đúng bài toán của tôi ở trên. Họ không có kinh nghiệm để ngửi thấy mùi N lần gọi. Nhưng họ có thể làm thế này. Chia việc làm hai: lấy dữ liệu cho một entry trước, rồi mới nghĩ đến nhiều entry. Hỏi agent một câu thẳng: với một trăm entry, đoạn này gọi ra ngoài bao nhiêu lần. Bảo agent in số lần gọi ra log để tự nhìn thay vì chỉ tin câu trả lời. Rồi mở tài liệu Supabase tìm xem có cách lấy nhiều bản ghi trong một truy vấn hay không.

Người đó chưa biết gì về rate limit lúc bắt đầu. Đi hết vòng đó, họ có thể biết, và cái biết này là của họ, không phải thứ AI viết sẵn vào comment. Chia nhỏ, hỏi, kiểm tra, đọc tài liệu. Bốn động tác ấy không thay được kinh nghiệm. Chúng giúp người mới học nhanh hơn, kiểm tra được nhiều hơn, và quan trọng nhất là biết chỗ nào mình chưa hiểu để đi nhờ người khác xem. Chúng không bảo chứng cho việc nhận code mà chưa hiểu.

Ngược lại, một người lâu năm bỏ qua bốn động tác đó thì kinh nghiệm cũng chưa chắc cứu được, như tôi đã tự chứng minh.

AI chấm được, nhưng điểm tự chấm không phải chứng cứ độc lập

Agent làm tốt ba việc: viết, kiểm tra và phản biện. Viết thì ai cũng biết. Kiểm tra là bảo nó tạo dữ liệu xấu, viết test cho một lỗi vừa sửa, đo số lần gọi. Phản biện là đưa code của chính nó, hoặc của tôi, rồi yêu cầu tìm ba lý do để chạy sai ở production. Nó cũng đề xuất được tiêu chí đánh giá, và nhiều khi đề xuất tốt hơn thứ tôi nghĩ ra lúc vội.

Vấn đề không nằm ở việc nó có chấm được hay không. Vấn đề là không lấy chính lời tự chấm ấy làm chứng cứ độc lập. Một agent viết hàm, rồi viết test cho hàm đó, rồi báo test pass. Nếu hàm hiểu sai yêu cầu, test có thể sai theo cùng một cách, vì cùng một đầu đọc đề. Test do AI viết vẫn hữu ích, nhưng nên có một mốc neo nằm ngoài nó: một giá trị kỳ vọng tôi tính tay, một bản ghi trong tài liệu chính thức, một phép đo thật, hay bản tái hiện của lỗi đã từng xảy ra. Không có mốc đó, test pass có thể chỉ đang nói rằng code nhất quán với chính nó, và ta không có cách nào biết chắc là không phải vậy.

Quay lại với cái lười của Larry Wall. Lười đúng cách vẫn tốn công, chỉ là công được bỏ vào chỗ khác. Trước đây công đó là ngồi viết script. Bây giờ công đó là ngồi viết ra, bằng lời, thế nào là đúng, rồi tìm một cách đo không dựa hoàn toàn vào lời tự báo cáo của agent. Việc gõ code đã có người gánh. Việc quyết định code ấy cần làm gì để được gọi là xong thì vẫn ở nguyên chỗ cũ, trên bàn của người bấm nút nhận.

No comments yet