
Đừng để LLM tự lái vòng lặp agent: FSM và cổng kiểm tra
Vòng lặp agent ngây thơ hỏng vì kiến trúc, không phải prompt. Kiến trúc OPVC, cổng verify hai lớp và một FSM đã cứu buổi phỏng vấn hỏng 20% số lần.
Agent của bạn chạy đẹp trong demo. Lên production, nó lặp lại đúng một tool call đang lỗi cho tới khi hết ngân sách token, hoặc nhảy thẳng sang bước cuối khi người dùng còn chưa gửi gì.
Bạn sửa system prompt, rồi sửa nữa, và tỉ lệ hỏng vẫn không nhúc nhích. Bài này trình bày cách tiếp cận khác từ hai bài viết gần đây: lấy quyền điều khiển luồng ra khỏi model và giao cho một finite state machine.
Vì sao vòng lặp agent ngây thơ hỏng
Vòng lặp mặc định ai cũng viết đầu tiên gồm năm bước: người dùng nhập prompt, LLM chọn tool, runtime chạy tool, kết quả được nối vào context, lặp lại cho tới khi model trả lời cuối cùng.
Theo bài phân tích kiến trúc, trong production vòng lặp này hỏng theo những cách rất khó chịu: agent kẹt trong vòng lặp vô hạn lặp lại tool call đang lỗi, bịa tham số khi schema output của tool đổi nhẹ, hoặc sinh ra output trung gian vi phạm invariant của domain — "costing hundreds of dollars in API credits while degrading user trust".
Tác giả chỉ ra nguyên nhân là kiến trúc chứ không phải prompt: chúng ta đối xử với agent như hàm tất định, trong khi để nó thay đổi trạng thái một cách phi tất định mà không có kiểm tra ở biên.
Vì sao "bảo model tự sửa" không đủ
Cách chữa phổ biến là viết vào system prompt: nếu tool chạy lỗi thì hãy tự nhìn lại và thử lại. Bài viết liệt kê ba lý do cách này hỏng trên production:
- Context pollution. Mỗi tool call lỗi và mỗi phản hồi bịa đặt đều làm bẩn context window. Context càng dài, attention càng suy giảm, và các lần tự sửa sau càng kém tin cậy.
- Side effect không idempotent. Nếu agent chạy một payload API nửa chừng — ví dụ tạo xong user record nhưng chưa gửi email — thì prompt lại một cách mù quáng dẫn tới mutation trùng.
- Bài toán dừng không có biên. Không có ràng buộc cấu trúc, không gian retry của agent là vô hạn. Nó sẽ đốt token để thử các biến thể của một chuyển trạng thái vốn không hợp lệ.
Kiến trúc OPVC: tách vòng lặp thành bốn pha
Đề xuất của bài viết là ngừng để LLM tự quản control flow, tách thành bốn pha tất định — Observe, Propose, Verify, Commit:
- Observe. Đọc trạng thái hiện tại và các ràng buộc đang có từ một event journal chỉ-đọc.
- Propose. LLM phát ra đúng một ứng viên chuyển trạng thái, dưới dạng đề xuất gọi tool có cấu trúc.
- Verify. Một cổng runtime tất định — Zod schema, AST parser, invariant của domain — soi đề xuất đó trước khi thực thi.
- Commit. Nếu qua cổng, lớp thực thi chạy tool, ghi kết quả vào journal bất biến, rồi đẩy state machine tiến một bước.
Điểm đáng chú ý trong phần cài đặt: cổng kiểm tra chạy hai lớp. Lớp một là parse schema, xác nhận tính đúng đắn về cấu trúc. Lớp hai là guard, kiểm tra invariant ngữ nghĩa mà JSON schema không diễn đạt được — ví dụ "không được rút số tiền lớn hơn số dư tính từ lịch sử trạng thái".
Runtime trong bài cũng có bộ đếm consecutiveFailures đối chiếu với maxRetries (mặc định 3). Vượt ngưỡng, engine ghi một event AGENT_HALTED và dừng hẳn, thay vì thử tiếp vô hạn.
Câu chuyện thực tế: 80% là không đủ
Bài thứ hai là ghi chép của một dev xây AI technical interviewer. Anh ta bắt đầu theo cách quen thuộc: ném một system prompt lớn vào LLM, bảo nó đóng vai người phỏng vấn, hỏi bài code, gợi ý khi ứng viên bí, và chấm điểm ở cuối.
Kết quả, theo lời tác giả, "It worked... about 80% of the time." Phần 20% còn lại gồm: model tự quyết định buổi phỏng vấn đã xong sau đúng một câu hỏi; lộ đáp án trong lúc cố gợi ý nhẹ nhàng; quên mất là đã hỏi rồi và hỏi lại; nhảy thẳng sang chấm điểm khi ứng viên còn chưa nộp code.
Tác giả nhấn mạnh đây không phải vấn đề kỹ năng viết prompt — anh ta đã viết lại system prompt khoảng mười lăm lần. Vấn đề mang tính cấu trúc: một LLM đang sinh câu tiếp theo không thực sự nhớ "chúng ta đang ở pha nào", và kể cả khi bạn nhắc hoàn hảo mỗi lượt, nó vẫn có thể quyết định làm việc khác.
Giải pháp: FSM cầm lái, LLM ngồi ghế phụ
Anh ta kéo toàn bộ cấu trúc buổi phỏng vấn ra khỏi LLM và đưa vào một finite state machine thuần túy, với chuỗi trạng thái SETUP → GREETING → QUESTION_ASKED → CANDIDATE_CODING → HINT_CHECK → EVALUATING → SCORING → DONE.
State machine — không phải model — quyết định đang ở pha nào và điều gì được phép xảy ra tiếp theo. LLM chỉ được gọi bên trong một trạng thái, để làm đúng việc mà trạng thái đó cần: sinh câu hỏi, sinh gợi ý, hoặc sinh scorecard.
Hai chi tiết đáng học trong phần triển khai:
- Gợi ý chỉ kích hoạt từ tín hiệu thật — hơn 35 giây không có thao tác gõ phím — chứ không phải khi model cảm thấy ứng viên có vẻ đang bí.
- Chấm điểm chỉ chạy sau khi code đã thực sự được nộp và thực thi trong sandbox, không phải khi model thấy nên kết thúc.
Kết quả khiến chính tác giả bất ngờ: anh ta tưởng sản phẩm sẽ cứng nhắc hơn, nhưng vì cấu trúc đã được bảo đảm, anh ta lại có thể để LLM tự do và tự nhiên hơn bên trong từng trạng thái. Theo cách anh ta tổng kết, ràng buộc bộ khung cho phép nới lỏng phần cơ.
Áp dụng vào hệ thống của bạn
Hai bài viết đến từ hai hướng khác nhau — một bên là kiến trúc runtime TypeScript có event sourcing, một bên là một sản phẩm đang chạy — nhưng cùng chốt lại ở một câu hỏi thiết kế.
Câu hỏi đó, theo tác giả bài thứ hai: model có thực sự cần quyết định điều gì xảy ra tiếp theo, hay nó chỉ cần sinh nội dung tốt bên trong một bước do thứ khác quyết định? Trong phần lớn trường hợp, câu trả lời là vế thứ hai.
Việc nên làm tuần này: mở log agent của bạn, lọc ra các lần agent tự quyết định chuyển pha, và đếm xem bao nhiêu trong số đó là quyết định bạn thực sự muốn giao cho model. Mỗi lần còn lại là một transition nên nằm trong code, kèm một test cho cặp (trạng thái, sự kiện) — thứ mà một system prompt không bao giờ cho bạn.
Không spam, hủy đăng ký bất kỳ lúc nào.
Bài viết liên quan

Sáu CLI coding agent: cái nào chặn được một tool call?
14 thg 9, 2026
