Skip to content

Learner Context Model ​

"Bối cảnh quanh học sinh này là gì?" — em đang học trường nào, ở school nào, có bao nhiêu thời gian, đang nhắm mục tiêu nào.

Thiết kế: SDD-002 §3, SDD-007 §6 · Code: modules/models/service.ts → runContextEngine() · Bảng nguồn: learner_context_models (0003) · Snapshot: learner_model_versions (model_kind='context')


1. Context trả lời câu mà Learner Model không trả lời ​

Learner Model biết đứa trẻ học tới đâu. Context Model biết đứa trẻ đang sống trong hoàn cảnh nào.

Cùng một mastery 0.6 ở Đại số nghĩa hoàn toàn khác nhau nếu:

  • em học lớp 8 vs lớp 10,
  • em vào Turtle (nền tảng) vs Shark (thi chuyên),
  • em có 300 phút/tuần vs 60 phút/tuần.

Không có Context, mọi khuyến nghị đều là lời khuyên chung chung cho một học sinh trung bình không tồn tại.

2. Ba tầng dễ nhầm nhau ​

TênLà gìCó version
learner_context_modelsBảng — dữ liệu thô do onboarding ghi. 1 dòng/learner❌
Context ModelSnapshot do Context Engine sinh, gom thêm enrollment + goal ref✅
learner_context_eventsLời khai theo thời điểm — "con ốm", "tuần này bận" (migration 0030)❌

Bảng chứa cái ổn định (lớp, trường, school). Context event chứa cái nhất thời (ốm, bận, sắp thi). Model là bức ảnh gộp lại.


3. Model chứa gì ​

jsonc
{
  "algorithm_version": "context-v1",
  "computed_at": "2026-08-15T…",
  "confidence": 0.83,
  "active_school_code": "turtle",
  "enrollments": [{ "school_code": "turtle", "status": "active" }],
  "current_grade": 8,
  "current_school": "THCS …",
  "province": "Hà Nội",
  "active_goal_ref": {
    "goal_id": "…", "title": "Thi học kỳ 1 — Toán",
    "kind": "semester_exam", "deadline": "2026-09-20",
    "horizon": "operational", "urgency": 0.77
  },
  "goal_blueprint_id": "…",
  "capacity": { "available_minutes_per_week": 180, "energy": "medium" },
  "context_events": []
}

active_goal_ref — tham chiếu, không phải dữ liệu gốc ​

sql
SELECT id, title, kind, deadline, horizon, urgency FROM learner_goal_entries
WHERE learner_id=?1 AND status='active' ORDER BY urgency DESC, priority ASC LIMIT 1

Đúng một goal — goal gấp nhất. Và đây chỉ là con trỏ: SDD-002 §3 cấm goal/deadline làm dữ liệu gốc ở đây. Cần mục tiêu đầy đủ thì đọc Goal Model.

Vì sao cứng rắn: deadline xuất hiện ở hai bảng là bảo đảm sẽ có ngày chúng lệch nhau, và không ai biết bên nào đúng. Một nguồn sự thật, mọi nơi khác trỏ về.

Fallback hai tầng ​

ts
current_grade:  row?.current_grade  ?? learner?.grade  ?? null
current_school: row?.current_school ?? learner?.current_school ?? null

Ưu tiên bảng context (do onboarding ghi cho school đang active), rồi mới tới hồ sơ chung. Một đứa trẻ có thể học lớp 8 ở trường nhưng vào Shark để luyện đề lớp 9 — context của school thắng.

Confidence = tỷ lệ ô đã biết ​

ts
const known = [grade, school, province, activeGoal, available_minutes_per_week, có enrollment];
confidence = số ô != null / 6

Không phải xác suất — là độ đầy của bức tranh (AS-05.5.4). Context toàn ô trống thì Planning không được coi nó như sự thật.

confidenceNghĩa
1.0Biết đủ 6 ô — kế hoạch cá nhân hoá được
0.5Biết một nửa — kế hoạch còn đoán
0.17Gần như chỉ biết em tồn tại

4. Cơ chế CẬP NHẬT ​

4.1 Ai ghi bảng nguồn ​

Chỉ một chỗ: modules/onboarding/routes.ts (~341), khi learner chọn school / khai hồ sơ. Không engine nào ghi vào learner_context_models — engine chỉ đọc.

available_minutes_per_week và energy cũng nằm ở bảng này, nhưng đó là chỗ trú tạm cho tới khi Constraint Model nhận (Q-096) — xem §6.

4.2 Engine chạy khi nào ​

WF-04 step 2 — sau Goal, trước Learner Model. Thứ tự bắt buộc vì Context cần goal urgent nhất để dựng active_goal_ref.

Trigger: AssessmentCompleted, GoalChanged, EXAM_RESCHEDULED, admin_recompute.

Context không nằm trong đường cập nhật nhẹ

Đường theo từng câu trả lời chỉ chạy Learner Model + Readiness. Context không đổi vì một câu trả lời — trường học, lớp, school, quỹ thời gian đều không phụ thuộc vào việc em vừa làm đúng hay sai.

Nhưng active_goal_ref.urgency và horizon phụ thuộc thời gian (tính trong Goal Engine). Một goal tactical hôm nay thành operational sau vài tuần mà không có gì kích hoạt tính lại — cùng khoảng trống với Goal Model.


5. Cách DÙNG ​

NơiĐọc gìĐể làm gì
Readiness Enginegoal_blueprint_idmột trong 3 nguồn chọn target
Goal Enginegoal_blueprint_iddựng goal nền từ enrollment
Planning Engineavailable_minutes_per_week, energychia việc vừa quỹ thời gian
Constraint Enginecapacitytách thành model riêng
Onboardingactive_school_codebiết learner đang ở school nào

Vòng phụ thuộc đáng chú ý: Goal Engine đọc goal_blueprint_id từ bảng, còn Context Model đọc goal từ learner_goal_entries. Không phải vòng lặp vô hạn vì hai bên đọc hai nguồn khác nhau — nhưng đó là lý do thứ tự Goal → Context trong WF-04 không được đảo.


6. Hai chỗ trống trong model ​

6.1 context_events: [] — luôn rỗng ​

ts
context_events: [] as unknown[], // Context Event Registry (§5b) — Phase 2.

Nhưng bảng learner_context_events (migration 0030) đã có và đang chạy thật: POST/GET /v1/learners/{id}/context-events, 8 loại (time_budget, illness, exam_soon, busy, stress, motivation, focus_subject, other), và Planning Engine đọc trực tiếp qua activeContextEvents().

Nghĩa là: context event đang được dùng, nhưng chưa được gộp vào Context Model snapshot. Hệ quả thực tế — đọc lại một version cũ của Context Model sẽ không thấy "hôm đó con đang ốm", dù thông tin ấy có ảnh hưởng tới kế hoạch hôm đó.

Đây là khoảng trống có thật, ghi ra để không ai tưởng model đã đầy đủ.

6.2 capacity đang trú tạm ​

available_minutes_per_week và energy nằm trong Context Model, nhưng Constraint Model đã tồn tại (modules/models/constraint.ts, constraint-v1) và đó mới là chỗ đúng của chúng (Q-096).

Constraint Model có capacity.source phân biệt learner_declaration vs unknown — chính là thứ Context Model không có. Hôm nay hai model cùng nói về quỹ thời gian; hợp nhất là việc còn lại.


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

  1. Goal/deadline không là dữ liệu gốc ở đây — chỉ active_goal_ref.
  2. Engine chỉ đọc learner_context_models; chỉ onboarding được ghi.
  3. Chạy sau Goal, trước Learner Model trong WF-04.
  4. confidence phản ánh độ đầy của bức tranh, không phải chất lượng học sinh.
  5. Context không nằm trong đường cập nhật theo từng câu trả lời.
  6. Fallback hai tầng: context của school thắng hồ sơ chung.

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

ViệcTrạng thái
Gộp learner_context_events vào snapshot⏳ §6.1 — hiện Planning đọc thẳng bảng
Chuyển capacity sang Constraint Model⏳ §6.2 — Constraint Model đã chạy
Cron tính lại active_goal_ref.horizon theo thời gian⏳ chung với Goal Model
deadline trong bảng learner_context_models⚠️ cột còn tồn tại nhưng engine không đọc — di sản, dễ gây hiểu nhầm là nguồn deadline

Trace ​