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ên | Là gì | Có version |
|---|---|---|
learner_context_models | Bảng — dữ liệu thô do onboarding ghi. 1 dòng/learner | ❌ |
| Context Model | Snapshot do Context Engine sinh, gom thêm enrollment + goal ref | ✅ |
learner_context_events | Lờ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ì
{
"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
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
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
const known = [grade, school, province, activeGoal, available_minutes_per_week, có enrollment];
confidence = số ô != null / 6Khô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.
| confidence | Nghĩa |
|---|---|
| 1.0 | Biết đủ 6 ô — kế hoạch cá nhân hoá được |
| 0.5 | Biết một nửa — kế hoạch còn đoán |
| 0.17 | Gầ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 Engine | goal_blueprint_id | một trong 3 nguồn chọn target |
| Goal Engine | goal_blueprint_id | dựng goal nền từ enrollment |
| Planning Engine | available_minutes_per_week, energy | chia việc vừa quỹ thời gian |
| Constraint Engine | capacity | tách thành model riêng |
| Onboarding | active_school_code | biế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
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
- Goal/deadline không là dữ liệu gốc ở đây — chỉ
active_goal_ref. - Engine chỉ đọc
learner_context_models; chỉ onboarding được ghi. - Chạy sau Goal, trước Learner Model trong WF-04.
confidencephản ánh độ đầy của bức tranh, không phải chất lượng học sinh.- Context không nằm trong đường cập nhật theo từng câu trả lời.
- Fallback hai tầng: context của school thắng hồ sơ chung.
8. Khoảng trống đã biết
| Việc | Trạ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
- REQ-PAR-14 (khai tự nhiên → Context Event), REQ-INT-29.
- Nguồn: SRC-032, SRC-105, SRC-130 · Q-096 (tách Constraint Model).
- Thiết kế: SDD-002 §3/§5b, SDD-007 §6.
- Kiểm chứng: QG-005.
- Liên quan: Learner Model · Goal Model · Readiness · Engines §4.