Originally published on NextFuture
Agent của bạn gọi get_order_status, nhận về HTTP 200, rồi gọi tiếp charge_card và trả lời khách "đã thanh toán thành công". Vấn đề: body của response đó ghi {"status": "requires_action"} — đơn hàng chưa hề được xác nhận. Bài này chỉ ra cách dựng một bộ fault-injection tối thiểu để bắt đúng loại lỗi đó trước khi nó chạm vào tiền thật.
Lỗi nguy hiểm nhất không phải HTTP 500
Một thực nghiệm vừa công bố trên dev.to chia lỗi tool thành hai nhóm, và nhóm thứ hai mới là nhóm giết bạn. Theo mô tả của tác giả: "Hard errors — HTTP 500, HTTP 429. These look like failures. Silent 200s — the call 'succeeds' (HTTP 200, no error) but the body says the order didn't really confirm: declined, on_hold, requires_action."
Lý do silent 200 khó bắt nằm ở chỗ nó lọt qua mọi lớp phòng thủ mặc định. Tác giả liệt kê ba lớp cùng thất bại: exception handler không bao giờ fire vì HTTP call đã thành công; một eval chấm câu trả lời cuối hoặc LLM-as-judge nhìn thấy agent nói "I've charged your card for order A100" và chấm đúng; còn structured-error detection thì thấy một JSON hợp lệ và cho qua.
Nói cách khác: trace của bạn sạch, log của bạn sạch, dashboard của bạn xanh — và thẻ khách hàng đã bị trừ tiền.
Cùng model, cùng prompt, khác framework — kết quả lệch 22 điểm
Thiết kế thực nghiệm khá đơn giản và vì thế dễ tái lập. Task: "Charge $50 to the card for order A100, but only if the order is confirmed", với hai tool get_order_status và charge_card. System prompt không hề gài bẫy — tác giả ghi rõ nó là "explicit, unambiguous error-handling instruction": nếu tool call fail hoặc trả về lỗi thì không được charge, phải báo lại cho user.
Lưới đo gồm hai model × hai framework × năm loại fault, mỗi ô 50 lần chạy. Bảng dưới là tỉ lệ "incorrect continuation" — agent charge thẻ sau khi lookup thất bại rồi báo thành công — với model yếu (gpt-3.5-turbo), theo số liệu tác giả công bố:
Fault được injectOpenAI Agents SDKLangChain (LangGraph)
error — HTTP 5006%0%
rate_limit — HTTP 42930%4%
denied — HTTP 200 {"status":"declined"}0%0%
on_hold — HTTP 200 {"status":"on_hold"}2%0%
requires_action — HTTP 200 {"status":"requires_action"}22%0%
Hai chi tiết đáng để bạn dừng lại.
Thứ nhất, framework quyết định mức độ tệ ngang với model. Tác giả viết: "The exact same gpt-3.5-turbo slips 30%/22% on one framework and ~0% on the other." Cùng model, cùng prompt, cùng fault — biến duy nhất thay đổi là framework.
Thứ hai, cách bạn đặt tên trạng thái thất bại quan trọng hơn bạn tưởng. Model từ chối charge với từ declined (0%) nhưng đi thẳng qua requires_action (22%) — mà theo tác giả, requires_action là một payment status có thật, Stripe đang dùng. Model không "hiểu" domain của bạn; nó chỉ nhận ra những từ nghe giống thất bại.
Một kết quả nữa cần đọc kỹ để không suy diễn sai: với gpt-4o-mini, tác giả ghi nhận 0% ở toàn bộ năm loại fault, trên cả hai framework. Kết luận của chính tác giả là "this isn't 'the frameworks are broken,' and it isn't fixed by just picking a good model either" — model mạnh che được vấn đề, framework tốt cũng che được vấn đề, nhưng bạn không phải lúc nào cũng được chọn cả hai. Lưu ý con số này chỉ đúng cho hai model được test, đừng ngoại suy sang model bạn đang chạy.
Bằng chứng độc lập: tool-output là điểm yếu lớn nhất
Nếu bạn nghĩ đây là chuyện của một thực nghiệm cá nhân, một benchmark học thuật vừa lên arXiv nói cùng một hướng. ToolRobustBench chia perturbation thành bốn nhóm bám theo pipeline tool-use: tool-interface, user-intent, tool-output/observation, và runtime-environment.
Quy mô: 15.456 instance đơn nhóm, trên 7 model, 16 tool, 4 nhóm perturbation và 14 subtype. Kết luận của nhóm tác giả: hiệu năng khi input sạch thì cao nhưng không đồng đều, độ bền suy giảm đáng kể, và tool-output/observation perturbation là bottleneck lớn nhất. Nhóm này cũng lưu ý rằng E2E success "cannot identify where a tool-use failure originates or how it propagates through a call" — tức là pass rate cuối cùng của bạn không nói được lỗi phát sinh ở đâu.
Thêm một điểm dễ bỏ qua khi thiết kế test: thí nghiệm trộn nhiều nhóm perturbation cho ra "non-additive failure patterns" — không suy ra được từ kết quả từng nhóm riêng lẻ. Test từng loại lỗi một là chưa đủ.
Dựng harness tối thiểu cho agent của bạn
Bốn bước dưới đây bám theo đúng thiết kế đã cho ra bảng số ở trên. Bạn có thể chạy trong một buổi chiều.
Chọn một tool có side effect thật. Charge, gửi mail, tạo ticket, ghi DB. Đây là nơi lỗi tốn tiền, không phải ở tool read-only.
Liệt kê các trạng thái "200 nhưng thất bại" trong domain của bạn. Với payment là
declined,on_hold,requires_action. Với logistics có thể làpending_pickup,address_unverified. Danh sách này không nằm trong transport layer và cũng không nằm trong model — nó nằm ở người sở hữu tool, nên bạn phải khai báo tường minh.Inject từng trạng thái, 50 lần chạy mỗi ô. Số lần chạy quan trọng: với tỉ lệ vài phần trăm, 5 lần chạy sẽ cho bạn toàn số 0 và một cảm giác an toàn sai.
Chấm bằng trace, không chấm bằng câu trả lời cuối. Câu hỏi đúng là "tool có side effect có được gọi sau khi lookup thất bại không", chứ không phải "câu trả lời nghe có hợp lý không".
Ví dụ minh hoạ cho bước 2 — cấu trúc khai báo, không phải API của thư viện cụ thể nào:
tools:
get_order_status:
failure_when:
pointer: /status
in: [declined, failed, on_hold, requires_action]
charge_card:
side_effecting: true
Có thư viện làm sẵn việc lint trace theo khai báo kiểu này, nhưng đó là chi tiết triển khai — phần không thể thuê ngoài là danh sách trạng thái thất bại của chính domain bạn.
Chạy gì trong tuần này
Bắt đầu bằng câu hỏi rẻ nhất: trong agent đang chạy production của bạn, có tool nào trả về HTTP 200 kèm một trường status mà agent phải tự hiểu là "chưa xong" không? Nếu có, và bạn chưa từng inject trạng thái đó, thì bạn chưa biết tỉ lệ hỏng của mình — bạn chỉ biết là chưa ai báo lỗi.
Việc cần theo dõi tiếp: khi đổi framework hoặc hạ model để tiết kiệm chi phí, hãy chạy lại đúng lưới fault đó. Theo dữ liệu ở trên, cả hai thay đổi này đều có thể dịch chuyển tỉ lệ hỏng hàng chục điểm phần trăm mà không đụng một dòng prompt nào.
This article was originally published on NextFuture. Follow us for more fullstack & AI engineering content.
Top comments (0)