AIDive

Claude Code Tự Chấm PASS Nhưng Code Vẫn Lỗi

Bởi AIDive · Đăng ngày

Coding agentMô hình AI

Nó tự chấm điểm: PASS

Công cụ kiểm tra tích hợp sẵn của Claude Code, /verify, trả về PASS cho một tính năng bị lỗi. Đó là kết quả của một bench gồm 116 phiên Claude Code trên một repository thực tế, nơi mỗi lần "done" được chấm điểm sau đó bằng các acceptance test mà agent chưa từng thấy.

Boris Cherny, người tạo ra Claude Code, gọi việc kiểm tra (verification) là điều quan trọng nhất bạn có thể trang bị cho nó. Công cụ kiểm tra tích hợp sẵn chỉ chạy khi được gọi kể từ Claude Code v2.1.215: bạn gõ /verify, hoặc nó không chạy. Trong bench này, nó trả về PASS ở 24 trên 24 lần chạy, và một trong số đó đã tung ra một tính năng bị lỗi. Bên dưới là những gì thực sự bắt được lỗi, cùng với thiết lập tốn gấp hai lần rưỡi chi phí mà chẳng thay đổi gì.

Bench: 116 lần chạy, test nó chưa từng thấy

Một "done" giả là một phiên làm việc kết thúc bằng cách tuyên bố công việc đã hoàn tất trong khi một acceptance test ẩn, hoặc bộ test suite của chính repository, bị fail. Bench này đo tần suất điều đó xảy ra dưới sáu cách thiết lập kiểm tra.

Sự nghi ngờ đằng sau bài viết này đến từ chính issue tracker của Claude Code: issue #96416, được mở ngày 2026-09-23, mô tả một review liệt kê 19 mối lo ngại, xác minh 5 trong số đó và vẫn kết luận "accept as is".

Tham số Giá trị
Repository msiemens/tinydb (cơ sở dữ liệu tài liệu Python), commit 18d73a1
Test suite của repository 226 test
Yêu cầu tính năng 6, mỗi yêu cầu nêu 5 requirement
Acceptance test ẩn một cho mỗi requirement được nêu, viết trước khi chạy bất kỳ phiên nào, không bao giờ cho agent thấy
Model Opus 5.5, Sonnet 5, Haiku 4.5
Claude Code 2.1.283, headless (claude -p), giới hạn 60 turn
Phiên 112 lần chạy task + 4 lần chạy /verify liên model = 116
Chi phí $66.29 quy đổi theo API

Sáu cách thiết lập chạy từ Claude Code thuần (chỉ có yêu cầu) đến một model thứ hai kiểm tra công việc tại thời điểm dừng:

Thiết lập Điều nó thêm vào
A plain không gì cả
B /verify /verify tích hợp sẵn, gõ như một turn thứ hai
C verify skill một project skill viết theo quy trình của Anthropic
D Stop hook một script chặn việc dừng lại khi suite còn đỏ và yêu cầu bằng chứng cho từng requirement
E tests first một quy tắc trong CLAUDE.md: viết một test fail cho mỗi requirement trước khi viết bất kỳ code nào
F Opus verifier một Stop hook chạy /verify bằng Opus trên thay đổi đó

Những yêu cầu rõ ràng trên một thư viện được test tốt là trường hợp dễ. Opus 5.5 thuần làm đúng cả 12 trên 12 lần chạy, và tự chạy test suite trước khi nói done ở 11 trong số đó.

/verify tích hợp sẵn: PASS, mọi lần, gấp 2.5x

/verify là skill kiểm tra đi kèm sẵn với Claude Code. Nó chạy thay đổi, đọc diff và viết ra một verdict từng bước. Kể từ v2.1.215, nó chỉ chạy khi người dùng tự gọi, nên trong bench này nó được gõ sau mỗi task như một turn thứ hai.

Nó trả về verdict PASS ở 24 trên 24 lần chạy trên cả ba model, bao gồm cả thay đổi bị lỗi của Haiku. Trên Opus 5.5, nó không thay đổi kết quả nào và tốn gấp 2,5 lần chi phí:

Opus 5.5, mỗi task Plain Với /verify
Chi phí $0.39 $0.96
Thời gian thực tế 75 s 125 s
Kết quả thay đổi 0 trên 12

Công bằng mà nói, /verify đã tìm ra một bug thật: trong một task của Opus, nó đã gắn cờ một vấn đề reuse-after-close vốn đã có sẵn trong thư viện, không phải do thay đổi mà nó đang kiểm tra gây ra. Với công việc vốn đã đúng, đây là một ý kiến thứ hai đắt đỏ.

Skill hay hook: cái nào hay bị bỏ qua

Một skill là một quy trình markdown mà Claude có thể mở ra khi nó đánh giá là phù hợp. Bài blog của Anthropic về verification loop mô tả một công thức sáu bước: chọn ra bước theo dõi thủ công bạn làm thường xuyên nhất, thử /verify tích hợp sẵn trước, viết quy trình đó bằng tiếng Anh đơn giản, biến nó thành một skill, rồi gọi nó trên một task mới và lặp lại. Skill của bench này, verify-change, được xây dựng đúng theo cách đó và yêu cầu Claude chứng minh từng requirement dựa trên code thật trước khi nói done.

Một skill chỉ là một gợi ý, và Claude tự quyết định có mở nó hay không:

Model Số phiên mở skill
Opus 5.5 8 trên 12
Sonnet 5 2 trên 6
Haiku 4.5 0 trên 6
Tổng 10 trên 24

Trường hợp "done" giả duy nhất của Sonnet đến từ một phiên mà skill không bao giờ được mở: phiên đó kết thúc với hai test mới do chính nó viết bị fail.

Một Stop hook là một script mà Claude Code chạy mỗi khi agent cố gắng kết thúc. Nó có thể từ chối việc dừng lại và gửi agent quay lại làm việc kèm theo lý do. Hook của bench này chạy test suite, chặn dừng lại khi suite còn đỏ, và ở lần dừng đầu tiên yêu cầu một dòng bằng chứng cho mỗi requirement. Nó kích hoạt ở mọi phiên. Trên Opus, nó tốn thêm 20% chi phí, $0.47 so với $0.39 mỗi task (94 s so với 75 s). Trong bench này, nó chưa bao giờ gặp một suite fail vào thời điểm dừng, nên không có gì để bắt: một hook luôn kích hoạt, nhưng nó chỉ kiểm tra những gì bạn bảo nó kiểm tra.

Điểm mù: bỏ sót 11 trên 11

Task bị lỗi yêu cầu các trường unique trên một bảng: hai user không thể dùng chung một email, và bất kỳ update nào khiến hai document có cùng giá trị unique đều phải raise DuplicateKeyError.

Haiku 4.5 đã test việc chuyển một user sang email của một user khác, và trường hợp đó bị từ chối đúng cách. Nó chưa bao giờ test một update khớp với nhiều document và ghi cùng một email mới vào tất cả. Trường hợp đó đi qua mà không báo lỗi trong 11 trên 11 phiên của Haiku, ở mọi thiết lập cùng model: plain, /verify, skill, Stop hook và tests first.

/verify của chính Haiku đã tích vào trường hợp nó đã thử và viết PASS. Test ẩn báo cáo DID NOT RAISE DuplicateKeyError. Sonnet 5, khi được yêu cầu chạy /verify trên cùng thay đổi đó, cũng trả về PASS. Cả Opus và Sonnet đều tự viết đúng tính năng này, nên đây chỉ là một model trên một task, nhưng một bản kiểm do chính model đó viết ra sẽ mang cùng điểm mù.

Bản kiểm từ bên ngoài: Opus nói FAIL

Cùng một /verify trên cùng thay đổi của Haiku, khi được Opus 5.5 chạy, trả về FAIL ở 3 trên 3 lần chạy, mỗi lần đều chỉ đúng trường hợp bị bỏ sót: một update khớp với nhiều document ghi cùng một giá trị vào tất cả mà không báo lỗi. Việc bắt lỗi này đến từ một model khác, không phải từ tác giả và cũng không phải từ bản kiểm của chính tác giả.

Được nối thành một Stop hook (thiết lập F), Opus kiểm tra công việc của Haiku mỗi khi Haiku cố gắng kết thúc. Kết quả:

T6, mỗi lần chạy Haiku + Opus checker Chỉ Opus 5.5
Bug được sửa / "done" giả sửa được ở 3 trên 3 0 "done" giả
Turn chạm giới hạn 60 turn ở mọi lần chạy
Chi phí $1.36 kể cả checker $0.76

Bản kiểm từ bên ngoài có hiệu quả. Trên task này, nó tốn chi phí cao hơn so với việc để model mạnh hơn tự viết tính năng một mình.

Nên áp dụng gì, và cái giá phải trả

Đừng để model đã viết ra thay đổi là bên duy nhất kiểm tra nó.

Nguyên tắc Vì sao Chi phí trong bench này
Giữ một Stop hook cho bất cứ điều gì một script có thể kiểm tra nó kích hoạt ở mọi phiên tốn thêm khoảng 20% trên Opus
Để bản kiểm thật sự đến từ bên ngoài tác giả: một model mạnh hơn ở cổng kiểm, hoặc test do chính bạn viết từ yêu cầu cùng một model đã bỏ sót bug của chính mình 11 trên 11 lần $1.36 mỗi task cho Haiku + Opus checker
Với một yêu cầu rõ ràng dùng Opus, bỏ qua việc gõ /verify 0 kết quả thay đổi tốn gấp 2,5 lần chi phí, 125 s so với 75 s

Giới hạn: một thư viện nhỏ, sáu yêu cầu rõ ràng, một đến ba lần chạy mỗi ô, các phiên headless, và các test ẩn chỉ kiểm tra những gì mỗi yêu cầu nêu ra. Với công việc phức tạp hơn, các con số này sẽ thay đổi.

Nguồn

Câu hỏi thường gặp

`/verify` của Claude Code có bắt được bug không?
Không đáng tin cậy khi cùng một model tự kiểm tra công việc của chính nó. Trong một bench 116 phiên, `/verify` trả về PASS ở 24 trên 24 lần, kể cả một thay đổi của Haiku 4.5 làm lỗi một requirement đã nêu; khi Opus 5.5 chạy `/verify` trên cùng thay đổi đó, nó trả về FAIL 3 trên 3 lần.
Dùng `/verify` với Opus trên Claude Code có đáng không?
Không đáng, với những yêu cầu rõ ràng. Trên Opus 5.5, nó đẩy chi phí mỗi task từ $0.39 lên $0.96 (gấp 2,5 lần) và thời gian từ 75 s lên 125 s, mà không thay đổi kết quả nào trong 12 lần chạy, vì Opus thuần đã tự chạy test suite ở 11 trên 12 lần.
Nên dùng skill hay Stop hook của Claude Code để kiểm tra?
Dùng Stop hook, cho bất cứ điều gì một script có thể kiểm tra. Claude chỉ mở verification skill ở 10 trên 24 phiên (Opus 8 trên 12, Sonnet 2 trên 6, Haiku 0 trên 6), trong khi Stop hook chạy mỗi khi agent cố gắng kết thúc; nó tốn thêm khoảng 20% trên Opus.
Stop hook trong Claude Code là gì?
Đó là một script mà Claude Code chạy mỗi khi agent cố gắng kết thúc. Nó có thể từ chối việc dừng lại bằng một quyết định JSON kiểu block kèm lý do, khiến agent quay lại làm việc; nó chỉ kiểm tra những gì script đó test.
Một model mạnh hơn có thể kiểm tra code của một model yếu hơn trong Claude Code không?
Có. Một `/verify` của Opus 5.5 được nối thành Stop hook đã khiến Haiku 4.5 sửa được trường hợp bị bỏ sót ở 3 trên 3 lần chạy. Nhưng tốn kém: mọi lần chạy đều chạm giới hạn 60 turn và tốn trung bình $1.36, so với $0.76 khi Opus tự viết tính năng một mình.
Vì sao một model AI lại bỏ sót bug trong chính code của nó?
Vì bản kiểm của nó chỉ test những trường hợp nó đã nghĩ tới. Haiku đã kiểm tra việc chuyển một user sang email của người khác nhưng chưa bao giờ test một update tác động tới nhiều document, nên `/verify` của chính nó tích vào trường hợp đã thử và viết PASS trong khi test ẩn báo fail.

Video liên quan