# Khi nào agent phải dừng: bốn lớp chặn và lúc bàn giao cho con người

> Một agent không có điều kiện dừng có thể chạy vô hạn và đốt chi phí mà không ném ra lỗi nào, và người phải giải thích với khách hàng là FDE.

Bản gốc: https://fdetimes.net/vi/bach-khoa/dieu-kien-dung-cua-agent/

Thử hình dung buổi sáng thứ Hai ở văn phòng khách hàng. Agent xử lý ticket hoàn tiền mà bạn deploy tuần trước đã chạy suốt đêm trên đúng một ticket: tra đơn hàng, đọc kết quả, tra lại đơn hàng, đọc lại. Không có lỗi nào được ném ra, chỉ có log dài hàng nghìn dòng và hóa đơn API tăng vọt.

Agent không trả lời sai. Nó chỉ không biết lúc nào phải dừng. Một FDE sẽ thấy sự cố kiểu này khó giải thích với khách hàng, vì không có dòng lỗi nào để chỉ vào, chỉ có một hệ thống cứ chạy mãi.

Tin tốt là cách phòng chuyện này không có gì bí ẩn. Nó gồm hai phần: đặt giới hạn cho vòng lặp, và thiết kế thời điểm agent bàn giao việc cho con người.

## Vì sao một agent không tự biết dừng?

Hugging Face Agents Course mô tả vòng lặp agent giống một vòng while: chu trình suy nghĩ, hành động, quan sát cứ tiếp tục cho đến khi mục tiêu được hoàn thành. Hành động cuối cùng là gửi câu trả lời về cho người dùng, và vòng lặp khép lại ở đó.

Vấn đề nằm ở chữ "cho đến khi". Nếu model không bao giờ tin rằng mình đã đạt mục tiêu, vì dữ liệu thiếu, tool trả lỗi mơ hồ hay prompt tự mâu thuẫn, thì điều kiện dừng tự nhiên sẽ không bao giờ thỏa. Tài liệu Vercel AI SDK nói thẳng: không có giới hạn bước, agent có thể chạy vô hạn hoặc gây chi phí lớn.

Vì thế, trong hướng dẫn xây dựng agent, Anthropic khuyên thêm điều kiện dừng, chẳng hạn số vòng lặp tối đa, để giữ quyền kiểm soát. Nhưng đặt ngưỡng ở đâu là một đánh đổi thật. Một bài hướng dẫn của Metric Coders tóm lại thế này: dừng quá sớm thì kết quả thiếu, dừng quá muộn thì lan man, lặp lại hoặc ảo giác.

## Bốn lớp chặn, mỗi lớp bắt một kiểu hỏng

Không giới hạn nào đủ dùng một mình, vì mỗi cái bắt một loại sự cố khác nhau. AI SDK cho phép ghép nhiều điều kiện dừng, và vòng lặp kết thúc khi bất kỳ điều kiện nào thỏa. Dù dùng framework nào, bạn cũng nên nghĩ theo cách đó.

| Lớp chặn | Bắt được gì | Điểm yếu cần biết |
|---|---|---|
| Số bước tối đa | Vòng lặp quay mãi, chu trình logic | Ngưỡng quá thấp làm tác vụ dài bị cắt |
| Ngân sách token | Chi phí phình to dù số bước ít | Chỉ giới hạn token đầu ra thì có thể bị cắt giữa câu |
| Deadline thời gian | Tool chậm, người dùng đang chờ | Không nói gì về chất lượng kết quả |
| Phát hiện lặp lại | Gọi cùng tool, cùng tham số nhiều lần | Phải định nghĩa thế nào là "giống nhau" |

Về số bước, AI SDK mặc định dừng ToolLoopAgent sau 20 bước. Đó là điểm khởi đầu hợp lý chứ không phải con số thần kỳ. Về token, bài của Metric Coders lưu ý rằng giới hạn số token được sinh ra là cách đơn giản nhưng dễ cắt ngang câu, nên khuyên kết hợp nhiều cách.

Stop sequence, tức dừng ngay khi model phát ra một chuỗi định trước, hợp với đầu ra có cấu trúc. Nhưng cách này mong manh nếu model không phát ra đúng chuỗi đó.

Trong ví dụ bên dưới, lớp phát hiện lặp lại và deadline thời gian đều được viết tay ngay trong vòng lặp, cạnh hai giới hạn quen thuộc là bước và token.

## Dừng không phải là bỏ cuộc: bàn giao cho con người

Anthropic mô tả việc agent có thể tạm dừng để lấy phản hồi của con người tại các điểm kiểm tra, hoặc khi gặp vật cản.

Trang agentpatterns.ai cụ thể hóa thành ba vị trí. Một là khi độ tin cậy thấp. Hai là khi vòng lặp đã tiêu một tỷ lệ cấu hình được của ngân sách token, tiền hoặc số lần gọi tool. Ba là trước mọi hành động mà vòng lặp không thể tự hoàn tác từ trạng thái của nó.

Với khách hàng doanh nghiệp, vị trí thứ ba đáng lo nhất. Tra cứu đơn hàng sai thì tra lại được. Hoàn tiền sai hay gửi nhầm email cho khách thì không lấy lại được.

**Điểm mấu chốt:** Giới hạn bước bảo vệ hóa đơn của khách hàng; điểm duyệt trước hành động không hoàn tác được bảo vệ khách hàng của khách hàng.

## Ví dụ: agent hoàn tiền cho một sàn thương mại điện tử

Giả sử bạn làm agent xử lý ticket hoàn tiền, với các tool: tra đơn, tra chính sách, hoàn tiền và gửi email. Các ngưỡng dưới đây chỉ là con số giả định để minh họa: tối đa 20 bước, ngân sách 40.000 token, bàn giao cho nhân viên khi đã dùng 80% ngân sách, deadline 90 giây.

```python
import time

MAX_STEPS = 20
TOKEN_BUDGET = 40_000
SOFT_FRACTION = 0.8          # chạm 80% ngân sách thì dừng để hỏi con người
DEADLINE_S = 90
MAX_REPEAT = 3               # cùng tool + cùng tham số quá 3 lần = đang lặp
IRREVERSIBLE = {"issue_refund", "send_email"}

def run(task):
s = {"steps": 0, "tokens": 0, "history": [], "calls": {}}
start = time.monotonic()
while True:
if s["steps"] >= MAX_STEPS:
return handoff(task, s, "max_steps")
if s["tokens"] >= TOKEN_BUDGET * SOFT_FRACTION:
return handoff(task, s, "budget")
if time.monotonic() - start > DEADLINE_S:
return handoff(task, s, "timeout")

step = llm_next_step(task, s["history"])
s["steps"] += 1
s["tokens"] += step.usage.total_tokens

if step.is_final:
return {"status": "done", "answer": step.answer, "steps": s["steps"]}

key = (step.tool, str(sorted(step.args.items())))
s["calls"][key] = s["calls"].get(key, 0) + 1
if s["calls"][key] > MAX_REPEAT:
return handoff(task, s, "repeat_loop", pending=step)

if step.tool in IRREVERSIBLE:
return handoff(task, s, "needs_approval", pending=step)

obs = call_tool(step.tool, step.args)
s["history"].append((step, obs))

def handoff(task, s, reason, pending=None):
return {
"status": "needs_human",
"reason": reason,
"steps": s["steps"],
"tokens": s["tokens"],
"summary": summarize(s["history"]),
"pending_action": pending,
}
```

Quay lại kịch bản ở đầu bài. Agent tra đơn, nhận lỗi timeout từ API đơn hàng, rồi tra lại với đúng mã đơn đó. Đến lần gọi thứ tư, bộ đếm lặp kích hoạt và trả về `repeat_loop` kèm tóm tắt lịch sử, thay vì quay suốt đêm.

Ở một ticket khác, agent tra đơn, tra chính sách, kết luận khách đủ điều kiện và định gọi `issue_refund`. Vòng lặp dừng với lý do `needs_approval`, kèm hành động đang chờ duyệt. Nhân viên chăm sóc khách hàng chỉ cần kiểm tra rồi bấm duyệt hoặc từ chối, không phải đọc lại từ đầu.

Hãy để ý rằng hàm `handoff` không chỉ báo "thất bại". Nó trả về lý do, số bước, số token và bản tóm tắt. Nhờ vậy người tiếp nhận có thể làm tiếp chỉ trong vài giây.

Mỗi lý do dừng nên dẫn tới một việc cụ thể của con người. Với `repeat_loop`, người nhận kiểm tra tool đang lỗi gì và xử lý ticket thủ công nếu cần. Với `budget` hay `timeout`, nhân viên đọc tóm tắt rồi quyết định cho agent chạy tiếp hay tự làm nốt. Còn `max_steps` là việc của kỹ sư: tìm chu trình trước khi nghĩ đến chuyện nâng giới hạn.

## Tự làm ở dự án của bạn

Bắt đầu bằng việc liệt kê mọi tool và chia thành ba nhóm: chỉ đọc, ghi nhưng hoàn tác được, và ghi không hoàn tác được. Nhóm cuối luôn phải qua điểm duyệt, ít nhất là trong giai đoạn đầu deploy.

Tiếp theo, chạy agent trên một mẫu tác vụ thật và ghi lại số bước, số token của những lần thành công. Đặt giới hạn cao hơn mức thường gặp một khoảng vừa đủ, thay vì đoán. Sau đó thống nhất với khách hàng: ai nhận việc khi agent dừng, qua kênh nào, và họ cần thấy những gì.

Cuối cùng, biến "lý do dừng" thành một metric. Nếu tỷ lệ dừng vì `max_steps` tăng lên sau một lần đổi prompt, bạn biết ngay có chỗ hỏng.

## Những lỗi hay gặp

Lỗi đầu tiên cần tránh là nâng giới hạn ngay khi thấy agent bị cắt. Tài liệu LangGraph về lỗi GRAPH_RECURSION_LIMIT nhắc rằng nếu bạn không nghĩ đồ thị sẽ lặp nhiều mà nó vẫn chạm giới hạn, nhiều khả năng đang có chu trình. Bạn có thể tăng `recursionLimit`, nhưng phải kiểm tra logic vòng lặp vô hạn trước.

Lỗi thứ hai là chỉ dựa vào giới hạn token đầu ra, khiến người dùng nhận câu trả lời đứt giữa chừng. Lỗi thứ ba là dừng trong im lặng: vòng lặp kết thúc mà người dùng không nhận được gì, trái với nguyên tắc vòng lặp phải khép lại bằng một câu trả lời.

Lỗi thứ tư khó thấy hơn: bàn giao cho nhân viên mà không kèm ngữ cảnh. Người nhận phải mở log và đọc lại toàn bộ. Khi đó, rất dễ hiểu nếu họ bắt đầu coi agent là gánh nặng chứ không phải trợ thủ.

## Biến kỹ năng này thành điểm cộng khi đi phỏng vấn

Khi đọc mô tả công việc FDE, hãy để ý xem có nhắc đến human-in-the-loop hay guardrails không. Nếu có, đó là chỗ để bạn đưa kỹ năng này ra. Trong CV, đừng viết chung chung "xây dựng AI agent".

Hãy ghi rõ bạn đã thiết kế điều kiện dừng và luồng bàn giao cho con người, kèm một con số đo được, ví dụ tỷ lệ tác vụ phải chuyển cho nhân viên xử lý.

Bài tập tuần này: lấy một agent bạn từng viết, cố tình làm cho một tool luôn trả lỗi, rồi chạy thử. Nếu agent tự dừng sau vài lần thử và trả về một bản bàn giao đọc hiểu được trong mười giây, bạn đã có một câu chuyện cụ thể để kể ở buổi phỏng vấn tiếp theo.

**Thử ngay tuần này:**

- Mở một agent bạn đang làm, liệt kê mọi tool và đánh dấu tool nào không thể hoàn tác. Đặt điểm duyệt của con người ngay trước những tool đó.
- Thêm bốn bộ đếm vào vòng lặp: số bước, số token, số giây đã chạy và số lần gọi lặp cùng tool với cùng tham số. Ghi log lý do dừng cho mọi lần chạy.
- Chạy lại 20 tác vụ thật, đếm số lần dừng vì đạt mục tiêu và số lần dừng vì chạm giới hạn, rồi dựa vào đó để chỉnh ngưỡng.

## Nguồn

- [Building effective agents (Anthropic)](https://www.anthropic.com/engineering/building-effective-agents)

- [Understanding AI Agents through the Thought-Action-Observation Cycle (Hugging Face Agents Course)](https://huggingface.co/learn/agents-course/en/unit1/agent-steps-and-structure)

- [Defining Stopping Criteria in Large Language Models: A Practical Guide (Metric Coders)](https://www.metriccoders.com/post/defining-stopping-criteria-in-large-language-models-a-practical-guide)

- [Loop Control (AI SDK)](https://ai-sdk.dev/docs/agents/loop-control)

- [GRAPH_RECURSION_LIMIT (LangGraph docs)](https://docs.langchain.com/oss/javascript/langgraph/errors/GRAPH_RECURSION_LIMIT)

- [Human-in-the-Loop Checkpoints as Loop Control (agentpatterns.ai)](https://agentpatterns.ai/loop-engineering/human-in-the-loop-checkpoints/)
