Skip to content

Quality Engine ​

Cổng cuối cùng trước khi một câu hỏi tới tay đứa trẻ. Assessment Engine lo "đo có đáng tin không"; engine này lo "cái đem ra đo có xứng đáng không".

Thiết kế: SDD-003 §6–§10 · Code: modules/content/quality.ts · screening.ts · lifecycle.ts · review-queue.ts · Test: quality.test.ts (22 ca) · Rubric: CC-QAF-1.0 (kế thừa chuyenchon)


1. Bốn lớp, không lớp nào thay được lớp nào ​

1. Sàng lọc máy      screening.ts    12 luật cấu trúc, tất định, rẻ
2. Chấm rubric       quality.ts      8 chiều × ≥2 evaluator độc lập, mỗi chiều kèm bằng chứng
3. Cổng publish      quality.ts      6 điều kiện SỐ, trả lý do bằng tiếng người
4. Vòng phản hồi     review-queue.ts learner báo sai → hàng đợi người soát → item lùi khỏi published

Lớp 1 bắt lỗi hình thức (đáp án ngoài phạm vi, lựa chọn trùng). Lớp 2 bắt lỗi nội dung (sai kiến thức, đoán được đáp án mà không cần biết bài). Lớp 3 quyết định. Lớp 4 sửa những gì ba lớp trên bỏ sót — vì sẽ luôn có.


2. Lớp 1 — Sàng lọc máy (12 luật) ​

Chặn (blocking) — 9 luật. Sai cấu trúc thì không có điểm số nào cứu được:

MãNghĩa
options_unparsableoptions_json không parse được
options_too_fewít hơn 2 lựa chọn
options_duplicatehai lựa chọn trùng nhau
option_emptycó lựa chọn rỗng — trên màn hình là một nút trắng không đọc được
correct_index_out_of_rangecorrect_index ngoài phạm vi
prompt_empty / prompt_too_shortđề bài rỗng hoặc dưới 10 ký tự
prompt_duplicate_conflictingtrùng đề và trùng lựa chọn nhưng khác đáp án đúng → chắc chắn một bản sai
node_missingnode_id trỏ tới skill node không tồn tại → không biết dạy/đo kỹ năng nào

Cảnh báo (warn) — 3 luật: prompt_duplicate, difficulty_out_of_range, discriminator_missing_misconception.

Ba cái bẫy đã bịt (có test riêng) ​

BẫyLuật đúng
Lựa chọn chỉ khác hoa/thườngKhông phải trùng — chính chữ hoa có thể là nội dung được hỏi (chính tả, tên riêng)
Cùng đề, khác bộ lựa chọnLà họ câu biến thể hợp lệ → chỉ warn
Cùng đề, cùng lựa chọn, khác đáp ánBlocking — chắc chắn một bản sai

Ba bẫy này là lý do sàng lọc phải là code có test, không phải một câu prompt gửi cho LLM.


3. Lớp 2 — Rubric CC-QAF, 8 chiều ​

Đúng 8 chiều theo SDD-003 §6 — không thêm, không bớt, không đổi tên khoá:

ChiềuNhãnTrọng số
accuracyChính xác3
assessment_validityGiá trị đo lường2
clarityRõ ràng1.5
pedagogySư phạm1.5
alignmentBám chương trình1
cognitive_demandMức tư duy1
accessibilityTiếp cận được1
engagementCuốn hút0.5

Trọng số nói lên thứ tự giá trị: sai kiến thức hoặc không đo được gì thì mọi chiều khác vô nghĩa. Một câu cuốn hút mà sai kiến thức là câu tệ hơn một câu khô khan mà đúng.

Evidence-gated scoring — luật gốc của rubric ​

ts
score > 0 && evidence.trim() === ""
  → score = 0, reason += "· điểm bị hạ về 0: chấm không kèm evidence"

Điểm trần không lý do là cách nhanh nhất để "đạt chuẩn" mà nội dung vẫn sai.

Mỗi DimensionScore bắt buộc có reason (vì sao chấm mức đó) và evidence (trích từ chính item). Ví dụ evaluator tất định chấm pedagogy:

score: 0.9
reason: "Có ghi lỗi sai học sinh hay mắc → sai còn học được điều gì đó"
evidence: 'misconception: "Nhầm dấu khi chuyển vế"'

Hai loại evaluator ​

Evaluator 1 — tất định (rule-engine@1). Không gọi AI, chạy được mọi lúc, kết quả tái lập được. Suy điểm từ những gì máy đo được: độ dài đề bài, có misconception không, role/difficulty, chênh lệch độ dài các lựa chọn.

Một luật đáng chú ý trong assessment_validity:

chênh lệch độ dài lựa chọn > 60 ký tự → 0.55
"Đáp án đúng dài hơn hẳn các phương án nhiễu — learner đoán được mà không cần biết bài"

Evaluator 2 — ngữ nghĩa (LLM), qua AI Gateway. Đây là chỗ tinh tế nhất:

Chấm hai lần cùng một câu hỏi thì chỉ là cùng một ý kiến nói to hơn, không phải multi-evaluator.

Nên hai lượt chấm dùng hai góc nhìn khác nhau (AS-10.4.2):

Góc nhìnSoi gì
teacher — giáo viên chấm đềĐúng sai kiến thức, chất lượng phương án nhiễu, câu có đo đúng kỹ năng cần đo
struggling_learner — học sinh yếu đọc thửCó hiểu được không, có mẹo đoán đáp án mà không cần biết bài không, chỗ nào gây rối

Góc nhìn thứ hai bắt được loại lỗi mà giáo viên hay bỏ sót: câu đúng về kiến thức nhưng đoán được.

SDD-003 §7.3 cấm hỏi chung "câu này có tốt không" — phải hỏi từng chiều kèm bằng chứng. Output AI qua zod schema trước khi vào profileFromRubricScores().


4. Lớp 3 — Cổng publish, 6 điều kiện bằng số ​

Ngưỡng khai tập trung, đổi thì đổi một chỗ (AS-06.3.3):

PUBLISH_MIN_QUALITY_SCORE      0.72     điểm tổng hợp
PUBLISH_MIN_DIMENSION_SCORE    0.50     không chiều nào được thủng sàn
PUBLISH_MIN_EVALUATORS         2        lượt đánh giá ĐỘC LẬP
PUBLISH_MAX_EVALUATOR_SPREAD   0.25     chênh điểm tổng giữa các evaluator
PUBLISH_MAX_DIMENSION_SPREAD   0.40     chênh trên MỘT chiều
REVIEW_QUEUE_QUALITY_SCORE     0.55     dưới mức này đẩy thẳng vào hàng đợi người

Sáu điều kiện, thiếu bất kỳ cái nào là không qua:

  1. Sàng lọc máy không còn lỗi chặn — cờ chặn thắng mọi điểm số
  2. Đủ ≥2 evaluator độc lập
  3. Điểm tổng ≥ 0.72
  4. Các evaluator không lệch nhau quá 0.25 — lệch nhiều nghĩa là cần người soát, không phải lấy trung bình
  5. Không chiều nào lệch quá 0.40 giữa các evaluator
  6. Mỗi evaluator chấm đủ 8 chiều; và trung bình từng chiều ≥ 0.5

Điều kiện 6 bịt một lỗ thật: điểm tổng cao vẫn có thể che một chiều bằng 0. Câu sai kiến thức nhưng rõ ràng, cuốn hút, dễ đọc vẫn có thể vượt 0.72 nếu chỉ nhìn điểm tổng.

Trả lý do, không trả true/false ​

jsonc
{
  "ok": false,
  "reasons": [
    "Mới có 1 lượt đánh giá, cần tối thiểu 2 lượt độc lập",
    "Chiều \"Giá trị đo lường\" chỉ 0.4 < sàn 0.5"
  ]
}

Coral hiện thẳng những câu này cho người soạn. Một cổng chỉ nói "không được" mà không nói vì sao thì người soạn chỉ có thể đoán.


5. Vòng đời nội dung — không ai sửa thẳng vào items ​

candidate  → đang chờ đủ điểm + đủ evaluator
published  → bản đang nằm trong `items`, đang phục vụ learner
superseded → từng published, bị version mới thay
rejected   → người soát bác bỏ

Luật gốc: bảng items là bản đang phục vụ learner, không ai sửa thẳng vào đó. Mọi thay đổi tạo một dòng mới trong item_versions ở trạng thái candidate; publish là hành động sao chép version đã đạt ngưỡng sang items; rollback là publish lại một version cũ.

Nhờ vậy luôn trả lời được: nội dung này version mấy, ai duyệt, lúc nào, dựa trên hồ sơ chất lượng nào.

SDD-003 §10 thiết kế 7 trạng thái (Draft → Review → Candidate → Evaluated → Approved → Published → Deprecated); schema 0032 rút về 4 — đủ để phân biệt những gì hệ thống thật sự cần hành xử khác nhau.


6. Lớp 4 — Vòng phản hồi từ learner ​

"Learner báo sai thì đi về đâu" — audit #001 chấm trượt đúng vì báo cáo rơi vào hư vô.

Hai nguồn đổ vào content_review_queue:

NguồnTừ đâu
Người báocontent_reports (forum/interaction) + nút báo sai trong app learn
Máy sàng lọcscripts/screen-items.mjs + screening.ts trong luồng sinh nội dung

Một target chỉ nằm một dòng khi còn mở: 5 learner báo cùng một câu thì người soát thấy một việc, không phải năm việc. Lý do mới được nối thêm vào dòng đang mở.

Trạng thái: open → in_review → resolved / dismissed.

Hệ quả tức thì khi một item bị gắn cờ: nó lùi khỏi published và không tới tay đứa trẻ tiếp theo. Đứa trẻ đang làm dở thì vẫn làm hết bài, nhưng câu đó không ghi evidence (Learning §5).

Spot-check bởi người — 20%, chọn bằng hash ​

ts
HUMAN_SPOT_CHECK_RATE = 0.2
isSpotCheckSelected(key) → FNV-1a hash % 1000 / 1000 < rate

Chọn bằng hash nên tái lập được: cùng một item luôn cho cùng kết luận, và đếm lại được "đã spot-check bao nhiêu %" từ chính id.

Máy chấm đạt ngưỡng vẫn không thay được việc người đọc thử một phần nội dung (AS-10.4.3).


7. Cách DÙNG ​

NơiEndpoint
Sinh item có chấm chất lượngPOST /v1/coral/generate-items
Sinh cả bộ theo blueprintPOST /v1/coral/blueprints/{id}/generate
Hàng đợi rà soátGET /v1/coral/review-queue · PATCH /v1/coral/review-queue/{id}
Xem prompt đang dùngGET /v1/coral/prompts

Giao diện: coral.nemo12.com. Rate limit ai-generate: 30 lượt/giờ/user (config §4).

quality_json lưu nguyên từng lượt chấm, không nuốt evidence — để sau này trả lời được "ai chấm, dựa vào cái gì".


8. Bất biến — vi phạm là bug ​

  1. Điểm > 0 mà không có evidence → hạ về 0, ghi lý do.
  2. Đủ 8 chiều hoặc không tính là hồ sơ hoàn chỉnh.
  3. ≥2 evaluator độc lập, và phải khác góc nhìn.
  4. Cờ chặn của sàng lọc máy thắng mọi điểm số.
  5. Evaluator lệch nhau nhiều → chặn, không lấy trung bình cho qua.
  6. Trung bình từng chiều phải ≥ sàn — điểm tổng cao không che được một chiều 0.
  7. Không ai sửa thẳng vào items — luôn qua item_versions.
  8. Cổng publish trả lý do, không trả true/false trần.
  9. Báo cáo của learner không được rơi vào hư vô.

9. Kiểm chứng ​

quality.test.ts — 22 ca, gồm ba ca "bẫy" ở §2 và những ca khoá cổng publish:

  • evaluator tất định chấm đủ 8 chiều, đúng rubric version
  • mỗi chiều kèm evidence + reason, không chỉ điểm số trần
  • chấm điểm dương mà evidence rỗng → hạ về 0
  • accuracy được cân nặng nhất — sai kiến thức thì mọi chiều khác không cứu nổi
  • một evaluator là chưa đủ, dù điểm cao
  • hai evaluator lệch nhau quá nhiều thì chặn dù điểm trung bình đẹp
  • thiếu một chiều là hồ sơ chưa hoàn chỉnh → chặn
  • cờ chặn của sàng lọc máy thắng mọi điểm số

Gate: QG-006.

10. Khoảng trống đã biết ​

ViệcTrạng thái
Evaluator learner_evidence — dùng kết quả làm bài thật để chấm lại chất lượng câu⚠️ kiểu đã khai trong evaluator_kind, chưa có luồng nào sinh
Evaluator graph — kiểm tính nhất quán với skill graph⚠️ như trên
Kho câu mỏng (trung bình 8,5 câu/bài)⚠️ gốc là thiếu nội dung, không phải lỗi engine
Spot-check 20% có bảng theo dõi "đã soát bao nhiêu"⏳ hàm chọn đã có, chưa có báo cáo độ phủ
Quality profile cho lab và đề thi⏳ hiện rubric chạy trên items

Dòng đầu đáng chú ý nhất: learner_evidence là evaluator mạnh nhất có thể có — 200 đứa trẻ làm một câu nói lên nhiều hơn hai lượt chấm. Dữ liệu đã có trong assessment_responses; chưa có luồng nối ngược về quality profile.

Trace ​