Đề cương phân tích đề tài · Đồ án Kỹ thuật phần mềm · Nhóm 5 sinh viên

Nền tảng đánh giá kỹ năng lập trình và kỹ năng nghề nghiệp tự động tích hợp AI

Automated Skill Assessment Platform

Hệ thống tổ chức bài đánh giá năng lực, nhận bài làm, tự chấm bằng test case và luật, dùng AI cho phần khó tự động hoá — rồi quy mọi kết quả về một hồ sơ kỹ năng mà mỗi con số đều truy vết được nguồn gốc.

Quy mô
5 sinh viên · 16 tuần (8 sprint × 2 tuần)
Loại bài trong MVP
Trắc nghiệm · Lập trình · REST API · Phỏng vấn AI
Kiến trúc
Modular monolith + Worker chấm bài tách biệt
Ba điểm nhấn kỹ thuật
Docker sandbox · Assessment Engine · AI có kiểm soát
Mục lục (15 phần)
  1. Định vị đề tài
  2. Phạm vi & MVP
  3. Tác nhân & chức năng
  4. Mô hình miền
  5. Các loại bài đánh giá
  6. Luồng chấm bất đồng bộ
  7. Mô hình tính điểm
  8. Sandbox & bảo mật
  9. Kiến trúc & công nghệ
  10. Dữ liệu & API
  11. Phân công 5 thành viên
  12. Lộ trình 16 tuần
  13. Phi chức năng & kiểm thử
  14. Rủi ro
  15. Cần chốt & chuẩn bị bảo vệ

§1Định vị đề tài

Bài toán không phải “làm website cho sinh viên nộp bài rồi AI chấm”. Bài toán là xây một Assessment Engine có thể cấu hình: giảng viên khai báo kỹ năng, bài đánh giá, test case, rubric và trọng số; hệ thống tự thu bài, chạy nhiều bộ chấm độc lập, tổng hợp điểm theo công thức công khai và cập nhật hồ sơ kỹ năng của từng người học.

Đề tài KHÔNG phải là

  • Một LMS — không quản lý khoá học, bài giảng, điểm danh.
  • Một hệ thống thi online chỉ có trắc nghiệm.
  • Một chatbot gửi câu trả lời sang ChatGPT rồi nhận về “8/10”.

Đề tài LÀ

  • Một engine chấm đa nguồn: Test Rule AI Human.
  • Một môi trường thực thi an toàn cho mã không tin cậy.
  • Một mô hình điểm kỹ năng giải thích được tới từng tiêu chí.

Bốn lớp giá trị

Đọc từ dưới lên: lớp dưới cung cấp dữ liệu cho lớp trên. Lớp Engine là “trái tim” — mọi loại bài đều đi qua nó.

Đầu raSkill Profile — điểm từng kỹ năng, tiến bộ theo thời gian, gợi ý học tiếp
Tăng cườngAI Evaluation & Feedback — chấm phần khó tự động hoá, viết nhận xét
LõiAutomated Assessment Engine — test runner, rule engine, score engine
Đầu vàoQuiz · Coding · REST API · Interview — các loại bài đánh giá

Bốn nguyên tắc thiết kế xuyên suốt

  1. Skill là thực thể trung tâm. Mỗi item chấm điểm phải khai báo nó đóng góp cho kỹ năng nào, bao nhiêu phần trăm.
  2. Mọi điểm đều truy vết được nguồn. Bấm vào bất kỳ con số nào cũng xem được tiêu chí, bộ chấm và bằng chứng tạo ra nó.
  3. AI không bao giờ là nguồn điểm duy nhất cho phần có thể kiểm chứng bằng test hoặc luật; AI chỉ là một evaluator trong pipeline.
  4. Mã bài làm là không tin cậy. Chỉ được build và chạy trong container bị giới hạn, trên máy worker tách khỏi máy chủ chính.

§2Phạm vi & MVP

Rủi ro lớn nhất của đề tài là phình phạm vi. Danh sách MVP dưới đây được khoá ở cuối tuần 2; mọi thứ ngoài danh sách chỉ làm khi MVP đã chạy end-to-end.

MVP Bắt buộc

  • Đăng nhập JWT, phân quyền RBAC 3 vai trò
  • Quản lý Skill dạng cây 2 cấp
  • Quản lý Assessment / Section / Item, câu hỏi, test case, rubric
  • Trắc nghiệm một và nhiều đáp án
  • Bài lập trình stdin/stdout, 2 ngôn ngữ
  • Bài REST API: dự án đóng gói Docker, test HTTP khai báo
  • Phỏng vấn kỹ thuật với AI dạng văn bản
  • AI feedback cho bài lập trình và REST API
  • Hàng đợi + worker + sandbox Docker
  • Score engine, skill profile, biểu đồ tiến bộ
  • GV duyệt / ghi đè điểm, audit log
  • CI trên GitHub Actions, triển khai Docker Compose

Mở rộng Nếu còn thời gian

  • AI code review chi tiết theo từng dòng
  • Bài SQL chạy trên database sandbox
  • Gợi ý học tập theo luật, AI viết lời giải thích
  • Phát hiện trùng lặp mã giữa các bài nộp
  • Thêm ngôn ngữ C/C++, Java
  • Phỏng vấn bằng giọng nói
  • Báo cáo nâng cao, xuất PDF

Ngoài phạm vi

  • Quản lý khoá học, bài giảng (LMS)
  • Ghép việc làm, tuyển dụng
  • Giám sát thi qua webcam
  • Ứng dụng di động
  • Microservices, Kubernetes
Giả định lập kế hoạch

16 tuần làm việc, mỗi sinh viên khoảng 12–15 giờ/tuần. Quy mô demo: một lớp khoảng 100 sinh viên. Hạ tầng: 1 máy chủ Linux (4 vCPU, 8 GB RAM) cho API + DB và 1 máy worker riêng. Nếu thực tế là 20 tuần, phần dư dùng cho mục “Mở rộng”, không kéo dài sprint.

§3Tác nhân & chức năng

Ba tác nhân người dùng, hai tác nhân hệ thống. Giảng viên là tác nhân quan trọng nhất vì mọi cấu hình chấm điểm đều do họ tạo ra.

Tác nhânMục tiêuChức năng chính
AdministratorVận hành hệ thống
  • Quản lý người dùng, vai trò, quyền
  • Cấu hình AI provider: khoá API, model, hạn mức token
  • Cấu hình giới hạn sandbox (CPU, RAM, timeout)
  • Xem audit log
InstructorThiết kế và kiểm soát việc đánh giá
  • Tạo skill, assessment, section, item
  • Soạn câu hỏi, test case, rubric; gán skill và trọng số
  • Publish assessment, giao cho lớp, đặt thời hạn và số lượt làm
  • Xem bài nộp, chi tiết chấm từng tiêu chí
  • Duyệt điểm AI, ghi đè điểm kèm lý do
  • Xem thống kê lớp theo kỹ năng
StudentĐược đánh giá và biết mình cần cải thiện gì
  • Xem bài được giao, bắt đầu một lượt làm
  • Làm quiz, viết code, nộp dự án REST API, trả lời phỏng vấn
  • Theo dõi trạng thái chấm theo thời gian thực
  • Xem điểm và nhận xét theo từng tiêu chí
  • Xem skill profile, tiến bộ; gửi yêu cầu phúc khảo
Worker (hệ thống)Chấm bàiNhận job từ hàng đợi, điều khiển sandbox, chạy test, gọi AI evaluator, ghi kết quả.
AI Provider (bên ngoài)Cung cấp mô hình ngôn ngữOpenAI hoặc Gemini, được gọi qua một interface chung để thay thế được.

Hành trình chính của sinh viên

Xem bài được giao Bắt đầu lượt làm Làm từng item Nộp bài Đang chấm Có kết quả Đọc nhận xét Skill profile cập nhật

Hành trình chính của giảng viên

Chọn skill Soạn assessment Thêm test case & rubric Chạy thử bằng lời giải mẫu Publish & giao lớp Duyệt điểm AI Xem thống kê lớp

Bước “chạy thử bằng lời giải mẫu” là bắt buộc trước khi publish: lời giải mẫu phải đạt 100% test, nếu không test case đang sai.

§4Mô hình miền

Mười một khái niệm dưới đây là ngôn ngữ chung của cả nhóm. Tên trong code, tên bảng và tên trong báo cáo dùng đúng các từ này.

Khái niệmÝ nghĩaVí dụ
SkillNăng lực đo được, tổ chức thành cây Category → SkillBackend Development → REST API
AssessmentMột bài đánh giá hoàn chỉnh: thời lượng, số lượt làm, đối tượngBackend Developer – Level 1
SectionNhóm item cùng chủ đề trong một assessmentPart 2: Database
ItemĐơn vị chấm nhỏ nhất, có loại (Quiz, Coding, RestApi, Sql, Interview) và trọng sốBài 1: CRUD Products
ItemSkillItem đóng góp cho skill nào, tỉ lệ bao nhiêu (nhiều–nhiều)CRUD Products → REST API 70%, Database 30%
Rubric · CriterionTiêu chí chấm của một item: trọng số, mô tả từng mức điểm, nguồn chấmError handling · 10% · Test
TestCaseĐầu vào + kỳ vọng + trọng số; công khai hoặc ẩnTC02: GET /api/products/abc → 404
AttemptMột lần sinh viên làm một assessmentLượt 2 của SV A, 14/03
SubmissionBài nộp cho một item trong một attemptFile zip dự án, mã nguồn, câu trả lời
EvaluationKết quả của một bộ chấm trên một tiêu chí, kèm bằng chứngFunctional · Test · 85 · “17/20 test đạt”
SkillScore · SnapshotĐiểm kỹ năng hiện tại và ảnh chụp theo thời gian để vẽ tiến bộREST API = 78.5 (tháng 3)
// Quan hệ cốt lõi — phía cấu hình (giảng viên tạo)
Skill ◄──ItemSkill (ratio)──► Item ∈ Section ∈ Assessment
                               Item ──► Rubric ──► Criterion[] (weight, source)
                               Item ──► TestCase[] | Question[]

// Quan hệ cốt lõi — phía thực thi (sinh viên tạo, hệ thống chấm)
Attempt ──► Submission ──► Evaluation[] ──► ItemResult ──► AssessmentResult
                                               └──ItemSkill──► SkillScore ──► SkillSnapshot
Điểm mấu chốt

ItemSkill là bảng biến “điểm bài thi” thành “điểm kỹ năng”. Thiếu nó, skill profile chỉ là con số trang trí. Ngoài ra, assessment đã publish và đã có bài nộp thì bất biến: muốn sửa test case hay trọng số phải tạo phiên bản mới, để hai sinh viên cùng làm một bài luôn được chấm cùng một bộ tiêu chí.

§5Các loại bài đánh giá

Năm loại item dùng chung một pipeline chấm; khác nhau ở bộ chấm nào tham gia và tỉ trọng của chúng.

Test chạy test case Rule luật tất định AI mô hình ngôn ngữ theo rubric Human giảng viên duyệt
LoạiSinh viên nộpCách chấmNguồn điểmTự độngPhạm vi
Trắc nghiệmĐáp án đã chọnSo đáp án; câu nhiều đáp án chấm từng phần, chọn sai bị trừRule100%MVP
Lập trìnhMã nguồn một fileCompile → chạy từng test qua stdin → so stdout → điểm theo trọng số test; AI nhận xét chất lượng mãTest AI ≤ 20%~90%MVP
REST APIDự án có Dockerfile (zip hoặc repo Git)Build image → chạy container → health check → chạy bộ test HTTP → kiểm luật thiết kế → AI reviewTest Rule AI ≤ 20%~85%MVP
SQLCâu truy vấnChạy trên DB sandbox dựng từ script seed, so tập kết quả với lời giải mẫuTest100%Mở rộng
Phỏng vấn kỹ thuậtCâu trả lời văn bản qua nhiều lượtAI hỏi từ ngân hàng câu hỏi, hỏi nối tiếp tối đa 2 lần, chấm theo rubric; GV duyệt trước khi chốtAI Human~60%MVP

Hợp đồng nộp bài REST API

Đây là điểm nhấn của đề tài và cũng dễ hỏng nhất nếu không chuẩn hoá đầu vào. Sinh viên bắt đầu từ starter template do giảng viên cung cấp và phải tuân thủ:

  • Dockerfile ở thư mục gốc; ứng dụng lắng nghe cổng 8080.
  • GET /health trả 200 trong vòng 60 giây sau khi container khởi động.
  • Dữ liệu nằm trong container (SQLite hoặc in-memory); không gọi dịch vụ bên ngoài.
  • File nộp tối đa 20 MB. Trước khi nộp chính thức, sinh viên bấm Kiểm tra build để chạy thử build + health check, không tính điểm.

Giảng viên khai báo test case dạng YAML. Test chạy tuần tự, giá trị capture ở test trước được dùng lại ở test sau, nhờ đó kiểm tra được cả luồng CRUD và luồng đăng nhập lấy token:

id: TC03
name: Tạo sản phẩm hợp lệ
weight: 2
criterion: functional
request:
  method: POST
  path: /api/products
  headers: { Authorization: "Bearer {{token}}" }   # token capture từ TC01 /api/auth/login
  body: { name: "Keyboard", price: 25.5 }
expect:
  status: 201
  headers:
    Location: { exists: true }
  json:
    - { path: "$.id",    op: exists }
    - { path: "$.name",  op: equals, value: "Keyboard" }
    - { path: "$.price", op: gt,     value: 0 }
capture:
  productId: "$.id"    # TC04: GET /api/products/{{productId}} → 200

Toán tử kiểm tra cần hỗ trợ: equals, notEquals, exists, gt/lt, contains, notContains, matches (regex), count (độ dài mảng), schema (JSON Schema cho toàn bộ response).

Hợp đồng bài lập trình

Thuộc tínhGiá trị mặc địnhGhi chú
Ngôn ngữPython 3.12, C# (.NET 8)Mỗi ngôn ngữ một runner image dựng sẵn
Thời gian mỗi test2 sVượt → TimeLimitExceeded, test đó 0 điểm
Bộ nhớ256 MBVượt → MemoryLimitExceeded
So khớp outputBỏ khoảng trắng cuối dòngCấu hình được: khớp tuyệt đối, hoặc số thực sai số 1e-6
Test công khai / ẩn2 công khai, còn lại ẩnSinh viên chỉ thấy chi tiết test công khai; test ẩn chỉ báo đạt/không

§6Luồng chấm bất đồng bộ

API không bao giờ chấm bài trong một request. Nộp bài chỉ lưu và xếp hàng, trả 202 Accepted ngay; việc build, chạy và gọi AI diễn ra trên worker, có thể mất từ vài giây tới vài phút.

MÁY WORKER RIÊNG Web App React + TS API Server ASP.NET Core Hàng đợi job: submissionId SQL Server Submission · Result Worker nhận job · điều phối · retry Container bài làm mã không tin cậy · no network Test Rule AI Score Engine rubric × trọng số → điểm ① nộp bài ② 202 ③ lưu Queued ④ enqueue ⑤ nhận job ⑥ build · run ⑧ ghi Result + SkillScore ⑨ đẩy trạng thái SignalR · poll 3 s
Mã sinh viên chỉ tồn tại bên trong khung đỏ; API server và database không bao giờ chạy nó.
  1. Web app gửi POST /api/submissions.
  2. API trả 202 Accepted + Location ngay lập tức.
  3. Bài nộp được lưu với trạng thái Queued.
  4. Job chỉ chứa submissionId, không chứa mã nguồn.
  5. Worker nhận job, giữ lease 5 phút có heartbeat.
  6. Build và chạy trong container giới hạn tài nguyên (§8).
  7. Test runner, rule engine, AI evaluator chấm độc lập; chỉ tiến trình AI evaluator được gọi ra ngoài tới AI provider.
  8. Score engine tổng hợp theo rubric rồi ghi kết quả.
  9. Giao diện nhận trạng thái qua SignalR, dự phòng bằng polling.

Vòng đời một bài nộp

SubmittedQueuedRunningEvaluatedFinalized
NhánhKhi nàoXử lý
RunningFailedLỗi hệ thống: worker chết, Docker lỗi, AI provider timeoutRetry tối đa 3 lần, giãn cách 30 s / 2 phút / 10 phút; hết lượt → DeadLetter, báo Admin
EvaluatedNeedsReviewAI chấm không ổn định, bài phỏng vấn, hoặc sinh viên gửi phúc khảoGiảng viên duyệt hoặc ghi đè, rồi chuyển Finalized
Phân biệt quan trọng

Lỗi của sinh viên — không compile được, timeout, sai output, container không qua health check — không phải Failed. Đó là kết quả chấm hợp lệ: bài vẫn đi tới Evaluated với điểm tương ứng. Failed chỉ dành cho lỗi của hệ thống, và chỉ lỗi hệ thống mới được retry. Worker kiểm tra trạng thái trước khi chạy để một job bị giao hai lần không chấm hai lần.

§7Mô hình tính điểm

Câu hội đồng chắc chắn hỏi: “Backend = 83% được tính thế nào?”. Câu trả lời là bốn tầng công thức, mỗi tầng do giảng viên cấu hình, không tầng nào để AI tự quyết.

Tầng 1 · Tiêu chí        c ∈ [0, 100]
  Test : c = 100 × Σ w(test đạt) / Σ w(mọi test của tiêu chí)
  Rule : c = 100 × Σ w(luật đạt)  / Σ w(mọi luật)
  AI   : c = 10 × điểm AI (thang 0–10, bước 0.5, đã qua kiểm soát)
  Human: c = điểm giảng viên nhập, thay thế nguồn gốc, bắt buộc có lý do

Tầng 2 · Item            I = Σ c_k × w_k                 (Σ w_k = 1, theo rubric của item)
Tầng 3 · Assessment      A = Σ I_j × W_j / Σ W_j         (W_j: trọng số item trong bài)
Tầng 4 · Skill s         S = Σ I_j × m_js / Σ m_js       (m_js: tỉ lệ ItemSkill; mỗi item lấy lượt gần nhất)

Phiếu tính điểm mẫu — kỹ năng REST API của một sinh viên

Ba item gắn với skill REST API, tỉ lệ đóng góp 20% / 50% / 30% như đề xuất ban đầu.

Thành phầnNguồnKết quả thôĐiểmTrọng sốĐóng góp
A · Quiz REST & HTTPRule8/10 câu đúng80.0080.00
B · REST API Practice — CRUD Products
FunctionalTest17/20 trọng số test đạt85.0060%51.00
API designRule7/10 luật (tên số nhiều, mã 201 + Location…)70.0015%10.50
Error handlingTest4/5 test lỗi (404, 400…)80.0010%8.00
SecurityRule3/5 kiểm tra (401 khi thiếu token…)60.0010%6.00
Code qualityAIAI 7.5/10, có trích dẫn75.005%3.75
Điểm item B100%79.25
C · Phỏng vấn kỹ thuật REST
Technical accuracyAI8.0/10 · GV đã duyệt80.0030%24.00
CompletenessAI7.0/1070.0025%17.50
ReasoningAI7.5/1075.0025%18.75
ClarityAI8.0/1080.0020%16.00
Điểm item C100%76.25
Tầng 4 · Skill REST API = Σ I × m
A · Quiz80.0020%16.000
B · Practice79.2550%39.625
C · Interview76.2530%22.875
REST API100%78.50

Trong item B, phần do AI chấm chỉ chiếm 5%, dưới trần 20%: dù AI chấm lệch hẳn 2 điểm thì điểm item chỉ đổi 1 điểm trên thang 100. Skill profile chỉ hiển thị một kỹ năng khi có ít nhất 2 item làm bằng chứng; ít hơn thì ghi “chưa đủ dữ liệu” thay vì một con số ảo. Mỗi lần có kết quả mới, hệ thống lưu một SkillSnapshot để vẽ biểu đồ tiến bộ.

Tám cơ chế kiểm soát điểm AI

Cơ chếCách làm cụ thể
Structured outputBắt buộc JSON theo schema {criterionId, score, evidence[], feedback}; sai schema → gọi lại 1 lần → vẫn sai → NeedsReview
Mô tả mức điểmRubric có mô tả cho các mức 0 / 2.5 / 5 / 7.5 / 10, đưa nguyên văn vào prompt; điểm làm tròn bước 0.5
Chấm hai lầnTemperature 0, chấm 2 lần độc lập; lệch > 1.5 điểm ở bất kỳ tiêu chí → NeedsReview
Bằng chứngMỗi điểm phải trích dẫn đoạn bài làm làm căn cứ; không có trích dẫn → không nhận điểm
Trần tỉ trọngAI ≤ 20% điểm item với Coding/REST; bài Interview do AI chấm phải qua giảng viên duyệt trước khi chốt
Lưu vếtLưu model, phiên bản prompt, số token, response gốc — tái lập được mọi lần chấm
Human overrideGiảng viên sửa điểm tiêu chí, bắt buộc ghi lý do; nguồn đổi thành Human, ghi audit log
Chống prompt injectionBài làm đặt trong delimiter, system prompt yêu cầu bỏ qua mọi chỉ dẫn nằm trong bài làm; chuỗi đáng ngờ → gắn cờ
Phần “nghiên cứu” của đề tài

Giảng viên chấm tay một gold set 30–50 câu trả lời phỏng vấn và bài code. Mỗi khi đổi prompt hoặc model, chạy lại AI trên gold set và đo sai số tuyệt đối trung bình (MAE) so với giảng viên. Mục tiêu: MAE ≤ 1.0 trên thang 10. Con số này biến “AI chấm khá ổn” thành một kết quả đo được để đưa vào báo cáo.

§8Sandbox & bảo mật thực thi

Chạy mã tuỳ ý là rủi ro bảo mật thật, không phải lý thuyết. Mỗi mối đe doạ dưới đây phải có một cấu hình cụ thể và một bài nộp độc hại tương ứng trong bộ kiểm thử.

Mối đe doạVí dụ bài nộpBiện pháp
Chạy vô hạnwhile(true){}Worker kill container sau 2 s/test (coding), 60 s khởi động + 120 s chạy test (REST)
Ngốn CPU / RAMCấp phát mảng 10 GB--cpus 1 · --memory 256m --memory-swap 256m (REST: 512m)
Fork bombos.fork() trong vòng lặp--pids-limit 128
Phá hệ thống fileGhi đè /etc, đọc bài người khác--read-only, --tmpfs /tmp:size=64m, mount bài làm chỉ đọc, mỗi job một thư mục
Truy cập mạngGửi dữ liệu ra ngoài, tấn công máy khác--network none cho coding; REST dùng mạng nội bộ riêng mỗi job, chỉ test runner vào được
Leo thang đặc quyềnKhai thác quyền root trong container--user 10001, --cap-drop ALL, --security-opt no-new-privileges, seccomp mặc định
Output khổng lồIn 1 GB ra stdoutChỉ đọc 64 KB đầu, phần còn lại bỏ
Chiếm máy chủThoát khỏi containerWorker chạy trên VM riêng; API server không có quyền Docker; container --rm, dọn image sau job
# Lệnh worker dùng cho một test case bài Python (qua Docker.DotNet SDK)
docker run --rm -i --network none --cpus 1 --memory 256m --memory-swap 256m \
  --pids-limit 128 --read-only --tmpfs /tmp:size=64m \
  --user 10001:10001 --cap-drop ALL --security-opt no-new-privileges \
  -v /jobs/8f2c/src:/app:ro  asap-runner-python:3.12  python /app/main.py
Mâu thuẫn cần xử lý: build cần mạng, run thì không

Dự án REST API phải tải thư viện (NuGet, npm) khi build. Tách thành hai bước: build chỉ được ra mạng tới registry qua proxy danh sách trắng, giới hạn 180 s; run hoàn toàn không có Internet. Starter template nên dùng base image có sẵn thư viện phổ biến để build nhanh và ổn định.

Mỗi worker chạy tối đa 4 job song song (semaphore); cần nhiều hơn thì thêm worker thứ hai trỏ vào cùng hàng đợi — không phải sửa code.

§9Kiến trúc & công nghệ

Modular monolith: một API ASP.NET Core chia module theo nghiệp vụ, mỗi module một project, giao tiếp qua interface; cộng một tiến trình Worker riêng. Với 5 sinh viên và 1 VPS, cách này cho ranh giới module rõ ràng mà không phải trả chi phí vận hành microservices.

TầngLựa chọnLý do
FrontendReact 18 + TypeScript + Vite, Ant Design, Monaco Editor, RechartsMonaco là editor của VS Code; Recharts đủ cho radar và line chart
BackendASP.NET Core 8 Web API, EF Core, FluentValidationTheo đề xuất; hệ sinh thái test tốt
DatabaseSQL Server 2022Chạy bằng container ở môi trường dev và CI
Hàng đợiHangfire (storage Redis) — hoặc Redis Streams nếu muốn tự làmHangfire có sẵn retry, dashboard, nhiều worker; giảm rủi ro cho SV3
Thời gian thựcSignalRĐẩy trạng thái chấm, dự phòng polling
SandboxDocker Engine + Docker.DotNet, runner image cho từng ngôn ngữĐiều khiển container từ C#
HTTP test runnerTự viết: HttpClient + JsonPath.Net + JsonSchema.NetPhần “tự xây” thể hiện năng lực của nhóm
AIInterface IAiProvider, cài đặt OpenAI và Gemini, thêm bản mock cho testĐổi provider không sửa nghiệp vụ
Xác thựcJWT access 15 phút + refresh token, băm mật khẩu PBKDF2RBAC dạng policy
Kiểm thửxUnit, Testcontainers, Playwright, k6Xem §13
CI/CDGitHub Actions → GHCR → Docker Compose trên VPSMỗi PR phải xanh mới được merge
Giám sátSerilog + Seq, health checksTruy lỗi chấm bài theo submissionId
# Cấu trúc solution đề xuất (Asap = tên tạm)
src/
  Asap.Api/                   # host, controller, xác thực, SignalR hub
  Asap.Modules.Identity/      # SV1
  Asap.Modules.Assessment/    # SV2 · Skill, Assessment, Item, Rubric, TestCase
  Asap.Modules.Submission/    # SV3 · Attempt, Submission, trạng thái, enqueue
  Asap.Modules.AI/            # SV4 · IAiProvider, prompt, interview
  Asap.Modules.Scoring/       # SV5 · Score Engine, SkillScore, gợi ý
  Asap.Worker/                # SV3 · nhận job, Docker, HTTP test runner
  Asap.Shared/                # DTO, enum trạng thái, hợp đồng giữa module
web/                          # React + TS
runners/                      # Dockerfile runner: python, dotnet
tests/  deploy/docker-compose.yml  .github/workflows/

§10Dữ liệu & API

Thực thể theo module

ModuleThực thểTrường và ràng buộc đáng chú ý
IdentityUser, Role, Permission, UserRole, RefreshToken, AuditLogAuditLog(actorId, action, entity, entityId, before/after JSON, ip, at)
AssessmentSkillCategory, Skill, Assessment, AssessmentSection, AssessmentItem, ItemSkill, Question, QuestionOption, TestCase, Rubric, RubricCriterionItem(type, weight, config JSON) · ItemSkill(itemId, skillId, ratio) · Criterion(weight, source, levelDescriptors) · Assessment(version, status: Draft/Published/Archived)
SubmissionAssignment, Attempt, Submission, ExecutionJob, TestResultSubmission(status, artifactPath, sha256) · ExecutionJob(tryCount, workerId, leaseUntil, lastError) · TestResult(testCaseId, passed, actual, durationMs)
AIAiProviderConfig, PromptTemplate, InterviewSession, InterviewTurn, AiEvaluationPromptTemplate(key, version) · AiEvaluation(model, promptVersion, rawResponse, score, evidence, tokens)
ScoringEvaluation, ItemResult, AssessmentResult, ScoreOverride, SkillScore, SkillSnapshot, LearningResource, RecommendationEvaluation(criterionId, source, score, evidence) · ScoreOverride(by, reason, oldScore) · SkillSnapshot(studentId, skillId, score, evidenceCount, at)

Các endpoint chính

NhómEndpointGhi chú
AuthPOST /api/auth/login · /refresh · /logoutRate limit 5 lần/phút cho login
Users/api/users · /api/rolesChỉ Admin
SkillsGET /api/skills?tree=true · CRUD /api/skillsInstructor
AssessmentsCRUD /api/assessments · POST /{id}/publish · POST /{id}/assignments · POST /{id}/dry-runPublish khoá cấu hình; dry-run chạy lời giải mẫu
Nội dung item/api/items/{id}/questions · /testcases · /rubricTest case nhận cả JSON và YAML
AttemptsPOST /api/attempts · POST /api/attempts/{id}/finishKiểm tra hạn nộp và số lượt
SubmissionsPOST /api/submissions202 + Location · GET /api/submissions/{id}Trả trạng thái, không bao giờ chấm đồng bộ
ResultsGET /api/results/{id} · PUT /api/results/{id}/criteria/{cid}/overrideOverride bắt buộc reason
InterviewsPOST /api/interviews · POST /api/interviews/{id}/answersMỗi câu trả lời nhận về câu hỏi tiếp theo
Skill profileGET /api/skill-profiles/me · GET /api/skill-profiles/{studentId}/progress?from=&to=SV chỉ xem được của mình
KhácGET /api/recommendations/me · GET /api/admin/audit-logs · hub /hubs/submissions

§11Phân công 5 thành viên

Chia theo module nghiệp vụ, không chia “người làm frontend, người làm backend”. Mỗi người sở hữu module của mình từ database tới giao diện.

SV1Identity & PlatformĐộ khó ★★★ · kiêm DevOps
Sở hữu
User, Role, Permission, RefreshToken, AuditLog
Màn hình
Đăng nhập, quản lý người dùng và vai trò, hồ sơ cá nhân, audit log
Kỹ thuật
JWT + refresh token, RBAC dạng policy, rate limiting, audit middleware, GitHub Actions, Docker Compose, triển khai VPS
SV2Assessment ManagementĐộ khó ★★★ · kiêm Quiz engine
Sở hữu
Skill, Assessment, Section, Item, ItemSkill, Question, TestCase, Rubric
Màn hình
Cây skill, trình soạn assessment, soạn test case REST (form + YAML), soạn rubric, màn làm bài quiz
Kỹ thuật
Phiên bản hoá và khoá khi publish, kiểm tra hợp lệ cấu hình (tổng trọng số = 100%), chấm quiz, dry-run lời giải mẫu
SV3Automated AssessmentĐộ khó ★★★★★
Sở hữu
Attempt, Submission, ExecutionJob, TestResult
Màn hình
Nộp code (Monaco), upload dự án REST, trạng thái chấm, chi tiết từng test
Kỹ thuật
Hàng đợi + worker, Docker sandbox, code runner, HTTP test runner (JSONPath, schema, capture), retry và lease
SV4AI AssessmentĐộ khó ★★★★
Sở hữu
AiProviderConfig, PromptTemplate, InterviewSession, InterviewTurn, AiEvaluation
Màn hình
Phỏng vấn dạng chat, hiển thị nhận xét AI, màn giảng viên duyệt điểm AI
Kỹ thuật
IAiProvider với 2 provider + mock, structured output, 8 cơ chế kiểm soát (§7), gold set đo MAE
SV5Scoring, Analytics & Skill ProfileĐộ khó ★★★★
Sở hữu
Evaluation, ItemResult, AssessmentResult, ScoreOverride, SkillScore, SkillSnapshot, Recommendation
Màn hình
Trang kết quả truy vết tới tiêu chí, skill profile (radar), tiến bộ (line), dashboard lớp cho giảng viên
Kỹ thuật
Score Engine 4 tầng — module có unit test dày nhất, mô hình skill, gợi ý học theo luật, báo cáo
Điều chỉnh so với đề xuất gốc

Score Engine được chuyển từ SV3 sang SV5. SV3 đã gánh phần khó nhất (sandbox + hai loại runner); còn Score Engine là nơi đầu ra của SV2, SV3, SV4 hội tụ, nên đặt ở người làm skill profile là hợp lý nhất.

Hợp đồng tích hợp phải chốt ở tuần 2

Hợp đồngBên cung cấp → bên dùngNội dung
TestCase schemaSV2 → SV3Định dạng YAML/JSON ở §5, danh sách toán tử, cú pháp capture
Rubric schemaSV2 → SV4, SV5Tiêu chí, trọng số, nguồn chấm, mô tả mức điểm
Evaluation recordSV3, SV4 → SV5{submissionId, criterionId, source, score, evidence}
Trạng thái SubmissionSV3 → cả nhómEnum trạng thái §6 và sự kiện SignalR
Claims & policiesSV1 → cả nhómTên role, tên policy, cách lấy userId từ token

Quy tắc chung: mỗi PR có ít nhất 1 người review chéo; ai viết module nào thì viết unit test và integration test cho module đó.

§12Lộ trình 16 tuần

Mỗi sprint kết thúc bằng một lát cắt dọc chạy được từ giao diện tới database. Không dồn tích hợp vào cuối kỳ.

TuầnSprintMục tiêuDemo cuối sprint
1–2S0 · Phân tíchSRS, use case, ERD, kiến trúc, 5 hợp đồng tích hợp, repo + CI khungDuyệt đề cương với GVHD
3–4S1 · Nền tảngAuth/RBAC, CRUD skill và assessment, Docker Compose, worker nhận job “hello”GV đăng nhập, tạo assessment
5–6S2 · Quiz end-to-endSV làm quiz → chấm → kết quả; Score Engine v1; khung IAiProviderLát cắt dọc đầu tiên
7–8S3 · Lập trìnhSandbox, code runner Python + C#, TestResult, AI feedback v1Kiểm tra giữa kỳ
9–10S4 · REST APIBuild image, health check, HTTP test runner, capture + authChấm tự động dự án CRUD Products
11–12S5 · Phỏng vấn AIInterview session, chấm theo rubric, màn duyệt, gold set + MAEPhỏng vấn 5 câu có điểm từng tiêu chí
13–14S6 · Skill profileSkillScore, snapshot, dashboard, gợi ý theo luật, override + auditHồ sơ kỹ năng và biểu đồ tiến bộ
15–16S7 · Hoàn thiệnKiểm thử tải và bảo mật, sửa lỗi, triển khai, báo cáo, slide, kịch bản demoBảo vệ

Mốc cắt phạm vi: nếu hết tuần 8 bài lập trình chưa chạy end-to-end, bỏ toàn bộ mục “Mở rộng” và dồn người hỗ trợ SV3.

§13Phi chức năng & kiểm thử

Mỗi yêu cầu phi chức năng phải có con số để kiểm chứng; nếu không đo được thì hội đồng không có lý do để tin.

NhómYêu cầu đo được
Hiệu năngAPI CRUD p95 < 300 ms với 100 người dùng đồng thời; nộp bài trả 202 trong < 500 ms
Thông lượng chấm100 bài nộp dồn cùng lúc được xếp hàng không mất bài nào; 1 worker × 4 slot chấm xong 100 bài lập trình trong ≤ 10 phút
Độ tin cậyWorker chết giữa chừng → job được nhận lại khi lease 5 phút hết hạn; retry tối đa 3 lần
Bảo mậtMọi endpoint yêu cầu xác thực trừ login; SV không đọc được kết quả của SV khác; mã bài làm không bao giờ chạy ngoài sandbox
Kiểm toánMọi thao tác đổi điểm, publish, phân quyền đều có bản ghi audit
Giải thích đượcMọi điểm hiển thị truy được tới tiêu chí, nguồn chấm và bằng chứng trong ≤ 2 lần bấm
Độ ổn định AIMAE AI so với GV ≤ 1.0/10 trên gold set; hai lần chấm lệch ≤ 1.5 điểm

“Hỗ trợ 100 submission đồng thời” trong đề xuất gốc nên hiểu là 100 bài được nhận và xếp hàng đồng thời, không phải 100 container chạy cùng lúc — một VPS không chạy nổi con số đó, và cũng không cần.

Chiến lược kiểm thử

MứcĐối tượngCông cụMục tiêu
UnitScoreEngine, RubricEngine, SkillCalculator, so khớp output, toán tử JSONxUnitCoverage ≥ 80% cho Scoring và runner
IntegrationSubmission → hàng đợi → worker → kết quả với container thậtTestcontainersLuồng chính xanh trên CI
APIAuth, assessment, submission, resultWebApplicationFactory hoặc NewmanMọi endpoint có test mã trạng thái
E2EĐăng nhập → làm bài → nộp → xem kết quảPlaywright5 kịch bản chính
Bảo mậtKhông token, JWT sai/hết hạn, SV gọi API của GV, xem kết quả người khác (IDOR), bộ bài nộp độc hại ở §8xUnit + bộ mẫu độc hạiChạy trên mỗi PR
TảiNộp bài và xem kết quảk6Đạt ngưỡng hiệu năng ở trên
Hồi quy AIGold set khi đổi prompt hoặc modelScript đo MAEMAE ≤ 1.0
# Pipeline CI/CD
Pull request → build → unit test → integration test (Testcontainers) → test bảo mật → docker build
merge main   → push image lên GHCR → SSH vào VPS → docker compose pull && docker compose up -d

§14Rủi ro

Rủi roDấu hiệu sớmGiảm thiểu
Phình phạm viTuần 8 chưa có bài lập trình chạy end-to-endKhoá MVP ở tuần 2; cắt mục “Mở rộng” theo mốc ở §12
AI chỉ là “gọi API”Điểm AI đi thẳng vào kết quảAI là một evaluator trong pipeline, có trần tỉ trọng, 8 cơ chế kiểm soát, gold set
Sandbox không an toànBài độc hại làm treo workerChỉ 2 ngôn ngữ, runner image dựng sẵn, worker trên VM riêng, bộ bài độc hại trên CI
AI chấm không ổn địnhHai lần chấm lệch nhiềuMô tả mức điểm, chấm hai lần, NeedsReview, human override
Điểm skill “ảo”Không giải thích được một con sốCông thức 4 tầng công khai, phiếu truy vết, cần ≥ 2 item mới hiển thị
Chi phí và hạn mức AIHết quota trước buổi demoGiới hạn token mỗi bài, provider dự phòng, mock provider cho dev và CI
Dự án REST của SV không buildTỉ lệ lỗi build cao ở lần nộp đầuStarter template, nút “Kiểm tra build” trước khi nộp
Tích hợp muộnModule chạy riêng được nhưng ghép lại hỏng5 hợp đồng ở tuần 2, lát cắt dọc mỗi sprint
Gian lậnNhiều bài nộp giống hệt nhauLưu hash bài nộp; phỏng vấn hỏi nối tiếp về chính bài làm; so trùng mã nằm ở “Mở rộng”

§15Cần chốt & chuẩn bị bảo vệ

Sáu quyết định cần chốt với giảng viên hướng dẫn

  1. Ngôn ngữ cho bài lập trình — đề xuất Python + C#.
  2. Stack cho bài REST API — đề xuất ASP.NET Core và Node/Express, mỗi stack một starter template.
  3. AI provider và ngân sách — OpenAI hay Gemini, hạn mức token mỗi tháng.
  4. Chính sách điểm AI — trần 20% cho Coding/REST có được chấp nhận không; bài phỏng vấn duyệt 100% hay duyệt mẫu.
  5. Hạ tầng demo — VPS của trường hay cloud; có được một máy riêng cho worker không.
  6. Thời lượng thực tế — 16 hay 20 tuần; quyết định có làm mục “Mở rộng” hay không.

Câu hỏi hội đồng dễ hỏi

“Điểm Backend 83% được tính thế nào?”
Mở phiếu tính điểm (§7): công thức 4 tầng, tỉ lệ ItemSkill do giảng viên cấu hình, bấm tới từng tiêu chí và bằng chứng.
“Nếu AI chấm sai thì sao?”
AI tối đa 20% điểm bài có test; lệch giữa hai lần chấm → giảng viên duyệt; mọi điểm có thể ghi đè kèm lý do; độ chính xác đã đo bằng MAE trên gold set.
“Sinh viên nộp mã độc thì sao?”
Trình diễn bộ bài độc hại ở §8 chạy trên CI: fork bomb, vòng lặp vô hạn, đọc file hệ thống, gọi mạng — tất cả bị chặn và vẫn nhận điểm 0 hợp lệ.
“Worker chết giữa chừng thì sao?”
Lease hết hạn, job được worker khác nhận lại, retry tối đa 3 lần, rồi vào DeadLetter; không bài nộp nào mất.
“Khác gì LeetCode hay HackerRank?”
Chấm cả dự án REST API thật và kỹ năng trình bày qua phỏng vấn, theo rubric giảng viên tự cấu hình, và quy tất cả về một hồ sơ kỹ năng theo thời gian.