agentic_huge_data_base / wiki
页面 Open WebUI · 9.3 协同编辑·DeepWiki 中文全文译文

9.3 · 协同编辑(Collaborative Editing)

多模型对话工作台与知识应用入口 · 本章是 Open WebUI DeepWiki 中文译文的独立章节页,保留原始链接、源码锚点、模块标签和章节层级。

项目Open WebUI 章节9.3 状态全文译文 模块系统架构、界面与交互、频道、笔记与协作、检索、召回与知识系统
源码线索
  • backend/open_webui/socket/main.py
  • backend/open_webui/socket/utils.py
  • backend/open_webui/tasks.py
  • backend/open_webui/test/util/test_redis.py
  • backend/open_webui/utils/rate_limit.py
  • backend/open_webui/utils/redis.py
  • src/lib/components/icons/AdjustmentsHorizontalOutline.svelte
  • src/lib/components/icons/ArrowUpLeft.svelte
  • src/lib/components/notes/NoteEditor.svelte
  • src/lib/components/notes/NoteEditor/Chat.svelte
模块标签
  • 系统架构
  • 界面与交互
  • 频道、笔记与协作
  • 检索、召回与知识系统
  • 接口与服务契约

中文译文

协同编辑(中文译文)

原始 DeepWiki 页面:https://deepwiki.com/open-webui/open-webui/9.3-collaborative-editing
翻译时间:2026-06-09T16:09:53.805Z
翻译模型:deepseek-chat
原文字符数:9505
项目:Open WebUI (open-webui)

---

协作编辑

相关源文件

以下文件为本 Wiki 页面的生成提供了上下文:

  • backend/open_webui/socket/main.py
  • backend/open_webui/socket/utils.py
  • backend/open_webui/tasks.py
  • backend/open_webui/test/util/test_redis.py
  • backend/open_webui/utils/rate_limit.py
  • backend/open_webui/utils/redis.py
  • src/lib/components/icons/AdjustmentsHorizontalOutline.svelte
  • src/lib/components/icons/ArrowUpLeft.svelte
  • src/lib/components/notes/NoteEditor.svelte
  • src/lib/components/notes/NoteEditor/Chat.svelte
  • src/lib/components/notes/NoteEditor/Chat/Message.svelte
  • src/lib/components/notes/NoteEditor/Chat/Messages.svelte
  • src/lib/components/notes/NoteEditor/Controls.svelte
  • src/lib/components/notes/NotePanel.svelte

目的与范围

本文档介绍 Open WebUI 富文本编辑器组件中内置的实时协作编辑系统。该系统允许多个用户同时编辑同一文档,并利用无冲突复制数据类型(CRDT)自动解决冲突。变更通过 WebSocket 连接实时同步,并显示远程用户的光标和选区等视觉指示。

该系统主要用于笔记系统(NoteEditor.svelte),支持多用户在共享笔记上进行协作 src/lib/components/notes/NoteEditor.svelte:188-196

---

架构概览

协作编辑系统集成了三个层次:

  1. CRDT 层yjs 提供 Y.Doc CRDT 结构,支持无需加锁的并发编辑。
  2. 传输层SocketIOCollaborationProvider 通过 socket.io-client 管理 WebSocket 通信。
  3. 编辑器集成层y-prosemirror 将 ProseMirror 文档状态绑定到 Yjs 的 Y.Doc

系统组件图

graph TB
    subgraph ClientA["客户端 A 浏览器 (RichTextInput.svelte)"]
        EditorA["editor: Editor<br/>(Tiptap/ProseMirror)"]
        YDocA["ydoc: Y.Doc<br/>(Yjs CRDT)"]
        ProviderA["provider:<br/>SocketIOCollaborationProvider"]
        SocketA["socket: Socket<br/>(socket.io-client)"]
    end

    subgraph ClientB["客户端 B 浏览器 (RichTextInput.svelte)"]
        EditorB["editor: Editor<br/>(Tiptap/ProseMirror)"]
        YDocB["ydoc: Y.Doc<br/>(Yjs CRDT)"]
        ProviderB["provider:<br/>SocketIOCollaborationProvider"]
        SocketB["socket: Socket<br/>(socket.io-client)"]
    end

    subgraph Backend["backend/open_webui/socket/main.py"]
        SocketIO["AsyncServer<br/>(python-socketio)"]
        YDocManager["YdocManager<br/>(Redis 后端)"]
        Redis["REDIS<br/>(get_redis_connection)"]
    end

    EditorA -->|"ProseMirror 事务"| YDocA
    YDocA -->|"Y.updates"| ProviderA
    ProviderA --> SocketA
    SocketA <-->|"WebSocket 事件<br/>'document-update'"| SocketIO

    EditorB -->|"ProseMirror 事务"| YDocB
    YDocB -->|"Y.updates"| ProviderB
    ProviderB --> SocketB
    SocketB <-->|"WebSocket 事件<br/>'document-update'"| SocketIO

    SocketIO <--> YDocManager
    YDocManager <--> Redis

来源: backend/open_webui/socket/main.py:73-84backend/open_webui/socket/main.py:167-170backend/open_webui/socket/utils.py:124-135src/lib/components/notes/NoteEditor.svelte:37-41

---

基于 Yjs 的 CRDT 基础

Yjs 是一种 CRDT 实现,能够实现共享数据结构的无冲突同步。在 Open WebUI 中,Yjs 维护一个共享的文档表示,多个编辑器可以同时修改而无需显式协调。

初始化条件

RichTextInput.svelte 组件满足特定条件时,协作系统会初始化:

变量来源用途
collaboration组件属性启用协作模式
documentId组件属性唯一的 Y.Doc 标识符(通常是笔记 ID)
socket$socket 存储Socket.IO 连接实例
user$user 存储当前用户,用于感知状态

提供者(provider)采用动态导入,以避免在非协作模式下加载协作依赖。在后端,pycrdt 用于 Python 环境中的 CRDT 操作 backend/open_webui/socket/main.py:10

来源: backend/open_webui/socket/main.py:10src/lib/components/notes/NoteEditor.svelte:37-41src/lib/components/notes/NoteEditor.svelte:103-106

---

后端管理:YdocManager

后端使用 backend/open_webui/socket/utils.py 中定义的 YdocManager 处理文档更新和持久化。它同时支持内存存储和 Redis 后端存储 backend/open_webui/socket/utils.py:137-151

压缩策略

为防止更新列表无限增长,YdocManager 在更新数量超过 COMPACTION_THRESHOLD(500)时,会执行滚动压缩策略 backend/open_webui/socket/utils.py:125

# backend/open_webui/socket/utils.py:152-166
async def _compact_updates_redis(self, document_id: str):
    """滚动压缩:将最旧的一半更新压缩为一个快照。"""
    redis_key = f'{self._redis_key_prefix}:{document_id}:updates'
    all_updates = await self._redis.lrange(redis_key, 0, -1)
    if len(all_updates) <= 1:
        return
    mid = len(all_updates) // 2
    ydoc = Y.Doc()
    for raw in all_updates[:mid]:
        ydoc.apply_update(bytes(json.loads(raw)))
    snapshot = json.dumps(list(ydoc.get_update()))
    pipe = self._redis.pipeline()
    pipe.delete(redis_key)
    pipe.rpush(redis_key, snapshot, *all_updates[mid:])
    await pipe.execute()

来源: backend/open_webui/socket/utils.py:124-166backend/open_webui/socket/main.py:167-170

---

Tiptap 编辑器集成

RichTextInput.svelte 组件将 Yjs 与 ProseMirror 的文档模型集成。启用协作时,编辑器的初始内容从提供者同步,而非使用本地状态。

协作数据流
sequenceDiagram
    participant User as "用户 (NoteEditor.svelte)"
    participant Socket as "socket (Socket.IO)"
    participant Srv as "main.py (Socket 服务器)"
    participant YMgr as "YdocManager (utils.py)"
    participant Redis as "Redis (存储)"

    User->>Socket: emit('join-note', {note_id})
    Socket->>Srv: @sio.on('join-note')
    Srv->>YMgr: get_updates(document_id)
    YMgr->>Redis: LRANGE updates
    Redis-->>YMgr: 二进制更新
    YMgr-->>Srv: 合并后的更新
    Srv-->>Socket: emit('note-events', updates)
    Socket-->>User: noteEventHandler(updates)

来源: src/lib/components/notes/NoteEditor.svelte:188-196backend/open_webui/socket/main.py:167-170backend/open_webui/socket/utils.py:179-188

---

初始化与生命周期

协作系统遵循特定的初始化顺序,以确保在开始编辑前完成正确的同步。

生命周期管理

NoteEditor.svelte 中,当用户通过 Socket.IO 加入笔记会话时,协作开始:

// src/lib/components/notes/NoteEditor.svelte:188-196
if (note?.write_access) {
    $socket?.emit('join-note', {
        note_id: id,
        auth: {
            token: localStorage.token
        }
    });
    $socket?.on('note-events', noteEventHandler);
}

后端 YdocManager 负责将这些更新持久化到 Redis,确保新用户加入时,能通过 get_updates 获取完整的文档历史 backend/open_webui/socket/utils.py:179-188

来源: src/lib/components/notes/NoteEditor.svelte:188-196backend/open_webui/socket/utils.py:137-151

---

状态同步机制

Yjs 实现了一种基于操作冲突解决的 CRDT。Y.Doc 结构使用向量时钟维护因果顺序,确保无论网络延迟如何,都能实现确定性收敛。

同步流程
  1. 本地编辑:用户在 TipTap 编辑器中做出更改。
  2. CRDT 更新:更改被转换为二进制 Yjs 更新。
  3. 传输:客户端通过 Socket.IO 发出 document-update 事件。
  4. 后端处理YdocManager.append_to_updates 将更新保存到 Redis backend/open_webui/socket/utils.py:137-144
  5. 广播:服务器将更新广播给同一文档房间内的所有其他客户端。
  6. 远程集成:其他客户端接收更新并将其应用到本地的 Y.Doc

如果 Redis 中的更新数量过大,将触发 _compact_updates_redis 将历史记录压缩为单个快照 backend/open_webui/socket/utils.py:152-166

来源: backend/open_webui/socket/utils.py:137-166backend/open_webui/socket/main.py:167-170