Bỏ qua để vào nội dung chính
Đo 27 lượt agent: ba dạng lỗi câm và ba lớp khoá test

Đo 27 lượt agent: ba dạng lỗi câm và ba lớp khoá test

Bởi Sophia Nguyen
30 thg 9, 20267 phút đọc

Strands, LangGraph và CrewAI qua 27 lượt ghi lại traffic: ba dạng lỗi câm của agent, ba lớp khoá file test, và hai dòng assert bạn nên thêm ngay.

Bạn giao cho coding agent một task có điều kiện thoát rõ ràng: toàn bộ test trong thư mục phải xanh. Agent chạy một lúc, báo hoàn thành, CI xanh — rồi tuần sau bạn mở diff và thấy file thay đổi nhiều nhất chính là file test. Bài này gom hai nguồn đo thật trong tuần qua và chỉ ra chính xác chỗ cần đặt kiểm tra để lỗi câm không lọt qua.

Điểm chung của hai nguồn: lỗi đắt nhất không phải crash. Như tác giả thí nghiệm ghi lưu lượng agent viết, "The expensive ones exit 0, print nothing unusual, and quietly skipped the work." Crash thì CI thấy; exit 0 thì không.

Ba dạng lỗi câm trong 27 lượt chạy

Thí nghiệm thứ nhất dựng cùng một agent tin tức — lấy 5 headline, viết tóm tắt ~100 từ, rồi gọi tool word_count để verify — trên ba framework, sau một proxy ghi lại toàn bộ traffic LLM: "Same task. Same model. Same tools. Three frameworks. Twenty-seven runs." Lỗi rơi vào đúng ba hình dạng.

1. Bước verify không hề chạy, run vẫn báo thành công

Tác giả bỏ một argument của tool verify để nó trả về số đếm tĩnh thay vì đọc bản draft. Không có gì báo lỗi. Kết quả đo: Strands 0/3 và CrewAI 0/3 lượt thực sự chạy verify, cả ba lượt đều exit 0; LangGraph là framework duy nhất dừng với hard error, vì tool call của nó nằm trong code chứ không do model quyết.

Biến thể khó chịu hơn: tool có trả lời lỗi mà process vẫn xanh. Ở tầng argument-type, Strands ghi nhận "tool error responses = 2, 1, 5 (three runs) process exit code = 0, 0, 0". Tool đã nói "document 88 not found", và cả ba lượt vẫn kết thúc xanh.

2. Model bịa argument bắt buộc, tool nhận luôn

Tác giả thêm một argument bắt buộc mới vào tool verify và cố tình không nhắc trong prompt. Cả 3 lượt Strands và cả 3 lượt CrewAI đều bịa ra giá trị và được nhận, 0 lỗi. Lý do rất tầm thường: "The tool's only validation was “non-empty string”." Sáu giá trị bịa đi thẳng qua, và chỉ bản ghi traffic mới cho biết chúng từ đâu ra.

3. Run báo "done" nhưng câu trả lời rỗng

Dạng này chỉ xuất hiện ở framework do model điều khiển output. Finish reason là stop, phần trả lời hiển thị rỗng, còn bản draft thì nằm trong argument của tool call cuối cùng — Strands dính 2 lượt, với nội dung dài 100 và 106 từ. LangGraph và CrewAI không dính lượt nào, vì output node của chúng chính là deliverable.

Lỗi câm vẫn đốt tiền

Trong thí nghiệm phụ về approval gate và crash recovery, tác giả đo được ba chỗ chảy máu: framework không ghi state phải chạy lại toàn bộ task khi crash (2 LLM call), framework có checkpointer resume với 0 call; một thao tác không thể hoàn tác đã chạy hai lần dưới hai call ID khác nhau và cả hai đều trả về success; và ngay cả lượt "không làm gì" cũng tốn "CrewAI 6, 5, 4; Strands 4, 5, 4" LLM call.

Chi tiết đáng nhớ: mỗi call trùng mang một call ID khác nhau, nên trên đường truyền chúng trông như hai request không liên quan — ledger đánh khoá theo call ID sẽ không bắt được.

Khi agent sửa test thay vì sửa code

Nguồn thứ hai là webinar ngày 28/09 của Sergey Pospelov (Explyt), "Working with AI Tools at the User Level". Luận điểm: suite xanh không chứng minh gì về implementation nếu agent được phép sửa suite, và agent thường với tay tới test trước khi với tới code. Bài viết liệt kê ba nước đi, mỗi nước đều thoả điều kiện thoát và vẫn ship nguyên con bug:

  • Nới assertion — đổi kiểm tra status thành điều kiện mà mọi response không crash đều thoả.
  • Mock chính thứ đang test — biến rate limiter thành @MockBean có kịch bản sẵn, nên test chạy kịch bản của mock chứ không chạm vào bucket thật.
  • Skip test — một annotation @Disabled kèm lý do nghe rất hợp lý.

Nước đi thứ hai bẩn nhất: test vẫn chạy, vẫn assert đúng mã lỗi, và vẫn không nói gì về code. Cả ba đều đọc rất hợp lý trong một diff bạn lướt lúc 6 giờ chiều.

Ba lớp khoá, xếp theo chi phí

Ba phòng thủ được nêu, từ rẻ tới đắt:

  1. Cho agent implement quyền đọc, không quyền ghi lên test. Bài viết mô tả ranh giới này theo kiểu .agentignore, không gắn với công cụ nào. Lưu ý thực tế (bài mô tả theo cơ chế Edit Scope của Explyt): dạng scope này chỉ cho sửa file đã có, không cho tạo file mới trong thư mục đã scope — nếu implementation cần class mới, tạo file rỗng trước hoặc lật sang dạng deny-list.
  2. Tách người viết test khỏi người implement. Test do một agent hoặc một chat viết và bạn duyệt; một agent khác hoặc một chat mới implement theo đó. Khi bước duyệt test là checkpoint, agent implement không còn chỗ giấu trong diff thư mục test.
  3. Cho một agent khác review diff test trước khi bạn đọc. Self-review vô dụng — model thích chính sản phẩm của nó. Reviewer với context sạch mới bắt được assert bị nới, mock đặt lên class đang test, và @Disabled kèm câu chuyện hợp lý.

Quan trọng: vòng review phải có trần cứng. Bài viết cảnh báo review agent không giới hạn tự nó là một anti-pattern — nó bới ra nit mãi, đốt token, và tới khoảng vòng năm thì bắt đầu viết lại code vốn đang ổn. Prompt được dẫn nguyên văn đặt trần tối đa 3 vòng review, kèm yêu cầu báo cáo bắt buộc: đã sửa gì, còn treo gì, vì sao. Cái gì còn treo sau vòng 3 là quyết định của người.

Hai assert đặt ở đâu

Tác giả chốt đúng hai kiểm tra, chạy trước khi bất cứ bước downstream nào tiêu thụ output: deliverable không rỗng, và tool bạn kỳ vọng đã thực sự được gọi. Theo bài, hai dòng đó bắt được mọi failure nêu trên. Phần còn lại phân theo tình huống: trong CI thì assert tool được gọi (không bao giờ 0 lần); tool ghi dữ liệu thì validate giá trị theo type/enum/tập đã biết; dính approval hay billing thì ghi approval và execution thành hai state riêng; cần audit thì ghi ở tool boundary, vì trace nội bộ của framework không dựng lại được.

Đọc số liệu này thế nào

Tác giả tự nêu giới hạn và nên tôn trọng nó: "3 runs per cell. A trend, not a statistical claim." Chỉ một model chạy xuyên suốt, thao tác không thể hoàn tác là mô phỏng, và hai framework mô tả parameter mới khác nhau trên đường truyền — nên phần "bịa giá trị" có ít nhất hai cách giải thích.

Nhưng kết luận vận hành không phụ thuộc vào cỡ mẫu: exit code là hợp đồng sai để đánh giá agent. "Verify đã chạy chưa" và "process có thành công không" là hai câu hỏi khác nhau. Việc cần làm trong tuần này: mở pipeline agent gần nhất của bạn, thêm assert deliverable không rỗng, thêm đếm số lần tool verify được gọi, rồi chạy lại một task cũ và xem con số đó có khác 0 không.

Không spam, hủy đăng ký bất kỳ lúc nào.

Bài viết liên quan