Imported from Tqan-Luowng/Quiz-app (
skills/quest-gen/SKILL.md). Install upstream withnpx skills add Tqan-Luowng/Quiz-app --skill quest-gen. Copyright stays with the author.
Sample-Based Question Generator
Skill này đọc câu hỏi mẫu + tài liệu chủ đề trong một folder, rồi sinh ra một bộ câu hỏi trắc nghiệm mới có số lượng, độ khó và chiều sâu tương ứng với mẫu, kèm đáp án đúng và giải thích cho từng câu. Skill tổng quát cho mọi môn học — bộ mẫu có thể là SQL, mạng máy tính, lịch sử, sinh học, kế toán... quy trình phân tích và chuẩn hóa đều như nhau.
Khi nào dùng
- Người dùng cung cấp đường dẫn folder chứa câu hỏi mẫu và yêu cầu sinh thêm câu hỏi cùng phong cách/độ khó
- Người dùng muốn mở rộng ngân hàng câu hỏi cho một chủ đề dựa trên tài liệu có sẵn (giáo trình, ghi chú, slide, PDF...) trong folder
- Người dùng đưa cả câu hỏi mẫu lẫn tài liệu chủ đề, muốn AI tự đối chiếu 2 nguồn để sinh câu hỏi hợp lý
Input cần có
Trước khi sinh, xác định rõ:
- Đường dẫn folder — nơi chứa câu hỏi mẫu và/hoặc tài liệu chủ đề. Nếu người dùng chỉ đưa 1 folder chung, tự phân loại file bên trong: file chứa câu hỏi/đáp án (mẫu) vs file tài liệu lý thuyết (nguồn nội dung).
- Số lượng câu hỏi cần sinh — mặc định 100 nếu không nói khác.
- Câu hỏi mẫu — bắt buộc phải có ít nhất vài câu mẫu để agent phân tích định dạng, phong cách, mức độ khó. Nếu folder không có câu hỏi mẫu nào, dừng lại và hỏi người dùng thay vì tự bịa chuẩn định dạng.
Quy trình thực hiện
Bước 1 — Đọc và phân tích câu hỏi mẫu
Đọc toàn bộ câu hỏi mẫu trong folder, rút ra:
- Định dạng: xác định đúng 1 trong các dạng câu hỏi sau cho từng câu mẫu (một bộ câu hỏi có thể trộn nhiều dạng), giữ nguyên tỉ lệ dạng này khi sinh câu hỏi mới:
single_choice— trắc nghiệm 1 đáp án đúng ("Select one")multi_select— trắc nghiệm nhiều đáp án đúng cùng lúc ("Select one or more"), số đáp án đúng có thể là 2, 3, hoặc cả 4true_false_set— một câu hỏi chứa nhiều statement con (A, B, C...), mỗi statement phải đánh giá độc lập là True/False ("Select an option for each statement")matching— nối các mục ở cột A với mục tương ứng ở cột B (matching/pairing 2 danh sách)ordering— sắp xếp một tập bước/thành phần rời rạc (A, B, C...) theo đúng trình tự logic hoặc trình tự thực hiện ("Arrange the following steps... in the correct sequence", "Drag and drop the steps into the correct sequence"). Khác vớimatching: ordering chỉ có MỘT danh sách cần xếp lại thứ tự, không phải nối 2 cột.- các dạng khác nếu mẫu có: điền khuyết, tự luận ngắn...
- Độ khó (difficulty): quan sát tỉ lệ câu dễ/trung bình/khó trong mẫu (nếu mẫu toàn câu khó thì bộ mới cũng phải khó tương ứng, không tự ý làm dễ đi)
- Chiều sâu (depth): câu mẫu chỉ hỏi ghi nhớ định nghĩa, hay đòi hỏi hiểu bản chất, vận dụng vào tình huống, phân tích/so sánh, hay đọc hiểu code? Xếp loại theo thang nhận thức:
nhớ→hiểu→vận dụng→phân tích - Văn phong: độ dài câu hỏi, cách đặt bẫy trong đáp án nhiễu (distractor), ngôn ngữ (Việt/Anh), có dùng code snippet/số liệu/tình huống thực tế hay không
Bước 1.5 — Chuẩn hóa độ khó và chiều sâu (áp dụng cho mọi môn)
Đây là bước hay bị làm hời hợt nhất — sinh đúng dạng câu hỏi thì dễ, nhưng sinh đúng độ khó và chiều sâu mới là thứ quyết định bộ câu hỏi có "giống mẫu" hay không. Dùng khung 4 mức nhận thức dưới đây để gắn nhãn từng câu mẫu, rồi áp lại tỉ lệ đó khi sinh câu mới. Ví dụ minh họa lấy từ một bộ mẫu SQL Server, nhưng khung này áp dụng cho bất kỳ môn nào — chỉ cần thay chủ đề.
| Mức | Đặc điểm nhận diện | Cách distractor được đặt bẫy | Ví dụ minh họa (SQL) |
|---|---|---|---|
| Nhớ (dễ) | Hỏi lại gần như nguyên văn một định nghĩa/phát biểu có trong tài liệu. Trả lời được chỉ cần nhớ đúng 1 câu, không cần suy luận. | Đáp án sai thường là phát biểu ngược nghĩa hoàn toàn hoặc lệch chủ đề rõ rệt, dễ loại trừ. | "A DBMS is a set of software programs that allow databases to be defined, constructed, and manipulated" — True/False |
| Hiểu (dễ–trung bình) | Hỏi về vai trò/chức năng/mối quan hệ giữa các khái niệm, không hỏi nguyên văn định nghĩa. Cần hiểu khái niệm đó dùng để làm gì hoặc khác gì với khái niệm liền kề. | Đáp án nhiễu là khái niệm "hàng xóm" dễ nhầm (cùng nhóm chủ đề), hoặc phát biểu đúng nhưng gán sai vào ngữ cảnh/khái niệm khác. | Matching "Foreign Key / Primary Key / Check / Not Null" ↔ vai trò referential integrity; true/false statement dùng phủ định để bẫy ("It does NOT allow...") |
| Vận dụng (trung bình) | Yêu cầu áp dụng một quy trình nhiều bước vào tình huống cụ thể, hoặc sắp xếp đúng trình tự thao tác thực tế (không chỉ nhớ khái niệm rời rạc). | Các bước bị xáo trộn có vẻ hợp lý ở nhiều thứ tự khác nhau nếu không thực sự hiểu quy trình; bẫy nằm ở việc đảo 2 bước liền kề dễ nhầm. | Ordering: "Run setup.exe → chọn Feature PolyBase → chạy sp_configure → tạo External Table" (đúng thứ tự cài đặt PolyBase) |
| Phân tích (khó) | Đưa dữ kiện/số liệu/đoạn code cụ thể, người học phải tự truy vết logic ra kết quả — không có câu nào trong tài liệu nói thẳng đáp án. Đây là mức khó nhất, đòi hỏi "chạy" quy tắc trong đầu. | Đáp án nhiễu là kết quả của một lỗi suy luận phổ biến (ví dụ hiểu sai phạm vi phủ định, sai một ký tự trong pattern, lệch chỉ số) chứ không phải bịa ngẫu nhiên. | SELECT * FROM Production.Product WHERE ProductID LIKE '3[^0123]5' — phải tự suy ra pattern này khớp với những giá trị nào (305/315/325/335, không phải 3015...) rồi mới chọn đáp án |
Cách dùng khung này khi phân tích mẫu:
- Gắn nhãn (difficulty, depth) cho từng câu mẫu theo bảng trên.
- Đếm tỉ lệ % của từng mức nhận thức trong tổng số câu mẫu.
- Khi sinh câu mới, giữ đúng tỉ lệ đó — nếu mẫu có ~15% câu ở mức "phân tích" (có số liệu/code cụ thể cần truy vết), bộ 100 câu mới cũng phải có ~15 câu dạng này, không thay hết bằng câu "nhớ" cho dễ sinh.
- Với câu mức "phân tích", bắt buộc agent phải tự giải/truy vết ra đáp án đúng trước khi viết câu hỏi (không được suy đoán), để đảm bảo pattern-trap trong distractor là hợp lý và có thật.
- Với câu mức "vận dụng" dạng
ordering, xác nhận trình tự đúng dựa trên logic quy trình thực tế của chủ đề (cài đặt phần mềm, các bước một thuật toán, trình tự lịch sử...), không bịa thứ tự.
Bước 2 — Đọc tài liệu chủ đề
Đọc các file tài liệu còn lại trong folder (nếu có) để lấy nội dung, khái niệm, thuật ngữ chính xác — tránh sinh câu hỏi dựa trên kiến thức chung chung không khớp với tài liệu người dùng cung cấp. Nếu không có tài liệu riêng, dùng chính nội dung ẩn trong câu hỏi mẫu làm phạm vi chủ đề.
Bước 3 — Lập kế hoạch phân bổ
- Liệt kê các chủ đề con xuất hiện trong tài liệu/câu hỏi mẫu, chia đều số câu cho từng chủ đề con
- Giữ tỉ lệ độ khó và chiều sâu giống hệt tỉ lệ quan sát được ở Bước 1/1.5 (không tự ý đổi tỉ lệ dễ/khó, không tự ý đổi tỉ lệ nhớ/hiểu/vận dụng/phân tích)
- Giữ tỉ lệ
question_type(single_choice/multi_select/true_false_set/matching/ordering) giống tỉ lệ trong mẫu — nếu mẫu chỉ có 1-2 câu multi_select trong 35 câu thì bộ 100 câu mới cũng chỉ nên chiếm tỉ lệ tương đương, không tự ý cân bằng đều mỗi dạng - Đảm bảo đa dạng dạng câu hỏi trong cùng một mức độ khó (không lặp mô-típ câu hỏi)
Bước 4 — Sinh câu hỏi
Quy tắc chung cho mọi câu hỏi mới:
- Nội dung không trùng lặp với câu mẫu và không trùng lặp giữa các câu mới với nhau
- Bắt buộc có đáp án đúng rõ ràng
- Bắt buộc có giải thích ngắn gọn nêu lý do đáp án đúng và vì sao các đáp án/statement khác sai (nếu phù hợp với độ sâu của câu)
- Câu ở mức "phân tích" phải kèm dữ kiện/số liệu/code cụ thể trong phần
question, không được để trừu tượng chung chung
Quy tắc riêng theo từng dạng:
single_choice: 4 đáp án (hoặc số lượng giống mẫu), chỉ 1 đáp án đúng. Đáp án nhiễu phải hợp lý, cùng mức "bẫy" như mẫu, không quá dễ loại trừ.multi_select: giữ đúng tỉ lệ số đáp án đúng (2/4, 3/4...) như phân bố quan sát được trong mẫu — không mặc định lúc nào cũng 2/4. Mỗi đáp án sai phải sai vì một lý do khác nhau (không lặp lại cùng một kiểu bẫy ở nhiều đáp án), tránh để đáp án đúng "quá hiển nhiên" so với đáp án sai.true_false_set: mỗi statement con (A, B, C...) độc lập, phải tự đứng vững đúng/sai mà không phụ thuộc statement khác. Trộn cả statement đúng lẫn sai trong cùng câu (không để toàn bộ statement cùng đúng hoặc cùng sai — trừ khi mẫu cho thấy xu hướng đó). Statement sai nên sai ở một chi tiết cụ thể (số liệu, thời gian, phạm vi, hoặc một phép phủ định lật nghĩa) chứ không sai toàn bộ, để giữ đúng mức "bẫy" như mẫu.matching: cột A và cột B phải cùng số lượng mục (hoặc lệch có chủ đích nếu mẫu làm vậy để tránh đoán loại trừ ở mục cuối). Mỗi cặp nối phải dựa trên một mối liên hệ khái niệm rõ ràng, không được đoán được qua cách diễn đạt trùng từ khóa giữa 2 cột.ordering: các bước phải xáo trộn từ một trình tự thực hiện/logic có thật của chủ đề (không tự bịa thứ tự tùy ý). Số bước giống số lượng quan sát được trong mẫu (thường 3–5 bước). Tránh để 2 bước có thể hoán đổi vị trí mà vẫn hợp lý — mỗi bước phải có đúng một vị trí hợp lệ trong trình tự.
Bước 5 — Validate trước khi ghi file
- Đủ số lượng yêu cầu
- Không câu nào thiếu đáp án đúng hoặc thiếu giải thích
- ID không trùng lặp
- Phân bổ độ khó/chiều sâu/question_type lệch không quá ~10% so với tỉ lệ mẫu gốc
- Không có câu hỏi trùng ý giữa các chủ đề con
multi_select:correct_answerscó ít nhất 2 phần tửtrue_false_set: mỗi statement có fieldanswerkiểu boolean, không statement nào bị bỏ trốngmatching:correct_pairsánh xạ đủ 1-1 giữa mọi key củacolumn_avàcolumn_b, không thiếu/thừa cặpordering:correct_orderliệt kê đủ tất cả label bước, không lặp, không thiếu
Bước 6 — Ghi file kết quả
Lưu vào output/{ten_chu_de}_generated.json (hoặc định dạng file trùng với câu hỏi mẫu nếu mẫu không phải JSON, ví dụ .md/.csv). Báo lại cho người dùng: tổng số câu đã sinh, phân bổ theo chủ đề con, phân bổ theo độ khó/chiều sâu/question_type.
Schema JSON mặc định (khi mẫu không quy định định dạng khác)
Field chung cho mọi câu: id, sub_topic, difficulty, depth, question_type, question, explanation. Field còn lại (options/correct_answer/statements/column_a/column_b/pairs/steps/correct_order) thay đổi theo question_type như dưới đây.
{
"topic": "Tên chủ đề",
"source_folder": "đường dẫn folder gốc",
"total_questions": 100,
"difficulty_distribution": { "easy": 30, "medium": 50, "hard": 20 },
"depth_distribution": { "nhớ": 25, "hiểu": 35, "vận dụng": 25, "phân tích": 15 },
"question_type_distribution": { "single_choice": 55, "multi_select": 15, "true_false_set": 15, "matching": 10, "ordering": 5 },
"questions": [
{
"id": "q_001",
"sub_topic": "Chủ đề con",
"difficulty": "medium",
"depth": "hiểu",
"question_type": "single_choice",
"question": "Nội dung câu hỏi (Select one)",
"options": { "A": "...", "B": "...", "C": "...", "D": "..." },
"correct_answer": "B",
"explanation": "Giải thích vì sao đáp án đúng, có thể kèm lý do các đáp án khác sai."
},
{
"id": "q_002",
"sub_topic": "Chủ đề con",
"difficulty": "medium",
"depth": "hiểu",
"question_type": "multi_select",
"question": "Nội dung câu hỏi (Select one or more)",
"options": { "A": "...", "B": "...", "C": "...", "D": "..." },
"correct_answers": ["A", "D"],
"explanation": "Giải thích vì sao A và D đúng, vì sao B và C sai."
},
{
"id": "q_003",
"sub_topic": "Chủ đề con",
"difficulty": "hard",
"depth": "phân tích",
"question_type": "true_false_set",
"question": "Đánh giá mỗi statement sau là True hay False (Select an option for each statement)",
"statements": [
{ "label": "A", "text": "...", "answer": true },
{ "label": "B", "text": "...", "answer": false },
{ "label": "C", "text": "...", "answer": true },
{ "label": "D", "text": "...", "answer": false },
{ "label": "E", "text": "...", "answer": true }
],
"explanation": "Giải thích ngắn cho từng statement, đặc biệt statement False sai ở chi tiết nào."
},
{
"id": "q_004",
"sub_topic": "Chủ đề con",
"difficulty": "medium",
"depth": "hiểu",
"question_type": "matching",
"question": "Nối mỗi mục ở cột A với mục tương ứng ở cột B",
"column_a": { "1": "...", "2": "...", "3": "...", "4": "..." },
"column_b": { "a": "...", "b": "...", "c": "...", "d": "..." },
"correct_pairs": { "1": "c", "2": "a", "3": "d", "4": "b" },
"explanation": "Giải thích lý do từng cặp nối đúng."
},
{
"id": "q_005",
"sub_topic": "Chủ đề con",
"difficulty": "medium",
"depth": "vận dụng",
"question_type": "ordering",
"question": "Sắp xếp các bước sau theo đúng trình tự thực hiện",
"steps": { "A": "...", "B": "...", "C": "...", "D": "..." },
"correct_order": ["B", "D", "A", "C"],
"explanation": "Giải thích vì sao thứ tự này đúng — bước nào phải xảy ra trước bước nào và tại sao."
},
{
"id": "q_006",
"sub_topic": "Chủ đề con",
"difficulty": "hard",
"depth": "phân tích",
"question_type": "single_choice",
"question": "Câu hỏi có kèm dữ kiện/số liệu/code cụ thể cần tự truy vết ra kết quả trước khi chọn đáp án",
"options": { "A": "...", "B": "...", "C": "...", "D": "..." },
"correct_answer": "A",
"explanation": "Trình bày cách suy luận/truy vết từng bước ra đáp án đúng, và lỗi suy luận nào dẫn tới từng đáp án nhiễu."
}
]
}
Nếu câu hỏi mẫu dùng định dạng khác các dạng trên (vd điền khuyết, tự luận ngắn), thêm question_type tương ứng và điều chỉnh field nội dung cho phù hợp, giữ nguyên các field chung còn lại.
Lưu ý
- Không tự ý hạ độ khó để "dễ sinh câu hỏi hơn" — mục tiêu là bám sát mẫu, không phải tối ưu tốc độ sinh.
- Câu mức "phân tích" là nơi dễ bị làm hời hợt nhất (agent có xu hướng viết câu hỏi trông có vẻ khó nhưng thực chất vẫn là "nhớ" trá hình) — luôn tự kiểm tra: câu hỏi có bắt người học phải tự suy luận/truy vết ra kết quả hay chỉ cần nhớ một câu trong tài liệu?
- Nếu tài liệu chủ đề quá ít để sinh đủ số lượng yêu cầu mà không lặp ý, báo lại cho người dùng thay vì sinh câu hỏi lan man ngoài phạm vi tài liệu.
- Nếu người dùng không cung cấp cả câu hỏi mẫu lẫn tài liệu, không tự suy diễn — hỏi lại để có ít nhất một nguồn tham chiếu.
- Skill này không giới hạn ở một môn học cụ thể nào — khung độ khó/chiều sâu ở Bước 1.5 dùng được cho SQL, lập trình, mạng máy tính, khoa học xã hội, y khoa... chỉ cần thay ví dụ minh họa bằng nội dung của chủ đề tương ứng.