Xây dựng infection-protocol-lab: Mô phỏng Đa tác nhân Full-Stack với TypeScript, LangChain và ReactFlow

06 tháng 4, 2026·9 phút đọc

Dự án infection-protocol-lab khám phá hành vi của các tác nhân LLM tự trị trong một hệ thống xã hội phức tạp với thông tin không hoàn chỉnh và sự lây lan ẩn. Bài viết này đi sâu vào kiến trúc hệ thống, kết hợp giữa engine mô phỏng TypeScript, lớp tác nhân LangChain, backend Express + WebSocket và trực quan hóa ReactFlow.

Xây dựng infection-protocol-lab: Mô phỏng Đa tác nhân Full-Stack với TypeScript, LangChain và ReactFlow

infection-protocol-lab bắt nguồn từ một câu hỏi đơn giản: Điều gì sẽ xảy ra khi các tác nhân LLM (Large Language Model) tự chủ sống trong một hệ thống xã hội có thông tin không đầy đủ, sự lây nhiễm bị che giấu, chiến lược không ngừng phát triển và sự bất đối xứng dựa trên vai trò?

Dự án này trả lời câu hỏi đó dưới dạng một ứng dụng cục bộ có thể chạy được (runnable local app). Nó là sự kết hợp của:

  • Một engine mô phỏng được viết bằng TypeScript
  • Một lớp tác nhân sử dụng sức mạnh của LangChain
  • Backend sử dụng Express và WebSocket
  • Giao diện trực quan hóa trực tiếp bằng ReactFlow
  • Một môi trường benchmark (đánh giá) để thực hiện các thí nghiệm lặp lại

Bài viết dưới đây sẽ đi sâu vào kiến trúc, những sự đánh đổi (tradeoffs) và các mẫu mã (code patterns) đằng sau hệ thống này.

Tại sao lại xây dựng dự án này?

Hầu hết các bản demo LLM hiện nay đều mang tính tuyến tính:

  • Gửi prompt (lời nhắc)
  • Nhận câu trả lời
  • Kết thúc tương tác

Tuy nhiên, nhiều hành vi hệ thống thú vị chỉ thực sự xuất hiện khi các tác nhân:

  • Tương tác liên tục nhiều lần
  • Nắm giữ những niềm tin không hoàn hảo (imperfect beliefs)
  • Phản ứng với các trạng thái ẩn (hidden state)
  • Thích nghi chiến lược của chính họ theo thời gian

Đó chính là lúc mô phỏng trở nên thú vị. infection-protocol-lab xây dựng môi trường đó xung quanh một giao thức lây nhiễm ẩn:

  • Một số tác nhân ban đầu bị nhiễm bệnh
  • Những tác nhân khác không biết ai là người bị nhiễm
  • Sự lây nhiễm lây lan thông qua tương tác
  • Một tác nhân đóng vai bác sĩ
  • Tác nhân bị nhiễm có thể hồi phục
  • Mọi người đều cố gắng sinh tồn và suy luận ra rủi ro

Kiến trúc tổng quát

Hệ thống bao gồm ba lớp chính:

  1. Engine mô phỏng (Simulation Engine)
  2. Lớp vận chuyển (Transport Layer)
  3. Giao diện trực quan hóa và Benchmark UI

Mô hình dữ liệu cốt lõi

Backend định nghĩa một mô hình tác nhân (agent model) nhỏ gọn nhưng giàu tính biểu đạt:

export type AgentState = "healthy" | "infected" | "immune";
export type AgentRole = "normal" | "doctor";

export interface AgentSnapshot {
  id: string;
  state: AgentState;
  role: AgentRole;
  strategy: string;
  beliefs: Record<string, number>;
  riskTolerance: number;
  memory: AgentMemoryEntry[];
}

Cấu trúc này rất quan trọng vì nó cân bằng được các yếu tố:

  • Đủ cấu trúc cho logic mô phỏng
  • Đủ ngữ cảnh cho các prompt của LLM
  • Đủ đơn giản để hiển thị trên UI

Lớp LLM

Wrapper cho LLM sử dụng ChatOpenAI của LangChain, nhưng nó cũng bao gồm một cơ chế dự phòng (graceful fallback) để ứng dụng vẫn có thể chạy khi không có khóa API (API key).

export class LLMClient {
  async generateResponse(prompt: string): Promise<string> {
    if (!this.client) {
      return this.fallback(prompt);
    }

    try {
      const response = await this.client.invoke(prompt);
      return typeof response.content === "string"
        ? response.content
        : response.content.map((part) => ("text" in part ? part.text : "")).join(" ").trim();
    } catch {
      return this.fallback(prompt);
    }
  }
}

Cơ chế dự phòng này rất quan trọng cho trải nghiệm của nhà phát triển. Nó giúp dự án luôn có thể demo được trong các trường hợp:

  • Bạn không có sẵn API key
  • Bạn đang ngoại tuyến (offline)
  • Cuộc gọi mô hình thất bại tạm thời

Prompt của tác nhân như là Giao diện hành vi

Mỗi tác nhân không chỉ là một đối tượng dữ liệu. Nó là một diễn viên được định hình bởi prompt. Dưới đây là ví dụ về prompt hội thoại:

const prompt = `You are Agent ${this.id}.

Strategy:
${this.strategy}

You are interacting with Agent ${otherAgent.id}.

Infection spreads via interaction, but identities are hidden.

Goals:
- survive
- infer who is infected
- influence others

Respond in <=20 words.`;

Đây là một mẫu rất hữu ích cho các ứng dụng đa tác nhân:

  • Giữ prompt cụ thể theo vai trò
  • Giữ cho chúng ngắn gọn
  • Hạn chế đầu ra (constrained output)
  • Giữ đủ không gian cho sự biến đổi nổi lên (emergent variation)

Mẫu này được tái sử dụng cho:

  • Cập nhật niềm tin (belief updates)
  • Tiến hóa chiến lược
  • Tạo bản phát thanh (broadcast generation)

Vòng lặp Mô phỏng

Engine nắm giữ trạng thái mô phỏng uy tín. Trong mỗi vòng (round), hệ thống thực hiện:

  1. Ghép đôi các tác nhân bằng cách sử dụng cơ chế đối kháng dựa trên sự nghi ngờ (suspicion-aware matching)
  2. Chạy các tương tác hai chiều
  3. Cập nhật niềm tin
  4. Giải quyết sự lây lan nhiễm trùng
  5. Cho phép bác sĩ hành động
  6. Áp dụng liệu pháp tự nhiên
  7. Kích hoạt bản phát thanh định kỳ
  8. Tiến hóa chiến lược
  9. Phát ra sự kiện và snapshot của vòng đó
private async runRound(onEvent?: EventHandler): Promise<void> {
  const pairs = createSmartPairs(this.agents, this.random);

  for (const [first, second] of pairs) {
    const [firstMessage, secondMessage] = await Promise.all([first.talk(second), second.talk(first)]);

    await Promise.all([
      first.updateBeliefs(second.id, secondMessage),
      second.updateBeliefs(first.id, firstMessage)
    ]);

    this.tryInfection(first, second, onEvent);
    this.tryInfection(second, first, onEvent);
  }

  await this.performDoctorActions(onEvent);
  this.performNaturalCures(onEvent);
  await this.performBroadcast(onEvent);
  await this.performEvolution(onEvent);
}

Tại sao ghép đôi thông minh (Smart Pairing) lại quan trọng?

Việc ghép đôi ngẫu nhiên rất dễ dàng, nhưng nó bỏ đi một trong những tín hiệu xã hội thú vị hơn: sự lựa chọn.

Trong dự án này, các tác nhân ưu tiên những đối tác có độ nghi ngờ thấp, trộn lẫn một chút ngẫu nhiên. Điều này tạo ra cấu trúc nổi lên (emergent structure):

  • Cách ly các tác nhân đáng ngờ
  • Các cụm tin cậy an toàn hơn
  • Động thái lan truyền bị chậm lại hoặc tăng tốc

Nó cũng làm cho niềm tin trở nên mang tính vận hành thay vì chỉ trang trí.

Luồng dữ liệu WebSocket

Frontend được điều khiển bởi sự kiện (event-driven). Thay vì thăm dò (polling) liên tục, backend sẽ đẩy (push):

  • Các cạnh tương tác trực tiếp
  • Chuyển đổi trạng thái nhiễm trùng
  • Sự kiện chữa bệnh
  • Lớp phủ bản phát thanh
  • Cập nhật chiến lược
  • Tóm tắt vòng

Quyết định này giúp giao diện cảm thấy sống động và làm cho hệ thống dễ lý luận hơn trong quá trình chạy.

ReactFlow như một bề mặt Mô phỏng

ReactFlow thường được sử dụng cho các trình chỉnh sửa nút (node editors) hoặc DAG, nhưng nó hoạt động tốt như một đồ thị xã hội động khi bạn:

  • Biểu diễn mỗi tác nhân là một nút
  • Sử dụng các cạnh hoạt hình tạm thời cho các tương tác
  • Màu nút theo trạng thái
  • Mã hóa vai trò trực quan

Frontend store chuyển đổi các sự kiện backend thành trạng thái đồ thị:

if (event.type === "interaction" && typeof event.from === "string" && typeof event.to === "string") {
  const edgeId = `${event.from}-${event.to}-${event.timestamp}`;
  nextState.edges = addEdge(
    {
      id: edgeId,
      source: event.from,
      target: event.to,
      animated: true,
      style: { stroke: "#38bdf8", strokeWidth: 2 }
    },
    state.edges
  ).slice(-24);
}

Tác giả cũng làm cho các cạnh này mờ dần sau vài giây để đồ thị truyền tải hoạt động hiện tại thay vì chỉ là lộn xộn lịch sử.

Chế độ Benchmark (Đánh giá)

Trình chạy benchmark là thứ biến dự án này từ một đồ chơi trực quan thành một công cụ thí nghiệm thực tế.

Thay vì chạy một mô phỏng trực tiếp, bạn có thể chạy hàng loạt:

for (let index = 0; index < runCount; index += 1) {
  const config = mergeConfig({
    ...request.config,
    seed: `${request.config.seed || "benchmark"}-${index + 1}`
  });
  const engine = new SimulationEngine(config);
  const result = await engine.runToCompletion(false);
  runs.push({
    runId: result.runId,
    seed: config.seed,
    config,
    metrics: result.metrics,
    finalAgents: result.agents
  });
}

Điều này mang lại cho bạn các chỉ số tổng hợp như:

  • Tỷ lệ sống sót
  • Tốc độ lây lan nhiễm trùng
  • Điểm tin cậy trung bình
  • Sự đa dạng chiến lược
  • Tỷ lệ miễn dịch

Điều này đặc biệt hữu ích để so sánh:

  • Các phong cách prompting
  • Thông số lây nhiễm
  • Số lượng vòng
  • Các mô hình tương thích OpenAI khác nhau

Pixel UI như khung sản phẩm

UI được thiết kế lại có chủ đích thành một bàn điều hành phong cách pixel-art thay vì một bảng điều khiển phân tích chung chung.

Điều đó không chỉ là trang trí.

Ngôn ngữ trực quan củng cố ứng dụng là gì:

  • Một phòng thí nghiệm mô phỏng
  • Một phòng điều khiển
  • Một hệ thống để quan sát và thực hiện thí nghiệm

Đối với một dự án như thế này, khung hình ảnh quan trọng vì nó truyền đạt mô hình tinh thần đúng đắn trước khi người dùng nhấp vào bất cứ thứ gì.

Các nhà phát triển có thể học được gì từ dự án này?

Có một vài mẫu hữu ích ở đây ngoài bản thân việc mô phỏng:

1. Giữ chân lý backend tách biệt với trình bày frontend

Backend sở hữu trạng thái mô phỏng. Frontend sở hữu trạng thái trực quan hóa. Ranh giới đó giữ cho cả hai phía đều đơn giản hơn.

2. Thiết kế prompt như API

Bề mặt prompt nên là:

  • Tối thiểu
  • Có cấu trúc
  • Nhận thức vai trò
  • Hạn chế đầu ra

3. Xây dựng các đường dẫn dự phòng khéo léo

Ứng dụng vẫn chạy mà không cần API key. Điều đó giúp việc chia sẻ và lặp lại trở nên dễ dàng hơn nhiều.

4. Đưa các chỉ số (metrics) lên hàng đầu

Nếu bạn đang chạy thí nghiệm, lớp benchmark không nên là một sự suy nghĩ sau cùng.

Hướng phát triển trong tương lai

Nếu tác giả mở rộng điều này tiếp theo, ông ấy sẽ thêm:

  • Chế độ phát lại (replay mode) với dòng thời gian vòng
  • Trực quan hóa tin cậy phong phú hơn
  • Nhiều vai trò tác nhân hơn
  • Lịch sử chạy tồn tại (persistent run history)
  • Chế độ đấu trường mô hình-vs-mô hình
  • Prompt có thể chỉnh sửa từ UI
  • Kết quả benchmark có thể xuất khẩu

infection-protocol-lab là một ví dụ nhỏ gọn nhưng giàu tính biểu cảm về điều gì xảy ra khi bạn coi các tác nhân LLM là những người tham gia trong một hệ thống thay vì các trình tạo câu trả lời một lần.

Sự thay đổi đó, từ tạo phản hồi sang mô phỏng tương tác, là nơi bắt đầu nhiều kỹ thuật thú vị.

GitHub Repo: https://github.com/harishkotra/infection-protocol-lab

Bài viết được tổng hợp và biên soạn bằng AI từ các nguồn tin tức công nghệ. Nội dung mang tính tham khảo. Xem bài gốc ↗