Home
Explore Uedu
Student Console
Register as Member/Login
(2) In future presentations of the research findings, in addition to the course project website and public presentations, your real name and personal information will not appear in this research report. If you are interested in the research results, we can provide you with an executive summary after the study is completed.
問卷中心
Teacher Console
Course Setup
Support & Messages
Uptime Data

UeduGPTs

--

Jupyters

0

Local AI

--

AI Reply Desktop Notifications

Show a desktop notification when the AI TA finishes replying

Chat Message Notifications

Notify me when classmates post messages in the forum

Sound notification

Play an alert sound whenever there is a new notification

METHODOLOGY

共編筆記
Real-Time Collaborative Editor

以 Yjs CRDT 為核心的多人即時共編文件系統,透過 Socket.IO 傳輸增量更新。支援快照式版本歷史、線次評論、剪貼簿圖片上傳,並可一鍵匯出為課程 RAG 知識庫。

1. 設計理念

Google Docs 之類的協作工具已是大學教學常態,但使用商用工具有兩個痛點:(1) 學生作品與學習歷程散落在外部平台,無法併入研究資料;(2) 無法與課程內的 RAG、Aida、Bloom 分析器整合

Uedu 共編筆記(UeduNote)內建於平台,讓師生在 Uedu 完成小組討論稿、共同筆記、專題報告草稿。完成後可一鍵匯出到課程 RAG,讓班級的 AI 助教理解這份文件。

選擇 Yjs 而非自製 OT

Operational Transformation(OT,Google Docs 使用)在處理複雜並發時需要中央伺服器做 transform,實作難度高、邊界 case 多。CRDT(Conflict-free Replicated Data Type)以資料結構保證可交換性,不依賴中心仲裁者,適合快速落地。Yjs 是目前最成熟的 CRDT 實作,帶有 Python 埠 pycrdt,讓後端能直接操作同一份 Y.Doc。

2. CRDT 選型:Yjs + pycrdt

UeduNote 後端使用 pycrdt(Python 版 Yjs),前端使用原版 Yjs(JavaScript)。兩端共用同一份 Y.Doc 二進位表示,意即:

  • 後端能在學生離線時持續接收其他協作者的更新,為後連線者準備累積 diff
  • 版本快照可在後端伺服器直接萃取純文字(例如為了 RAG 匯出),不需前端渲染
  • APScheduler 可定期將記憶體中的 Y.Doc 落地 DB,並在需要時從 DB 還原

後端管理層:yjs_doc_manager

位於 utils/yjs_doc_manager.py。全域單例 yjs_manager 負責:

  • Lazy load:第一次收到某 doc_uuid 的請求時,從 MySQL 的 yjs_state BLOB 欄位載入 Y.Doc 至記憶體
  • Diff 計算:呼叫 get_update_for_client(state_vector) 產生增量更新
  • Apply update:收到來自任一 client 的 incremental update,套用至記憶體 Y.Doc,並廣播給其他 client
  • Idle eviction:文件閒置超過 5 分鐘且房間無人時,自 cache 移除

3. 同步協定(Socket.IO)

Namespace:/collab-editor。事件流程遵循 Yjs 標準的三階段交握 + 連續 update:

事件方向用途
join_documentC → S加入房間,權限驗證(_collab_socket_room_auth),載入 Y.Doc
yjs_sync_step1C → S客戶端送出自己的 state vector
yjs_sync_step1_responseS → C伺服器回傳客戶端缺少的 updates + 伺服器自己的 state vector
yjs_sync_step2C → S客戶端送回伺服器缺少的 updates
yjs_updateC ↔ S後續的所有增量編輯,雙向廣播
yjs_awarenessC ↔ S遠端游標、選取區塊(y-protocols/awareness)
yjs_save_versionC → S手動儲存版本快照
yjs_full_resetS → C還原歷史版本後強制客戶端重新同步

權限守護

所有 socket 事件都透過 _collab_socket_room_auth() 快取驗證(一次查 DB、存入 collab_editor_rooms 記憶體結構)。後續事件以純記憶體 lookup 判斷是否允許 edit、comment、view。避免每個事件都打 DB。

4. 資料模型

核心 schema 定義於 sql/collab_editor.sqlsql/collab_editor_yjs.sql

資料表用途
collab_documents文件主表。UUID、title、content(純文字)、yjs_state(BLOB,CRDT 二進位)、owner、permission、word_count
collab_document_versions版本快照。version_number 自增、完整 content、edit_summary、edited_by
collab_document_yjs_updates增量更新緩衝區(用於未來的 update compaction)
collab_document_collaborators協作者列表。role ∈ {editor, commenter, viewer}
collab_document_comments評論。line_ref(可選的行號錨)、parent_id(threading)、is_resolved
collab_groupscollab_group_members群組化協作(邀請碼機制)
為什麼保留純文字 content 欄位

Y.Doc 的 yjs_state 是二進位,無法被 MySQL 全文索引或直接給 AI 讀取。每次儲存版本或閒置落地時,同步 extract 純文字寫入 content 欄位,供搜尋、匯出、RAG 使用。

5. 權限模型

採 4 級角色 + 兩個維度:

角色動作
admin(擁有者)刪除文件、管理協作者、改變 permission 層級
edit修改內容、上傳圖片、儲存版本
comment新增 / 回覆 / 解決評論
view瀏覽內容

除了逐人指定的 collab_document_collaborators.collab_role 外,文件有文件層級的 permission 欄位,可設為 editablecommentableview_onlyprivate。Group members 與社群成員的權限也會並入判斷。

授權快取

權限計算集中於 _collab_get_doc_perm(doc_uuid, user_id)。結果存入 collab_editor_rooms 記憶體結構,後續 socket 事件純記憶體 lookup,避免 hot path DB 查詢。

6. 版本策略與持久化

何時寫入版本

  • 手動儲存:前端觸發 yjs_save_version,立即寫入 collab_documents + 新增一筆 collab_document_versions 紀錄
  • 去重:若最新版本 content 與當前 content 相同,跳過此次版本建立(避免連點版本爆量)
  • 週期性落地:APScheduler 每 60 秒掃描 dirty 的 Y.Doc,寫入 yjs_state + content,但建立版本紀錄
  • 閒置驅逐:房間無人 + 5 分鐘未活動 → 落地後自 cache 移除

還原歷史版本

POST /api/editor/documents/<doc_uuid>/versions/<version_id>/restore 的行為:

  1. 從該版本 content 建立新的 Y.Doc
  2. 呼叫 yjs_manager.reset_doc() 取代記憶體版本
  3. 廣播 yjs_full_reset 給所有連線客戶端,強制重新同步
  4. 同時建立「還原自版本 N」的新版本紀錄,保留還原事件於歷史中

舊文件遷移

早期 UeduNote 僅儲存純文字,無 CRDT。第一次載入舊文件時,會從 content 建立初始 Y.Doc 並寫回 yjs_state,之後以 CRDT 模式運作。此為 lazy migration,無需停機。

7. 評論、反白與圖片

評論錨定(line-based)

評論以行號line_ref)錨定,而非 CRDT 位置 index。好處是:

  • 即使文字內容被編輯,行號仍有穩定語意,簡單易懂
  • 不需與 Y.Doc 的 item-level tracking 耦合,減少維護成本

代價是:在行內大幅增刪後,評論對應的「那一行」語意可能漂移。實務上適合大學教學場景(評論頻率中等、大段修改後會另開對話)。

反白(highlight)

反白顏色設定由前端處理,最終寫入 Y.Text 的 attribute,與文字內容一併 CRDT 同步。不在後端額外儲存反白資料。

圖片上傳與剪貼簿貼入

  • 路徑:正式環境為 NAS_UEDU_PATH/collab_editor_images/,測試機為本機 uploads/collab_editor_images/
  • 命名{timestamp}_{uuid}.{ext},避免命名碰撞
  • 容量:單檔 15 MB 上限
  • 安全:Magic byte + Pillow 雙層驗證(見 utils/file_validation.py),防止 polyglot 攻擊
  • 剪貼簿:前端監聽 paste 事件,將圖片 blob 以 multipart 上傳
  • serving/api/editor/documents/<doc_uuid>/images/<filename>,驗證 view 權限;secure_filename() 防止 path traversal

8. 匯出到 RAG 知識庫

endpoint:POST /api/editor/documents/<doc_uuid>/export-to-rag。流程:

  1. collab_documents.content 取純文字
  2. 寫入臨時 .md
  3. rag_documents 表插入一筆記錄(classroom_id、file_type=md、status=completed)
  4. 觸發現有 RAG pipeline 對該檔案進行 chunking + embedding
教學應用場景

小組完成專題筆記後,教師可匯出到課程 RAG,讓班級的 AI 助教能引用這份共同整理的知識。學生與 AI 的後續對話即建立在「同學們共同建構的理解」之上,實現社會建構主義(Social Constructivism)的數位落實。

目前匯出為單向(文件 → RAG)。未來規劃支援雙向同步:RAG 內容更新時自動通知原筆記擁有者。

9. 研究應用

共編筆記為 Sociomics 維度(社會互動)提供細粒度資料。可研究的議題:

  • 協作分工分析:從 Y.Doc update 的 clientID 可重建每個字元的作者,量化各組員貢獻比例
  • 編輯動態序列:誰先寫、誰修、誰加結構(標題 / 列表),識別「組織者」「貢獻者」「編輯者」等角色
  • 編輯時間分佈:集中式截止前趕工 vs. 分散式漸進積累,對成品品質的影響
  • 評論 → 修改轉化率:同儕評論後,多少比例被採納?採納延遲時間?
  • 共編 + AI 整合:匯出到 RAG 後,AI 對話的引用頻率與準確度是否提升?
技術優勢(研究資料完整性)

傳統 Google Docs 的活動紀錄需透過第三方工具(如 Draftback)重建,且內容屬於 Google。UeduNote 的 Y.Doc updates 完整保留於伺服器,每個鍵擊等級的編輯歷史都可重播,是協作學習研究的理想資料來源。

引用建議

引用本系統時,請標註:「UeduNote: Yjs-based real-time collaborative editor with CRDT replay capability (https://uedu.tw)」。分析編輯歷史時,建議說明所使用的 Y.Doc update 解析工具與時間粒度。