📋 MBE Desktop 创建知识库和上传文档完整流程

🎯 完整流程概览

创建知识库 → 上传文档 → 处理文档 → 知识库就绪

📝 阶段 1-6: 创建知识库流程

(详见 KB_CREATE_COMPLETE_FLOW.md

简要总结:

  1. ✅ 用户填写表单(名称、描述、分块大小等)
  2. ✅ 提交创建请求
  3. ✅ 后端创建知识库(状态:pending
  4. ✅ 显示成功提示
  5. ✅ 列表刷新

创建后的状态: pending(等待上传文件)


📤 阶段 7: 文档上传流程(新增)

7.1 上传方式

方式 1: 拖放上传(推荐)

位置: 知识库卡片上

操作步骤:

  1. 创建知识库后,在知识库列表中看到新创建的知识库卡片
  2. 将文件(PDF/TXT/MD)拖放到卡片上
  3. 卡片高亮显示(蓝色边框)
  4. 释放鼠标,文件开始上传

代码实现 (index.tsx:343-353):

const handleDrop = async (e: React.DragEvent) => {
  e.preventDefault()
  e.stopPropagation()
  setDragActive(false)

  const files = Array.from(e.dataTransfer.files)
  const file = files.find(f => /\.(pdf|txt|md)$/i.test(f.name))
  if (file) {
    await onUpload(kb.id, file)
  }
}

方式 2: 点击上传按钮

位置: 知识库卡片上的上传按钮

操作步骤:

  1. 点击卡片上的上传图标
  2. 选择文件(PDF/TXT/MD)
  3. 文件开始上传

代码实现 (index.tsx:355-360):

const handleFileSelect = async (e: React.ChangeEvent<HTMLInputElement>) => {
  const file = e.target.files?.[0]
  if (file && /\.(pdf|txt|md)$/i.test(file.name)) {
    await onUpload(kb.id, file)
  }
}

方式 3: 详情页面上传

位置: 知识库详情模态框

操作步骤:

  1. 点击知识库卡片上的"查看详情"按钮
  2. 在详情页面中上传文件
  3. 文件开始上传

代码实现 (index.tsx:692-697):

const handleUpload = async (file: File) => {
  setUploading(true)
  try {
    await onUpload(kb.id, file)
    await onRefresh()
  } finally {
    setUploading(false)
  }
}

7.2 前端上传处理

文件: src/renderer/pages/KnowledgeBase/index.tsx

处理函数 (handleUploadFile):

const handleUploadFile = async (kbId: string, file: File) => {
  // 1. 设置上传状态
  setUploadingFile(kbId)  // 显示"上传中..."
  
  try {
    // 2. 调用上传 API
    const result = await knowledgeService.uploadFile(kbId, file, {
      use_ocr: false,      // 是否使用 OCR(扫描版 PDF 需要)
      ocr_lang: 'ch',      // OCR 语言
    })
    
    // 3. 上传成功,刷新列表
    if (result.success) {
      await loadKnowledgeBases()
    }
    
    return result
  } catch (error) {
    console.error('Failed to upload file:', error)
    throw error
  } finally {
    // 4. 清除上传状态
    setUploadingFile(null)
  }
}

上传状态显示:

  • ✅ 卡片显示"上传中..."状态
  • ✅ 显示加载动画
  • ✅ 禁用上传按钮

7.3 API 调用

文件: src/renderer/services/knowledgeService.ts

API 方法:

async uploadFile(
  kbId: string,
  file: File,
  options?: {
    use_ocr?: boolean
    ocr_lang?: string
  }
): Promise<UploadFileResponse> {
  // 1. 创建 FormData
  const formData = new FormData()
  formData.append('file', file)
  formData.append('use_ocr', String(options?.use_ocr || false))
  formData.append('ocr_lang', options?.ocr_lang || 'ch')

  // 2. 获取 Token
  const token = getAccessToken()
  
  // 3. 发送 POST 请求
  const { data } = await axios.post<UploadFileResponse>(
    `${API_BASE_URL}/admin/knowledge/upload/${kbId}`,
    formData,
    {
      headers: {
        'Content-Type': 'multipart/form-data',
        ...(token ? { Authorization: `Bearer ${token}` } : {}),
      },
    }
  )
  
  return data
}

请求详情:

  • URL: POST /admin/knowledge/upload/{kb_id}
  • 方法: POST
  • Content-Type: multipart/form-data
  • Body: FormData(包含文件和选项)

7.4 后端处理

文件: mises-behavior-engine/src/api/knowledge.py

API 端点 (@router.post("/upload/{kb_id}")):

@router.post("/upload/{kb_id}")
async def upload_file(
    kb_id: str,
    background_tasks: BackgroundTasks,
    file: UploadFile = File(...),
    use_ocr: bool = False,
    ocr_lang: str = 'ch'
):
    # 1. 检查知识库是否存在
    kb = manager.get_knowledge_base(kb_id)
    if not kb:
        raise HTTPException(status_code=404, detail="知识库不存在")
    
    # 2. 检查文件类型
    ext = filename.split(".")[-1].lower()
    if ext not in ["pdf", "txt", "md"]:
        raise HTTPException(status_code=400, detail="不支持的文件类型")
    
    # 3. 读取文件内容
    content = await file.read()
    
    # 4. 大文件处理(> 1MB)- 异步处理
    if len(content) > 1024 * 1024:
        # 提交到消息队列
        result = process_document_upload_task.apply_async(...)
        return {
            "success": True,
            "status": "processing",
            "message": "文件正在后台处理,请稍后查看状态"
        }
    
    # 5. 小文件处理 - 同步处理
    kb = await manager.upload_file(kb_id, filename, content, use_ocr, ocr_lang)
    
    return {
        "success": True,
        "kb_id": kb_id,
        "status": kb.status,
        "message": "文件上传成功"
    }

7.5 文件处理流程

文件: mises-behavior-engine/src/knowledge/knowledge_manager.py

处理步骤 (upload_file 方法):

async def upload_file(...) -> KnowledgeBase:
    # 1. 设置状态为 "processing"
    kb.status = "processing"
    kb.updated_at = datetime.utcnow().isoformat()
    
    # 2. 保存上传的文件
    save_path = UPLOAD_DIR / f"{kb_id}_{file_path.name}"
    with open(save_path, "wb") as f:
        f.write(file_content)
    
    # 3. 提取文本
    if file_type == "pdf":
        text, metadata = self.pdf_processor.extract_text(
            str(file_path), 
            use_ocr=use_ocr, 
            ocr_lang=ocr_lang
        )
        kb.page_count = metadata.pages
        kb.language = metadata.language
    elif file_type in ["txt", "md"]:
        with open(file_path, "r", encoding="utf-8", errors="ignore") as f:
            text = f.read()
        kb.language = self._detect_language(text)
    
    # 4. 文档质量评估
    quality_report = self.pdf_processor.evaluate_quality(text, metadata)
    kb.quality_score = quality_report.overall_score
    kb.quality_level = quality_report.quality_level
    
    # 5. 权威性评估
    authority_report = evaluator.evaluate(...)
    kb.authority_score = authority_report.score
    kb.authority_level = authority_report.level
    
    # 6. 文本分块
    chunks = self._chunk_text(
        text, 
        kb.chunk_size,      # 使用创建时设置的分块大小
        kb.chunk_overlap    # 使用创建时设置的重叠大小
    )
    kb.chunk_count = len(chunks)
    kb.char_count = len(text)
    
    # 7. 生成向量嵌入(如果启用)
    if kb.enable_embedding:
        embeddings = await self._generate_embeddings(chunks)
        kb.embedding_file = save_embeddings(embeddings)
    
    # 8. 更新状态为 "ready"
    kb.status = "ready"
    
    # 9. 保存到索引
    self._save_index()
    
    return kb

7.6 知识库状态流转

pending (创建后,等待上传文件)
    ↓
processing (上传文件后,正在处理)
    ↓
ready (处理完成,可以使用)
    ↓
error (如果处理失败)

状态说明:

  • pending: 知识库已创建,等待上传文件
  • processing: 文件正在处理(提取文本、分块、生成向量)
  • ready: 处理完成,知识库可以使用
  • error: 处理失败,显示错误信息

7.7 上传反馈

上传中

  • ✅ 卡片显示"上传中..."状态
  • ✅ 显示加载动画(旋转图标)
  • ✅ 禁用上传按钮
  • ✅ 卡片半透明显示(opacity-50

上传成功

  • ✅ 列表自动刷新
  • ✅ 知识库状态更新为 "processing" 或 "ready"
  • ✅ 显示统计信息:
    • 块数(chunk_count
    • 字符数(char_count
    • 页数(page_count,仅 PDF)
    • 文件类型(file_type

上传失败

  • ❌ 显示错误信息
  • ❌ 知识库状态可能变为 "error"
  • ❌ 显示错误消息(error_message

📊 完整流程时序图

用户操作流程
    ↓
1. 点击"创建知识库"
    ↓
2. 填写表单(名称、描述、分块大小、重叠大小)
    ↓
3. 点击"创建"按钮
    ↓
4. POST /admin/knowledge/create
    ↓
5. 后端创建知识库(状态:pending)
    ↓
6. 返回成功响应
    ↓
7. 显示成功提示
    ↓
8. 关闭创建模态框
    ↓
9. 刷新知识库列表
    ↓
10. 用户看到新创建的知识库(状态:pending)
    ↓
11. 【文档上传步骤】
    ↓
12. 拖放文件到知识库卡片(或点击上传按钮)
    ↓
13. POST /admin/knowledge/upload/{kb_id}
    ↓
14. 后端处理文件:
    - 保存文件
    - 提取文本
    - 质量评估
    - 权威性评估
    - 文本分块(使用创建时设置的分块大小和重叠大小)
    - 生成向量嵌入(如果启用)
    ↓
15. 更新知识库状态为 "ready"
    ↓
16. 返回成功响应
    ↓
17. 刷新知识库列表
    ↓
18. 用户看到知识库状态变为 "ready"
    ↓
19. 显示统计信息(块数、字符数、页数等)

🔍 关键点检查

✅ 创建知识库时设置的分块参数

在创建知识库时设置:

  • chunk_size: 1000(默认)
  • chunk_overlap: 200(默认)
  • enable_embedding: true(默认)

这些参数在文档上传时使用:

  • ✅ 文本分块时使用 chunk_sizechunk_overlap
  • ✅ 如果 enable_embedding 为 true,会生成向量嵌入

✅ 文档上传后的处理

处理步骤:

  1. ✅ 保存文件到服务器
  2. ✅ 提取文本(PDF/TXT/MD)
  3. ✅ 文档质量评估
  4. ✅ 权威性评估
  5. ✅ 文本分块(使用创建时的参数)
  6. ✅ 生成向量嵌入(如果启用)
  7. ✅ 更新知识库状态

📋 完整流程检查清单

创建知识库阶段

  • 打开创建模态框
  • 填写名称(必填)
  • 填写描述(可选)
  • 设置分块大小(默认 1000)
  • 设置重叠大小(默认 200)
  • 选择是否启用向量嵌入(默认 true)
  • 点击"创建"按钮
  • 显示"创建中..."状态
  • 显示成功提示
  • 模态框关闭
  • 列表刷新
  • 新知识库出现在列表中(状态:pending)

文档上传阶段

  • 选择上传方式(拖放/点击/详情页)
  • 选择文件(PDF/TXT/MD)
  • 文件开始上传
  • 显示"上传中..."状态
  • 后端接收文件
  • 后端处理文件(提取文本、分块、生成向量)
  • 知识库状态更新为 "processing" 或 "ready"
  • 列表刷新
  • 显示统计信息(块数、字符数、页数)

🎯 总结

完整流程包含两个主要阶段

  1. 创建知识库(阶段 1-6)

    • 用户填写表单
    • 创建空的知识库
    • 状态:pending
  2. 上传文档(阶段 7)

    • 用户上传文件
    • 后端处理文件
    • 状态:processingready

关键点

  • ✅ 创建知识库时设置的分块参数会在文档上传时使用
  • ✅ 文档上传是独立的步骤,在创建知识库之后进行
  • ✅ 支持多种上传方式(拖放、点击、详情页)
  • ✅ 大文件会自动异步处理
  • ✅ 上传后会进行质量评估和权威性评估

最后更新:2026-02-08