Thách thức của sự tự do trong kiến trúc AI Agent

Khi xây dựng các hệ thống trí tuệ nhân tạo dựa trên mô hình ngôn ngữ lớn (LLM), mô hình ReAct (Reasoning and Acting) thường được áp dụng để giúp Agent tự đưa ra quyết định, lựa chọn công cụ (tool calling) và thực thi các tác vụ phức tạp. Tuy nhiên, trong môi trường production của doanh nghiệp, sự tự do này lại là một rủi ro lớn.

Nếu không có cơ chế kiểm soát, AI Agent có thể gặp phải các vấn đề nghiêm trọng như:

  • Vòng lặp vô hạn (Infinite Loops): Agent liên tục gọi một công cụ với cùng một tham số do không nhận diện được lỗi từ hệ thống downstream.
  • Thực thi sai trình tự nghiệp vụ: Agent quyết định gọi công cụ thanh toán (Payment Tool) trước khi thực hiện bước kiểm tra tồn kho (Inventory Check).
  • Mất kiểm soát chi phí: Việc lặp lại các lượt gọi API (LLM calls) không kiểm soát dẫn đến hóa đơn OpenAI tăng vọt chỉ trong vài phút.

Để giải quyết bài toán này, các kỹ sư phần mềm cần áp dụng một mô hình kinh điển trong khoa học máy tính: Finite State Machine (FSM - Máy trạng thái hữu hạn) để thiết lập các ranh giới (guardrails) cứng, định hướng luồng thực thi của Agent một cách an toàn và có thể dự đoán được.

Kiến trúc State Machine kết hợp AI Agent

Trong kiến trúc này, AI Agent không còn toàn quyền quyết định bước tiếp theo một cách tự phát. Thay vào đó, nó hoạt động dưới sự giám sát của một State Machine. State Machine định nghĩa trước các trạng thái hợp lệ và các bước chuyển cảnh (transitions) được phép xảy ra.

Hãy tưởng tượng một Agent xử lý đơn hàng (Order Processing Agent). Chúng ta định nghĩa các trạng thái sau:

  • IDLE: Trạng thái ban đầu, chờ yêu cầu từ người dùng.
  • CHECKING_STOCK: Agent đang gọi công cụ kiểm tra kho hàng.
  • PROCESSING_PAYMENT: Agent thực hiện thanh toán sau khi xác nhận đủ hàng.
  • CONFIRMING_ORDER: Gửi email xác nhận và cập nhật trạng thái đơn hàng.
  • COMPLETED: Trạng thái hoàn thành thành công.
  • FAILED: Trạng thái lỗi khi có bất kỳ bước nào thất bại hoặc vi phạm quy tắc.

Agent chỉ có thể chuyển từ CHECKING_STOCK sang PROCESSING_PAYMENT nếu và chỉ nếu công cụ kiểm tra kho hàng trả về kết quả hợp lệ. Nó không thể nhảy cóc từ IDLE thẳng sang PROCESSING_PAYMENT.

Triển khai State Machine cho AI Agent trong Node.js

Dưới đây là hướng dẫn chi tiết cách xây dựng một hệ thống điều phối Agent sử dụng State Machine thuần bằng Node.js. Chúng ta sẽ không sử dụng các thư viện quá phức tạp để hiểu rõ bản chất vận hành bên dưới.

1. Định nghĩa cấu trúc dữ liệu và các Trạng thái

Trước hết, chúng ta định nghĩa các trạng thái, sự kiện và cấu trúc của State Machine bằng JavaScript chuẩn ES6.

const States = {
  IDLE: "IDLE",
  CHECKING_STOCK: "CHECKING_STOCK",
  PROCESSING_PAYMENT: "PROCESSING_PAYMENT",
  CONFIRMING_ORDER: "CONFIRMING_ORDER",
  COMPLETED: "COMPLETED",
  FAILED: "FAILED"
};

const Events = {
  START: "START",
  STOCK_CONFIRMED: "STOCK_CONFIRMED",
  STOCK_OUT: "STOCK_OUT",
  PAYMENT_SUCCESS: "PAYMENT_SUCCESS",
  PAYMENT_FAILED: "PAYMENT_FAILED",
  CONFIRM_SUCCESS: "CONFIRM_SUCCESS",
  ERROR: "ERROR"
};

// Định nghĩa bảng chuyển trạng thái (Transition Table)
const stateTransitions = {
  [States.IDLE]: {
    [Events.START]: States.CHECKING_STOCK
  },
  [States.CHECKING_STOCK]: {
    [Events.STOCK_CONFIRMED]: States.PROCESSING_PAYMENT,
    [Events.STOCK_OUT]: States.FAILED,
    [Events.ERROR]: States.FAILED
  },
  [States.PROCESSING_PAYMENT]: {
    [Events.PAYMENT_SUCCESS]: States.CONFIRMING_ORDER,
    [Events.PAYMENT_FAILED]: States.FAILED,
    [Events.ERROR]: States.FAILED
  },
  [States.CONFIRMING_ORDER]: {
    [Events.CONFIRM_SUCCESS]: States.COMPLETED,
    [Events.ERROR]: States.FAILED
  },
  [States.COMPLETED]: {},
  [States.FAILED]: {}
};

2. Xây dựng bộ điều phối Agent (Agent Orchestrator)

Bộ điều phối này có nhiệm vụ lưu giữ trạng thái hiện tại, nhận kết quả từ LLM/Tools, đối chiếu với State Machine để quyết định xem hành động tiếp theo có hợp lệ hay không.

class AgentStateMachine {
  constructor(orderId) {
    this.orderId = orderId;
    this.currentState = States.IDLE;
    this.context = {
      orderId: orderId,
      items: [],
      paymentStatus: null,
      errorLog: []
    };
  }

  transition(event) {
    const nextState = stateTransitions[this.currentState]?.[event];
    if (!nextState) {
      throw new Error(`Chuyển trạng thái không hợp lệ: Từ ${this.currentState} qua sự kiện ${event}`);
    }
    console.log(`[State Transition] ${this.currentState} ---(${event})---> ${nextState}`);
    this.currentState = nextState;
    return this.currentState;
  }

  updateContext(data) {
    this.context = { ...this.context, ...data };
  }
}

3. Tích hợp OpenAI SDK để điều khiển luồng

Bây giờ, chúng ta sẽ viết mã nguồn tích hợp OpenAI SDK. Thay vì để OpenAI tự do gọi bất kỳ tool nào, chúng ta sẽ giới hạn danh sách công cụ (tools) dựa trên trạng thái hiện tại của State Machine. Đây chính là kỹ thuật State-based Tool Gating.

const { OpenAI } = require("openai");
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });

// Giả lập các công cụ hệ thống (System Tools)
const toolsDefinition = {
  checkStock: {
    name: "checkStock",
    description: "Kiểm tra số lượng hàng tồn kho của sản phẩm.",
    parameters: {
      type: "object",
      properties: {
        productId: { type: "string" },
        quantity: { type: "number" }
      },
      required: ["productId", "quantity"]
    }
  },
  processPayment: {
    name: "processPayment",
    description: "Thực hiện trừ tiền tài khoản khách hàng.",
    parameters: {
      type: "object",
      properties: {
        amount: { type: "number" }
      },
      required: ["amount"]
    }
  }
};

// Hàm lấy danh sách công cụ được phép sử dụng dựa trên trạng thái hiện tại
function getAllowedTools(state) {
  if (state === States.CHECKING_STOCK) {
    return [{ type: "function", function: toolsDefinition.checkStock }];
  }
  if (state === States.PROCESSING_PAYMENT) {
    return [{ type: "function", function: toolsDefinition.processPayment }];
  }
  return []; // Các trạng thái khác không được phép gọi tool
}

4. Vòng lặp thực thi có kiểm soát (Controlled Execution Loop)

Chúng ta xây dựng hàm chạy chính để điều phối toàn bộ quá trình xử lý đơn hàng. Mỗi bước đi của Agent đều được kiểm duyệt nghiêm ngặt bởi State Machine.

async function runOrderAgent(orderId, userPrompt) {
  const fsm = new AgentStateMachine(orderId);
  console.log(`Bắt đầu xử lý đơn hàng: ${orderId}`);
  
  // Khởi động State Machine
  fsm.transition(Events.START); // Chuyển sang CHECKING_STOCK

  let isRunning = true;
  let currentPrompt = userPrompt;

  while (isRunning) {
    console.log(`\n[Vòng lặp] Trạng thái hiện tại: ${fsm.currentState}`);
    
    if (fsm.currentState === States.COMPLETED) {
      console.log("Đơn hàng đã được xử lý thành công hoàn toàn!");
      isRunning = false;
      break;
    }
    if (fsm.currentState === States.FAILED) {
      console.error("Xử lý đơn hàng thất bại. Dừng hệ thống.", fsm.context.errorLog);
      isRunning = false;
      break;
    }

    const allowedTools = getAllowedTools(fsm.currentState);
    
    // Gọi OpenAI để phân tích bước tiếp theo dựa trên trạng thái hiện tại
    const response = await openai.chat.completions.create({
      model: "gpt-4o",
      messages: [
        { role: "system", content: `Bạn là trợ lý xử lý đơn hàng. Hiện tại hệ thống đang ở trạng thái: ${fsm.currentState}. Chỉ sử dụng công cụ được cung cấp.` },
        { role: "user", content: currentPrompt }
      ],
      tools: allowedTools.length > 0 ? allowedTools : undefined,
      tool_choice: allowedTools.length > 0 ? "auto" : undefined
    });

    const message = response.choices[0].message;

    if (message.tool_calls) {
      for (const toolCall of message.tool_calls) {
        const toolName = toolCall.function.name;
        const args = JSON.parse(toolCall.function.arguments);
        
        console.log(`Agent yêu cầu gọi công cụ: ${toolName} với tham số:`, args);

        // Kiểm tra bảo mật chéo: Đảm bảo tool được gọi khớp với trạng thái hiện tại
        if (toolName === "checkStock" && fsm.currentState === States.CHECKING_STOCK) {
          // Giả lập xử lý kiểm tra kho
          const isAvailable = args.quantity <= 10; // Giả sử tồn kho dưới 10 là còn hàng
          if (isAvailable) {
            fsm.updateContext({ items: [{ productId: args.productId, quantity: args.quantity }] });
            fsm.transition(Events.STOCK_CONFIRMED);
            currentPrompt = `Hệ thống xác nhận còn hàng. Hãy tiến hành thanh toán số tiền 150 USD.`;
          } else {
            fsm.updateContext({ errorLog: ["Hết hàng trong kho"] });
            fsm.transition(Events.STOCK_OUT);
          }
        } 
        else if (toolName === "processPayment" && fsm.currentState === States.PROCESSING_PAYMENT) {
          // Giả lập xử lý thanh toán
          console.log(`Đang xử lý thanh toán số tiền: ${args.amount} USD...`);
          fsm.updateContext({ paymentStatus: "PAID" });
          fsm.transition(Events.PAYMENT_SUCCESS);
          currentPrompt = `Thanh toán thành công. Hãy gửi email xác nhận cho khách hàng.`;
        } 
        else {
          // Phát hiện hành vi bất thường hoặc cố gắng gọi sai tool của LLM
          console.error(`Cảnh báo bảo mật: Agent cố gắng gọi tool ${toolName} tại trạng thái ${fsm.currentState}`);
          fsm.updateContext({ errorLog: [`Gọi sai công cụ ${toolName} tại trạng thái ${fsm.currentState}`] });
          fsm.transition(Events.ERROR);
        }
      }
    } else {
      // Nếu LLM không gọi tool mà chỉ trả về text
      console.log(`Phản hồi từ Agent: ${message.content}`);
      
      if (fsm.currentState === States.CONFIRMING_ORDER) {
        // Giả lập gửi email xác nhận thành công
        fsm.transition(Events.CONFIRM_SUCCESS);
      } else {
        // Nếu ở các trạng thái cần gọi tool mà LLM không gọi tool, coi như lỗi luồng
        fsm.updateContext({ errorLog: ["Agent không thực hiện đúng hành động yêu cầu"] });
        fsm.transition(Events.ERROR);
      }
    }
  }
}

// Chạy thử nghiệm hệ thống
runOrderAgent("ORDER-999", "Tôi muốn mua sản phẩm PRO-102 với số lượng 5 chiếc.");

Ưu điểm và Nhược điểm của giải pháp

Ưu điểm

  • Tính Deterministic (Xác định): Luồng nghiệp vụ của doanh nghiệp được đảm bảo tuyệt đối. Không có chuyện Agent tự ý bỏ qua các bước kiểm tra nghiệp vụ quan trọng.
  • Tiết kiệm chi phí: Ngăn chặn hoàn toàn hiện tượng Agent bị lặp vô hạn (infinite loop) nhờ vào việc giới hạn số lần chuyển trạng thái hoặc bắt lỗi ngay lập tức khi xảy ra sai sót.
  • Dễ dàng Debug: Khi hệ thống gặp lỗi, bạn có thể biết chính xác Agent bị lỗi ở trạng thái nào (ví dụ: PROCESSING_PAYMENT) và ngữ cảnh (context) tại thời điểm đó ra sao.

Nhược điểm

  • Giảm tính linh hoạt: Agent không thể tự sáng tạo ra các luồng xử lý mới nằm ngoài thiết kế ban đầu của State Machine.
  • Độ phức tạp khi mở rộng: Khi hệ thống có hàng chục trạng thái và hàng trăm sự kiện, bảng chuyển trạng thái (Transition Table) sẽ trở nên rất lớn và khó bảo trì nếu không được module hóa tốt.

Best Practices khi thiết kế State Machine cho AI Agent

Để triển khai giải pháp này thành công trong các dự án thực tế quy mô lớn, bạn nên tuân thủ các nguyên tắc sau:

  1. Áp dụng cơ chế Max Retries cho mỗi Trạng thái: Luôn lưu trữ một biến đếm số lần thử lại (retry counter) trong ngữ cảnh (context). Nếu Agent thử gọi một công cụ thất bại quá 3 lần tại một trạng thái, hãy chủ động chuyển sang trạng thái FAILED để nhân viên vận hành (human-in-the-loop) can thiệp.
  2. State Persistence với Redis: Trong môi trường phân tán, các yêu cầu của người dùng có thể được xử lý qua nhiều server khác nhau. Hãy lưu trữ toàn bộ trạng thái và ngữ cảnh của State Machine vào Redis sử dụng orderId làm key để đảm bảo tính nhất quán dữ liệu.
  3. Phân tách rõ ràng giữa LLM Reasoning và State Transition: LLM chỉ đóng vai trò đề xuất hành động (qua tool calling hoặc text response). Quyết định chuyển trạng thái cuối cùng phải thuộc về code Node.js của bạn, tuyệt đối không để LLM tự viết trạng thái mới vào database.

Việc làm chủ kỹ thuật thiết kế State Machine kết hợp với AI Agent là một bước tiến quan trọng giúp bạn dịch chuyển từ việc làm các bản demo chatbot đơn giản sang việc xây dựng các hệ thống AI cấp doanh nghiệp (Enterprise-grade AI Systems) hoạt động ổn định và an toàn.

Nếu bạn muốn đi sâu hơn vào việc tự tay thiết kế các kiến trúc Agent phức tạp, hiểu rõ cơ chế vận hành từ tầng SDK thuần của OpenAI cho đến các mô hình Multi-Agent phối hợp nhịp nhàng, hãy tham gia lộ trình bài bản tại Unicode Academy. Tham khảo khóa học "Lập trình AI Agent với NodeJS & OpenAI" tại đây để nâng cao kỹ năng lập trình AI chuyên nghiệp của mình.