Giới thiệu dự án

Trong bối cảnh chuyển đổi số giáo dục và sự gia tăng mạnh mẽ về nhu cầu nhân lực công nghệ thông tin (CNTT), việc đào tạo lập trình viên chất lượng cao đòi hỏi các phương pháp đánh giá thực hành nhanh chóng, chính xác và minh bạch. Theo thống kê giáo dục kỹ thuật, hơn 70% thời lượng của các khóa học lập trình cơ sở và nâng cao dành cho việc thực hành viết mã. Tuy nhiên, quy trình chấm bài truyền thống tại nhiều trường đại học vẫn mang tính thủ công: trợ giảng phải tải từng tệp nén .zip từ hệ thống quản lý học tập (LMS), giải nén, thiết lập môi trường trong IDE cục bộ, biên dịch và chạy từng bộ dữ liệu thử nghiệm. Quy trình này tiêu tốn trung bình từ 8 đến 15 phút cho mỗi bài nộp, dễ phát sinh sai sót do môi trường không đồng nhất và gây chậm trễ trong việc phản hồi kết quả cho người học.

Khóa luận tốt nghiệp "Xây dựng hệ thống hỗ trợ chấm điểm lập trình tự động" (Building an Automated Code Scoring System), thuộc ngành Kỹ thuật Phần mềm – Khoa Công nghệ Phần mềm, Trường Đại học Công nghệ Thông tin (ĐHQG TP.HCM), do sinh viên Phùng Thanh Tú và Nguyễn Hoàng Thái Dương thực hiện dưới sự hướng dẫn của ThS. Thái Thụy Hàn Uyển, được nghiên cứu nhằm giải quyết triệt để bài toán này.

flowchart LR
    A[Sinh viên nộp mã nguồn] --> B[Web Application - React/Monaco]
    B --> C[API Server - ExpressJS]
    C --> D[(SQL Server)]
    C --> E[RabbitMQ Message Broker]
    E --> F[Judge Engine Worker]
    F --> G[Isolate Sandbox Container]
    G --> F
    F --> D
    F --> H[Azure Blob Storage]

Mục tiêu dự án cụ thể

  1. Xây dựng nền tảng học và dạy lập trình trực tuyến PLP (Programming Learning Platform) tích hợp trình soạn thảo mã nguồn trực tiếp trên trình duyệt (Monaco Editor).
  2. Thiết kế Engine chấm điểm tự động độc lập, thực thi mã nguồn an toàn trong môi trường hộp cát (Sandbox Isolation) với độ trễ thấp và khả năng ngăn chặn mã độc.
  3. Hiện thực hóa cơ chế mở rộng ngôn ngữ động (Dynamic Language Extension) thông qua cấu hình cơ sở dữ liệu (Database Metadata) mà không cần biên dịch lại mã nguồn hệ thống.
  4. Phát triển tính năng chấm điểm theo trọng số kiểm thử (Weighted Test Cases) kết hợp cơ chế chấm điểm lai (Hybrid Scoring: Tự động kết hợp Thủ công) nhằm đánh giá toàn diện cả tính đúng đắn logic lẫn chuẩn thiết kế phần mềm (OOP, Clean Code).
  5. Đóng gói và triển khai hệ thống linh hoạt trên cả môi trường cục bộ (Local Docker Compose) lẫn hạ tầng đám mây phân tán (Distributed Cloud Infrastructure).

Phạm vi và giới hạn đề tài

  • Phạm vi chức năng: Tập trung tối ưu hóa trải nghiệm dạy và học lập trình, quản lý khóa học, tài liệu (Markdown, PDF, Video), bài tập lập trình, cấu hình bộ kiểm thử, chấm điểm tự động và thống kê kết quả học tập.
  • Ngôn ngữ hỗ trợ mặc định: C, C++, Python, JavaScript (NodeJS), Ruby, và Lua.
  • Giới hạn đề tài: Không bao gồm các nghiệp vụ quản lý hành chính nhân sự trường học, tài chính hay học phí; hệ thống tập trung hoàn toàn vào vòng đời tạo đề, nộp bài, thực thi và đánh giá kết quả thực hành lập trình.

Phân tích và thiết kế giải pháp

Phân tích hiện trạng

Khảo sát các giải pháp hiện hành trên thị trường cho thấy sự phân hóa rõ rệt giữa hai nhóm nền tảng:

Tiêu chí LeetCode / HackerRank Moodle / Google Classroom Wecode / PC2CSS Hệ thống PLP (Đề tài)
Mục đích chính Luyện thuật toán cá nhân Quản lý học tập tổng quát Chấm thi lập trình Dạy, học & chấm điểm toàn diện
Môi trường soạn thảo Monaco / CodeMirror tích hợp Nộp file tĩnh (.zip, .cpp) Nộp file hoặc Textarea thô Monaco Editor (VSCode Core)
Cơ chế chấm điểm All-or-Nothing / Đếm test Chấm thủ công 100% Đếm test case chuẩn Trọng số riêng + Chấm điểm lai
Mở rộng ngôn ngữ Cố định theo nền tảng Phụ thuộc plugin server Phụ thuộc file cấu hình tĩnh Động qua CSDL & CLI Container
Tài liệu & Bài giảng Hạn chế, chỉ có Editorial Tốt (hỗ trợ nhiều định dạng) Kém, chỉ có mô tả đề bài Tích hợp Markdown, PDF, Video

Phân tích yêu cầu theo mô hình MoSCoW

  • Must Have (Bắt buộc): Xác thực người dùng (JWT), tạo khóa học, tạo bài tập với test cases, trình soạn thảo Monaco, hàng đợi RabbitMQ, môi trường sandbox Isolate, chấm điểm tự động và tính điểm trung bình môn học.
  • Should Have (Nên có): Cơ chế chia sẻ tài liệu Markdown/PDF/Video qua Azure Blob Storage, thiết lập trọng số test case động, thêm ngôn ngữ lập trình không sửa mã nguồn.
  • Could Have (Có thể có): Chấm điểm thủ công kết hợp tỉ lệ phần trăm tự động cho bài tập lập trình hướng đối tượng.
  • Won't Have (Chưa thực hiện): Tích hợp kiểm tra đạo văn mã nguồn (Plagiarism Detection) và hệ thống chat thời gian thực.

Thiết kế hệ thống

Kiến trúc hệ thống được xây dựng theo mô hình hướng dịch vụ (Service-Oriented Architecture), phân tách hoàn toàn giữa tầng xử lý giao diện, tầng điều phối API và tầng thực thi chấm điểm nền.

+-------------------------------------------------------------------------+
|                        Client Layer (ReactJS 18)                        |
|   +---------------------+   +---------------------+   +---------------+ |
|   |   Redux / Saga      |   |   Monaco Code View  |   | Material-UI   | |
|   +----------+----------+   +----------+----------+   +-------+-------+ |
+--------------|-------------------------|----------------------|---------+
               +-------------------------+----------------------+
                                         | REST APIs (HTTPS)
+----------------------------------------v--------------------------------+
|                   Backend API Layer (ExpressJS / Node 18)               |
|   +---------------------+   +---------------------+   +---------------+ |
|   |  Joi Schema Validate|   |  Course/Doc Service |   | Auth (JWT)    | |
|   +----------+----------+   +----------+----------+   +-------+-------+ |
+--------------|-------------------------|----------------------|---------+
               |                         |                      |
      +--------+--------+       +--------+--------+    +--------+--------+
      | Azure Blob S3   |       | SQL Server DB   |    | RabbitMQ Queue  |
      | (Media Storage) |       | (Data Entities) |    | (amqp:// broker)|
      +-----------------+       +-----------------+    +--------+--------+
                                                                |
                                                                | Payload Msg
+---------------------------------------------------------------v---------+
|                     Judging Engine Layer (NodeJS Worker)                |
|   +-----------------------------------------------------------------+   |
|   | 1. Fetch Task Info & Language Config from SQL Server            |   |
|   | 2. Spawn Isolate Sandbox (/usr/local/bin/isolate --init)        |   |
|   | 3. Compile Source Code (if NeedCompile = 1)                     |   |
|   | 4. Feed stdin, Capture stdout / stderr, Monitor CPU/Memory      |   |
|   | 5. Smart Diff Evaluation -> Compute Weighted Scores             |   |
|   | 6. Persist TestResults & Submissions -> Cleanup Sandbox         |   |
|   +-----------------------------------------------------------------+   |
+-------------------------------------------------------------------------+

Technology Stack và phiên bản chi tiết

  • Frontend: React 18.2.0, TypeScript 5.0, Redux Toolkit 1.9, Redux-Saga 1.2, Material-UI (MUI) 5.11, Monaco Editor Core.
  • Backend: Node.js 18 LTS, ExpressJS 4.18, Joi Validation 17.8, MSSQL Driver (mssql v9.1).
  • Database: Microsoft SQL Server 2019 / Azure SQL Database.
  • Message Broker: RabbitMQ 3.11 (CloudAMQP).
  • Sandbox Security Engine: ioi/isolate v1.10+ tích hợp Linux Control Groups (cgroups v2) và Namespaces.
  • Cloud Storage & Infra: Azure Blob Storage, Azure Container Instances (ACI), Terraform 1.4, Docker 20.10+, Vietnix VPS.

Thiết kế cơ sở dữ liệu trọng tâm

Hệ thống sử dụng mô hình cơ sở dữ liệu quan hệ với các thực thể cốt lõi:

-- Cấu hình động ngôn ngữ lập trình
CREATE TABLE ProgrammingLanguage (
    LanguageId VARCHAR(50) PRIMARY KEY,
    LanguageName NVARCHAR(100) NOT NULL,
    NeedCompile BIT NOT NULL DEFAULT 0,
    CompileCommand NVARCHAR(500) NULL,
    RunCommand NVARCHAR(500) NOT NULL,
    SourceFileName NVARCHAR(100) NOT NULL,
    DefaultMemoryLimitMB INT DEFAULT 128,
    DefaultTimeLimitSec FLOAT DEFAULT 2.0
);

-- Quản lý bài tập và trọng số kiểm thử
CREATE TABLE TestCase (
    TestCaseId INT IDENTITY(1,1) PRIMARY KEY,
    ExerciseId INT FOREIGN KEY REFERENCES DocumentExercise(ExerciseId),
    InputData NVARCHAR(MAX),
    ExpectedOutput NVARCHAR(MAX),
    Weight FLOAT DEFAULT 1.0,
    IsHidden BIT DEFAULT 0
);

-- Chi tiết kết quả chấm từng test case
CREATE TABLE TestResult (
    ResultId INT IDENTITY(1,1) PRIMARY KEY,
    SubmissionId INT FOREIGN KEY REFERENCES SubmissionUser(SubmissionId),
    TestCaseId INT FOREIGN KEY REFERENCES TestCase(TestCaseId),
    ActualOutput NVARCHAR(MAX),
    ExecutionTimeMs FLOAT,
    MemoryUsedKB INT,
    Status NVARCHAR(50), -- Accepted, Wrong Answer, Time Limit Exceeded, Runtime Error
    ScoreEarned FLOAT
);

Methodology

Quy trình phát triển được triển khai theo khung làm việc Agile/Scrum qua các giai đoạn từ 25/02/2023 đến 20/07/2023:

  • Sprint 1 - 2 (25/02 - 05/04/2023): Khảo sát nghiệp vụ, phân tích kiến trúc, thiết kế lược đồ CSDL và hiện thực Module Authentication/Authorization.
  • Sprint 3 - 4 (06/04 - 20/05/2023): Xây dựng Core API quản lý khóa học, tích hợp Monaco Editor và kết nối Azure Blob Storage.
  • Sprint 5 - 6 (21/05 - 05/06/2023): Hiện thực Judge Engine Worker, tích hợp ioi/isolate trên Linux, triển khai cơ chế trao đổi dữ liệu bất đồng bộ qua RabbitMQ.
  • Sprint 7 - 8 (06/06 - 20/07/2023): Đóng gói Docker, tự động hóa hạ tầng bằng Terraform trên Azure, kiểm thử tích hợp (Integration Test) và thử nghiệm UAT thực tế trên lớp học.

Implementation và kết quả

Development process & Core Algorithm

Quy trình chấm bài được trừu tượng hóa thành một pipeline phi đồng bộ khép kín nhằm bảo đảm an toàn máy chủ và hiệu năng tối đa.

// Cấu trúc message gửi từ API Server vào RabbitMQ
const judgeTaskPayload = {
  submissionId: 10428,
  exerciseId: 512,
  languageId: "cpp",
  timestamp: "2023-07-10T14:32:00Z"
};

// Luồng thực thi tại Worker Engine với Isolate Sandbox
async function processSubmission(msg) {
  const { submissionId, languageId } = JSON.parse(msg.content.toString());
  const boxId = Math.floor(Math.random() * 1000); // Đảm bảo cô lập đa luồng
  
  try {
    // 1. Khởi tạo Sandbox Box
    await execPromise(`isolate --box-id=${boxId} --init`);
    
    // 2. Lấy cấu hình ngôn ngữ từ CSDL
    const langConfig = await db.getLanguageConfig(languageId);
    
    // 3. Biên dịch nếu cần (C++, Java, Rust...)
    if (langConfig.NeedCompile) {
      const compileRes = await execPromise(
        `isolate --box-id=${boxId} --processes=20 --mem=512000 --time=10 --run -- ${langConfig.CompileCommand}`
      );
      if (compileRes.exitCode !== 0) {
        return await updateSubmissionStatus(submissionId, 'Compile Error', compileRes.stderr);
      }
    }
    
    // 4. Chạy từng Test Case và tính điểm
    let totalScore = 0;
    const testCases = await db.getTestCases(submissionId);
    
    for (const test of testCases) {
      const runResult = await runInSandbox(boxId, langConfig.RunCommand, test.InputData, test.TimeLimit);
      const isPassed = smartDiff(runResult.stdout, test.ExpectedOutput);
      const score = isPassed ? test.Weight : 0;
      totalScore += score;
      
      await db.saveTestResult({
        submissionId,
        testCaseId: test.TestCaseId,
        status: isPassed ? 'Accepted' : (runResult.timeout ? 'Time Limit Exceeded' : 'Wrong Answer'),
        executionTime: runResult.time,
        memoryUsed: runResult.memory,
        scoreEarned: score
      });
    }
    
    // 5. Cập nhật bảng điểm môn học
    await db.updateFinalScore(submissionId, totalScore);
  } finally {
    // 6. Xóa môi trường Sandbox giải phóng tài nguyên
    await execPromise(`isolate --box-id=${boxId} --cleanup`);
  }
}

Testing và validation

Hệ thống được kiểm thử thực tế qua 2 giai đoạn tại Trường Đại học Công nghệ Thông tin (ĐHQG TP.HCM):

  1. Thử nghiệm trực tiếp (Live UAT): Áp dụng trong kỳ thi thử môn Lập trình Hướng đối tượng (OOP - Mã lớp IT002.N25) với 48 sinh viên trực tiếp thao tác và nộp bài đồng thời.
  2. Kiểm thử đối chiếu (Batch Evaluation): Nhập 96 bài thi chính thức của sinh viên từ hệ thống cũ vào hệ thống PLP để tự động hóa đối chiếu kết quả so với bảng điểm chấm tay của trợ giảng.
               KẾT QUẢ ĐỐI SOÁT VÀ HIỆU NĂNG THỬ NGHIỆM
+-------------------------------------+---------------------------------+
| Chỉ số kiểm thử                     | Giá trị đo lường thực tế       |
+-------------------------------------+---------------------------------+
| Tổng số bài nộp xử lý               | 284 lượt nộp                    |
| Tỷ lệ thực thi thành công           | 99.65%                          |
| Thời gian phản hồi trung bình C++   | 1.12 giây / bài nộp (5 test)    |
| Thời gian phản hồi trung bình Python| 1.74 giây / bài nộp (5 test)    |
| Mức tiêu hao CPU Worker cao điểm    | 38% (4 Cores VPS)               |
| Mức tiêu hao RAM Worker cao điểm    | 1.8 GB / 4 GB                   |
| Độ sai lệch điểm so với chấm tay    | 0.00% (Về mặt logic test cases) |
+-------------------------------------+---------------------------------+

Đổi mới và đóng góp

  1. Cơ chế thêm ngôn ngữ lập trình không chạm mã nguồn (Zero-Downtime Language Extensibility): Không cần sửa code backend hay deploy lại hệ thống. Quản trị viên chỉ cần thêm bản ghi metadata vào bảng ProgrammingLanguage và cài đặt trình thông dịch/biên dịch tương ứng trong container của Worker.
  2. Cơ chế đánh giá điểm lai (Hybrid Flexible Scoring): Cho phép kết hợp điểm số kiểm thử tự động với trọng số điểm chấm tay của giảng viên (đánh giá chuẩn coding conventions, cấu trúc lớp OOP, tính đóng gói và kế thừa).
  3. Môi trường Sandbox tối ưu đa luồng bằng Random Box ID: Hệ thống gán định danh ngẫu nhiên boxId cho mỗi luồng xử lý của ioi/isolate, cho phép tận dụng tối đa CPU đa nhân trên một máy chủ duy nhất mà không gây xung đột tài nguyên hay rò rỉ dữ liệu giữa các bài nộp.
  4. Giảm thiểu 92% thời gian chấm bài thực hành: Giảm thời gian công sức của giảng viên và trợ giảng từ 10 phút/bài xuống còn dưới 2 giây tự động hóa, cung cấp phản hồi tức thì cho người học.

Ứng dụng thực tế và triển khai

Chiến lược triển khai đa nền tảng

  • Môi trường cục bộ (Local Development): Khởi chạy toàn bộ hệ thống bằng Docker Compose qua một lệnh duy nhất:
    docker compose up -d
    cd frontend && npm install && npm run dev
    
  • Môi trường Đám mây (Cloud Production):
    • Frontend: Tự động build & deploy trên Netlify kết nối nhánh main của GitHub Repo, gắn tên miền laptrinh.one.
    • Backend API & CSDL: Triển khai qua Azure Container Instances (ACI) và Azure SQL Database bằng mã nguồn Terraform.
    • Judge Engine: Máy chủ riêng (VPS Vietnix chạy Ubuntu 22.04 LTS) để hỗ trợ đầy đủ quyền truy cập Kernel Cgroups cho isolate.
    • Queue & Media: RabbitMQ qua CloudAMQP và Azure Blob Storage cho lưu trữ tệp đa phương tiện.
       BẢNG PHÂN TÍCH HIỆU QUẢ ĐẦU TƯ (ROI) CHO LỚP HỌC 100 SINH VIÊN
+------------------------------+--------------------+-------------------+
| Hạng mục so sánh             | Chấm thủ công      | Hệ thống PLP      |
+------------------------------+--------------------+-------------------+
| Thời gian chấm 1 bài tập/SV  | ~10 phút           | ~2 giây           |
| Tổng thời gian cho 10 bài/kỳ | 166.6 giờ          | 0.55 giờ          |
| Chi phí nhân sự trợ giảng    | ~8.000.000 VNĐ/lớp | ~300.000 VNĐ (VPS)|
| Độ trễ nhận phản hồi của SV  | 3 - 7 ngày         | Ngay lập tức      |
| Tiết kiệm thời gian trung bình: 99.6% | Tỷ lệ ROI: Giảm 96.2% chi phí |
+-----------------------------------------------------------------------+

Hạn chế và hướng phát triển

Hạn chế kỹ thuật hiện tại

  • Hệ thống chưa tích hợp thuật toán kiểm tra sự tương đồng mã nguồn (Source Code Plagiarism Detection như Moss/JPlag) để phát hiện gian lận thi cử.
  • Engine hiện tại tập trung vào các ngôn ngữ chạy trên môi trường dòng lệnh (CLI), chưa hỗ trợ chấm điểm tương tác đồ họa (GUI) hoặc ứng dụng Web Fullstack (HTML/CSS/JS DOM).

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

  • Tích hợp Trí tuệ nhân tạo (LLMs) để tạo tính năng AI Tutor: tự động giải thích nguyên nhân lỗi biên dịch hoặc lỗi thuật toán và gợi ý hướng sửa chữa cho sinh viên mà không lộ đáp án.
  • Mở rộng cụm Worker chấm bài theo mô hình Kubernetes Autoscaling (KEDA) dựa trên độ dài hàng đợi RabbitMQ khi có kỳ thi quy mô lớn hàng nghìn thí sinh đồng thời.

Đối tượng hưởng lợi

  • Sinh viên CNTT: Nhận kết quả đánh giá mã nguồn tức thì, xem chi tiết lỗi runtime/logic, làm quen với môi trường thi đấu và tiêu chuẩn công nghiệp thông qua Monaco Editor.
  • Giảng viên và Trợ giảng: Tự động hóa toàn bộ công đoạn chấm bài, dễ dàng thiết lập bài tập có trọng số, theo dõi trực quan bảng điểm và phân phối tài liệu học tập đa định dạng.
  • Nhà trường & Trung tâm đào tạo: Nâng cao chất lượng đào tạo thực hành, chuẩn hóa ngân hàng đề thi và tiết kiệm ngân sách vận hành đội ngũ chấm bài.
  • Lập trình viên & Nhà nghiên cứu: Cung cấp tài liệu tham khảo kiến trúc hoàn chỉnh về việc tích hợp Message Broker, Sandbox Isolation và Database-driven Compiler Extensibility.

Câu hỏi thường gặp

1. Yêu cầu phần cứng tối thiểu để triển khai hệ thống là gì?

Máy chủ cài đặt Engine cần chạy hệ điều hành Linux (khuyến nghị Ubuntu 20.04 LTS trở lên) có hỗ trợ quyền root/cgroups để chạy ioi/isolate, tối thiểu 2 Cores CPU, 4 GB RAM và 20 GB SSD.

2. Hệ thống xử lý thế nào khi sinh viên nộp mã độc hại (như vòng lặp vô tận hoặc lệnh xóa file)?

Công cụ ioi/isolate thiết lập một không gian tên (namespace) hoàn toàn cô lập, chặn toàn bộ lời gọi hệ thống nguy hiểm (system calls), giới hạn quyền ghi tệp ngoài thư mục sandbox, và tự động dừng tiến trình (Kill Process) ngay khi vượt quá ngưỡng thời gian (Time Limit) hoặc dung lượng bộ nhớ (Memory Limit) quy định.

3. Làm thế nào để thêm một ngôn ngữ lập trình mới vào hệ thống?

Quản trị viên chỉ cần cài đặt trình biên dịch/thông dịch của ngôn ngữ đó vào máy chủ Worker, sau đó chèn 1 dòng cấu hình vào bảng ProgrammingLanguage trên CSDL (bao gồm câu lệnh biên dịch CompileCommand và câu lệnh thực thi RunCommand).

4. Hệ thống có khả năng mở rộng khi lượng sinh viên nộp bài tăng đột biến không?

Nhờ kiến trúc Message Queue với RabbitMQ, các bài nộp được xếp hàng an toàn và phân phối tự động. Hệ thống có thể mở rộng quy mô (Scale Out) bằng cách bật thêm nhiều Worker Engine trên các máy chủ khác nhau cùng lắng nghe chung một Queue.

5. Chi phí duy trì hệ thống trên Cloud là bao nhiêu?

Với kiến trúc tối ưu hóa tài nguyên (kết hợp Netlify Free Tier, CloudAMQP Free Tier, Azure Container Instances chạy theo nhu cầu và 1 VPS Vietnix cơ bản), chi phí duy trì chỉ dao động từ 150.000 đến 350.000 VNĐ/tháng cho quy mô 300 - 500 sinh viên thường xuyên.


Kết luận

Đồ án "Xây dựng hệ thống hỗ trợ chấm điểm lập trình tự động" đã giải quyết xuất sắc bài toán tối ưu hóa quy trình đào tạo và đánh giá thực hành lập trình trong môi trường đại học. Bằng việc kết hợp kiến trúc phân tán hiện đại, giải pháp bảo mật hộp cát với ioi/isolate, hàng đợi điều phối RabbitMQ và trình soạn thảo Monaco Editor, hệ thống đã chứng minh tính thực tiễn cao qua các kỳ thi thực tế tại Trường Đại học Công nghệ Thông tin (ĐHQG TP.HCM). Dự án không chỉ là một sản phẩm phần mềm hoàn chỉnh, sẵn sàng ứng dụng vào công tác giảng dạy mà còn là nền tảng kỹ thuật mở, tạo tiền đề vững chắc cho việc tích hợp các công nghệ thông minh hỗ trợ giáo dục trong tương lai.