Agent loop: vòng lặp biến LLM thành tác nhân biết hành động
Một mô hình ngôn ngữ chỉ sinh chữ. Thứ biến nó thành agent là một vòng lặp nghĩ – làm – nhìn. Đi từ Chain-of-Thought, ReAct, tool use tới self-reflection, với paper gốc và vài quan sát từ thực tế.
Hãy tưởng tượng bạn nhờ một trợ lý “sửa cái test đang fail”. Nó mở log lỗi ra đọc, đoán nguyên nhân, sửa một dòng trong file, chạy lại test, thấy vẫn fail ở chỗ khác, sửa tiếp, rồi cuối cùng test pass và dừng. Toàn bộ quá trình đó — đọc, đoán, sửa, chạy, nhìn kết quả, lặp lại — chính là thứ tôi muốn nói tới trong bài này. Người ta hay gọi nó bằng cái tên nghe rất kêu là “agent”, nhưng bóc hết lớp vỏ ra thì bên trong chỉ là một vòng lặp.
Điều thú vị là bản thân mô hình ngôn ngữ không hề biết làm những việc đó. Nó không chạy được test, không mở được file, không biết test pass hay fail. Vậy nên trước khi nói về vòng lặp, phải nói về cái giới hạn đã sinh ra nó.
Một mô hình, tự thân, không làm được gì
Một LLM nhận vào một chuỗi token và sinh ra chuỗi token tiếp theo. Chấm hết. Nó không có tay chân, không có bộ nhớ giữa các lần gọi, không có cách nào “bấm nút”. Khi bạn thấy nó có vẻ đang làm việc gì đó, thực ra có một lớp phần mềm bên ngoài đang diễn giải phần chữ nó sinh ra thành hành động, thực thi hành động đó, rồi nhét kết quả trở lại làm đầu vào cho lần sinh kế tiếp.
Cái vòng “sinh chữ → diễn giải thành hành động → thực thi → đưa kết quả về” ấy là toàn bộ câu chuyện. Mọi framework agent bạn từng nghe tên, cho dù chúng khoác lên bao nhiêu lớp trừu tượng, rốt cuộc đều đang quay đúng vòng này. Phần còn lại của bài là kể xem con người đã dạy mô hình quay vòng đó cho ra hồn như thế nào.
Trước khi làm, hãy dạy nó dừng lại mà nghĩ
Bước nền móng không liên quan gì tới hành động, mà liên quan tới suy nghĩ. Chain-of-Thought1 là một quan sát đơn giản đến bất ngờ: nếu bạn yêu cầu mô hình viết ra các bước trung gian trước khi trả lời, thay vì phun đáp án ngay, thì nó giải đúng nhiều hơn hẳn ở các bài toán cần lập luận. Viết ra “5 quả táo, cho đi 2, còn 3” nghe ngớ ngẩn với con người, nhưng với mô hình, mỗi bước trung gian là một chỗ tựa để bước sau bám vào.
Điểm yếu của Chain-of-Thought là nó khép kín. Mô hình nghĩ hay hơn, nhưng vẫn chỉ nghĩ trong đầu — nó không kiểm chứng được suy nghĩ của mình với bất kỳ thứ gì bên ngoài. Nếu ở bước hai nó nhớ nhầm một con số, cả chuỗi lập luận phía sau sẽ trôi theo cái sai đó mà không có gì kéo lại. Suy luận thuần tuý, không có mỏ neo thực tế, là mảnh đất màu mỡ cho việc bịa.
ReAct: nghĩ, làm, rồi nhìn cho kỹ
Bước ngoặt đến từ ReAct2, mà cái tên là viết tắt của Reasoning và Acting. Ý tưởng gọn nhưng mạnh: đừng tách “nghĩ” và “làm” thành hai giai đoạn, hãy đan xen chúng trong cùng một vòng. Mô hình luân phiên sinh ra một suy nghĩ (giờ nên làm gì), một hành động (gọi công cụ để làm việc đó), rồi nhận về một quan sát (kết quả thực tế của hành động), và lặp lại từ đầu với thông tin mới trong tay.
Chính cái quan sát ấy mới là nhân vật chính. Khi mô hình gọi tìm kiếm và nhận về một kết quả thật, hay chạy một lệnh và đọc được output thật, nó không còn lơ lửng trong suy đoán nữa — nó bị buộc vào sự thật. Một chuỗi ReAct điển hình đọc gần như một cuốn nhật ký làm việc: nghĩ rằng cần biết phiên bản Python, chạy python --version, thấy 3.11, nghĩ rằng vậy thì thư viện kia tương thích, cài nó, thấy lỗi thiếu dependency, nghĩ tiếp… Mỗi vòng, cái sai của vòng trước có cơ hội lộ ra và được sửa, thay vì âm thầm nhân lên.
Vòng lặp, khi vẽ ra
Bỏ hết thuật ngữ, đây là hình dạng mà mọi agent quy về:
flowchart LR
G([Mục tiêu]) --> T[Suy luận<br/>nên làm gì tiếp?]
T --> A[Hành động<br/>gọi tool / chạy code]
A --> O[Quan sát<br/>đọc kết quả thật]
O --> C{Đã đạt<br/>mục tiêu?}
C -- chưa --> T
C -- rồi --> D([Trả lời])
Nhìn thì đơn giản, nhưng cả nghề xây agent nằm gọn trong bốn chỗ của cái hình này. Bạn mô tả mục tiêu rõ tới đâu. Bạn cho phép những hành động nào và cấm những gì. Bạn đưa quan sát trở lại context sạch sẽ hay để nó ngập rác. Và bạn định nghĩa khi nào thì dừng — một điều nghe tầm thường nhưng là nơi vô số agent hoặc dừng quá sớm khi việc chưa xong, hoặc quay mãi không thoát ra được.
Công cụ, và tại sao code lại thắng
Hành động chỉ có nghĩa nếu mô hình biết gọi công cụ đúng cách. Toolformer3 cho thấy điều này có thể học được: mô hình tự nhận ra chỗ nào trong câu thì nên chèn một lời gọi API, gọi cái gì, và dùng kết quả ra sao — mà không cần ai gán nhãn từng chỗ. Nó tự dạy mình dùng công cụ.
Nhưng có một câu hỏi tinh tế hơn: hành động nên trông như thế nào? Cách phổ biến là để mô hình phát ra một lời gọi tool dạng JSON, kiểu {"tool": "search", "query": "..."}. CodeAct4 lập luận rằng nên để hành động chính là code thực thi được, và bằng chứng khá thuyết phục. Một đoạn code có thể rẽ nhánh theo điều kiện, lặp qua một danh sách, gọi ba công cụ rồi ghép kết quả lại, lưu một giá trị vào biến để bước sau dùng — tất cả trong một hành động. Một lời gọi JSON đơn lẻ không làm được thế; bạn phải quay thêm nhiều vòng để đạt cùng kết quả. Và may thay, code lại đúng là thứ mô hình được cho ăn nhiều nhất trong quá trình huấn luyện, nên nó viết code thường trôi chảy hơn là bịa ra một lược đồ JSON lạ.
Đây cũng là lý do các công cụ như Claude Code cho agent chạy thẳng Bash, Edit, Read rồi đọc lại output. Đó không phải sự trùng hợp — đó là công thức ReAct, với code làm ngôn ngữ của hành động.
Khi agent đâm đầu vào tường
Vòng lặp trần trụi có một tật xấu: nó có thể lặp lại chính cái sai của mình một cách kiên nhẫn, hết lần này tới lần khác. Thử, sai, rồi thử lại y hệt, lại sai y hệt. Reflexion5 thêm một lớp nghe rất giống cách con người rút kinh nghiệm: sau mỗi lần thất bại, agent dừng lại, tự viết ra một đoạn ghi nhớ bằng lời — lần này sai vì tôi giả định nhầm cấu trúc dữ liệu, lần sau nên kiểm tra schema trước — rồi cất vào một bộ nhớ ngắn hạn. Ở lần thử kế tiếp, đoạn ghi chú đó được nạp lại vào context như một lời nhắc.
Cái hay ở đây là agent học được mà không hề đụng tới trọng số của mô hình. Nó không “train” lại gì cả; nó chỉ ghi chú cho chính mình bằng tiếng Anh và đọc lại ghi chú đó. Phản hồi bằng ngôn ngữ, hoá ra, là một dạng củng cố cực rẻ và cực nhanh — không GPU, không dữ liệu gán nhãn, chỉ vài câu tự rút kinh nghiệm.
Ghép tất cả thành một kiến trúc
Đến lúc này bạn có kha khá mảnh ghép: lập luận, gọi công cụ, một vòng lặp, một chút bộ nhớ để tự sửa. Làm sao xếp chúng vào cùng một khung tư duy? CoALA6 đề xuất nhìn agent như một kiến trúc nhận thức, mượn từ ngành khoa học nhận thức cũ. Ở đó có nhiều loại bộ nhớ tách bạch — bộ nhớ làm việc (những gì đang trong đầu ngay lúc này), bộ nhớ episodic (những gì đã xảy ra), bộ nhớ semantic (những sự thật về thế giới), bộ nhớ thủ tục (biết cách làm việc gì). Có một không gian hành động tác động cả vào bộ nhớ trong lẫn thế giới ngoài. Và có một quy trình ra quyết định lặp đi lặp lại để chọn hành động kế tiếp.
Tôi không nghĩ CoALA là công thức để bê nguyên vào code. Giá trị của nó là làm tấm bản đồ: khi bạn đọc một paper agent mới hay dựng một hệ thống, bạn có chỗ để đặt từng mảnh vào và thấy mình đang thiếu gì.
Quay về mặt đất
Vòng lặp này không nằm đâu xa trong phòng lab — nó chạy ngay trên máy bạn. Claude Code là một ví dụ sống: nó nhận yêu cầu của bạn, suy luận, gọi tool, đọc kết quả, và lặp cho tới khi xong. Hooks là cách bạn chèn quy tắc của mình vào giữa các mốc của vòng lặp đó — thứ gì bắt buộc phải xảy ra thì chắc chắn xảy ra, không phó mặc cho model tự nhớ. Còn Model Context Protocol7 chuẩn hoá cách agent với ra ngoài chạm vào công cụ và dữ liệu, để cùng một vòng lặp có thể làm việc với nhiều hệ thống khác nhau mà không phải viết lại từ đầu mỗi lần.
Vậy nên khi thiết kế một agent, đừng dồn hết tâm sức vào việc gọt prompt cho thật khéo. Hãy hỏi những câu về chính cái vòng lặp:
- Agent của bạn được phép làm những hành động nào?
- Nó quan sát kết quả rõ ràng đến đâu?
- Nó có biết khi nào nên dừng lại không?
Prompt hay giúp một vòng lặp chạy tốt hơn, nhưng chính hình dạng của vòng lặp mới quyết định agent của bạn thông minh hay ngờ nghệch.
Tài liệu tham khảo
- Wei, J., Wang, X., Schuurmans, D., Bosma, M., Ichter, B., Xia, F., Chi, E., Le, Q., Zhou, D. (2022). Chain-of-Thought Prompting Elicits Reasoning in Large Language Models. arXiv:2201.11903
- Yao, S., Zhao, J., Yu, D., Du, N., Shafran, I., Narasimhan, K., Cao, Y. (2022). ReAct: Synergizing Reasoning and Acting in Language Models. arXiv:2210.03629
- Schick, T., Dwivedi-Yu, J., Dessì, R., Raileanu, R., Lomeli, M., Zettlemoyer, L., Cancedda, N., Scialom, T. (2023). Toolformer: Language Models Can Teach Themselves to Use Tools. arXiv:2302.04761
- Wang, X., Chen, Y., Yuan, L., Zhang, Y., Li, Y., Peng, H., Ji, H. (2024). Executable Code Actions Elicit Better LLM Agents (CodeAct). ICML 2024. arXiv:2402.01030
- Shinn, N., Cassano, F., Berman, E., Gopinath, A., Narasimhan, K., Yao, S. (2023). Reflexion: Language Agents with Verbal Reinforcement Learning. NeurIPS 2023. arXiv:2303.11366
- Sumers, T., Yao, S., Narasimhan, K., Griffiths, T. (2023). Cognitive Architectures for Language Agents (CoALA). arXiv:2309.02427
- Anthropic (2024). Model Context Protocol · Building Effective Agents. modelcontextprotocol.io · anthropic.com/engineering