PRD: Tokyo Live Learning Workspace
Sản phẩm:
tokyo.conan.school· Tính năng: Live Learning Workspace · Trạng thái: Draft v1.0 Người dùng: Host, Learner · Nền tảng video: Zoom (Tokyo không làm video/audio)
1. Một câu định nghĩa
Tokyo Live Learning là lớp tương tác học tập cho workshop Zoom. Nó cho host một Live Stage và một bộ nhỏ hoạt động học, Quiz, Poll, Reflection, Questions, Tasks, đồng thời biến mọi tương tác của người tham dự thành learning evidence dùng lại được sau buổi học.
Zoom lo video + audio + meeting. Tokyo lo learning interaction + participation + evidence. Learner không phải nhảy qua Kahoot → Google Form → Padlet → Docs.
2. Bốn nguyên tắc
- Few things, done well, không dựng LMS thứ hai. Chỉ những tương tác cần trong một buổi live.
- Host controls the flow, host quyết activity nào đang mở, cái gì lên Stage, khi nào đóng.
- Everything happens in context, mọi tương tác nằm trong một workspace duy nhất.
- Interaction produces evidence, quiz/reflection/question/task đều để lại dấu vết học tập.
3. Kiến trúc thông tin
Về dữ liệu, có 7 khối; về giao diện, chỉ 4 mục + Stage luôn hiện diện.
Event
└── Live Learning Workspace
├── Resources
├── Activity (Quiz · Poll · Reflection · Task, một họ, bốn kind)
├── Live Stage (component trung tâm, không phải tab)
├── Questions (+ Vote)
├── Participants
└── Session RecordGiao diện learner (Stage là trung tâm, đây là UX signature):
┌──────────────────────────────────────────────┐
│ LIVE STAGE │
│ What is the customer's problem? │
├──────────────────────────────────────────────┤
│ Resources │ Activities │ Questions │ People │
└──────────────────────────────────────────────┘Learner luôn trả lời được câu "bây giờ đang làm gì?" mà không cần hỏi.
4. Vai trò
| Learner | Host | |
|---|---|---|
| Resources | xem | thêm/sửa/xoá/sắp xếp/ẩn-hiện |
| Quiz · Poll | trả lời, bầu | tạo, start/pause/close, xem kết quả |
| Reflection | nộp | tạo (chọn framework), xem |
| Questions | hỏi, upvote / bỏ upvote | duyệt, ẩn, đóng vote, đưa lên Stage |
| Tasks | nộp, nhận đánh giá AI | tạo, chấm bằng AI, đưa bài lên Stage |
| Participants | xem hồ sơ rút gọn | xem hồ sơ đầy đủ |
| Live Stage | xem | show / remove |
5. Module 1: Resources
V1: chỉ URL + PDF. Không xây document viewer riêng; PDF mở bằng embedded viewer của trình duyệt. PPTX/DOCX/ảnh để sau. Host: thêm, sửa, xoá, đổi thứ tự, ẩn/hiện.
Đã có tiền lệ trong repo: bảng workshop_resources (migration 0065) với đúng các cột title/url/type/sort_order/is_active, Resources của workspace nên tái dùng hình dạng này, không phát minh lại.
6. Module 2: Activities (một họ, bốn kind)
Đây là đề xuất 1 đã chốt về mặt kiến trúc: Quiz, Poll, Reflection, Task là bốn kind của cùng một bảng activity, không phải bốn module riêng. UI chỉ có một mục "Activities"; host bấm Create Activity rồi chọn loại. Mental model đơn giản hơn hẳn, và thêm loại thứ năm sau này không phải đổi kiến trúc.
Mỗi activity có vòng đời chung:
DRAFT → ACTIVE → PAUSED → CLOSEDQuiz
Single choice hoặc multiple choice. Sau khi nộp, host quyết định có hiện đáp án / giải thích hay không.
Poll
Không có đúng/sai. Kết quả hiển thị theo phần trăm.
Reflection
Không phải một loại quiz, là trải nghiệm phản tư có khung. V1 có hai framework:
- 4F, Facts / Feelings / Findings / Future
- Start / Continue / Stop
Framework là dữ liệu, không phải mã: thêm What–So What–Now What, Gibbs, Plus/Delta sau này chỉ là thêm một bản ghi framework, không sửa schema.
Task
Prompt + thời gian + free text. Sau khi nộp → AI evaluation theo rubric, không phải "Good answer!".
7. Live Stage
Live Stage là core component, không phải một activity. Host broadcast cho toàn phòng: câu hỏi, kết quả poll, kết quả quiz, một câu trả lời được chọn, hoặc prompt của một task.
EMPTY → ACTIVE → PAUSED → CLOSEDHost có đúng hai hành động: Show on Stage và Remove from Stage. Stage luôn có một nội dung tại một thời điểm, đó là điều làm nó trả lời được câu "đang làm gì".
8. Questions
Learner đặt câu hỏi; câu hỏi vào danh sách chung, sắp theo số upvote.
↑ 24 Vì sao phải phân biệt triệu chứng với vấn đề?
↑ 17 Cho thêm một ví dụ được không?Chỉ có Upvote / Remove Upvote, không có downvote. Mục đích là đo bao nhiêu người quan tâm, không phải chấm câu hỏi hay/dở. Host đặt được hạn đóng vote (Voting closes in 05:00); sau hạn, cả upvote lẫn bỏ upvote đều khoá, và khoá phải nằm ở API, không phải ẩn nút ở giao diện.
9. Participants
Hồ sơ: Full name · Province/City · Birth year · Occupation · Industry (dropdown).
Đây là dữ liệu cá nhân. Vì có năm trường ghép lại (năm sinh + nơi ở + nghề + ngành), mặc định public toàn bộ là sai. V1 tách hai lớp hiển thị (đề xuất 3 đã chốt):
| Learner thấy | Host thấy |
|---|---|
| Nguyễn Văn A · Hà Nội · Education | Nguyễn Văn A · Hà Nội · 1987 · Teacher · Education |
Learner tự chọn Visible to participants hoặc Visible to host only. Ranh giới vẽ ở API: trường không được phép thấy thì không có mặt trong phản hồi, chứ không phải bị ẩn bằng CSS, cùng luật với nội dung trả phí ở www.
10. Tasks & mô hình chấm
Task
├── Prompt
├── Response
└── Evaluation
├── Criterion 1 … 3
├── Overall
└── FeedbackVí dụ đầu ra:
| Criterion | Score |
|---|---|
| Clarity | 4/5 |
| Customer focus | 3/5 |
| Specificity | 4/5 |
Host thấy tổng hợp: 98 submissions · Excellent 12 · Good 51 · Needs Work 27 · Incomplete 8, và có thể xem từng bài, xem đánh giá, đưa lên Stage.
Rubric của task đi cùng task và lưu lại theo bản, sau này map được vào Competency / Indicator.
11. Luồng
Host: Join → Resources → Poll → Stage(kết quả) → dạy → Quiz → Stage(kết quả) → dạy → Reflection → Questions → Task → AI evaluation → Stage(bài được chọn) → Wrap-up.
Learner: Join → workspace → Resources → tham gia (Quiz/Poll/Reflection/Question/Task) → AI evaluation → Session Record.
12. Session Record
Sau buổi học, learner xem lại toàn bộ dấu vết của mình: tài liệu, câu trả lời quiz/poll, bản reflection, câu hỏi đã đặt, bài task + đánh giá AI. Đây là phần biến 90 phút Zoom thành một artifact quay lại được
- và là lý do tồn tại của nguyên tắc 4.
13. Mô hình dữ liệu (conceptual)
Event
├── Participant (+ profile visibility)
├── Resource
├── Activity kind ∈ {quiz, poll, reflection, task}, state ∈ {draft,active,paused,closed}
│ ├── ActivityItem (câu hỏi / lựa chọn / prompt của framework)
│ └── Response (+ Evaluation cho task)
├── Question ── Vote
└── StageState (một dòng cho mỗi event: đang chiếu gì)Mọi tương tác gắn event · session · user · timestamp, không có bốn trường đó thì không dựng được learning evidence.
14. V1 KHÔNG làm
Whiteboard · breakout room · live reactions · analytics điểm danh phức tạp · chat · ghi hình · LMS đầy đủ · document editor · leaderboard · gamification · AI chatbot.
Zoom đã làm tốt phần lớn những thứ này. Tokyo tập trung vào learning interaction.
15. Quyết định đã chốt (08.09.2026, người soạn duyệt)
| Câu hỏi | Chốt |
|---|---|
| Đồng bộ Live Stage | Polling 3 giây trên GET /api/events/:slug/live. Mỗi lượt ghi của host nhích revision; client chỉ render lại khi số đó đổi. Không dựng Durable Object cho V1. |
| Gắn vào họ bảng nào | events, họ đã có RSVP, event_meetings, email. Không dùng workshops. |
| Ai vào được | Member đã đăng nhập và RSVP yes. Gác nằm trong guard() ở API. |
| Host quản trị ở đâu | admin.conan.school → Events → Live Workspace (/ops/events/live). |
| Màn hình người học | tokyo, cùng gốc URL với event: /online-events/:slug/live. |
| Chấm task bằng AI | Ngay khi learner nộp (lượt sau, Task chưa nằm trong V1). |
| Rubric | Mỗi task một rubric riêng, host chuẩn bị trước (lượt sau). |
| Ngôn ngữ | Tiếng Việt. |
| Hồ sơ participant | Mặc định chỉ host thấy; learner tự bật cho cả phòng. |
| Câu hỏi | Không ẩn danh, không duyệt trước. Host chỉ ẩn khi cần. |
| Phạm vi V1 | Trọn 7 khối. Lượt 1: Resources, Quiz/Poll, Live Stage, Questions. Lượt 2: Task + AI evaluation. Lượt 3: Reflection. Lượt 4 (09.09.2026): Participants + Session Record. |
Vì sao polling chứ không Durable Object
| Polling | Durable Object | |
|---|---|---|
| Cách hoạt động | Trình duyệt hỏi lại api mỗi 3 giây: "có gì mới không?" | Một kết nối WebSocket mở suốt; server đẩy thay đổi xuống ngay |
| Độ trễ learner cảm nhận | 0–3 giây | ~tức thì |
| Hạ tầng thêm | Không, dùng đúng api hiện có | Một worker + binding DO mới, như play.conan.school |
| Chi phí | Mỗi người ~20 request/phút vào D1 | Một kết nối/người |
Với một workshop Zoom vài chục người, 3 giây trễ không ai nhận ra, người nói vẫn đang nói. Đổi sang DO sau này không phải viết lại giao diện: cùng hình dạng dữ liệu, chỉ thay nguồn đẩy.
16. Đã dựng những gì
| Lớp | Chỗ |
|---|---|
| Lược đồ | api.conan.school/migrations/20260916d_live_learning_workspace.sql, 6 bảng live_* |
| API người học | api.conan.school/src/domains/events/live.ts → /api/events/:slug/live/* |
| API host | api.conan.school/src/domains/admin/live_workspace.ts → /api/admin/live-workspace/:eventId/* |
| Màn hình người học | com.conan.school/app/routes/online-events.$slug.live.tsx |
| Bàn điều khiển host | admin.conan.school/legacy/admin-marcom/src/pages/LiveWorkspacePage.tsx (/ops/events/live) |
| Task + rubric | api.conan.school/migrations/20260916e_live_workspace_tasks.sql |
| Chấm bằng AI | api.conan.school/src/domains/events/task-evaluation.ts |
| Reflection | api.conan.school/migrations/20260916f_live_workspace_reflection.sql · src/domains/events/reflection-frameworks.ts |
| Participants | api.conan.school/migrations/20260916g_live_workspace_participants.sql |
Ba ranh giới vẽ ở API, không ở giao diện, đây là phần dễ làm sai nhất:
- Activity còn
draftkhông có mặt trong phản hồi của learner. - Đáp án đúng (
is_correct) vàexplanationchỉ rời server khi activity đã đóng và host bậtreveal_answer. Trước đó chúng lànull, không phải bị ẩn bằng CSS. - Hạn đóng vote kiểm ở endpoint vote. Nút mờ đi ở giao diện không phải là khoá.
16b. Task và AI evaluation (lượt 2)
Task là kind thứ ba của live_activities. Ba luật:
- Không có rubric thì không tạo được task (422 ở API). Rubric là điều kiện, không phải tuỳ chọn: thiếu nó, model chỉ còn cách khen chung chung, đúng thứ mô hình chấm này sinh ra để tránh. Tối đa 6 tiêu chí, mỗi tiêu chí một thang điểm riêng do host đặt.
- Rubric hiện cho người học TRƯỚC khi làm bài. Bị chấm theo tiêu chí mình không được biết là một cách chấm tồi.
- Chấm ngay trong request nộp bài, nhưng bài luôn được ghi vào D1 TRƯỚC khi gọi model. Model hỏng thì mất bản chấm, không mất bài.
Điểm do model trả về được neo lại vào rubric thật trước khi lưu: khớp theo nhãn tiêu chí, cắt về [0, max_score], làm tròn. Model trả nhãn lệch hay điểm 8/5 là chuyện xảy ra thường xuyên, và điểm hiển thị cho người học phải nằm trên thang mà host đã đặt.
live_task_evaluations.status có ba giá trị vì failed không được lẫn với "điểm thấp": learner thấy "Chưa chấm được bài này, bài của bạn đã được lưu", host thấy số bài chưa chấm được và có nút Chấm lại (POST /api/admin/live-workspace/:eventId/responses/:id/evaluate). Không có nút đó thì một lượt model hỏng là bài đó vĩnh viễn không có đánh giá, và không ai biết vì sao.
Model: @cf/meta/llama-3.1-8b-instruct-fast qua binding AI, response_format: json_schema, đi qua AI Gateway với metadata.engine = 'live-task-evaluation', cùng khuôn với learner-model-ai.
Sửa rubric của task đã có người nộp bị chặn 409, cùng lý do với sửa phương án của quiz đã có người trả lời.
16c. Reflection (lượt 3)
Reflection không phải một loại quiz, nó là trải nghiệm phản tư có khung. V1 có hai khung: 4F (Facts / Feelings / Findings / Future) và Start / Continue / Stop.
Điều quan trọng nhất về kiến trúc: khung là dữ liệu, không phải mã. Host chọn một khung, server đổ các ô của khung đó thành các dòng live_reflection_prompts gắn với activity. Từ giây phút đó, khung của activity này sống trong D1, sửa nhãn hay gợi ý được, và thêm What / So What / Now What, Gibbs, Plus / Delta chỉ là thêm một mục vào REFLECTION_FRAMEWORKS, không đổi lược đồ.
Mỗi ô lưu một dòng live_reflection_answers chứ không phải một khối văn bản. Lý do: câu hỏi "cả phòng học được gì" là câu hỏi về ô Findings, không phải về cả bài, gộp làm một khối là tự đóng cửa với việc tổng hợp sau này.
Phải điền đủ mọi ô mới nộp được (mỗi ô ≥ 5 ký tự): một khung phản tư điền dở dang thì không còn là khung nữa. Kiểm ở API.
Bài phản tư không ai đọc được ngoài chính người viết và host, snapshot của learner chỉ trả về ô của chính họ. Trừ một trường hợp: host chiếu một bài lên Live Stage, và khi đó cả phòng thấy đủ các ô.
16d. Participants và Session Record (lượt 4)
Participants: không thêm cột hồ sơ nào
users đã có đủ năm trường: full_name, location, birth_year, job_title, industry. Dựng một bảng hồ sơ riêng cho workspace là tạo ra một nguồn sự thật thứ hai cho cùng một con người. Thứ duy nhất thiếu là mức hiển thị, và nó thuộc về từng sự kiện chứ không phải về tài khoản: người ta có thể muốn lộ diện ở workshop nội bộ mà kín ở workshop mở. Nên bảng mới chỉ có đúng ba cột nghĩa: event_id, user_id, profile_visibility.
Hai lớp hiển thị, vẽ ở API:
| Learner thấy | Host thấy | |
|---|---|---|
| Họ tên | ✓ | ✓ |
| Nơi ở · Ngành | chỉ với người đã tự bật | ✓ |
| Năm sinh · Nghề nghiệp | không bao giờ | ✓ |
Mặc định là host_only. Hai trường năm sinh và nghề nghiệp không có mặt trong câu truy vấn của endpoint learner, không phải bị lọc ở tầng hiển thị. Đó là khác biệt giữa "learner không thấy" và "learner không nhận được".
Session Record
GET /api/events/:slug/live/me trả về dấu vết của chính người gọi: tài liệu, mọi hoạt động đã tham gia (lựa chọn quiz/poll đã đổi từ id sang nhãn, bài task + bản chấm, các ô phản tư) và câu hỏi đã đặt.
Nó không phụ thuộc trạng thái Stage hay việc phòng còn mở, đây đúng là thứ biến 90 phút Zoom thành một artefact quay lại được. Cửa vào vẫn là RSVP yes, nên record vẫn đọc được sau khi buổi học kết thúc.
Luật đúng/sai giữ nguyên như snapshot: is_correct chỉ có giá trị khi activity đã đóng và host bật reveal_answer. Nếu không, Session Record sẽ là cửa hậu để biết đáp án của một câu đang mở.
17. Ràng buộc kỹ thuật còn hiệu lực
- Lược đồ nằm trong
migrations/, CI áp trước bước deploy. KhôngCREATE TABLE IF NOT EXISTSlúc chạy. - Giao diện: shadcn/ui +
motion+ token củapackages/design-system/theme.css. Trangonline-events.$slug.tsxcũ hard-code#6f3f75, nó nằm trong tồn đọng củacheck:colors; màn hình mới viết bằng token, không chép theo trang đó. - Sửa phương án của một activity đã có người trả lời bị chặn (409): kết quả sẽ không còn nói về cùng một câu hỏi. Host phải tạo hoạt động mới.