Tác nhân ReAct của bạn đang lãng phí 90% ngân sách thử lại — Đây là cách khắc phục triệt để
Hầu hết các tác nhân ReAct đang âm thầm lãng phí đến 90,8% ngân sách thử lại vào các lỗi không thể khắc phục, đặc biệt là các lời gọi công cụ bị ảo giác. Bài viết này phân tích nguyên nhân sâu xa và đề xuất ba thay đổi kiến trúc cốt lõi giúp loại bỏ hoàn toàn sự lãng phí này.

Tác nhân ReAct của bạn đang lãng phí 90% ngân sách thử lại — Đây là cách khắc phục triệt để
Hầu hết các tác nhân ReAct (ReAct-style agents) đang âm thầm tiêu hao phần lớn ngân sách thử lại (retry budget) vào những lỗi hoàn toàn không thể khắc phục. Trong một bài kiểm chuẩn với 200 nhiệm vụ, tới 90,8% số lần thử lại đã bị lãng phí — không phải do mô hình sai, mà do hệ thống liên tục thử gọi các công cụ không tồn tại.
Đây không phải là vấn đề có thể giải quyết bằng cách tinh chỉnh prompt (prompt tuning). Nguyên nhân sâu xa nằm ở một khiếm khuyết kiến trúc: để cho mô hình lựa chọn tên công cụ (tool name) ngay tại thời điểm chạy. Bài viết này sẽ chỉ ra lý do tại sao việc điều chỉnh thông số không giải quyết được gốc rễ vấn đề, và ba thay đổi cấu trúc cần thiết để loại bỏ hoàn toàn sự lãng phí này.
Nguyên nhân gốc rễ: Đường mã đang "ngốn" ngân sách của bạn
Vấn đề bắt nguồn từ một dòng mã xuất hiện trong hầu hết các hướng dẫn về ReAct mà các kỹ sư ML thường viết:
tool_fn = TOOLS.get(tool_name) # ◄─ DÒNG NGUY HIỂM
if tool_fn is None:
# Không có phân loại lỗi ở đây.
# TOOL_NOT_FOUND trông giống hệt một lỗi mạng tạm thời.
# Bộ đếm thử lại toàn cầu sẽ tiêu hao ngân sách cho một công cụ
# sẽ không bao giờ tồn tại — và ghi nhận đó là một "thất bại".
Khi một LLM bị ảo giác và đưa ra một tên công cụ sai — ví dụ như web_browser, sql_query, python_repl — lệnh TOOLS.get() sẽ trả về None. Tác nhân biết rằng công cụ này không tồn tại, nhưng bộ đếm thử lại toàn cầu thì không. Nó coi lỗi TOOL_NOT_FOUND (không tìm thấy công cụ) y hệt như lỗi TRANSIENT (tạm thời), tiêu tốn cùng một lượt ngân sách, áp dụng cùng logic thử lại và cùng cơ chế backoff.
Hệ quả là: mỗi lần ảo giác sẽ tiêu tốn các slot thử lại lẽ ra có thể xử lý một sự cố thực tế. Khi một lỗi timeout mạng thực sự xảy ra sau đó vài bước, ngân sách đã cạn kiệt. Nhiệm vụ thất bại và được ghi nhận là "hết lượt thử lại", mà không để lại dấu hiệu nào cho thấy tên công cụ bị ảo giác mới là thủ phạm thực sự.
So sánh kiến trúc định tuyến công cụ
Thực tế từ bài kiểm chuẩn: 90,8% lãng phí
Trong một bài kiểm chuẩn mô phỏng với 200 nhiệm vụ, các tác nhân ReAct tiêu chuẩn đã tiêu tốn 513 lượt thử lại. Trong số đó, 466 lượt (tương đương 90,8%) nhắm vào các lỗi không thể thành công theo định nghĩa. Ngược lại, một luồng công việc được tối ưu hóa chỉ thực hiện 80 lượt thử lại, và mọi lượt đều hữu ích.
Đây không phải là sự khác biệt về hiệu suất, mà là sự khác biệt về kiến trúc.
Phân bổ ngân sách thử lại giữa ReAct và Workflow
Bảng thống kê cho thấy rõ ràng sự lãng phí:
- ReAct: Tổng 513 lần thử, nhưng 466 lần lãng phí (90,8%).
- Workflow (đã sửa): Tổng 80 lần thử, 0 lần lãng phí.
Ngoài ra, 19 trong số 21 trường hợp thất bại của ReAct đều có cùng một nguyên nhân: tên công cụ bị ảo giác, ngân sách thử lại toàn cầu bị cạn kiệt. Không phải lỗi mạng, không phải giới hạn tốc độ (rate limit), mà đơn giản là các chuỗi ký tự ảo giác được thử lại mãi cho đến khi không còn gì lại.
Ba giải pháp cấu trúc để khắc phục triệt để
Để giải quyết vấn đề này, chúng ta cần áp dụng ba thay đổi cấu trúc sau đây vào hệ thống của mình.
1. Phân loại lỗi trước khi quyết định thử lại
Sửa đổi cơ bản nhất là phân loại lỗi tại điểm nó phát sinh. Chỉ có ba loại lỗi nên được thử lại, ba loại còn lại thì không:
- Có thể thử lại (Retryable):
TRANSIENT(tạm thời),RATE_LIMITED(giới hạn tốc độ),DEPENDENCY_DOWN(dịch vụ phụ thuộc bị sập). - Không thể thử lại (Non-retryable):
INVALID_INPUT(dữ liệu đầu vào không hợp lệ),TOOL_NOT_FOUND(không tìm thấy công cụ),BUDGET_EXCEEDED(hết ngân sách).
Khi mọi lỗi đều mang một phân loại, quyết định thử lại trở nên đơn giản:
if not exc.is_retryable():
log(RETRY_SKIPPED) # tiêu thụ 0 ngân sách
break
Việc này giúp hệ thống dừng lại ngay lập tức khi gặp lỗi vĩnh viễn thay vì lãng phí tài nguyên.
2. Sử dụng Cầu chì (Circuit Breaker) cho từng công cụ
Một bộ đếm thử lại toàn cầu coi tất cả các công cụ như một miền lỗi duy nhất. Khi một công cụ bị suy giảm, nó sẽ làm cạn kiệt ngân sách của mọi công cụ khác. Cầu chì từng công cụ (Per-tool circuit breakers) sẽ chứa đựng sự thất bại tại chỗ:
- CLOSED (Đóng): Các cuộc gọi đi qua bình thường.
- OPEN (Mở): Các cuộc gọi thất bại ngay lập tức, không ảnh hưởng đến dịch vụ upstream, không tiêu tốn ngân sách.
- HALF-OPEN (Bán mở): Một cuộc gọi thăm dò; nếu thành công, cầu chì đóng lại.
Trong bài kiểm chuẩn, luồng công việc đã ghi nhận 49 sự kiện CIRCUIT_OPEN — mỗi sự kiện là một lần thất bại nhanh mà không chạm đến dịch vụ upstream đang bị suy giảm và không tiêu tốn ngân sách thử lại.
3. Định tuyến công cụ xác định (Deterministic Tool Routing)
Đây là giải pháp khác biệt nhất giúp loại bỏ vấn đề ảo giác ở lớp định tuyến. Thay vì để LLM xuất ra tên công cụ dưới dạng chuỗi ký tự, chúng ta dùng Python để định tuyến:
- ReAct (Cũ): Tên công cụ đến từ đầu ra của LLM, có thể là bất kỳ chuỗi nào. Nếu bị ảo giác,
TOOLS.get()trả vềNonevà ngân sách bị đốt cháy. - Workflow (Mới): Tên công cụ được giải quyết từ kế hoạch (plan) ngay khi bắt đầu nhiệm vụ, luôn hợp lệ.
# Workflow — tên công cụ được giải quyết từ dict tại thời điểm chạy
STEP_TO_TOOL = {
StepKind.SEARCH: "search",
StepKind.CALCULATE: "calculate",
StepKind.SUMMARISE: "summarise",
}
tool_name = STEP_TO_TOOL[step.kind] # KeyError là không thể; ảo giác là không thể
Sử dụng LLM cho lập luận (reasoning) — các bước cần thiết, thứ tự, tham số. Nhưng hãy sử dụng Python cho định tuyến công cụ. Mô hình đóng góp cấu trúc kế hoạch (loại bước), không phải chuỗi tên công cụ.
Áp dụng vào hệ thống của bạn ngay hôm nay
Ba sửa đổi này có thể được áp dụng từng bước cho bất kỳ khung công cụ nào — LangChain, LangGraph, AutoGen hay vòng lặp công cụ tùy chỉnh.
- Thêm phân loại lỗi (30 phút): Định nghĩa hai lớp ngoại lệ: một cho lỗi có thể thử lại (
TransientToolError) và một cho lỗi vĩnh viễn (ToolNotFoundError,InvalidInputError). - Phạm vi thử lại theo lớp lỗi (15 phút): Nếu dùng thư viện
tenacity, hãy đổiretry_if_exceptionthànhretry_if_exception_type(TransientToolError). - Chuyển định tuyến công cụ vào dict (1 giờ): Nếu bạn có cấu trúc nhiệm vụ cố định, hãy định nghĩa nó dưới dạng enum
StepKindvà giải quyết tên công cụ từdict[StepKind, str]tại thời điểm lập kế hoạch.
Bằng cách áp dụng các thay đổi này, bạn không chỉ tiết kiệm chi phí token mà còn cải thiện độ tin cậy và khả năng dự đoán của hệ thống AI trong môi trường sản xuất. Độ lệch chuẩn (σ) về số bước thực thi giảm từ 1,36 xuống còn 0,46, mang lại sự ổn định mà các chỉ số thành công đơn thuần không thể phản ánh.
Bài viết liên quan

Phần mềm
Anthropic ra mắt Claude Opus 4.7: Nâng cấp mạnh mẽ cho lập trình nhưng vẫn thua Mythos Preview
16 tháng 4, 2026

Công nghệ
Qwen3.6-35B-A3B: Quyền năng Lập trình Agentic, Nay Đã Mở Cửa Cho Tất Cả
16 tháng 4, 2026

Công nghệ
Spotify thắng kiện 322 triệu USD từ nhóm pirate Anna's Archive nhưng đối mặt với bài toán thu hồi
16 tháng 4, 2026
