Mở đầu: Ghép 2 viên gạch cuối cùng thành 1 Agent thật

Bài 7 cho agent khả năng dùng tool. Bài 8 cho agent khả năng nhớ và lắp ghép prompt an toàn. Bài này ghép cả hai lại thành thứ mọi người vẫn gọi là "AI Agent": một vòng lặp suy luận từng bước, tự quyết định khi nào cần tool, khi nào đủ thông tin để trả lời, và biết dừng lại đúng lúc thay vì lặp vô hạn.

Đây là bài cuối cùng của Track C — sau bài này, aisys-agent-kernel.js đã đủ đầy đủ để Track B (Bài 10–12) dùng lại nguyên vẹn Agent class làm đơn vị điều phối trong hệ multi-agent.


📚 Điều kiện tiên quyết
Bắt buộc đã đọc Bài 7Bài 8 — bài này dùng lại nguyên vẹn ToolRegistry và không giới thiệu lại từ đầu.

1. Vòng Lặp Thought → Action → Observation

ReAct (Reasoning + Acting) là mẫu hình phổ biến nhất cho agent: thay vì trả lời ngay lập tức, agent "nghĩ thành lời" (Thought) trước khi hành động (Action), rồi quan sát kết quả (Observation) để quyết định bước tiếp theo — lặp lại tới khi đủ thông tin trả lời:

react-loop-sketch.js
// Phác thảo ý tưởng (bản đầy đủ có retry/giới hạn bước ở mục 3 và 4):
while (true) {
  const { thought, action, finalAnswer } = decide(query);   // Thought
  if (!action) return finalAnswer;                          // đủ thông tin -> dừng
  const observation = toolRegistry.execute(action.tool, action.args); // Action
  query = incorporateObservation(query, observation);        // Observation -> vòng lặp tiếp
}

Điểm mấu chốt: agent KHÔNG biết trước nó sẽ cần mấy bước — số bước phụ thuộc vào độ phức tạp câu hỏi, khác hẳn một hàm xử lý tuyến tính cố định.

2. Event Emitter Cho Streaming

Thay vì đợi cả vòng lặp chạy xong mới trả về 1 cục kết quả, runtime thật phát ra sự kiện NGAY khi có — để giao diện hiển thị "agent đang nghĩ..." theo thời gian thực. Cách đơn giản nhất: truyền các hàm callback vào, gọi ngay tại thời điểm có dữ liệu:

agent-run-streaming.js
class Agent {
  run(userQuery, { onThought, onAction, onObservation, onError, onFinish } = {}) {
    // ... trong vòng lặp:
    onThought && onThought(decision.thought, step);   // phát NGAY khi có Thought
    onAction && onAction(decision.action, step);       // phát NGAY khi có Action
    // ...
    onObservation && onObservation(observation, step); // phát NGAY khi có Observation
  }
}

Đây chính xác là cách demo mục 5 hiển thị log — mỗi dòng xuất hiện ngay khi callback tương ứng được gọi, không đợi run() return.

3. Xử Lý Lỗi Tool — Retry/Backoff

Tool có thể lỗi (input không hợp lệ, service ngoài timeout...). Agent không nên bỏ cuộc ngay ở lần lỗi đầu tiên, nhưng cũng không được thử lại vô hạn:

retry-once.js
try {
  const observation = toolRegistry.execute(action.tool, action.args);
  // ... thành công, trả lời luôn
} catch (err) {
  onError && onError(err, step);
  if (retriedThisStep) {
    return giveUp(err); // đã retry 1 lần rồi vẫn lỗi -> dừng hẳn, KHÔNG lặp mãi
  }
  retriedThisStep = true;
  step++; // cho thử lại ở vòng lặp kế tiếp
}

Demo mục 5 có preset "🧪 Bơm lỗi" cố tình gửi một biểu thức sai cú pháp — bạn sẽ thấy agent thử, lỗi, thử lại lần 2 (do mockDecide tất định nên vẫn ra cùng lỗi), rồi dừng hẳn với thông báo rõ ràng thay vì treo mãi.

💡 Mẹo: Production dùng exponential backoff, không chỉ retry-once
Agent trong bài này retry đúng 1 lần cho đơn giản sư phạm. Hệ thống thật thường dùng exponential backoff — chờ lâu hơn theo cấp số nhân giữa các lần thử (100ms, 200ms, 400ms...) — vì phần lớn lỗi tool trong thực tế là timeout mạng tạm thời, không phải lỗi logic cố định như demo này; retry ngay lập tức nhiều lần có thể làm nghẽn thêm 1 service đã quá tải.

4. Giới Hạn Số Bước & Phát Hiện Vòng Lặp Vô Hạn

🕳️ Cạm bẫy: Agent không có giới hạn bước có thể chạy MÃI MÃI
Nếu "bộ não" quyết định (model thật hoặc mock-LLM) rơi vào vòng lặp — liên tục nghĩ cần gọi tool nhưng không bao giờ thấy đủ thông tin để dừng — agent sẽ chạy vô hạn, tốn chi phí compute (và tiền API thật) không kiểm soát được. Giải pháp bắt buộc: một tham số maxSteps cứng, agent PHẢI dừng và trả về thông báo rõ ràng khi chạm giới hạn, thay vì im lặng treo máy:
max-steps-guard.js
while (step < this.maxSteps) {
  // ... toàn bộ logic ReAct + retry ở trên
}
// Ra khỏi vòng lặp vì hết maxSteps mà vẫn chưa xong -> PHẢI báo rõ, không im lặng:
return { finalAnswer: "Đã đạt giới hạn số bước tối đa — dừng lại để tránh vòng lặp vô hạn.", steps: step };

5. Thực hành tương tác: ReAct Runtime

Chọn 1 trong 4 preset — quan sát log Thought/Action/Observation xuất hiện theo thứ tự, và đặc biệt thử preset "Bơm lỗi" để xem cơ chế retry-rồi-dừng hoạt động trực tiếp:

🤖 ReAct Runtime
aisys-react-lab.js (trích)
import { Agent } from "./aisys-agent-kernel.js";
import { createDemoRegistry } from "./aisys-tool-lab.js"; // TÁI SỬ DỤNG registry Bài 7

const agent = new Agent({ toolRegistry: createDemoRegistry(), maxSteps: 4 });
agent.run(query, { onThought, onAction, onObservation, onError, onFinish });

Track C đã hoàn chỉnh: ToolRegistry (Bài 7) + Memory/PromptTemplate (Bài 8) + Agent ReAct loop (bài này). Bài 10 sẽ mở Track B — dùng nhiều Agent như thế này cùng lúc, điều phối qua một message bus.

Tải file code thực hành minh họa bài học

File JavaScript aisys-agent-kernel.js — bản HOÀN CHỈNH của cả 3 bài Track C (Tool + Memory + Agent/ReAct), cùng aisys-react-lab.js wiring demo bài này:

Tải về aisys-agent-kernel.js

📖 Tài liệu tham khảo

Bài viết liên quan trong series

Bài 8: Memory & Prompt Template Engine Bài 10: Kiến Trúc Multi-Agent — Vai Trò & Giao Tiếp Quay lại Lộ trình Kỹ Thuật Hệ Thống AI

Bình luận