ClassLensPortal / QUESTION_CATEGORIZATION.md
taboola
UI redesign to light theme, question bank creation, report redesign, info tooltips, and icon labels
e2fcda8
|
Raw
History Blame Contribute Delete
6.83 kB

ClassLens — Question Categorization & Bank System

Overview

When a teacher uploads a questions PDF, ClassLens automatically classifies every question into a pre-defined taxonomy using an LLM. This classification feeds three downstream features:

  1. Richer student reports — wrong-answer blocks show the specific grammar/vocabulary tag that was tested, and the Weakness Summary groups errors by tag.
  2. Shared question bank — classified questions accumulate across all uploads and all teachers, organized by category and tag.
  3. Practice question generation — teachers can generate new questions by selecting a category and tag; the system rephrases questions already in the bank rather than hallucinating new ones.

Taxonomy

The taxonomy has 6 main categories. Two of them (字詞理解 and 語法結構) have sub-tags; the other four are used as-is.

字詞理解 (Vocabulary & Word Usage)

Sub-group Tags
詞性 形容詞用法, 情狀副詞, 頻率副詞, 數量形容詞, 介系詞
花費動詞 spend/cost/take/pay 用法
字彙 動詞與動詞片語, 名詞與名詞片語, 形容詞與副詞, 介系詞與連接詞片語, 情境字彙推斷

語法結構 (Grammar Structures)

Sub-group Tags
時態 現在簡單式, 現在簡單式第三人稱單數, 現在進行式, 現在完成式, 過去式be動詞, 過去簡單式, 過去進行式, 過去完成式, 未來式
語態 被動語態
代名詞 人稱代名詞主格, 人稱代名詞受格, 所有格代名詞, 反身代名詞, 不定代名詞, 指示代名詞
連接詞 對等連接詞, when/before/after, if/although, why/because/so, as long as/as soon as, when/while, 相關連接詞
關係子句 關係代名詞, 關係代名詞省略
動詞型態 動名詞, 不定詞, 感官動詞, 使役動詞, 連綴動詞, 授與動詞, 助動詞, used to
句型結構 祈使句, 附加問句, 附和句, 間接問句, 比較級, 最高級, There be 句型, It takes 句型, Too...to 句型, So...that 句型, Enough...to 句型, 動名詞當主詞, 虛主詞it
疑問句型 Who/What/Where/When/Which/How/Why/How often/How much/How long 問答句

文意推論 (Inference)

No sub-tags. Covers questions that require reading between the lines.

篇章大意 (Main Idea)

No sub-tags. Covers questions about the overall topic or gist of a passage.

篇章細節 (Reading for Detail)

No sub-tags. Covers questions about specific facts stated in a passage.

篇章結構 (Text Organization)

No sub-tags. Covers questions about how a passage is organized (e.g., ordering paragraphs, identifying transitions).


How Categorization Works

Trigger

Categorization runs once per questions PDF upload, in the background after the upload response is returned to the client. It does not block the upload or report generation.

Flow

Teacher uploads questions PDF
        │
        ▼
Parser extracts [{number, text}, ...] from PDF
        │
        ▼
process_uploaded_files() saves rows to ParsedData (main_category = "", tags = [])
        │
        ▼
Upload response returned to client immediately
        │
        ▼  (asyncio.create_task — background)
categorize_questions(session_id, teacher_id, questions)
        │
        ├── Builds LLM prompt with full taxonomy + all question texts
        ├── Calls gpt-4o-mini → returns JSON [{question_num, main_category, tags}, ...]
        ├── Validates: main_category must be one of the 6; tags must match that category
        ├── update_parsed_data_categories() — writes main_category + tags to ParsedData rows
        └── save_question_bank_batch() — inserts into shared question_bank table

If the LLM call or DB write fails, the error is logged and the upload is unaffected.

Files

File Purpose
chatkit/backend/app/taxonomy.py Single source of truth for all categories and tags
chatkit/backend/app/question_categorizer.py LLM categorization + DB writes
chatkit/backend/app/models.py QuestionBank ORM model, ParsedData columns main_category + tags
chatkit/backend/app/database.py update_parsed_data_categories, save_question_bank_batch, query_question_bank
chatkit/backend/alembic/versions/0003_question_bank.py Migration: creates the question_bank table

Question Bank

The question_bank table is shared across all teachers. Questions persist even if the original quiz or teacher account is deleted (FK uses ON DELETE SET NULL).

Schema

Column Type Notes
id integer PK
quiz_id integer, nullable Source quiz (SET NULL on delete)
teacher_id integer, nullable Uploading teacher (SET NULL on delete)
question_text text Full question as extracted from PDF
answer varchar(10) Correct answer (may be empty if uploaded without answer key)
main_category varchar(64), indexed One of the 6 taxonomy categories
tags JSON array Tags from the category's sub-list
created_at timestamptz

Querying the bank

query_question_bank(main_category, tags, limit) in database.py:

  • Fetches up to limit×3 candidates by category (most recent first)
  • If tags are specified, filters in Python to rows that share at least one tag
  • Falls back to category-only results if no tag match is found

Practice Question Generation

When a teacher requests practice questions for a category/tag combination:

  1. query_question_bank(category, tags) fetches matching source questions from the bank.
  2. If no bank questions match the filter, the endpoint returns an empty list — no AI fallback.
  3. If matches are found, gpt-4o-mini is asked to rephrase the source questions (same grammar point, varied vocabulary and context) — not to generate new topics.

This ensures generated questions always test real skills from past exams and never drift outside the taxonomy.


How Categories Appear in Reports

When a student report is generated, main.py fetches the ParsedData rows for the session and builds a categories dict (question_num → (main_category, tags)). This is passed to generate_student_report.

Each wrong-answer block in the LLM prompt includes a 分類 line:

**分類**: 語法結構 > 時態, 過去簡單式

Or for tag-free categories:

**分類**: 篇章細節

The Weakness Summary section of the report instructs the LLM to:

  • Name the specific tags that recurred most (e.g., 過去簡單式, 被動語態)
  • Fall back to the category name for the four tag-free categories
  • Group errors by these tags — no question numbers, no vague summaries