Skip to content

增量同步

增量同步是 Luker 對 SillyTavern 資料傳輸架構的根本性改進,用增量 patch 替代全量覆蓋,大幅降低頻寬消耗並消除並行寫入衝突。

問題背景

SillyTavern 的資料儲存採用全量傳輸模式:每次修改(哪怕只是編輯一條訊息的一個字),都會將整個聊天記錄序列化後傳送到後端覆蓋寫入。這帶來了幾個嚴重問題:

  • 頻寬浪費 — 一個包含數百條訊息的聊天記錄可能有數百 MB(尤其是有些外掛會在 chat metadata 裡儲存大量資料),每次操作都要傳輸全量資料
  • 寫入衝突 — 多個分頁或裝置同時操作時,後寫入的會覆蓋先寫入的,導致資料遺失
  • 效能瓶頸 — 大型聊天記錄的序列化和傳輸本身就是效能負擔
  • 儲存延遲 — 全量寫入的 I/O 開銷使得儲存操作無法做到即時

增量端點

Luker 新增了三個增量端點,覆蓋聊天資料的不同修改場景:

追加訊息(append)

當使用者傳送新訊息或 AI 生成新回覆時,只需將新訊息追加到聊天檔案末尾,而非重寫整個檔案。這是最常見的操作路徑,也是效能收益最大的場景。

後端收到請求後,直接將新訊息追加到檔案末尾,時間複雜度為 O(1),與聊天記錄的總長度無關。後端還會對最後一條已儲存訊息進行去重檢查,防止因網路重試導致的重複追加。

修補訊息(patch)

當使用者編輯已有訊息(如修改內容、切換 swipe、更新訊息中繼資料)時,按訊息索引修補指定行,只更新變更的訊息。

支援在一次請求中批次修補多條訊息,並具有冪等性——自動偵測已套用的操作,跳過重複提交。

更新中繼資料(patch-metadata)

當聊天的中繼資料(如標題、標籤、聊天設定等)發生變化時,使用深度合併(deep merge)更新,而非替換整個中繼資料物件。

深度合併意味著只有請求中包含的欄位會被更新,未提及的欄位保持不變。這對於包含大量擴充中繼資料的聊天尤為重要。

Integrity Hash 並行衝突偵測

每次寫入操作完成後,後端會生成一個新的 UUID,寫入聊天狀態檔案,同時在回應中回傳。前端快取該值,並在後續寫入請求中攜帶:

  1. 匹配 — 正常執行寫入,回傳新的 integrity UUID
  2. 不匹配 — 回傳 409 Conflict,表示檔案在上次操作後已被其他來源修改

這從根本上防止了並行寫入衝突——無論是多分頁、多裝置還是多使用者場景。

衝突處理

收到 409 Conflict 時,前端需要重新取得最新資料並基於最新狀態重試。這確保了資料一致性,但也意味著在極端並行場景下使用者可能需要等待重試完成。

設計參考

中繼資料更新端點的設計參考了 RFC 6902(JSON Patch)的理念,透過結構化操作實現增量更新。訊息修補端點則針對行式儲存做了簡化——使用行索引定位,更適合聊天記錄的線性結構。

設定資料的增量 Patch 儲存

除了聊天資料,Luker 的設定資料(使用者偏好、擴充配置、預設參數等)同樣支援增量 patch 儲存。設定變更透過深度合併套用到伺服器端的設定檔案中,同樣帶有 integrity hash 衝突偵測,避免並行修改導致設定遺失。

這意味著修改一個取樣參數不再需要傳輸整個設定物件——只需傳送變更的欄位即可。

延遲觸發機制

為了避免頻繁的小修改產生過多的網路請求,增量同步實作了延遲觸發(debounce)機制:

  • 短時間內的多次修改會被合併為一次 patch 請求傳送
  • 使用者連續編輯訊息時,只有在停頓後才會觸發實際的網路請求
  • 設定變更同樣適用延遲合併,避免滑桿拖動等操作產生大量請求

延遲觸發在減少網路開銷的同時,不影響資料安全——因為後端即時儲存確保了資料到達伺服器端後立即持久化。

與後端即時儲存的協作

增量同步與後端即時儲存緊密配合,形成完整的資料安全鏈路:

  1. 前端偵測到資料變更
  2. 延遲觸發合併短時間內的多次修改
  3. 傳送增量 patch 請求(攜帶 integrity UUID)
  4. 前端透過序列化聊天寫入流程將本地寫入任務串行送出
  5. 驗證 integrity → 套用 patch → 持久化到磁碟 → 更新狀態檔案
  6. 回傳新的 integrity UUID 供下次請求使用

這個流程確保了資料變更從前端到磁碟的完整鏈路,既高效又安全。

效能收益

對於一個包含 500 條訊息的聊天記錄,編輯一條訊息時:SillyTavern 需要傳輸約 2-5 MB 的全量資料,而 Luker 只需傳輸約 1-2 KB 的 patch 資料。在行動網路或高延遲環境下,差異尤為明顯。

相關頁面

Built upon SillyTavern