%% 機構設計整合部 AI Agent 系統 - 文件上傳與向量化流程時序圖
%% 渲染工具:https://mermaid.live / VS Code Mermaid Preview
sequenceDiagram
autonumber
actor User as 👤 使用者 (研發同仁/管理員)
participant Browser as 🌐 瀏覽器 (React)
participant API as ⚡ FastAPI
participant Auth as 🔐 JWT 驗證
participant BG as 🔄 背景任務 (BackgroundTask)
participant Ingest as 📥 DocumentIngestionService
participant PDF as 📄 PDFProcessor
participant Chunker as ✂️ TextChunker
participant Ollama as 🦙 Ollama Service\n(nomic-embed-text)
participant Vision as 👁️ Ollama Vision\n(qwen2.5-vl:7b)
participant PG as 🗄️ PostgreSQL + pgvector
participant FS as 💾 File Storage\n(本機磁碟)
participant Audit as 📋 稽核日誌
%% ===================================================
%% 路徑 A:單一文件上傳
%% ===================================================
rect rgb(230, 245, 255)
Note over User,Audit: 📤 路徑 A:單一文件上傳 (POST /api/v1/documents/upload)
User->>Browser: 選擇檔案 + 填寫 metadata\n(類別、標籤)
Browser->>API: POST /api/v1/documents/upload\n(multipart/form-data)\nfile: <binary>\nmetadata: {category, tags}
API->>Auth: 驗證 Bearer Token
Auth-->>API: ✅ user_id
API->>FS: 寫入原始檔案\nD:\storage\documents\{UUID}.pdf
FS-->>API: 儲存成功,file_path
API->>PG: INSERT INTO documents\n{filename, file_type, file_path, file_size,\n status="pending", uploaded_by=user_id}
PG-->>API: document_id (UUID)
API->>BG: add_background_task(\n DocumentIngestionService.ingest_file,\n document_id, file_path)
Note over BG: 背景任務非同步執行\nAPI 立即回應 202
API-->>Browser: 202 Accepted\n{document_id, status:"processing", job_id}
Browser->>User: 顯示上傳成功,處理中...
API->>Audit: 寫入稽核日誌\naction="upload", resource="document"
end
%% ===================================================
%% 路徑 B:批次匯入
%% ===================================================
rect rgb(255, 245, 230)
Note over User,Audit: 📦 路徑 B:批次匯入 (POST /api/v1/documents/batch-import)
User->>Browser: 輸入伺服器本地資料夾路徑\nD:\cad_manuals\
Browser->>API: POST /api/v1/documents/batch-import\n{source_path, file_extensions, metadata}
API->>Auth: 驗證 Bearer Token(需 admin 角色)
Auth-->>API: ✅ user_id, role="admin"
API->>FS: 掃描目錄,列出所有符合副檔名的檔案
FS-->>API: 檔案列表 [file1.pdf, file2.docx, ...]
API->>PG: INSERT INTO batch_jobs\n{source_path, total_files=N, status="pending"}
PG-->>API: job_id
loop 每個檔案
API->>PG: INSERT INTO documents (pending 狀態)
end
API->>BG: add_background_task(\n DocumentIngestionService.ingest_batch,\n job_id, file_list)
API-->>Browser: 202 Accepted\n{job_id, total_files:N, status:"processing"}
Browser->>User: 顯示批次任務進度條
end
%% ===================================================
%% 核心向量化流程(A 和 B 共用)
%% ===================================================
rect rgb(240, 255, 240)
Note over BG,PG: ⚙️ 核心向量化流程(背景任務執行,A 和 B 共用)
BG->>PG: UPDATE documents SET status="processing"
BG->>Ingest: ingest_file(document_id, file_path)
%% ----- 依檔案類型分支處理 -----
alt 檔案類型 == PDF
Ingest->>PDF: process(pdf_path)
PDF->>PDF: pymupdf4llm.to_markdown(pdf_path)\n擷取結構化文字 + 表格 → Markdown
PDF->>PDF: fitz.open(pdf_path)\n取得所有內嵌圖片(get_images)
loop 每張圖片
PDF->>Vision: POST /api/chat\n{model: "qwen2.5-vl:7b",\n prompt: "詳細描述這張工程圖片的內容...",\n image: <base64>}
Vision-->>PDF: 圖片文字描述\n"此為SolidWorks伸長特徵設定頁面,圖示顯示..."
Note over PDF: 將描述以\n[圖片說明 第N頁 圖M]:\n{description}\n格式附加至 Markdown
end
PDF-->>Ingest: 完整 Markdown 文字\n(含圖片描述)
else 檔案類型 == DOCX
Ingest->>Ingest: python-docx 解析\n→ 純文字 + 表格
else 檔案類型 == MD / TXT
Ingest->>Ingest: 直接讀取文字內容
else 檔案類型 == 圖片 (JPG/PNG)
Ingest->>Vision: 視覺模型描述整張圖片
Vision-->>Ingest: 圖片完整描述文字
end
%% ----- 文字分塊 -----
Ingest->>Chunker: split(full_text)\nRecursiveCharacterTextSplitter\nchunk_size=512, chunk_overlap=64
Chunker-->>Ingest: chunks = ["段落1...", "段落2...", ...]
Note over Chunker: 分塊策略:\n優先在段落換行處切割\n保留 64 字元重疊避免語意割裂
%% ----- 向量嵌入(批次處理)-----
loop 每批 32 個 chunks
Ingest->>Ollama: POST /api/embed\n{model: "nomic-embed-text",\n input: [chunk1, chunk2, ..., chunk32]}
Ollama-->>Ingest: embeddings [[0.12,...], [0.34,...], ...]\n每個向量 768 維
end
%% ----- 批次寫入 PostgreSQL -----
Ingest->>PG: COPY / batch INSERT INTO document_chunks\n{document_id, content, embedding (vector),\n chunk_index, page_number, chunk_type, metadata}
PG-->>Ingest: 寫入成功,chunk_count
%% ----- 更新文件狀態 -----
Ingest->>PG: UPDATE documents\nSET status="completed",\n chunk_count=N,\n updated_at=NOW()
PG-->>Ingest: ✅ 更新成功
opt 批次任務(路徑 B)
Ingest->>PG: UPDATE batch_jobs\nSET processed_files += 1\n(或 failed_files += 1 若失敗)
end
end
%% ===================================================
%% 錯誤處理
%% ===================================================
rect rgb(255, 235, 235)
Note over Ingest,PG: ❌ 錯誤處理路徑
alt 解析失敗(損壞/加密 PDF 等)
Ingest->>PG: UPDATE documents\nSET status="failed",\n metadata={error: "無法解析 PDF:檔案已加密"}
opt 批次任務
Ingest->>PG: UPDATE batch_jobs\nSET failed_files += 1,\n error_log += {filename, reason}
end
else Ollama 服務不可用
Ingest->>PG: UPDATE documents SET status="failed"\n{error: "Embedding 服務無法連線"}
Note over Ingest: 重試機制:每隔 30 秒最多重試 3 次
end
end
%% ===================================================
%% 前端輪詢進度
%% ===================================================
rect rgb(248, 248, 255)
Note over Browser,PG: 🔄 前端進度輪詢(每 3 秒)
loop 任務未完成
Browser->>API: GET /api/v1/documents/batch-jobs/{job_id}
API->>PG: SELECT * FROM batch_jobs WHERE id=$1
PG-->>API: {processed:10, total:23, failed:1, status:"processing"}
API-->>Browser: 200 OK\n{progress_percent:43, processed:10, total:23}
Browser->>User: 更新進度條 43%
end
Browser->>API: GET /api/v1/documents/batch-jobs/{job_id}
API->>PG: 查詢
PG-->>API: {status:"completed", processed:22, failed:1}
API-->>Browser: 200 OK {status:"completed"}
Browser->>User: ✅ 匯入完成!22 個成功,1 個失敗
end
Note over User,Audit: ✅ 文件已向量化完成\n後續對話的 RAG 檢索即可搜尋到此文件
Comments...
No Comments Yet...