Tình huống này nên nhận ra ngay khi gặp, vì vậy hãy bắt đầu từ nó. Bạn vừa viết xong một trò chơi, điểm số bị đưa về 0 sau khi khởi động lại, và bạn hỏi agent:

Tại sao điểm số bị mất sau khi khởi động lại?

Câu trả lời đến, điềm tĩnh và mạch lạc:

Điểm số trong dự án đã được lưu, chỉ là việc ghi chưa được bật ở đâu cả.
Chỉ cần đổi một cài đặt, và kết quả sẽ được giữ lại sau khi khởi động lại.

Bạn đi kiểm tra. Trong dự án không có cài đặt nào như vậy, cũng không có chỗ nào lưu điểm số, và trước giờ chưa từng có - không một dòng, không một dấu vết. Câu trả lời đọc lên như một phân tích mã của bạn, nhưng thực ra chỉ là phỏng đoán về cách những dự án như vậy thường được tổ chức.

Vậy mà trước đó không hề có chút ngập ngừng hay lưu ý nào. Giọng điệu y hệt mọi câu trả lời trước, những câu hóa ra đều đúng. Công cụ vẫn hoạt động bình thường, và mô hình cũng không kém đi so với hôm qua.

Lý do nằm ở cách câu trả lời được tạo ra. Mô hình không lấy một dữ kiện có sẵn từ kho lưu trữ - nó chọn phần tiếp theo có khả năng nhất cho văn bản của bạn, từng mẩu một. Mà có khả năng và đúng là hai chuyện khác nhau, nhất là khi nói về dự án của bạn, dự án mà mô hình chưa hề thấy. Những chi tiết bịa ra nhưng nghe hợp lý được gọi là ảo giác.

Một chiếc bàn chia làm hai nửa: bên trái, trong bóng tối, là những lá bài bói và xúc xắc; bên phải, dưới luồng sáng, là một cuốn sách mở có đánh dấu trang và một chiếc kính lúp

Tiếp theo - những cái bẫy nằm ở đâu, vì sao lỗi luôn đi kèm giọng điềm tĩnh, và ba bước kiểm tra nào xử lý được phần lớn những trường hợp như vậy.

Mô hình thấy gì trong yêu cầu của bạn

Cái bẫy ở đây đơn giản nhất trong số tất cả: bạn tưởng mô hình đang nhìn vào dự án của mình. Thực ra nó chỉ nhìn vào những gì được đưa cho nó, và đó là những thứ sau:

  • Kiến thức được tích hợp sẵn. Những quy luật đã được ghi lại trong các tham số của mô hình trong quá trình đào tạo. Không phải những tấm thẻ chứa dữ kiện và nguồn dẫn, mà là các con số, vì vậy ngay cả một dữ kiện quen thuộc mô hình cũng có thể tái hiện không chính xác. Và kiến thức này có giới hạn về thời gian: về một thư viện ra đời sau đó, mô hình chỉ biết đúng bằng những gì người ta cho nó xem.
  • Cuộc trò chuyện hiện tại. Yêu cầu của bạn, lịch sử cuộc trò chuyện, tập tin mà agent đã đọc, đầu ra của các lệnh mà nó chạy.
  • Bộ nhớ ứng dụng. Những gì được lưu vào các tập tin riêng và được đưa vào cuộc trò chuyện khi bắt đầu phiên tiếp theo. Trong Claude Code, đó là CLAUDE.md do bạn viết, và auto memory, nơi agent tự ghi chú.
  • Nguồn bên ngoài. Tài liệu, tìm kiếm, cơ sở dữ liệu, tập tin dự án - mọi thứ có thể không có trong dữ liệu đào tạo hoặc đã thay đổi kể từ đó.

Ba nguồn sau cùng đi vào theo cùng một con đường: để ảnh hưởng đến câu trả lời, chúng phải có mặt trong cuộc trò chuyện hiện tại. Bản thân mô hình không tự nhìn vào dự án.

Từ đây rút ra một hệ quả, và chỉ cần thay đổi cách nghĩ một lần. Tập tin mà chưa ai mở thì đối với mô hình không tồn tại. Chừng nào nội dung của nó chưa vào cuộc trò chuyện, câu hỏi "trong dự án của mình, xác thực được xử lý ở đâu" nên hiểu một cách trung thực hơn là "trong những dự án tương tự, tập tin như vậy thường được đặt tên là gì". Bạn sẽ nhận được câu trả lời trong cả hai trường hợp, và trông chúng giống hệt nhau.

Bên cạnh đó có một cái bẫy nhỏ hơn, nhưng dễ khiến bạn bực mình. Tập tin đã đọc nằm lại trong cuộc trò chuyện đúng như lúc nó được đọc. Bạn đã sửa một dòng trong trình soạn thảo, còn trong lịch sử cuộc trò chuyện vẫn là phiên bản cũ. Claude Code sẽ ghi nhận rằng tập tin đã thay đổi, nhưng agent có đọc lại hay không thì do mô hình quyết định. Nếu bạn tự tay sửa code giữa chừng cuộc trò chuyện - chắc chắn hơn là hãy yêu cầu agent đọc lại một cách rõ ràng.

Nên để ý điều gì trong Claude Code

Claude Code đọc dự án và chạy lệnh trước, rồi mới trả lời. Trong câu trả lời thường thấy được nó đã mở những tập tin nào và đã chạy gì. Nếu câu trả lời về mã của bạn xuất hiện trước khi agent kịp đọc bất cứ thứ gì, thì bạn đang xem một phỏng đoán về một dự án điển hình, chứ không phải phân tích dự án của bạn.

Tại sao cách diễn đạt thay đổi kết quả

Trong tất cả những gì đi vào cuộc trò chuyện, yêu cầu của bạn là thứ duy nhất do chính bạn viết. Vì thế những chi tiết nhỏ trong nó mới có sức quyết định: đảo vài từ, thêm một chi tiết làm rõ - và câu trả lời rẽ sang hướng khác. Đây không phải chuyện công cụ thất thường.

Chỗ này dễ bị nhầm, nên tôi sẽ nói riêng. Cùng một yêu cầu gửi hai lần cũng cho ra hai câu trả lời khác nhau: phần tiếp theo được chọn không nhất thiết là phương án có khả năng cao nhất, việc sinh văn bản có những tham số quy định mô hình sẵn sàng lấy phương án xếp thấp hơn đến mức nào. Câu hỏi tự nhiên xuất hiện: nếu câu trả lời vốn đã dao động, thì câu chữ của tôi có liên quan gì. Khác biệt nằm ở chỗ cái gì dao động. Độ chênh giữa những yêu cầu giống hệt nhau là nhiễu quanh cùng một lựa chọn: câu chữ sẽ khác, hướng đi vẫn như cũ. Còn các từ trong yêu cầu thì dịch chuyển chính hướng đi đó, và dịch chuyển một cách ổn định.

Yêu cầu mơ hồ cho kết quả tệ nhất. Nó để lại quyền chọn cho mô hình, mà mô hình thì chọn phương án có khả năng nhất nói chung - trung bình của tất cả những gì nó từng thấy. Lấy ví dụ câu đơn giản "rút gọn văn bản". Rút bao nhiêu - một phần trăm, một phần ba, một nửa? Cái gì được bỏ, cái gì không được đụng vào? Yêu cầu không trả lời câu nào trong số đó, nghĩa là mô hình sẽ tự trả lời, và về mặt hình thức nó vẫn đúng. Còn "rút gọn phần mô tả 30%" hay "rút gọn phần mô tả xuống 300 ký tự" thì không để mô hình tự chọn ở chỗ mà quyền chọn thuộc về bạn.

Nhìn vào hai yêu cầu về cùng một việc sẽ thấy rõ. "Sửa phần sắp xếp" bỏ ngỏ mọi thứ: danh sách nào, theo trường nào, thế nào mới gọi là hỏng. Còn cách diễn đạt thứ hai thì gần như không bỏ ngỏ gì:

Trong danh sách đơn hàng, ngày đang xếp từ cũ đến mới, tôi cần ngược lại.
Trước tiên hãy chỉ ra tập tin và dòng nơi đặt thứ tự sắp xếp, sau đó mới đề xuất cách sửa.

Nó dài hơn đúng bằng phần mà yêu cầu đầu tiên bắt mô hình phải đoán. Và nó còn làm giúp bạn một nửa việc kiểm tra: agent chỉ ra vị trí trong mã trước cả khi bạn kịp hỏi.

Kết luận đơn giản: yêu cầu càng ít chỗ tự do, câu trả lời càng ít phỏng đoán. Đây không phải câu thần chú gì cả, mà là chuyện bình thường - hoặc bạn tự đặt khung, hoặc khung sẽ được chọn thay cho bạn.

Giọng tự tin không phải là sự thật

Và đây là cái bẫy chính. Mô hình không có một giọng riêng cho "cái này tôi biết chắc" và một giọng khác cho "cái này tôi vừa tự điền vào": cả hai câu được tạo ra theo cùng một cách và nghe điềm tĩnh như nhau. Mối liên hệ giữa độ tự tin và độ chính xác không hẳn bằng không, nhưng chẳng giúp được gì: mô hình luôn tự tin quá mức, nên chỉ dựa vào giọng điệu thì bạn không phân biệt được điều đã kiểm chứng với điều được tự điền vào.

Trong văn bản thông thường, một khẳng định kỳ lạ sẽ khiến bạn khựng lại, và bạn tự chậm lại. Trong mã, cơ chế phòng vệ này không hoạt động: tên gọi trông đúng, cấu trúc quen thuộc, còn phương thức thật của thư viện và phương thức được ghép theo lối tương tự thì nhìn bằng mắt không phân biệt nổi, trừ khi bạn thuộc lòng thư viện đó. Vì vậy lỗi thường lộ ra không phải lúc đọc mà lúc chạy.

Hơn nữa, cái tên hiếm khi được bịa ra từ con số không. Trong một nghiên cứu về mã do AI sinh ra, khi người ta tìm các tham chiếu tới những thư viện không tồn tại, hơn một phần ba số tên đó hóa ra là ghép từ hai cái tên có thật. Cảm giác đáng tin đến từ đó: từng phần của cái tên bạn đã thấy ở đâu đó, chỉ là không theo cách ghép này.

Bốn trường hợp bạn sẽ nhận ra ngay từ lần đầu, nếu biết cần tìm gì:

Trông như thế nàoChuyện gì đã xảy raBắt bằng cách nào
lệnh được gán một cờ mà nó không hề cómô hình ghép dòng lệnh theo khuôn quen thuộc, nên cờ trông như thậtphần trợ giúp của lệnh hoặc tài liệu của nó
nhắc tới một tập tin, hàm hoặc trường cấu hình không có trong dự áncái tên trông điển hình cho kiểu dự án này, nhưng chưa ai đọc dự ántìm trong dự án, yêu cầu chỉ ra dòng
tự tin kể dự án của bạn được tổ chức ra saotrong cuộc trò chuyện chưa có tập tin nào của dự án, nên thứ được mô tả là một dự án điển hìnhhỏi xem nó đã mở những tập tin nào trước khi trả lời
báo "xong", nhưng lệnh chưa hề được chạybản báo cáo được dựng từ nội dung nhiệm vụ, không phải từ kết quả chạytự chạy và xem đầu ra

Điểm chung của cả bốn: câu trả lời nghe như kết quả của một công việc, dù đằng sau nó chẳng có công việc nào.

Trường hợp cuối đáng để xem xét riêng, vì nó trông giống lừa dối, nhưng không hề có sự lừa dối nào. Agent dừng lại khi công việc trông như đã xong. Nếu không có gì để chạy, "trông như đã xong" là tín hiệu duy nhất nó có, và khi đó chính bạn trở thành vòng kiểm tra.

Cái giá của chuyện này đo được. Trong một phân tích log của agent, những lần báo "xong" sai chiếm gần một nửa tổng số thất bại. Còn ở những nơi trạng thái được kiểm tra độc lập với báo cáo của chính agent, những trường hợp như vậy gần như biến mất. Đó chính là ý nghĩa của bước kiểm tra thứ hai trong phần tiếp theo: nó thay bản báo cáo bằng thứ bạn có thể tận mắt xem.

Còn lại câu hỏi: vì sao mô hình lại thích tự điền vào hơn là thừa nhận. Vấn đề nằm ở cách nó được đào tạo và được đánh giá. Hãy hình dung một bài thi trắc nghiệm, bỏ trống thì chắc chắn được 0 điểm, còn đánh bừa thì còn có cơ hội ăn điểm. Trong hệ thống như vậy, im lặng luôn thiệt, và cứ trả lời dù chưa chắc là hợp lý. Con người về sau học lại qua những nhiệm vụ thực tế, nơi một câu trả lời sai đắt hơn câu "tôi không biết" thành thật. Mô hình thì không được học lại như thế.

Yêu cầu mô hình thành thật báo khi nó không chắc thì có ích - nhưng đó cũng là văn bản, được tạo ra bởi cùng một cơ chế. Dùng làm tín hiệu phụ thì được, thay cho việc kiểm tra thì không.

Công cụ thu hẹp các vùng mù

Ở đây nên tách bạch hai từ mà khi nói chuyện người ta hay gộp làm một. Mô hình chỉ làm được đúng một việc: nhận cuộc trò chuyện và đưa ra phần tiếp theo. Agent là chương trình bao quanh mô hình, có trong tay các công cụ: đọc tập tin, chạy lệnh, chỉnh sửa dự án.

Giờ đến một chi tiết quyết định lỗi có thể nằm ở đâu. Không phải chương trình chọn công cụ. Mô hình, trong câu trả lời của mình, yêu cầu mở tập tin này hay chạy lệnh kia, còn chương trình kiểm tra yêu cầu đó, thực thi và trả kết quả về cuộc trò chuyện. Mô hình ra quyết định, chương trình thực hiện hành động. Nghĩa là chính quyết định cũng có thể trật: sai tập tin, sai lệnh, sai test.

Dù vậy, khác biệt vẫn rất lớn. Chừng nào tập tin chưa được mở, câu trả lời về nó được dựng từ trí nhớ về những dự án tương tự. Ngay khi tập tin đã được đọc và test đã được chạy, trong cuộc trò chuyện xuất hiện dữ kiện: đây là nội dung, đây là đầu ra. Đó không còn là hồi tưởng mà là kiểm chứng, và chất lượng câu trả lời sau đó khác hẳn.

Từ đây có một mẹo chỉ tốn một dòng ở đầu cuộc trò chuyện:

Trước tiên hãy tìm và chỉ ra chỗ này nằm ở đâu trong dự án, rồi mới giải thích.

Vùng mù nhờ vậy hẹp lại, nhưng không biến mất. Agent có thể mở tập tin bên cạnh thay vì tập tin cần mở, hiểu đầu ra của lệnh theo cách riêng, hoặc dừng lại ở lời giải thích hợp lý đầu tiên mà chưa tới được nguyên nhân thật. Tất cả những chuyện đó tôi đều đã gặp, và không chỉ một lần. Vì vậy một tín hiệu bên ngoài - đầu ra của test, nội dung tài liệu, nội dung tập tin - có trọng lượng hơn lập luận tự tin nhất mà không có gì làm chỗ dựa.

Ba bước kiểm tra, mỗi bước chưa tới một phút

Tin tốt là những lỗi như vậy bắt được gần như không tốn sức. Tất cả quy về một ngã ba.

Ngã ba kiểm tra câu trả lời: nếu câu trả lời không dẫn tới tập tin, lệnh hoặc test nào, thì coi nó là giả thuyết và yêu cầu chỉ ra vị trí, còn nếu có dẫn chứng, thì tự mình xác nhận kết quảCâu trả lời của agentCó dẫn tới tập tin,lệnh hoặc test không?Coi là giả thuyếtyêu cầu chỉ ra vị tríTự xác nhậnmở ra, chạy thửkhôngcó
Ngã ba này giống nhau trong mọi nhiệm vụ: trước tiên tìm chỗ dựa trong câu trả lời, sau đó mới quyết định nên kiểm tra hay hỏi lại cho rõ

Thứ cần nhìn ở đây không phải là câu chữ thuyết phục đến đâu, mà là trong câu trả lời có gì để bám vào hay không: một tập tin cụ thể, một dòng cụ thể, một lệnh kèm đầu ra của nó. Không có chỗ dựa - nghĩa là trước mặt bạn tạm thời vẫn là giả thuyết, chưa phải kết quả. Rất nhiều câu trả lời mở đầu đúng như vậy, vấn đề chỉ là bạn sẽ làm gì tiếp.

Sau đây là ba bước kiểm tra. Mỗi bước mất chưa tới một phút, và hiếm khi cần cả ba cùng lúc.

  1. Yêu cầu chỉ ra vị trí. Không phải "giải thích kỹ hơn", mà là "cho xem tập tin, dòng và chính đoạn mã đó". Nếu không có chỗ nào như vậy, điều đó sẽ lộ ra ngay.
  2. Tự mở hoặc tự chạy kết quả. Trang trong trình duyệt, lệnh trong terminal, test trong dự án. Báo cáo về công việc và bản thân công việc - không phải là một.
  3. Đối chiếu khẳng định về một công cụ với chính công cụ đó. Lệnh có phần trợ giúp tích hợp sẵn, thư viện có tài liệu. Cờ hay khóa cấu hình được nhắc tới hoặc có ở đó, hoặc không, và nửa phút là biết. Với phương thức của thư viện thì cần thận trọng hơn: không phải thứ gì cũng được đưa vào tài liệu, nên việc không tìm thấy nó là lý do để hỏi lại, chứ chưa phải phán quyết.
Cách diễn đạt tiết kiệm thời gian

Thay vì tranh cãi với câu trả lời, hãy trực tiếp yêu cầu chỗ dựa:

Cho tôi xem tập tin và dòng nơi điều này được mô tả.
Nếu trong dự án không có chỗ nào như vậy, hãy nói thẳng rằng đây là phỏng đoán.

Hai dòng này nên luôn để sẵn trong tầm tay. Dòng đầu đưa agent vào dự án thay vì trả lời theo trí nhớ về những dự án tương tự. Dòng thứ hai cho nó một lối thoát: nói rằng không có chỗ nào như vậy hóa ra lại rẻ hơn là tự điền câu trả lời cho đến khi nghe hợp lý.

Với bản báo cáo "xong", cách hỏi tương tự cũng dùng được, chỉ là hỏi về lần chạy: "cho tôi xem lệnh bạn đã chạy và toàn bộ đầu ra của nó".

Đây không phải là bảo đảm: câu "đây là phỏng đoán" cũng được tạo ra bởi đúng cơ chế đó, như mọi thứ khác. Nó giảm khả năng trượt, nhưng không thay được việc kiểm tra.

Và thêm ngay một lưu ý, để không đọng lại ấn tượng sai. Kiểm tra không có nghĩa là không tin công cụ. Chính diff của mình bạn cũng đọc lại trước khi commit, dù tự tay viết nó. Ở đây cũng vậy, chỉ là lý do có phần nặng hơn.

Điều này có ý nghĩa gì khi làm việc với Claude Code

Những gì rút ra được từ tất cả chuyện này, ngay khi ngồi trước bàn phím:

  • câu trả lời có dẫn tới tập tin, dòng hoặc đầu ra của lệnh thì kiểm tra được trong vài giây. Câu trả lời không có những thứ đó thì chẳng có gì để kiểm tra: chỉ còn cách đọc lại và đồng ý;
  • hãy để agent đọc những gì cần thiết trước khi bạn đặt câu hỏi. Thêm một bước như vậy là loại bỏ được cả một nhóm lỗi;
  • không thể đánh giá qua độ tự tin: nó luôn bị thổi phồng, bất kể đằng sau câu chữ là kiểm chứng hay phỏng đoán;
  • "nghe có vẻ đúng" và "chạy đúng" là hai chuyện khác nhau, và chỉ có việc chạy thử mới phân biệt được chúng;
  • cái giá của lỗi càng cao, thì thứ gì đó từ bên ngoài càng nên xuất hiện sớm trong cuộc trò chuyện: test, tài liệu, tập tin thật. Với một chỉnh sửa nhỏ, liếc qua diff là đủ;
  • nếu agent sai, hãy nói với nó bằng dữ kiện, không phải bằng lời phán xét. "Trong dự án không có hàm này, đây là những gì có trong thư mục" giúp công việc tiến lên, còn "bạn lại bịa ra mọi thứ rồi" thì chẳng đưa đến đâu. Và hãy nhớ: trước một lời phản bác đầy tự tin, agent dễ chiều theo hơn là tranh luận, kể cả khi nó mới là bên đúng. Việc agent đồng ý không xác nhận rằng bạn đúng.

Một cơ chế đôi khi sai một cách đầy tự tin vẫn cực kỳ hữu ích. Biết giới hạn của nó là để khỏi mất thời gian ngạc nhiên và chuyển ngay sang kiểm tra.