# 離線 AI Agent 系統分析文件(SA)
> **版本**:1.0
> **日期**:2026-03-05
> **適用環境**:機構設計研發人員離線繪圖筆電 + 整合部後端伺服器
> **文件定位**:本文件為可執行開發之系統分析文件,涵蓋系統架構、元件設計、資料模型、API 規格、流程定義及開發里程碑。
---
## 目錄
1. [專案概述](#1-專案概述)
2. [設計原則與約束](#2-設計原則與約束)
3. [系統架構總覽](#3-系統架構總覽)
4. [元件設計](#4-元件設計)
5. [資料模型與資料庫設計](#5-資料模型與資料庫設計)
6. [API 規格](#6-api-規格)
7. [核心流程定義](#7-核心流程定義)
8. [技術選型決策矩陣](#8-技術選型決策矩陣)
9. [安全性設計](#9-安全性設計)
10. [開發計畫與里程碑](#10-開發計畫與里程碑)
11. [驗收準則](#11-驗收準則)
12. [風險管理與降級策略](#12-風險管理與降級策略)
13. [交付物清單](#13-交付物清單)
14. [附錄:Mermaid 圖檔索引](#14-附錄mermaid-圖檔索引)
---
## 1. 專案概述
### 1.1 目標
建置一套部署於**離線環境**的 AI Agent 系統,提供機構設計研發人員以下能力:
- **文件知識庫**:批次匯入含文字與圖片的 PDF,建立可檢索的向量知識庫。
- **RAG 對話**:基於檢索增強生成(Retrieval-Augmented Generation)的智慧問答。
- **多模態支援**:處理文字與圖片的混合查詢與回應。
- **MCP/Skills 整合**:可插拔的工具與技能模組,擴展 Agent 能力。
### 1.2 使用者角色
| 角色 | 說明 |
|------|------|
| **研發工程師** | 主要使用者,透過 UI 或 CLI 進行 RAG 對話、上傳文件 |
| **系統管理員** | 負責部署、維護後端服務、管理使用者權限 |
| **整合人員** | 透過 RESTful API 將 Agent 能力串接至其他系統 |
### 1.3 系統邊界
```
┌─────────────────────────┐ 內部網路 ┌──────────────────────────┐
│ Client(繪圖筆電) │ ◄──────────────► │ Server(整合部 Host) │
│ - UI / CLI │ REST API │ - API Gateway │
│ - Local Agent Client │ (TLS + Token) │ - Inference Service │
│ - Local Cache (SQLite) │ │ - RAG Pipeline │
│ - Local Skills/MCP │ │ - PostgreSQL + pgvector │
│ - Optional Local LLM │ │ - Document Processor │
└─────────────────────────┘ │ - MCP/Skills Engine │
└──────────────────────────┘
▲
│ 批次匯入
┌─────────┴──────────┐
│ KM 系統(文件來源)│
└────────────────────┘
```
---
## 2. 設計原則與約束
### 2.1 強制約束
| 約束項目 | 說明 |
|----------|------|
| **離線運行** | 核心功能(推理、檢索、向量搜尋、文件匯入)必須在無外網環境運作 |
| **無 Docker** | 不以容器為部署單位,使用系統服務或 Python 虛擬環境 |
| **資源限制** | Client 端:~8GB RAM + 獨立 GPU(與 CAD 共享);需預留資源給 CAD 應用 |
| **資料庫** | PostgreSQL + pgvector 作為關聯式 + 向量資料庫 |
| **可複製部署** | 任何人依照本文件即可自行架設系統 |
### 2.2 設計原則
| 原則 | 實踐方式 |
|------|----------|
| **最小依賴** | 優先選擇輕量、單一用途的工具,避免全棧框架鎖定 |
| **可替換性** | 每個元件定義清晰介面,可獨立替換底層實作 |
| **漸進增強** | MVP 先跑通核心流程,再逐步增加多模態、安全性等功能 |
| **失敗安全** | 推理/檢索失敗時提供降級回應,不讓系統崩潰 |
### 2.3 推理引擎優先順序
```
1. llama.cpp ← 首選(輕量、純 API、離線原生支援)
2. Foundry Local ← 次選
3. LM Studio CLI ← 需確認離線 API 能力
4. Ollama ← 備援(功能較多但非必要)
```
---
## 3. 系統架構總覽
> 完整架構圖請參閱 [architecture.mmd](architecture.mmd)
### 3.1 分層架構
系統採用四層架構,Client 與 Server 透過 RESTful API 通訊:
```
┌──────────────────────────────────────────────────────┐
│ Presentation Layer │
│ (Desktop UI / CLI / Web UI) │
├──────────────────────────────────────────────────────┤
│ Client Agent Layer │
│ (連線管理 / 本地快取 / 離線 Skills / 本地推理) │
├──────────────────────────────────────────────────────┤
│ API Gateway Layer │
│ (認證 / 路由 / Rate Limiting / Request Validation) │
├──────────────────────────────────────────────────────┤
│ Service Layer │
│ ┌───────────┐ ┌───────────┐ ┌───────────────────┐ │
│ │ Inference │ │ Retrieval │ │ Document Processor│ │
│ │ Service │ │ Service │ │ Service │ │
│ └───────────┘ └───────────┘ └───────────────────┘ │
│ ┌───────────┐ ┌───────────┐ │
│ │ MCP/Skill │ │ Context │ │
│ │ Engine │ │ Manager │ │
│ └───────────┘ └───────────┘ │
├──────────────────────────────────────────────────────┤
│ Data Layer │
│ (PostgreSQL + pgvector / File Storage) │
└──────────────────────────────────────────────────────┘
```
### 3.2 部署拓撲
| 部署位置 | 元件 | 資源預算 |
|----------|------|----------|
| **Client(繪圖筆電)** | UI, Agent Client, SQLite Cache, Local Skills | RAM ≤ 1.5GB, GPU 共享 |
| **Server(整合部 Host)** | API Gateway, All Services, PostgreSQL, Model Files | RAM ≥ 16GB 建議, GPU 用於推理 |
---
## 4. 元件設計
### 4.1 Client 端元件
#### 4.1.1 UI 層(Presentation)
| 屬性 | 說明 |
|------|------|
| **技術選項** | Electron(桌面應用)或本地 Web UI(Flask/FastAPI 提供靜態頁面) |
| **啟動方式** | 桌面 icon 單擊啟動 |
| **功能** | 對話介面、文件上傳介面、系統狀態顯示 |
| **資源上限** | RAM ≤ 500MB |
#### 4.1.2 Agent Client
```python
# 模組結構
agent_client/
├── __init__.py
├── connection.py # 伺服器連線管理(含斷線重連、健康檢查)
├── cache.py # SQLite 本地快取管理
├── local_inference.py # 本地推理(可選,quantized LLM)
├── skills/ # 本地 Skills/MCP 工具
│ ├── __init__.py
│ ├── base.py # Skill 基底類別
│ ├── file_converter.py
│ └── cad_helper.py
└── config.py # 設定管理
```
| 職責 | 說明 |
|------|------|
| **連線管理** | 維持與 Server API 的連線、自動重連、健康檢查 |
| **本地快取** | SQLite 存放近期對話、常用檢索結果、技能快取 |
| **離線 Skills** | 不需遠端的本地工具(檔案格式轉換、簡單計算等) |
| **本地推理** | (可選)使用 quantized LLM + llama.cpp 做簡單推理,RAM 限制 ≤ 2GB |
#### 4.1.3 Local Cache Schema(SQLite)
```sql
-- 對話快取
CREATE TABLE conversation_cache (
id TEXT PRIMARY KEY,
session_id TEXT NOT NULL,
role TEXT NOT NULL CHECK(role IN ('user', 'assistant', 'system')),
content TEXT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
expires_at DATETIME
);
-- 技能快取
CREATE TABLE skill_cache (
key TEXT PRIMARY KEY,
value BLOB,
skill_name TEXT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
ttl_seconds INTEGER DEFAULT 3600
);
```
### 4.2 Server 端元件
#### 4.2.1 API Gateway
| 屬性 | 說明 |
|------|------|
| **框架** | FastAPI(Python,async 支援、自動 OpenAPI 文件) |
| **認證** | Bearer Token(JWT 或 API Key) |
| **傳輸** | HTTPS(TLS,內部 CA) |
| **功能** | 路由分發、請求驗證、Rate Limiting、錯誤處理 |
```python
# 模組結構
server/
├── main.py # FastAPI 應用入口
├── api/
│ ├── __init__.py
│ ├── routes/
│ │ ├── upload.py # 文件上傳相關端點
│ │ ├── query.py # RAG 查詢端點
│ │ ├── document.py # 文件管理端點
│ │ └── health.py # 健康檢查
│ ├── deps.py # 依賴注入(DB session, auth)
│ └── middleware.py # 中介層(logging, CORS, rate limit)
├── core/
│ ├── config.py # 環境設定
│ ├── security.py # 認證與授權邏輯
│ └── exceptions.py # 自訂例外
├── services/
│ ├── inference.py # 推理服務介面
│ ├── retrieval.py # 檢索服務
│ ├── document.py # 文件處理服務
│ ├── embedding.py # Embedding 服務
│ ├── context.py # 上下文管理
│ └── skill_engine.py # MCP/Skill 引擎
├── models/
│ ├── database.py # SQLAlchemy ORM 模型
│ └── schemas.py # Pydantic 請求/回應 schema
├── workers/
│ ├── doc_processor.py # 背景文件處理 worker
│ └── task_queue.py # 任務佇列管理
└── utils/
├── chunking.py # 文本切片策略
├── pdf_parser.py # PDF 解析
├── ocr.py # OCR 處理
└── reranker.py # Reranking 模組
```
#### 4.2.2 Inference Service(推理服務)
```
┌─────────────────────────────────────────┐
│ Inference Service │
│ │
│ ┌──────────────────────────────────┐ │
│ │ Inference Adapter Interface │ │
│ │ (統一介面) │ │
│ ├──────────────────────────────────┤ │
│ │ LlamaCppAdapter (首選) │ │
│ │ FoundryAdapter (次選) │ │
│ │ OllamaAdapter (備援) │ │
│ └──────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────┐ │
│ │ Context Manager │ │
│ │ - Token 計數 │ │
│ │ - Sliding Window │ │
│ │ - Summarization Fallback │ │
│ └──────────────────────────────────┘ │
└─────────────────────────────────────────┘
```
**Adapter Interface 定義:**
```python
from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import List, Optional
@dataclass
class InferenceRequest:
messages: List[dict] # [{"role": "user", "content": "..."}]
max_tokens: int = 2048
temperature: float = 0.7
stop_sequences: Optional[List[str]] = None
@dataclass
class InferenceResponse:
content: str
usage: dict # {"prompt_tokens": N, "completion_tokens": M}
model: str
finish_reason: str
class InferenceAdapter(ABC):
@abstractmethod
async def generate(self, request: InferenceRequest) -> InferenceResponse:
"""送出推理請求並取得回應"""
...
@abstractmethod
async def health_check(self) -> bool:
"""檢查推理引擎是否可用"""
...
@abstractmethod
def get_context_limit(self) -> int:
"""回傳此引擎的 context window 上限(token 數)"""
...
```
#### 4.2.3 Retrieval Service(檢索服務)
| 職責 | 說明 |
|------|------|
| **向量檢索** | 使用 pgvector 的 cosine similarity 搜尋 |
| **Metadata 過濾** | 依來源、日期、文件類型等條件篩選 |
| **Reranking** | 使用 Cross-Encoder 對初步結果重新排序 |
| **Multi-hop** | 支援多輪檢索以處理複雜查詢 |
```python
@dataclass
class RetrievalResult:
chunk_id: str
content: str
score: float # 相似度分數
metadata: dict # 來源、頁碼、檔案名稱等
rerank_score: Optional[float] # reranking 後的分數
class RetrievalService:
async def search(
self,
query_embedding: List[float],
top_k: int = 10,
filters: Optional[dict] = None
) -> List[RetrievalResult]:
"""向量相似搜尋 + metadata 篩選"""
...
async def rerank(
self,
query: str,
results: List[RetrievalResult],
top_n: int = 5
) -> List[RetrievalResult]:
"""使用 Cross-Encoder 重新排序"""
...
async def multi_hop_search(
self,
query: str,
max_rounds: int = 3
) -> List[RetrievalResult]:
"""多輪檢索:檢索 → 生成中間查詢 → 再檢索"""
...
```
#### 4.2.4 Document Processor Service(文件處理服務)
```
文件上傳 ──► 格式偵測 ──► 解析 ──► 清理 ──► 切片 ──► Embedding ──► 存入 pgvector
│ │ │
▼ ▼ ▼
PDF: PyMuPDF HTML清理 語意切片
掃描PDF: Tesseract 去雜訊 固定長度切片
圖片: PIL + CLIP 正規化 Overlap 切片
```
#### 4.2.5 MCP/Skill Engine
```python
class SkillRegistry:
"""技能註冊中心,管理所有可用的 MCP 工具與 Skills"""
def register(self, skill: BaseSkill) -> None:
"""註冊一個 Skill"""
...
def match(self, query: str, context: dict) -> Optional[BaseSkill]:
"""根據查詢內容與上下文,判斷是否有適合的 Skill"""
...
def list_skills(self) -> List[SkillInfo]:
"""列出所有已註冊的 Skills"""
...
class BaseSkill(ABC):
name: str
description: str
trigger_conditions: List[str] # 觸發條件描述
@abstractmethod
async def execute(self, params: dict) -> dict:
"""執行 Skill 並回傳結果"""
...
@abstractmethod
def can_handle(self, query: str, context: dict) -> float:
"""回傳信心分數 0.0~1.0,表示此 Skill 處理該查詢的適合度"""
...
```
#### 4.2.6 Context Manager(上下文管理器)
處理 LLM context window 限制的核心元件:
```python
class ContextManager:
"""管理 LLM 的 context window,避免超限導致異常"""
def __init__(self, max_tokens: int, strategy: str = "adaptive"):
self.max_tokens = max_tokens
self.strategy = strategy # "truncate" | "summarize" | "adaptive"
async def fit_context(
self,
system_prompt: str,
conversation_history: List[dict],
retrieved_chunks: List[str],
reserved_for_output: int = 1024
) -> dict:
"""
調整輸入使其符合 context window 限制。
策略優先順序:
1. 篩選/Rerank:取 top-k 最相關的檢索結果
2. 精簡(Condense):摘要化多個片段
3. 滑動窗口:截斷較早的對話歷史
4. 錯誤回退:若推理仍異常,減少 context 並重試
回傳:
{
"messages": [...], # 調整後的訊息列表
"total_tokens": N, # 預估 token 數
"strategy_used": "...", # 使用的策略
"dropped_chunks": [...] # 被丟棄的片段(供 logging)
}
"""
...
def count_tokens(self, text: str) -> int:
"""估算文本的 token 數"""
...
```
---
## 5. 資料模型與資料庫設計
> 完整 ER 圖請參閱 [database_schema.mmd](database_schema.mmd)
### 5.1 PostgreSQL Schema
```sql
-- ===================================================
-- 使用者與認證
-- ===================================================
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
username VARCHAR(100) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL,
role VARCHAR(20) NOT NULL DEFAULT 'user'
CHECK(role IN ('admin', 'user', 'viewer')),
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TABLE api_tokens (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id),
token_hash VARCHAR(255) NOT NULL,
name VARCHAR(100),
expires_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- ===================================================
-- 文件管理
-- ===================================================
CREATE TABLE documents (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
filename VARCHAR(500) NOT NULL,
original_path TEXT,
file_type VARCHAR(20) NOT NULL, -- 'pdf', 'image', 'zip'
file_size_bytes BIGINT,
file_hash VARCHAR(64), -- SHA-256 用於去重
source VARCHAR(100) DEFAULT 'upload', -- 'upload', 'km_system'
uploaded_by UUID REFERENCES users(id),
status VARCHAR(20) DEFAULT 'pending'
CHECK(status IN ('pending', 'processing', 'completed', 'failed')),
error_message TEXT,
metadata JSONB DEFAULT '{}',
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_documents_status ON documents(status);
CREATE INDEX idx_documents_file_hash ON documents(file_hash);
-- ===================================================
-- 文件切片與向量
-- ===================================================
CREATE EXTENSION IF NOT EXISTS vector;
CREATE TABLE document_chunks (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
document_id UUID NOT NULL REFERENCES documents(id) ON DELETE CASCADE,
chunk_index INTEGER NOT NULL, -- 在原文件中的順序
content TEXT NOT NULL, -- 文字內容
content_type VARCHAR(20) DEFAULT 'text'
CHECK(content_type IN ('text', 'image', 'table', 'mixed')),
embedding vector(768), -- 文字 embedding 維度(依模型調整)
image_embedding vector(512), -- 圖像 embedding(CLIP,可選)
token_count INTEGER,
metadata JSONB DEFAULT '{}', -- 頁碼、段落索引、座標等
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_chunks_document ON document_chunks(document_id);
CREATE INDEX idx_chunks_embedding ON document_chunks
USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);
CREATE INDEX idx_chunks_image_embedding ON document_chunks
USING ivfflat (image_embedding vector_cosine_ops) WITH (lists = 100);
-- ===================================================
-- 批次上傳任務
-- ===================================================
CREATE TABLE upload_tasks (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES users(id),
total_files INTEGER NOT NULL DEFAULT 0,
processed_files INTEGER NOT NULL DEFAULT 0,
failed_files INTEGER NOT NULL DEFAULT 0,
status VARCHAR(20) DEFAULT 'pending'
CHECK(status IN ('pending', 'processing', 'completed', 'partial', 'failed')),
error_log JSONB DEFAULT '[]',
created_at TIMESTAMPTZ DEFAULT NOW(),
completed_at TIMESTAMPTZ
);
CREATE TABLE upload_task_files (
task_id UUID NOT NULL REFERENCES upload_tasks(id) ON DELETE CASCADE,
document_id UUID NOT NULL REFERENCES documents(id),
PRIMARY KEY (task_id, document_id)
);
-- ===================================================
-- 對話與審計
-- ===================================================
CREATE TABLE conversations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES users(id),
title VARCHAR(500),
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TABLE messages (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
conversation_id UUID NOT NULL REFERENCES conversations(id) ON DELETE CASCADE,
role VARCHAR(20) NOT NULL CHECK(role IN ('user', 'assistant', 'system')),
content TEXT NOT NULL,
token_count INTEGER,
sources JSONB DEFAULT '[]', -- 引用的 chunk IDs 與置信度
skill_used VARCHAR(100), -- 使用的 Skill 名稱
model_used VARCHAR(100), -- 使用的模型名稱
latency_ms INTEGER, -- 回應延遲
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_messages_conversation ON messages(conversation_id);
-- ===================================================
-- MCP/Skills 註冊
-- ===================================================
CREATE TABLE skills (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(100) UNIQUE NOT NULL,
description TEXT,
version VARCHAR(20),
is_active BOOLEAN DEFAULT TRUE,
config JSONB DEFAULT '{}',
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- ===================================================
-- 審計日誌
-- ===================================================
CREATE TABLE audit_logs (
id BIGSERIAL PRIMARY KEY,
user_id UUID REFERENCES users(id),
action VARCHAR(50) NOT NULL, -- 'query', 'upload', 'login', 'skill_exec'
resource VARCHAR(100),
details JSONB DEFAULT '{}',
ip_address INET,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_audit_created ON audit_logs(created_at);
```
### 5.2 Embedding 維度規劃
| 用途 | 模型選項 | 維度 | 說明 |
|------|----------|------|------|
| 文字 Embedding | Sentence-BERT / BGE / Qwen-Embed | 768 | 切片文字向量化 |
| 圖像 Embedding | CLIP ViT-B/32 | 512 | 圖片特徵抽取 |
| 多模態 Embedding | CLIP Text + Vision | 512 | 統一文字圖片空間(可選) |
> **注意**:向量維度需依實際選用模型調整,上表為建議值。Schema 中 `vector(768)` 與 `vector(512)` 需對應實際值。
---
## 6. API 規格
### 6.1 共用約定
| 項目 | 規格 |
|------|------|
| **Base URL** | `https://{server_host}:{port}/api/v1` |
| **認證** | `Authorization: Bearer <token>` |
| **Content-Type** | `application/json`(查詢)/ `multipart/form-data`(上傳) |
| **錯誤格式** | `{"error": {"code": "ERR_XXX", "message": "..."}}` |
### 6.2 端點定義
#### 6.2.1 文件上傳
```
POST /api/v1/upload-batch
```
**Request**(multipart/form-data):
| 欄位 | 類型 | 必填 | 說明 |
|------|------|------|------|
| files | File[] | 是 | PDF、圖片或 ZIP 檔案(上限 50 個) |
| source | string | 否 | 來源標記,預設 `"upload"` |
| metadata | JSON string | 否 | 附加 metadata |
**Response**(202 Accepted):
```json
{
"task_id": "550e8400-e29b-41d4-a716-446655440000",
"total_files": 5,
"status": "pending",
"created_at": "2026-03-05T10:00:00Z"
}
```
#### 6.2.2 上傳進度查詢
```
GET /api/v1/upload-status/{task_id}
```
**Response**(200 OK):
```json
{
"task_id": "550e8400-...",
"status": "processing",
"total_files": 5,
"processed_files": 3,
"failed_files": 0,
"error_log": [],
"progress_percent": 60.0
}
```
#### 6.2.3 RAG 查詢
```
POST /api/v1/query
```
**Request**:
```json
{
"query": "這個零件的材質規格是什麼?",
"conversation_id": "optional-uuid",
"options": {
"top_k": 5,
"use_reranking": true,
"enable_multi_hop": false,
"filters": {
"source": "design_manual",
"file_type": "pdf"
}
}
}
```
**Response**(200 OK):
```json
{
"answer": "根據設計手冊第 3.2 節,該零件主要材質為 SUS304 不鏽鋼...",
"sources": [
{
"chunk_id": "abc-123",
"document_id": "doc-456",
"filename": "design_manual_v2.pdf",
"page": 42,
"relevance_score": 0.92,
"content_preview": "3.2 材質規格:本零件採用 SUS304..."
}
],
"confidence": 0.87,
"model_used": "qwen2-7b-q4",
"skill_used": null,
"conversation_id": "conv-789",
"usage": {
"prompt_tokens": 1520,
"completion_tokens": 256,
"retrieval_time_ms": 120,
"inference_time_ms": 3400
}
}
```
#### 6.2.4 文件查詢與下載
```
GET /api/v1/doc/{document_id}
```
**Response**(200 OK):
```json
{
"id": "doc-456",
"filename": "design_manual_v2.pdf",
"file_type": "pdf",
"file_size_bytes": 2048000,
"source": "km_system",
"status": "completed",
"chunk_count": 47,
"metadata": { "pages": 120, "author": "設計部" },
"created_at": "2026-03-01T08:00:00Z"
}
```
```
GET /api/v1/doc/{document_id}/download
```
回傳原始檔案(`application/octet-stream`)。
#### 6.2.5 健康檢查
```
GET /api/v1/health
```
**Response**(200 OK):
```json
{
"status": "healthy",
"components": {
"database": "ok",
"inference_engine": "ok",
"embedding_service": "ok"
},
"version": "1.0.0",
"uptime_seconds": 86400
}
```
#### 6.2.6 對話歷史
```
GET /api/v1/conversations
GET /api/v1/conversations/{conversation_id}/messages
```
---
## 7. 核心流程定義
### 7.1 文件上傳與向量化流程
> Sequence 圖請參閱 [sequence_upload.mmd](sequence_upload.mmd)
```
步驟 動作 負責元件 失敗處理
──── ──── ──────── ────────
1 用戶上傳檔案(PDF/圖片/ZIP) API Gateway 驗證格式,拒絕不支援類型
2 建立 upload_task 記錄 API → DB 回傳 task_id
3 檔案寫入暫存目錄 API Gateway 磁碟滿 → 回傳 503
4 觸發背景處理 Worker Task Queue Worker 不可用 → 標記 pending
5 格式偵測與解壓縮 Document Processor ZIP 損壞 → 記錄錯誤,跳過
6 PDF 解析 pdf_parser PyMuPDF 失敗 → fallback pdfminer
6a 文字抽取 PyMuPDF / pdfminer —
6b 圖像區塊抽取 PyMuPDF —
6c 掃描 PDF → OCR Tesseract OCR 錯誤 → 記錄,存空白
7 文字清理與正規化 Document Processor —
8 文本切片(Chunking) chunking module —
8a 語意/段落切片(優先) — —
8b 固定長度 + 重疊切片(備選) — —
8c 記錄 metadata(頁碼、段落索引) — —
9 Embedding 生成 Embedding Service 模型載入失敗 → 排入重試佇列
9a 文字片段 → 文字 embedding Sentence-BERT/BGE —
9b 圖像片段 → 圖像 embedding CLIP —
10 寫入 document_chunks 表 DB(pgvector) 寫入失敗 → 重試 3 次
11 更新 document 狀態 → completed DB —
12 更新 upload_task 計數 DB —
```
### 7.2 RAG 對話流程
> Sequence 圖請參閱 [sequence_rag.mmd](sequence_rag.mmd)
```
步驟 動作 負責元件 說明
──── ──── ──────── ────
1 接收使用者查詢 API Gateway 含 query、conversation_id、options
2 載入對話歷史 DB 取最近 N 輪對話
3 查詢 Embedding 生成 Embedding Service 將 query 轉為向量
4 向量檢索 Retrieval Service pgvector cosine similarity
5 Metadata 篩選 Retrieval Service 依 filters 參數過濾
6 Reranking(可選) Reranker Cross-Encoder 重排序
7 Skill 匹配檢查 Skill Engine 判斷是否需呼叫 MCP 工具
7a 若匹配 → 執行 Skill Skill Engine 取得 Skill 輸出
7b 若不匹配 → 跳過 — —
8 Context 組裝 Context Manager 組合 system prompt + history + chunks
8a Token 計數 Context Manager 確認不超過 context window
8b 若超限 → 執行精簡策略 Context Manager Rerank/Summarize/Truncate
9 送入 LLM 推理 Inference Service 透過 Adapter 呼叫推理引擎
9a 推理成功 → 取得回應 — —
9b 推理異常 → 錯誤回退 Context Manager 減少 context 重試 / 降級回應
10 Answer Verification(可選) Retrieval Service 用檢索片段驗證答案正確性
11 組裝回應(含來源、置信度) API Gateway —
12 儲存對話記錄 DB messages 表
13 寫入審計日誌 DB audit_logs 表
14 回傳回應給使用者 API Gateway —
```
### 7.3 上下文管理策略流程
```
輸入 token 數 = system_prompt + conversation_history + retrieved_chunks + reserved_output
│
▼
超過 context window?
/ \
否 是
│ │
▼ ▼
直接送入推理 ┌─ 策略 1: Top-K 篩選(降低 chunks 數量)
│ 重新計算 token → 仍超限?
│ │
│ 是
│ ▼
├─ 策略 2: Summarize(合併 chunks 為摘要)
│ 重新計算 token → 仍超限?
│ │
│ 是
│ ▼
├─ 策略 3: Sliding Window(截斷早期對話)
│ 重新計算 token → 仍超限?
│ │
│ 是
│ ▼
└─ 策略 4: 強制截斷 + 降級警告
```
---
## 8. 技術選型決策矩陣
### 8.1 推理引擎比較
| 評估項目 | llama.cpp | Foundry Local | LM Studio CLI | Ollama |
|----------|-----------|---------------|---------------|--------|
| **離線支援** | ✅ 原生 | ✅ 原生 | ⚠️ 需確認 | ✅ |
| **API 介面** | HTTP Server (OpenAI 相容) | REST API | ⚠️ 需確認 | REST + OpenAI 相容 |
| **資源需求** | 極低(純 C++) | 中等 | 中等 | 中等 |
| **Context 超限行為** | 明確截斷 | 待測 | 待測 | ⚠️ 可能回傳異常 |
| **社群活躍度** | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| **授權風險** | MIT(低風險) | 待確認 | 商業 | MIT→ 需追蹤 |
| **更新頻率** | 每週 | 月度 | 月度 | 每週 |
| **GPU 加速** | CUDA/Metal/Vulkan | DirectML | CUDA | CUDA/Metal |
| **優先等級** | 🥇 首選 | 🥈 次選 | 🥉 需評估 | 🔄 備援 |
### 8.2 LLM 模型選項
| 模型 | 參數量 | 量化 | RAM 需求 | 授權 | 適用場景 |
|------|--------|------|----------|------|----------|
| Qwen2-7B | 7B | Q4_K_M | ~5GB | Apache 2.0 | 主力對話推理 |
| Gemma3-9B | 9B | Q4_K_M | ~6GB | Gemma License | 備選對話推理 |
| Qwen2-VL-7B | 7B | Q4 | ~5GB | Apache 2.0 | 多模態(含視覺) |
| LLaMA3-8B | 8B | Q4_K_M | ~5GB | Meta License | 英文場景 |
| BGE-M3 | — | FP16 | ~1GB | MIT | 文字 Embedding |
| CLIP ViT-B/32 | — | FP16 | ~0.5GB | MIT | 圖像 Embedding |
### 8.3 其他技術選型
| 元件 | 選型 | 替代方案 | 理由 |
|------|------|----------|------|
| **Web 框架** | FastAPI | Flask | Async 支援、自動 OpenAPI 文件 |
| **ORM** | SQLAlchemy 2.0 | — | 成熟、支援 async |
| **任務佇列** | Celery + Redis 或 Python asyncio | RQ | 離線環境可用 Redis;若不裝則用 asyncio |
| **PDF 解析** | PyMuPDF | pdfminer.six | 速度快、支援圖像抽取 |
| **OCR** | Tesseract + pytesseract | PaddleOCR | 離線可用、多語言 |
| **Reranker** | Cross-Encoder (sentence-transformers) | BGE-Reranker | 離線可用 |
| **Client UI** | Electron 或 Tauri | Flask 本地 Web | 桌面體驗 |
| **本地 Cache** | SQLite | — | 輕量、零配置 |
---
## 9. 安全性設計
### 9.1 網路安全
```
┌─────────────┐ ┌──────────────────┐
│ Client │──── TLS 1.2+ ────►│ Server │
│ (內部網段) │ API Token │ (整合部網段) │
└─────────────┘ │ │
│ 僅開放端口: │
│ - 443 (HTTPS) │
│ - 5432 (PG, │
│ 僅 localhost) │
└──────────────────┘
```
### 9.2 認證與授權
| 機制 | 實作方式 |
|------|----------|
| **使用者認證** | JWT Token(有效期 24h)或長期 API Key |
| **權限模型** | RBAC(admin / user / viewer) |
| **密碼儲存** | bcrypt hash |
### 9.3 權限矩陣
| 操作 | admin | user | viewer |
|------|-------|------|--------|
| 上傳文件 | ✅ | ✅ | ❌ |
| RAG 查詢 | ✅ | ✅ | ✅ |
| 下載文件 | ✅ | ✅ | ✅ |
| 管理使用者 | ✅ | ❌ | ❌ |
| 查看審計日誌 | ✅ | ❌ | ❌ |
| 管理 Skills | ✅ | ❌ | ❌ |
### 9.4 資料保護
- **備份策略**:PostgreSQL `pg_dump` 每日排程備份,保留 7 天
- **檔案加密**:視需求啟用磁碟層級加密(BitLocker / LUKS)
- **審計日誌**:所有 API 操作記錄至 `audit_logs` 表,不可刪除
---
## 10. 開發計畫與里程碑
### 10.1 總覽時程
```
Week 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
├──M1──┤ 環境 + MVP
├─────M2─────┤ 文件匯入
├─────M3──────┤ RAG 對話
├─────M4─────┤ 多模態 + 前端
├──M5──┤ 安全+部署
├─────E1──────┤ 推理引擎評估
├──E2──┤ 效能測試
```
### 10.2 M1:環境準備與 MVP(第 0–2 週)
| 工作項目 | 交付物 | 負責 |
|----------|--------|------|
| PostgreSQL + pgvector 安裝與設定 | 可連線的 DB、pgvector 擴充啟用 | 後端 |
| Python 虛擬環境建置 | `requirements.txt`、venv 建立腳本 | 後端 |
| FastAPI 骨架建置 | `/health` 端點可回應 | 後端 |
| llama.cpp 安裝與測試 | 本地推理可透過 HTTP API 呼叫 | 後端 |
| Inference Adapter 介面定義 | `InferenceAdapter` ABC | 後端 |
| LlamaCppAdapter 實作 | 可呼叫 llama.cpp 的 adapter | 後端 |
| 基礎 DB Schema 建立 | Migration 腳本(Alembic) | 後端 |
| 開發環境文件 | `docs/setup.md` | 全員 |
**M1 驗收條件**:
- [x] `GET /api/v1/health` 回傳 200
- [x] llama.cpp 可載入量化模型並回應簡單 prompt
- [x] PostgreSQL + pgvector 可進行向量寫入/查詢
### 10.3 M2:文件匯入與向量化 Pipeline(第 3–6 週)
| 工作項目 | 交付物 | 負責 |
|----------|--------|------|
| PDF 解析模組 | `pdf_parser.py`(文字 + 圖像抽取) | 後端 |
| OCR 模組 | `ocr.py`(Tesseract 整合) | 後端 |
| Chunking 模組 | `chunking.py`(語意切片 + 固定長度切片) | 後端 |
| Embedding 服務 | `embedding.py`(文字 + 圖像 embedding) | 後端 |
| 批次上傳 API | `POST /api/v1/upload-batch` | 後端 |
| 任務狀態 API | `GET /api/v1/upload-status/{task_id}` | 後端 |
| 背景 Worker | `doc_processor.py`(非同步文件處理) | 後端 |
| 文件管理 API | `GET /api/v1/doc/{id}` | 後端 |
| 單元測試 | 各模組測試(≥ 80% coverage) | 後端 |
**M2 驗收條件**:
- [x] 上傳 1 個 PDF → 系統完成解析、切片、embedding、存入 pgvector
- [x] 批次上傳 10 個 PDF → 任務狀態 API 正確回報進度
- [x] 支援含圖片的 PDF 與掃描 PDF
### 10.4 M3:RAG 與基本對話 API(第 7–10 週)
| 工作項目 | 交付物 | 負責 |
|----------|--------|------|
| Retrieval Service | `retrieval.py`(向量搜尋 + metadata filter) | 後端 |
| Reranker 模組 | `reranker.py`(Cross-Encoder) | 後端 |
| Context Manager | `context.py`(token 管理、精簡策略) | 後端 |
| RAG 查詢 API | `POST /api/v1/query` | 後端 |
| Prompt 組裝邏輯 | System prompt + context template | 後端 |
| MCP/Skill Engine 基礎框架 | `skill_engine.py` + `BaseSkill` | 後端 |
| 範例 Skill x 2 | 檔案查詢 Skill、計算 Skill | 後端 |
| 對話歷史 API | conversations 相關端點 | 後端 |
| RAG 精準度基準測試 | 測試報告(含 reranking 效果比較) | 後端 |
| 整合測試 | 上傳 → 檢索 → 回應端到端測試 | 全員 |
**M3 驗收條件**:
- [x] 上傳文件後,RAG 查詢可回傳正確答案與來源
- [x] Reranking 啟用後精準度提升可量化
- [x] context 超限時系統不崩潰,正確降級
- [x] 至少 1 個 Skill 可被自動觸發
### 10.5 M4:多模態支援與前端整合(第 11–14 週)
| 工作項目 | 交付物 | 負責 |
|----------|--------|------|
| 圖像 Embedding 支援 | CLIP 整合至 pipeline | 後端 |
| 多模態查詢處理 | 支援含圖片的查詢 | 後端 |
| Client Agent 開發 | `agent_client/`(連線、快取、離線 skills) | 前端 |
| 桌面 UI 或 CLI 工具 | 可操作的前端介面 | 前端 |
| Local Cache 實作 | SQLite 快取模組 | 前端 |
| Client ↔ Server 整合測試 | 端到端功能驗證 | 全員 |
**M4 驗收條件**:
- [x] 圖片文件可被正確 embedding 並檢索
- [x] 前端 UI 可完成上傳、查詢、查看來源的完整流程
- [x] Client 離線時 Local Skills 可獨立運作
### 10.6 M5:安全性強化與部署文件(第 15–18 週)
| 工作項目 | 交付物 | 負責 |
|----------|--------|------|
| JWT 認證實作 | `security.py` | 後端 |
| TLS 設定 | 內部 CA 憑證 + HTTPS 設定 | 運維 |
| RBAC 權限控制 | 中介層權限檢查 | 後端 |
| 備份排程 | `pg_dump` cron job + 腳本 | 運維 |
| 審計日誌完善 | 所有端點 audit logging | 後端 |
| 部署手冊 | `docs/deployment.md`(可複製步驟) | 全員 |
| 維運手冊 | `docs/operations.md` | 全員 |
| Mermaid 圖檔定版 | 所有 `.mmd` 圖檔 | 全員 |
**M5 驗收條件**:
- [x] 無 Token 的請求被拒絕(401)
- [x] 非 admin 無法存取管理端點(403)
- [x] 備份腳本可自動執行
- [x] 新人可依部署手冊在新環境完成安裝
### 10.7 E1-E2:推理引擎評估(並行,第 0–6 週)
| 階段 | 工作內容 | 交付物 |
|------|----------|--------|
| **E1(第 0–4 週)** | 功能矩陣比較:llama.cpp / Foundry Local / LM Studio CLI / Ollama | 比較報告(含 API 支援、離線能力、資源使用、授權分析) |
| **E2(第 4–6 週)** | 效能與穩定性測試(含 context 超限邊界測試) | 測試報告(含錯誤模式與回退策略建議) |
---
## 11. 驗收準則
### 11.1 系統級驗收
| 項次 | 驗收條件 | 對應里程碑 |
|------|----------|-----------|
| AC-1 | 在離線環境下,從文件上傳 → 向量化 → 檢索 → RAG 回應完整走通 | M3 |
| AC-2 | 批次匯入 100 篇 PDF,處理成功率 ≥ 90% | M2 |
| AC-3 | RAG 回答包含來源 metadata(檔案名、頁碼)且通過功能驗收 | M3 |
| AC-4 | 推理引擎在 context 超限時不崩潰,系統正確降級 | M3 |
| AC-5 | 多模態查詢(含圖片)可正確回應 | M4 |
| AC-6 | TLS + Token 認證在所有端點生效 | M5 |
| AC-7 | 新環境部署可在 4 小時內完成 | M5 |
### 11.2 效能指標(參考值)
| 指標 | 目標值 | 量測方式 |
|------|--------|----------|
| 單文件向量化延遲 | ≤ 30 秒 / 頁 | M2 階段測試 |
| RAG 查詢延遲(端到端) | ≤ 15 秒 | M3 階段測試 |
| 向量檢索延遲(10K chunks) | ≤ 500ms | M3 階段測試 |
| 系統 RAM 佔用(Server) | ≤ 12GB | 持續監控 |
| Client RAM 佔用 | ≤ 1.5GB | M4 階段測試 |
---
## 12. 風險管理與降級策略
| 風險項目 | 可能性 | 影響 | 降級策略 |
|----------|--------|------|----------|
| Ollama 授權變更或停止維護 | 中 | 中 | 降級至 llama.cpp(已有 Adapter) |
| LLM 模型授權限制收緊 | 低 | 高 | 切換至其他 Apache 2.0 / MIT 授權模型 |
| Agent 框架(Dify/LangFlow)商業化 | 中 | 中 | 回退至 LangChain 純程式化實作 |
| GPU 記憶體不足以執行推理 | 中 | 高 | 使用更小量化模型(Q2/Q3)或純 CPU 推理 |
| Tesseract OCR 辨識率不佳 | 中 | 低 | 替換為 PaddleOCR 或 EasyOCR |
| pgvector 效能瓶頸(>100K chunks) | 低 | 中 | 加入 HNSW 索引、分區表 |
| 模型 embedding 維度不匹配 | 低 | 高 | Schema migration + 重新 embedding |
---
## 13. 交付物清單
| 交付物 | 格式 | 狀態 |
|--------|------|------|
| SA 文件(本檔) | Markdown | ✅ |
| [architecture.mmd](architecture.mmd) — 系統架構圖 | Mermaid | ✅ |
| [use_case.mmd](use_case.mmd) — Use Case 圖 | Mermaid | ✅ |
| [uml_class.mmd](uml_class.mmd) — UML Class 圖 | Mermaid | ✅ |
| [database_schema.mmd](database_schema.mmd) — Database ER 圖 | Mermaid | ✅ |
| [sequence_rag.mmd](sequence_rag.mmd) — RAG 對話 Sequence 圖 | Mermaid | ✅ |
| [sequence_upload.mmd](sequence_upload.mmd) — 文件上傳 Sequence 圖 | Mermaid | ✅ |
| 原始碼 | Python | 依里程碑交付 |
| 部署手冊 | Markdown | M5 交付 |
| 維運手冊 | Markdown | M5 交付 |
---
## 14. 附錄:Mermaid 圖檔索引
| 圖檔 | 說明 |
|------|------|
| [architecture.mmd](architecture.mmd) | 系統整體架構圖,展示 Client/Server 元件與資料流 |
| [use_case.mmd](use_case.mmd) | 使用者與系統互動的 Use Case 圖 |
| [uml_class.mmd](uml_class.mmd) | 核心類別的 UML Class 圖(Adapter, Service, Skill 等) |
| [database_schema.mmd](database_schema.mmd) | PostgreSQL 資料表的 ER 圖 |
| [sequence_rag.mmd](sequence_rag.mmd) | RAG 對話流程的 Sequence 圖 |
| [sequence_upload.mmd](sequence_upload.mmd) | 文件上傳與向量化流程的 Sequence 圖 |
---
> **文件維護**:本文件隨開發迭代更新。每個里程碑結束時,需檢視並更新對應章節內容。
Comments...
No Comments Yet...