Home
學生控制台
註冊會員/登入
研究知情同意書
UeduGPTs
Aida 優學伴
Uedu Open
支援與訊息
Uptime 數據

UeduGPTs

--

Jupyters

14

AI 回覆桌面通知

AI 助教回覆完成時顯示桌面通知

聊天訊息通知

同學在討論區發送訊息時通知

聲音通知

每當有新通知時播放提示音

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 解析工具與時間粒度。