Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5e896fb509 | |||
| c15f7cd4af | |||
| 4686c5abc4 | |||
| e84982e7d9 | |||
| 1f84e5469f | |||
| 59809dae1f | |||
| 13dd3b30f3 | |||
| f45ecf4643 | |||
| d12caba00a | |||
| 395f74bf07 | |||
| 363d6913f9 | |||
| 6d5d121d0f | |||
| 4109ec3d95 | |||
| 576f58df71 | |||
| 37d2b66c56 | |||
| 95b44f1e55 | |||
| 2393d81a3f | |||
| 82955504f3 | |||
| 80399b1c12 | |||
| ceb33877ff | |||
| dacfb7e083 | |||
| fb60858cec | |||
| f1d7077e40 | |||
| 4f402c873b | |||
| a89d94bc67 | |||
| 17cab667f9 | |||
| f8925ab994 | |||
| dac2b234d0 | |||
| 67c8c60ceb |
@@ -1,40 +1,10 @@
|
||||
# Database Configuration
|
||||
DATABASE_URL=postgres://accusys@localhost:5432/momentry
|
||||
|
||||
# Redis
|
||||
# Format: redis://[username][:password]@host:port
|
||||
# Users: default (with password), accusys (custom user with password)
|
||||
REDIS_URL=redis://accusys:accusys@localhost:6379
|
||||
|
||||
# MongoDB
|
||||
MONGODB_URL=mongodb://accusys:Test3200Test3200@localhost:27017/admin
|
||||
MONGODB_DATABASE=momentry
|
||||
|
||||
# Qdrant Vector Database
|
||||
QDRANT_URL=http://localhost:6333
|
||||
DB_MAX_CONNECTIONS=50
|
||||
DB_ACQUIRE_TIMEOUT=30
|
||||
DATABASE_SCHEMA=dev
|
||||
QDRANT_URL=http://127.0.0.1:6333
|
||||
QDRANT_API_KEY=Test3200Test3200Test3200
|
||||
QDRANT_COLLECTION=chunks_v3
|
||||
|
||||
# Gitea
|
||||
GITEA_URL=http://localhost:3000
|
||||
|
||||
# API Server (Production)
|
||||
MOMENTRY_SERVER_PORT=3002
|
||||
QDRANT_COLLECTION=momentry_rule1
|
||||
MONGODB_URL=mongodb://localhost:27017
|
||||
MONGODB_CACHE_ENABLED=false
|
||||
MOMENTRY_REDIS_PREFIX=momentry:
|
||||
API_HOST=127.0.0.1
|
||||
API_PORT=3002
|
||||
|
||||
# Worker Configuration (Production)
|
||||
MOMENTRY_WORKER_ENABLED=true
|
||||
MOMENTRY_MAX_CONCURRENT=2
|
||||
MOMENTRY_POLL_INTERVAL=5
|
||||
|
||||
# Watch Directories (comma separated)
|
||||
WATCH_DIRECTORIES=~/Videos,~/momentry_core_project/test_video
|
||||
|
||||
# Ollama (for Mistral 7B LLM)
|
||||
OLLAMA_HOST=http://localhost:11434
|
||||
|
||||
# Model Paths
|
||||
# EMBEDDING_MODEL_PATH=./models/comic-embed-text
|
||||
# LLM_MODEL_PATH=./models/mistral-7b
|
||||
REDIS_URL=redis://:accusys@localhost:6379
|
||||
+19
-9
@@ -14,25 +14,27 @@ MOMENTRY_MAX_CONCURRENT=1
|
||||
MOMENTRY_POLL_INTERVAL=10
|
||||
MOMENTRY_WORKER_BATCH_SIZE=5
|
||||
|
||||
# Database (same as production, but could use separate dev database)
|
||||
# Database (PostgreSQL) - Schema isolation
|
||||
DATABASE_URL=postgres://accusys@localhost:5432/momentry
|
||||
DATABASE_SCHEMA=dev
|
||||
|
||||
# MongoDB
|
||||
MONGODB_URL=mongodb://accusys:Test3200Test3200@localhost:27017/admin
|
||||
MONGODB_DATABASE=momentry
|
||||
# MongoDB - Database isolation
|
||||
MONGODB_URL=mongodb://localhost:27017
|
||||
MONGODB_DATABASE=momentry_dev
|
||||
|
||||
# Redis
|
||||
# Redis (already isolated via prefix)
|
||||
REDIS_URL=redis://:accusys@localhost:6379
|
||||
REDIS_PASSWORD=accusys
|
||||
|
||||
# Qdrant Vector Database (same as production)
|
||||
# Qdrant Vector Database - Collection isolation
|
||||
QDRANT_URL=http://localhost:6333
|
||||
QDRANT_API_KEY=Test3200Test3200Test3200
|
||||
QDRANT_COLLECTION=chunks_v3
|
||||
QDRANT_COLLECTION=momentry_dev_rule1
|
||||
|
||||
# Paths
|
||||
MOMENTRY_OUTPUT_DIR=/Users/accusys/momentry/output_dev
|
||||
MOMENTRY_BACKUP_DIR=/Users/accusys/momentry/backup/momentry_dev
|
||||
MOMENTRY_SFTP_ROOT=/Users/accusys/momentry/var/sftpgo/data/demo/
|
||||
|
||||
# Python (for processing scripts)
|
||||
MOMENTRY_PYTHON_PATH=/opt/homebrew/bin/python3.11
|
||||
@@ -51,10 +53,18 @@ MOMENTRY_CUT_TIMEOUT=3600
|
||||
MOMENTRY_DEFAULT_TIMEOUT=7200
|
||||
|
||||
# Cache Settings
|
||||
MONGODB_CACHE_ENABLED=true
|
||||
MONGODB_CACHE_ENABLED=false
|
||||
MONGODB_CACHE_TTL_VIDEOS=300
|
||||
MONGODB_CACHE_TTL_SEARCH=300
|
||||
MONGODB_CACHE_TTL_HYBRID_SEARCH=600
|
||||
MONGODB_CACHE_TTL_VIDEO_META=3600
|
||||
REDIS_CACHE_TTL_HEALTH=30
|
||||
REDIS_CACHE_TTL_VIDEO_META=3600
|
||||
REDIS_CACHE_TTL_VIDEO_META=3600
|
||||
# 同義詞配置文件(可選)
|
||||
# 取消註釋並設置為您的同義詞JSON檔案路徑以啟用同義詞擴展
|
||||
# MOMENTRY_SYNONYM_FILE=/Users/accusys/momentry_core_0.1/docs/examples/custom_synonyms.json
|
||||
#
|
||||
# 多個同義詞檔案(逗號分隔),會覆蓋 MOMENTRY_SYNONYM_FILE
|
||||
# MOMENTRY_SYNONYM_FILES=/path/to/first.json,/path/to/second.json
|
||||
#
|
||||
# 示例檔案:docs/examples/custom_synonyms.json
|
||||
+1
-1
@@ -24,7 +24,7 @@ MONGODB_DATABASE=momentry
|
||||
# ===========================================
|
||||
QDRANT_URL=http://localhost:6333
|
||||
QDRANT_API_KEY=your_qdrant_api_key
|
||||
QDRANT_COLLECTION=chunks_v3
|
||||
QDRANT_COLLECTION=momentry_rule1
|
||||
|
||||
# ===========================================
|
||||
# API Server Configuration
|
||||
|
||||
@@ -38,3 +38,10 @@ id_*
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# Documentation backups
|
||||
# docs_v1.0/ (Moved to active tracking)
|
||||
|
||||
# Frontend dependencies
|
||||
node_modules/
|
||||
portal/src-tauri/target/
|
||||
|
||||
@@ -182,6 +182,15 @@ src/
|
||||
### Server
|
||||
- `MOMENTRY_SERVER_PORT` - API server port (default: `3002` for production, `3003` for playground)
|
||||
- `MOMENTRY_REDIS_PREFIX` - Redis key prefix (default: `momentry:` for production, `momentry_dev:` for playground)
|
||||
- `MOMENTRY_API_KEY` - API key for Player online mode testing
|
||||
|
||||
### Testing API Key
|
||||
```bash
|
||||
export MOMENTRY_API_KEY="muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69"
|
||||
|
||||
# Test Player online mode
|
||||
cargo run --features player --bin momentry_player -- -o
|
||||
```
|
||||
|
||||
### Database
|
||||
- `DATABASE_URL` - PostgreSQL (default: `postgres://accusys@localhost:5432/momentry`)
|
||||
@@ -201,6 +210,10 @@ src/
|
||||
- `MOMENTRY_CUT_TIMEOUT` - CUT timeout in seconds (default: 3600)
|
||||
- `MOMENTRY_DEFAULT_TIMEOUT` - Default timeout (default: 7200)
|
||||
|
||||
### Synonym Expansion
|
||||
- `MOMENTRY_SYNONYM_FILES` - Comma-separated paths to synonym JSON files (e.g., `data/english_synonyms.json,data/llm_synonyms.json`)
|
||||
- `MOMENTRY_SYNONYM_FILE` - Single synonym JSON file path (deprecated, use above)
|
||||
|
||||
### Logging
|
||||
- `RUST_LOG` or `MOMENTRY_LOG_LEVEL` - Log level (default: `info`)
|
||||
|
||||
@@ -213,6 +226,23 @@ src/
|
||||
- PythonExecutor provides unified script execution with timeout support
|
||||
- Redis 1.0.x for improved performance
|
||||
|
||||
### LLM Synonym Generation
|
||||
|
||||
Generate synonym database using llama.cpp (Gemma4):
|
||||
|
||||
```bash
|
||||
# Generate full database (162 entries, ~5 minutes)
|
||||
python3 scripts/generate_synonyms_llamacpp.py
|
||||
|
||||
# Quick test
|
||||
python3 scripts/generate_synonyms_llamacpp.py --test
|
||||
|
||||
# Resume from existing file
|
||||
python3 scripts/generate_synonyms_llamacpp.py --resume
|
||||
|
||||
# Output: data/llm_synonyms.json (27 Chinese + 135 English words)
|
||||
```
|
||||
|
||||
## Task Management
|
||||
|
||||
### 使用 todowrite 追蹤任務
|
||||
|
||||
Generated
+888
-273
File diff suppressed because it is too large
Load Diff
+37
-8
@@ -13,6 +13,7 @@ tokio = { version = "1", features = ["full"] }
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = "0.3"
|
||||
once_cell = "1.19"
|
||||
libc = "0.2"
|
||||
dotenv = "0.15"
|
||||
|
||||
# CLI
|
||||
@@ -32,25 +33,31 @@ sha2 = "0.10"
|
||||
hex = "0.4"
|
||||
uuid = { version = "1.0", features = ["v4"] }
|
||||
|
||||
# Security
|
||||
subtle = "2.5"
|
||||
aes-gcm = "0.10"
|
||||
base64 = "0.22"
|
||||
# Security
|
||||
subtle = "2.5"
|
||||
aes-gcm = "0.10"
|
||||
base64 = "0.22"
|
||||
|
||||
# Text processing
|
||||
jieba-rs = "0.8.1"
|
||||
ferrous-opencc = { version = "0.3.1", features = ["s2t-conversion", "t2s-conversion"] }
|
||||
|
||||
# Cache
|
||||
moka = { version = "0.12", features = ["future"] }
|
||||
|
||||
# Database
|
||||
redis = { version = "1.0", features = ["tokio-comp", "connection-manager"] }
|
||||
sqlx = { version = "0.8", features = ["runtime-tokio", "postgres", "sqlite", "json", "chrono"] }
|
||||
sqlx = { version = "0.8", features = ["runtime-tokio", "postgres", "sqlite", "json", "chrono", "uuid"] }
|
||||
mongodb = { version = "2", features = ["tokio-runtime"] }
|
||||
bson = { version = "2", features = ["chrono-0_4"] }
|
||||
qdrant-client = "1.7"
|
||||
reqwest = { version = "0.12", features = ["json"] }
|
||||
pgvector = { version = "0.3", features = ["sqlx"] }
|
||||
|
||||
# HTTP Server
|
||||
axum = "0.7"
|
||||
axum = { version = "0.7", features = ["multipart"] }
|
||||
tower = "0.4"
|
||||
tower-http = { version = "0.5", features = ["cors"] }
|
||||
|
||||
# API Documentation
|
||||
utoipa = { version = "4", features = ["axum_extras", "chrono", "uuid"] }
|
||||
@@ -73,7 +80,6 @@ crossterm = "0.28"
|
||||
atty = "0.2"
|
||||
|
||||
# System
|
||||
libc = "0.2"
|
||||
|
||||
[lib]
|
||||
name = "momentry_core"
|
||||
@@ -81,7 +87,11 @@ path = "src/lib.rs"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
player = []
|
||||
player = ["sdl2"]
|
||||
|
||||
[dependencies.sdl2]
|
||||
version = "0.35"
|
||||
optional = true
|
||||
|
||||
[[bin]]
|
||||
name = "momentry"
|
||||
@@ -94,3 +104,22 @@ path = "src/player/main.rs"
|
||||
[[bin]]
|
||||
name = "momentry_playground"
|
||||
path = "src/playground.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "fix_chunks"
|
||||
path = "src/bin/fix_chunks.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "migrate_chinese_text"
|
||||
path = "src/bin/migrate_chinese_text.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "test_bm25_simple"
|
||||
path = "src/bin/test_bm25_simple.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "integrated_player"
|
||||
path = "src/bin/integrated_player.rs"
|
||||
|
||||
[build-dependencies]
|
||||
chrono = "0.4"
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
use chrono::Local;
|
||||
use std::env;
|
||||
|
||||
fn main() {
|
||||
let now = Local::now();
|
||||
let build_time = now.format("%Y-%m-%d %H:%M:%S").to_string();
|
||||
|
||||
// Get version from Cargo.toml
|
||||
let version = env!("CARGO_PKG_VERSION");
|
||||
let full_version = format!("{} (build: {})", version, build_time);
|
||||
|
||||
// Set build-time environment variables
|
||||
println!("cargo:rustc-env=BUILD_VERSION={}", full_version);
|
||||
println!("cargo:rustc-env=BUILD_TIME={}", build_time);
|
||||
println!("cargo:rustc-env=VERSION={}", version);
|
||||
|
||||
// Also print for debugging
|
||||
println!("cargo:warning=Building version: {}", full_version);
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>com.momentry.api</string>
|
||||
|
||||
<key>UserName</key>
|
||||
<string>accusys</string>
|
||||
|
||||
<key>GroupName</key>
|
||||
<string>staff</string>
|
||||
|
||||
<key>WorkingDirectory</key>
|
||||
<string>/Users/accusys/momentry_core_0.1</string>
|
||||
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>/Users/accusys/momentry_core_0.1/target/release/momentry</string>
|
||||
<string>server</string>
|
||||
<string>--port</string>
|
||||
<string>3002</string>
|
||||
</array>
|
||||
|
||||
<key>EnvironmentVariables</key>
|
||||
<dict>
|
||||
<key>PATH</key>
|
||||
<string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
|
||||
|
||||
<key>DATABASE_URL</key>
|
||||
<string>postgres://accusys@localhost:5432/momentry</string>
|
||||
|
||||
<key>DB_MAX_CONNECTIONS</key>
|
||||
<string>50</string>
|
||||
|
||||
<key>DB_ACQUIRE_TIMEOUT</key>
|
||||
<string>30</string>
|
||||
|
||||
<key>REDIS_URL</key>
|
||||
<string>redis://:accusys@localhost:6379</string>
|
||||
|
||||
<key>REDIS_PASSWORD</key>
|
||||
<string>accusys</string>
|
||||
|
||||
<key>OLLAMA_HOST</key>
|
||||
<string>http://localhost:11434</string>
|
||||
|
||||
<key>QDRANT_URL</key>
|
||||
<string>http://127.0.0.1:6333</string>
|
||||
</dict>
|
||||
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
|
||||
<key>KeepAlive</key>
|
||||
<true/>
|
||||
|
||||
<key>StandardOutPath</key>
|
||||
<string>/Users/accusys/momentry/log/momentry_api.log</string>
|
||||
|
||||
<key>StandardErrorPath</key>
|
||||
<string>/Users/accusys/momentry/log/momentry_api.error.log</string>
|
||||
</dict>
|
||||
</plist>
|
||||
+45
-8
@@ -1,5 +1,23 @@
|
||||
# Momentry Core API 存取指南
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 版本 | V1.3 |
|
||||
| 日期 | 2026-03-25 |
|
||||
| 用途 | API 存取方式、端點與整合指南 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.3 | 2026-03-25 | 更新: n8n 搜尋回傳 `file_path` 取代 `media_url`,新增 API Key 驗證說明 | OpenCode | deepseek-reasoner |
|
||||
| V1.2 | 2026-03-24 | 更新網址與服務列表 | Warren | OpenCode / MiniMax M2.5 |
|
||||
| V1.1 | 2026-03-23 | 初始版本 | Warren | OpenCode / MiniMax M2.5 |
|
||||
|
||||
---
|
||||
|
||||
## 基本網址
|
||||
|
||||
| 環境 | URL | 說明 |
|
||||
@@ -20,7 +38,16 @@
|
||||
- 生產環境
|
||||
|
||||
## 認證
|
||||
目前為開放狀態(示範用途無需認證)。正式環境將實作 API Key。
|
||||
所有 `/api/v1/*` 端點(除了健康檢查 `/health` 與 `/health/detailed`)都需要 API Key 認證。
|
||||
|
||||
請在請求標頭中加入:
|
||||
```
|
||||
X-API-Key: YOUR_API_KEY
|
||||
```
|
||||
|
||||
**目前示範使用的 API Key**: `demo_api_key_12345`
|
||||
|
||||
> **注意**: 正式環境請使用安全的 API Key 管理機制,避免在客戶端暴露 API Key。
|
||||
|
||||
---
|
||||
|
||||
@@ -91,12 +118,14 @@
|
||||
"title": "Chunk sentence_0006",
|
||||
"text": "fun plot twists...",
|
||||
"score": 0.526,
|
||||
"media_url": "https://wp.momentry.ddns.net/Old_Time_Movie_Show_-_Charade_1963.HD.mov"
|
||||
"file_path": "/Users/accusys/momentry/var/sftpgo/data/demo/Old_Time_Movie_Show_-_Charade_1963.HD.mov"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
> **注意**: API 現在返回 `file_path`(檔案系統路徑)而非 `media_url`(網頁 URL)。如需在網頁中播放影片,請將檔案路徑轉換為可訪問的 URL(例如透過 SFTPGo 分享連結)。
|
||||
|
||||
---
|
||||
|
||||
## 影片管理 API
|
||||
@@ -134,7 +163,10 @@
|
||||
```javascript
|
||||
const response = await fetch('http://localhost:3002/api/v1/search', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-API-Key': 'YOUR_API_KEY' // 替換為實際的 API Key
|
||||
},
|
||||
body: JSON.stringify({ query: 'charade', limit: 5 })
|
||||
});
|
||||
const data = await response.json();
|
||||
@@ -149,7 +181,10 @@ curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
|
||||
'query' => 'charade',
|
||||
'limit' => 5
|
||||
]));
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'Content-Type: application/json',
|
||||
'X-API-Key: YOUR_API_KEY' // 替換為實際的 API Key
|
||||
]);
|
||||
$response = curl_exec($ch);
|
||||
$data = json_decode($response, true);
|
||||
```
|
||||
@@ -158,10 +193,12 @@ $data = json_decode($response, true);
|
||||
|
||||
## 影片嵌入網址
|
||||
|
||||
影片可透過 SFTPGo 分享連結存取:
|
||||
```
|
||||
https://wp.momentry.ddns.net/{檔案名稱}
|
||||
```
|
||||
> **重要**: API 現在返回 `file_path`(檔案系統路徑),而非直接可訪問的網址。您需要將檔案路徑轉換為 SFTPGo 分享連結才能嵌入影片。
|
||||
|
||||
**檔案路徑轉換為網址:**
|
||||
- API 返回的 `file_path` 範例:`/Users/accusys/momentry/var/sftpgo/data/demo/video.mp4`
|
||||
- 對應的 SFTPGo 分享連結:`https://wp.momentry.ddns.net/demo/video.mp4`
|
||||
- 轉換方式:移除 `/Users/accusys/momentry/var/sftpgo/data/` 前綴,將剩餘路徑附加到 `https://wp.momentry.ddns.net/`
|
||||
|
||||
**手動建立分享連結:**
|
||||
1. 開啟 SFTPGo Web UI:`http://localhost:8080`
|
||||
|
||||
+105
-11
@@ -2,12 +2,23 @@
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 版本 | V1.2 |
|
||||
| 日期 | 2026-03-23 |
|
||||
| 版本 | V1.4 |
|
||||
| 日期 | 2026-03-26 |
|
||||
| Base URL | `http://localhost:3002` |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.4 | 2026-03-26 | 新增: 任務管理端點 (`/api/v1/jobs`, `/api/v1/jobs/:uuid`),更新註冊端點回應格式 | OpenCode | deepseek-reasoner |
|
||||
| V1.3 | 2026-03-25 | 更新: n8n 搜尋回傳 `file_path` 取代 `media_url`,新增 API Key 驗證說明 | OpenCode | deepseek-reasoner |
|
||||
| V1.2 | 2026-03-23 | 建立 curl 範例文件 | Warren | OpenCode / MiniMax M2.5 |
|
||||
| V1.1 | 2026-03-18 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
|
||||
|
||||
---
|
||||
|
||||
> **狀態說明**:
|
||||
> - ✅ **已實作**: 健康檢查、搜尋、影片管理端點
|
||||
> - ⚠️ **規劃中**: API Key 管理功能
|
||||
@@ -76,6 +87,20 @@ sudo launchctl load /Library/LaunchDaemons/com.momentry.api.plist
|
||||
|
||||
---
|
||||
|
||||
## API 認證
|
||||
|
||||
所有 `/api/v1/*` 端點(除了健康檢查)都需要 API Key 認證。請在請求標頭中加入:
|
||||
|
||||
```
|
||||
-H "X-API-Key: YOUR_API_KEY"
|
||||
```
|
||||
|
||||
**目前示範使用的 API Key**: `demo_api_key_12345`
|
||||
|
||||
> **注意**: 正式環境請使用安全的 API Key 管理機制。
|
||||
|
||||
---
|
||||
|
||||
## 1. 已實作端點
|
||||
|
||||
### 健康檢查
|
||||
@@ -161,6 +186,7 @@ curl -X GET http://localhost:3002/api/v1/api-keys/stats \
|
||||
```bash
|
||||
curl -X POST http://localhost:3002/api/v1/register \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{"path": "/path/to/video.mp4"}'
|
||||
```
|
||||
|
||||
@@ -168,30 +194,31 @@ curl -X POST http://localhost:3002/api/v1/register \
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 1,
|
||||
"uuid": "a1b2c3d4e5f6g7h8",
|
||||
"file_path": "/path/to/video.mp4",
|
||||
"video_id": 1,
|
||||
"job_id": 123,
|
||||
"file_name": "video.mp4",
|
||||
"duration": 120.5,
|
||||
"width": 1920,
|
||||
"height": 1080
|
||||
"height": 1080,
|
||||
"already_exists": false
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 列出所有影片 ✅
|
||||
|
||||
```bash
|
||||
curl http://localhost:3002/api/v1/videos
|
||||
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/videos
|
||||
```
|
||||
|
||||
### 3.3 查詢影片 ✅
|
||||
|
||||
```bash
|
||||
# 依 UUID 查詢
|
||||
curl "http://localhost:3002/api/v1/lookup?uuid=a1b2c3d4e5f6g7h8"
|
||||
curl -H "X-API-Key: YOUR_API_KEY" "http://localhost:3002/api/v1/lookup?uuid=a1b2c3d4e5f6g7h8"
|
||||
|
||||
# 依路徑查詢
|
||||
curl "http://localhost:3002/api/v1/lookup?path=/path/to/video.mp4"
|
||||
curl -H "X-API-Key: YOUR_API_KEY" "http://localhost:3002/api/v1/lookup?path=/path/to/video.mp4"
|
||||
```
|
||||
|
||||
### 3.4 處理影片 🔧 *(CLI - 非 API)*
|
||||
@@ -209,7 +236,7 @@ cargo run --bin momentry -- process <uuid1> <uuid2> <uuid3>
|
||||
### 3.5 取得處理進度 ✅
|
||||
|
||||
```bash
|
||||
curl http://localhost:3002/api/v1/progress/<uuid>
|
||||
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/progress/<uuid>
|
||||
```
|
||||
|
||||
**回應範例**:
|
||||
@@ -247,6 +274,67 @@ curl http://localhost:3002/api/v1/progress/<uuid>
|
||||
}
|
||||
```
|
||||
|
||||
### 3.6 任務管理 ✅
|
||||
|
||||
```bash
|
||||
# 列出所有任務
|
||||
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/jobs
|
||||
|
||||
# 取得特定任務詳情
|
||||
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/jobs/<uuid>
|
||||
```
|
||||
|
||||
**任務列表回應範例**:
|
||||
```json
|
||||
{
|
||||
"jobs": [
|
||||
{
|
||||
"id": 123,
|
||||
"uuid": "a1b2c3d4e5f6g7h8",
|
||||
"status": "pending",
|
||||
"current_processor": null,
|
||||
"progress_current": 0,
|
||||
"progress_total": 100,
|
||||
"created_at": "2026-03-26 10:30:00",
|
||||
"started_at": null
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**任務詳情回應範例**:
|
||||
```json
|
||||
{
|
||||
"id": 123,
|
||||
"uuid": "a1b2c3d4e5f6g7h8",
|
||||
"status": "processing",
|
||||
"current_processor": "asr",
|
||||
"progress_current": 50,
|
||||
"progress_total": 100,
|
||||
"processors": [
|
||||
{
|
||||
"processor_type": "asr",
|
||||
"status": "complete",
|
||||
"started_at": "2026-03-26 10:30:00",
|
||||
"completed_at": "2026-03-26 10:35:00",
|
||||
"duration_secs": 300.5,
|
||||
"error_message": null
|
||||
},
|
||||
{
|
||||
"processor_type": "cut",
|
||||
"status": "pending",
|
||||
"started_at": null,
|
||||
"completed_at": null,
|
||||
"duration_secs": null,
|
||||
"error_message": null
|
||||
}
|
||||
],
|
||||
"created_at": "2026-03-26 10:30:00",
|
||||
"started_at": "2026-03-26 10:30:00",
|
||||
"updated_at": "2026-03-26 10:35:00"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 查詢與搜索
|
||||
@@ -256,6 +344,7 @@ curl http://localhost:3002/api/v1/progress/<uuid>
|
||||
```bash
|
||||
curl -X POST http://localhost:3002/api/v1/search \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{
|
||||
"query": "測試關鍵字",
|
||||
"limit": 5
|
||||
@@ -286,6 +375,7 @@ curl -X POST http://localhost:3002/api/v1/search \
|
||||
```bash
|
||||
curl -X POST http://localhost:3002/api/v1/n8n/search \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{
|
||||
"query": "測試關鍵字",
|
||||
"limit": 5
|
||||
@@ -307,7 +397,7 @@ curl -X POST http://localhost:3002/api/v1/n8n/search \
|
||||
"title": "Chunk sentence_0006",
|
||||
"text": "fun plot twists...",
|
||||
"score": 0.92,
|
||||
"media_url": "https://wp.momentry.ddns.net/video.mp4"
|
||||
"file_path": "/Users/accusys/momentry/var/sftpgo/data/demo/video.mp4"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -318,6 +408,7 @@ curl -X POST http://localhost:3002/api/v1/n8n/search \
|
||||
```bash
|
||||
curl -X POST http://localhost:3002/api/v1/search/hybrid \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{
|
||||
"query": "測試關鍵字",
|
||||
"limit": 5
|
||||
@@ -425,6 +516,8 @@ A: 需要將工作流程切換為 Active 狀態 (右上角開關)
|
||||
| `/api/v1/lookup` | GET | ✅ | 查詢影片 |
|
||||
| `/api/v1/videos` | GET | ✅ | 列出所有影片 |
|
||||
| `/api/v1/progress/:uuid` | GET | ✅ | 處理進度 |
|
||||
| `/api/v1/jobs` | GET | ✅ | 任務列表 |
|
||||
| `/api/v1/jobs/:uuid` | GET | ✅ | 任務詳情 |
|
||||
| `/api/v1/api-keys` | * | ⚠️ | API Key 管理 (規劃中) |
|
||||
|
||||
### C. 常見錯誤
|
||||
@@ -475,11 +568,12 @@ curl -s "$API_URL/health" | jq .
|
||||
echo -e "\n=== Search ==="
|
||||
curl -s -X POST "$API_URL/api/v1/search" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{"query": "test", "limit": 3}' | jq .
|
||||
|
||||
# 列出影片
|
||||
echo -e "\n=== Videos ==="
|
||||
curl -s "$API_URL/api/v1/videos" | jq '.videos | length'
|
||||
curl -s -H "X-API-Key: YOUR_API_KEY" "$API_URL/api/v1/videos" | jq '.videos | length'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
+83
-6
@@ -2,8 +2,20 @@
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 版本 | V1.1 |
|
||||
| 日期 | 2026-03-25 |
|
||||
| 建立者 | Warren |
|
||||
| 建立時間 | 2026-03-18 |
|
||||
| 文件版本 | V1.3 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 |
|
||||
|------|------|------|--------|
|
||||
| V1.0 | 2026-03-18 | 創建文件 | OpenCode |
|
||||
| V1.1 | 2026-03-23 | 更新端點與實際一致 | OpenCode |
|
||||
| V1.2 | 2026-03-25 | 新增快取/刪除 API | OpenCode |
|
||||
| V1.3 | 2026-03-26 | 更新API回應格式 (media_url→file_path) | OpenCode |
|
||||
|
||||
---
|
||||
|
||||
@@ -70,6 +82,7 @@ curl http://localhost:3002/health
|
||||
```bash
|
||||
curl -X POST http://localhost:3002/api/v1/search \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: your-api-key" \
|
||||
-d '{"query": "test", "limit": 10}'
|
||||
```
|
||||
|
||||
@@ -77,6 +90,7 @@ curl -X POST http://localhost:3002/api/v1/search \
|
||||
```bash
|
||||
curl -X POST http://localhost:3002/api/v1/n8n/search \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: your-api-key" \
|
||||
-d '{"query": "test", "limit": 10}'
|
||||
```
|
||||
|
||||
@@ -96,13 +110,29 @@ curl -X POST http://localhost:3002/api/v1/n8n/search \
|
||||
```bash
|
||||
curl -X POST http://localhost:3002/api/v1/register \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: your-api-key" \
|
||||
-d '{"path": "/path/to/video.mp4"}'
|
||||
```
|
||||
|
||||
**註冊回應範例**:
|
||||
```json
|
||||
{
|
||||
"uuid": "a1b10138a6bbb0cd",
|
||||
"video_id": 1,
|
||||
"job_id": 10,
|
||||
"file_name": "video.mp4",
|
||||
"duration": 120.5,
|
||||
"width": 1920,
|
||||
"height": 1080,
|
||||
"already_exists": false
|
||||
}
|
||||
```
|
||||
|
||||
**探測影片** (不註冊,只取得影片資訊):
|
||||
```bash
|
||||
curl -X POST http://localhost:3002/api/v1/probe \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: your-api-key" \
|
||||
-d '{"path": "./demo/video.mp4"}'
|
||||
```
|
||||
|
||||
@@ -139,17 +169,61 @@ curl -X POST http://localhost:3002/api/v1/probe \
|
||||
|
||||
**列出影片**:
|
||||
```bash
|
||||
curl http://localhost:3002/api/v1/videos
|
||||
curl -H "X-API-Key: your-api-key" http://localhost:3002/api/v1/videos
|
||||
```
|
||||
|
||||
**查詢影片**:
|
||||
```bash
|
||||
curl "http://localhost:3002/api/v1/lookup?uuid=5dea6618a606e7c7"
|
||||
curl -H "X-API-Key: your-api-key" "http://localhost:3002/api/v1/lookup?uuid=5dea6618a606e7c7"
|
||||
```
|
||||
|
||||
**處理進度**:
|
||||
```bash
|
||||
curl http://localhost:3002/api/v1/progress/5dea6618a606e7c7
|
||||
curl -H "X-API-Key: your-api-key" http://localhost:3002/api/v1/progress/5dea6618a606e7c7
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 工作管理
|
||||
|
||||
| 方法 | 端點 | 說明 |
|
||||
|------|------|------|
|
||||
| GET | `/api/v1/jobs` | 列出所有工作 |
|
||||
| GET | `/api/v1/jobs/:uuid` | 取得指定工作的詳細資訊 |
|
||||
|
||||
**列出工作**:
|
||||
```bash
|
||||
curl -H "X-API-Key: your-api-key" http://localhost:3002/api/v1/jobs
|
||||
```
|
||||
|
||||
**取得工作詳細資訊**:
|
||||
```bash
|
||||
curl -H "X-API-Key: your-api-key" http://localhost:3002/api/v1/jobs/a03485a40b2df2d3
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 系統管理
|
||||
|
||||
| 方法 | 端點 | 說明 |
|
||||
|------|------|------|
|
||||
| POST | `/api/v1/config/cache` | 切換快取功能(管理員) |
|
||||
| POST | `/api/v1/unregister` | 刪除影片及其所有資料(管理員) |
|
||||
|
||||
**快取設定**:
|
||||
```bash
|
||||
curl -X POST http://localhost:3002/api/v1/config/cache \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: your-api-key" \
|
||||
-d '{"enabled": true}'
|
||||
```
|
||||
|
||||
**刪除影片**:
|
||||
```bash
|
||||
curl -X POST http://localhost:3002/api/v1/unregister \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: your-api-key" \
|
||||
-d '{"uuid": "5dea6618a606e7c7"}'
|
||||
```
|
||||
|
||||
---
|
||||
@@ -165,6 +239,9 @@ curl http://localhost:3002/api/v1/progress/5dea6618a606e7c7
|
||||
| 列出影片 | ✓ | ✓ | ✓ |
|
||||
| 查詢影片 | ✓ | ✓ | ✓ |
|
||||
| 處理進度 | ✓ | ✓ | ✓ |
|
||||
| 工作管理 | ✓ | ✓ | ✓ |
|
||||
| 快取設定 | ✓ (管理員) | ✓ (管理員) | ✓ (管理員) |
|
||||
| 刪除影片 | ✓ (管理員) | ✓ (管理員) | ✓ (管理員) |
|
||||
|
||||
---
|
||||
|
||||
@@ -184,7 +261,7 @@ curl http://localhost:3002/api/v1/progress/5dea6618a606e7c7
|
||||
"title": "Chunk sentence_0001",
|
||||
"text": "...",
|
||||
"score": 0.92,
|
||||
"media_url": "https://wp.momentry.ddns.net/video.mp4"
|
||||
"file_path": "/Users/accusys/momentry/var/sftpgo/data/demo/video.mp4"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
+62
-17
@@ -2,13 +2,22 @@
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 版本 | V2.0 |
|
||||
| 日期 | 2026-03-25 |
|
||||
| 版本 | V2.1 |
|
||||
| 日期 | 2026-03-26 |
|
||||
| Base URL (本地) | `http://localhost:3002` |
|
||||
| Base URL (外部) | `https://api.momentry.ddns.net` |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 |
|
||||
|------|------|------|--------|
|
||||
| V2.0 | 2026-03-25 | 創建完整範例總覽 | OpenCode |
|
||||
| V2.1 | 2026-03-26 | 更新API回應格式 (media_url→file_path) 與認證標頭 | OpenCode |
|
||||
|
||||
---
|
||||
|
||||
## 快速參考
|
||||
|
||||
### 環境 URL 選擇
|
||||
@@ -105,16 +114,19 @@ curl http://localhost:3002/health/detailed
|
||||
# 標準格式搜尋
|
||||
curl -X POST http://localhost:3002/api/v1/search \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{"query": "charade", "limit": 5}'
|
||||
|
||||
# n8n 格式搜尋(推薦)
|
||||
curl -X POST http://localhost:3002/api/v1/n8n/search \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{"query": "charade", "limit": 5}'
|
||||
|
||||
# 混合搜尋
|
||||
curl -X POST http://localhost:3002/api/v1/search/hybrid \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{"query": "charade", "limit": 5}'
|
||||
```
|
||||
|
||||
@@ -150,7 +162,7 @@ curl -X POST http://localhost:3002/api/v1/search/hybrid \
|
||||
"title": "Chunk sentence_0001",
|
||||
"text": "fun plot twists...",
|
||||
"score": 0.92,
|
||||
"media_url": "https://wp.momentry.ddns.net/video.mp4"
|
||||
"file_path": "/Users/accusys/momentry/var/sftpgo/data/demo/video.mp4"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -160,26 +172,28 @@ curl -X POST http://localhost:3002/api/v1/search/hybrid \
|
||||
|
||||
```bash
|
||||
# 列出所有影片
|
||||
curl http://localhost:3002/api/v1/videos
|
||||
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/videos
|
||||
|
||||
# 查詢特定影片(依 UUID)
|
||||
curl "http://localhost:3002/api/v1/lookup?uuid=a1b10138a6bbb0cd"
|
||||
curl -H "X-API-Key: YOUR_API_KEY" "http://localhost:3002/api/v1/lookup?uuid=a1b10138a6bbb0cd"
|
||||
|
||||
# 查詢特定影片(依路徑)
|
||||
curl "http://localhost:3002/api/v1/lookup?path=/path/to/video.mp4"
|
||||
curl -H "X-API-Key: YOUR_API_KEY" "http://localhost:3002/api/v1/lookup?path=/path/to/video.mp4"
|
||||
|
||||
# 取得處理進度
|
||||
curl http://localhost:3002/api/v1/progress/a1b10138a6bbb0cd
|
||||
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/progress/a1b10138a6bbb0cd
|
||||
|
||||
# 探測影片(不註冊)
|
||||
curl -X POST http://localhost:3002/api/v1/probe \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{"path": "/path/to/video.mp4"}'
|
||||
|
||||
# 註冊影片
|
||||
curl -X POST http://localhost:3002/api/v1/register \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"path": "/path/to/video.mp4", "file_name": "video.mp4"}'
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{"path": "/path/to/video.mp4"}'
|
||||
```
|
||||
|
||||
### 1.4 批次測試腳本
|
||||
@@ -196,10 +210,11 @@ curl -s "$API_URL/health" | jq .
|
||||
echo -e "\n=== 語意搜尋 ==="
|
||||
curl -s -X POST "$API_URL/api/v1/search" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{"query": "charade", "limit": 3}' | jq .
|
||||
|
||||
echo -e "\n=== 影片列表 ==="
|
||||
curl -s "$API_URL/api/v1/videos" | jq '.videos | length'
|
||||
curl -s -H "X-API-Key: YOUR_API_KEY" "$API_URL/api/v1/videos" | jq '.videos | length'
|
||||
```
|
||||
|
||||
### 1.5 外部 URL 範例
|
||||
@@ -211,6 +226,7 @@ curl https://api.momentry.ddns.net/health
|
||||
# 外部搜尋
|
||||
curl -X POST https://api.momentry.ddns.net/api/v1/n8n/search \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{"query": "charade", "limit": 5}'
|
||||
```
|
||||
|
||||
@@ -227,11 +243,14 @@ Node: HTTP Request
|
||||
├── Authentication: None
|
||||
├── Send Body: ✓ (checked)
|
||||
├── Content Type: JSON
|
||||
└── Body:
|
||||
{
|
||||
"query": "={{ $json.query }}",
|
||||
"limit": "={{ $json.limit || 10 }}"
|
||||
}
|
||||
├── Body:
|
||||
│ {
|
||||
│ "query": "={{ $json.query }}",
|
||||
│ "limit": "={{ $json.limit || 10 }}"
|
||||
│ }
|
||||
├── Send Headers: ✓ (checked)
|
||||
└── Header Parameters:
|
||||
└── X-API-Key: {{ $env.MOMENTRY_API_KEY }}
|
||||
```
|
||||
|
||||
### 2.2 基本搜尋 Workflow
|
||||
@@ -460,6 +479,24 @@ searchVideos('charade', 5)
|
||||
|
||||
```php
|
||||
<?php
|
||||
// 將文件路徑轉換為可訪問的 URL
|
||||
function convert_file_path_to_url($file_path) {
|
||||
// 範例: 將 SFTPGo 文件路徑轉換為 web URL
|
||||
// /Users/accusys/momentry/var/sftpgo/data/demo/video.mp4
|
||||
// → https://sftpgo.example.com/demo/video.mp4
|
||||
|
||||
// 移除基本路徑
|
||||
$base_path = '/Users/accusys/momentry/var/sftpgo/data/';
|
||||
if (strpos($file_path, $base_path) === 0) {
|
||||
$relative_path = substr($file_path, strlen($base_path));
|
||||
// 替換為實際的 SFTPGo web URL
|
||||
return 'https://sftpgo.example.com/' . $relative_path;
|
||||
}
|
||||
|
||||
// 如果無法轉換,返回原始路徑
|
||||
return $file_path;
|
||||
}
|
||||
|
||||
// 註冊短碼
|
||||
add_shortcode('momentry_search', function($atts) {
|
||||
$atts = shortcode_atts([
|
||||
@@ -472,7 +509,10 @@ add_shortcode('momentry_search', function($atts) {
|
||||
}
|
||||
|
||||
$response = wp_remote_post('https://api.momentry.ddns.net/api/v1/n8n/search', [
|
||||
'headers' => ['Content-Type' => 'application/json'],
|
||||
'headers' => [
|
||||
'Content-Type' => 'application/json',
|
||||
'X-API-Key' => 'YOUR_API_KEY' // 替換為實際的 API Key
|
||||
],
|
||||
'body' => json_encode([
|
||||
'query' => $atts['query'],
|
||||
'limit' => (int)$atts['limit']
|
||||
@@ -492,10 +532,15 @@ add_shortcode('momentry_search', function($atts) {
|
||||
|
||||
$output = '<ul class="momentry-results">';
|
||||
foreach ($data['hits'] as $hit) {
|
||||
// 注意: API 現在返回 file_path 而非 media_url
|
||||
// 需要將文件路徑轉換為可訪問的 URL
|
||||
$file_path = $hit['file_path'];
|
||||
$video_url = convert_file_path_to_url($file_path); // 需要實作此函數
|
||||
|
||||
$output .= sprintf(
|
||||
'<li>%s <a href="%s?start=%s">播放</a></li>',
|
||||
esc_html($hit['text']),
|
||||
$hit['media_url'],
|
||||
$video_url,
|
||||
$hit['start']
|
||||
);
|
||||
}
|
||||
@@ -569,7 +614,7 @@ Body: {"query": "charade", "limit": 5}
|
||||
"title": "Chunk sentence_0001",
|
||||
"text": "fun plot twists...",
|
||||
"score": 0.92,
|
||||
"media_url": "https://wp.momentry.ddns.net/video.mp4"
|
||||
"file_path": "/Users/accusys/momentry/var/sftpgo/data/demo/video.mp4"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
+20
-3
@@ -2,8 +2,19 @@
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 版本 | V2.1 |
|
||||
| 日期 | 2026-03-25 |
|
||||
| 建立者 | OpenCode |
|
||||
| 建立時間 | 2026-03-25 |
|
||||
| 文件版本 | V2.2 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V2.0 | 2026-03-22 | 創建 API 文件總覽 | Warren | OpenCode |
|
||||
| V2.1 | 2026-03-24 | 新增文件分類與快速選擇指南 | OpenCode | deepseek-reasoner |
|
||||
| V2.2 | 2026-03-25 | 更新 API Key 驗證說明與文件連結 | OpenCode | deepseek-reasoner |
|
||||
|
||||
---
|
||||
|
||||
@@ -14,11 +25,15 @@ docs/
|
||||
├── API_INDEX.md ← 本文件:總覽與入口
|
||||
├── API_ENDPOINTS.md ← API 端點完整說明
|
||||
├── API_EXAMPLES.md ← 完整範例總覽(curl / n8n / WordPress)
|
||||
├── API_REFERENCE.md ← 詳細技術參考
|
||||
├── DEMO_MANUAL.md ← ⭐ 示範手冊(含 Demo API Key)
|
||||
├── API_N8N_GUIDE.md ← n8n 詳細指南
|
||||
├── API_WORDPRESS_GUIDE.md ← WordPress 詳細指南
|
||||
├── API_CURL_EXAMPLES.md ← curl 快速範例
|
||||
└── API_REFERENCE.md ← 詳細技術參考
|
||||
│
|
||||
├── API_TRAINING_MARCOM.md ← ⭐ marcom 團隊教育訓練手冊
|
||||
├── API_WORKFLOW_WORDPRESS_N8N.md ← WordPress/n8n 完整工作流程
|
||||
└── CHUNK_DATA_STRUCTURE.md ← Chunk 資料結構說明
|
||||
```
|
||||
|
||||
---
|
||||
@@ -29,7 +44,9 @@ docs/
|
||||
|------|----------|
|
||||
| **我要快速開始測試** | ⭐ [DEMO_MANUAL.md](./DEMO_MANUAL.md) |
|
||||
| **我要查看所有範例** | [API_EXAMPLES.md](./API_EXAMPLES.md) |
|
||||
| **我是 marcom 團隊** | ⭐ [API_TRAINING_MARCOM.md](./API_TRAINING_MARCOM.md) |
|
||||
| 我想了解有哪些 API 端點 | [API_ENDPOINTS.md](./API_ENDPOINTS.md) |
|
||||
| 我要整合 WordPress/n8n | [API_WORKFLOW_WORDPRESS_N8N.md](./API_WORKFLOW_WORDPRESS_N8N.md) |
|
||||
| 我要在 n8n workflow 中呼叫 API | [DEMO_MANUAL.md](./DEMO_MANUAL.md#2-n8n-範例) |
|
||||
| 我要在 WordPress 中呼叫 API | [DEMO_MANUAL.md](./DEMO_MANUAL.md#3-wordpress-範例) |
|
||||
| 我要用 curl 快速測試 | [DEMO_MANUAL.md](./DEMO_MANUAL.md#1-curl-範例) |
|
||||
|
||||
@@ -2,9 +2,23 @@
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 版本 | V1.2 |
|
||||
| 日期 | 2026-03-21 |
|
||||
| 狀態 | 開發中 |
|
||||
| 建立者 | Warren |
|
||||
| 建立時間 | 2026-03-21 |
|
||||
| 文件版本 | V1.2 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-03-18 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
|
||||
| V1.1 | 2026-03-20 | 新增 Key 類型與管理流程 | Warren | OpenCode |
|
||||
| V1.2 | 2026-03-21 | 更新 API Key 格式與驗證流程 | Warren | OpenCode |
|
||||
|
||||
---
|
||||
|
||||
**狀態**: 開發中
|
||||
|
||||
---
|
||||
|
||||
|
||||
+43
-14
@@ -2,9 +2,22 @@
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 版本 | V1.0 |
|
||||
| 日期 | 2026-03-23 |
|
||||
| 用途 | 在 n8n workflow 中呼叫 Momentry API |
|
||||
| 建立者 | Warren |
|
||||
| 建立時間 | 2026-03-23 |
|
||||
| 文件版本 | V1.1 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-03-23 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
|
||||
| V1.1 | 2026-03-26 | 新增 API Key 驗證說明,更新 HTTP Request Node 設定 | OpenCode | deepseek-reasoner |
|
||||
|
||||
---
|
||||
|
||||
**用途**: 在 n8n workflow 中呼叫 Momentry API
|
||||
|
||||
---
|
||||
|
||||
@@ -29,6 +42,8 @@ https://api.momentry.ddns.net
|
||||
| GET | `/api/v1/videos` | 列出所有影片 |
|
||||
| GET | `/api/v1/lookup` | 查詢影片 |
|
||||
| GET | `/api/v1/progress/:uuid` | 處理進度 |
|
||||
| GET | `/api/v1/jobs` | 任務列表 |
|
||||
| GET | `/api/v1/jobs/:uuid` | 任務詳情 |
|
||||
|
||||
---
|
||||
|
||||
@@ -43,11 +58,14 @@ Node: HTTP Request
|
||||
├── Authentication: None
|
||||
├── Send Body: ✓ (checked)
|
||||
├── Content Type: JSON
|
||||
└── Body:
|
||||
{
|
||||
"query": "={{ $json.query }}",
|
||||
"limit": "={{ $json.limit || 10 }}"
|
||||
}
|
||||
├── Body:
|
||||
│ {
|
||||
│ "query": "={{ $json.query }}",
|
||||
│ "limit": "={{ $json.limit || 10 }}"
|
||||
│ }
|
||||
├── Send Headers: ✓ (checked)
|
||||
└── Header Parameters:
|
||||
└── X-API-Key: {{ $env.MOMENTRY_API_KEY }}
|
||||
```
|
||||
|
||||
### 測試用(固定關鍵字)
|
||||
@@ -58,11 +76,14 @@ Node: HTTP Request
|
||||
├── Method: POST
|
||||
├── Send Body: ✓
|
||||
├── Content Type: JSON
|
||||
└── Body:
|
||||
{
|
||||
"query": "charade",
|
||||
"limit": 3
|
||||
}
|
||||
├── Body:
|
||||
│ {
|
||||
│ "query": "charade",
|
||||
│ "limit": 3
|
||||
│ }
|
||||
├── Send Headers: ✓ (checked)
|
||||
└── Header Parameters:
|
||||
└── X-API-Key: {{ $env.MOMENTRY_API_KEY }}
|
||||
```
|
||||
|
||||
---
|
||||
@@ -174,13 +195,21 @@ sudo launchctl load /Library/LaunchDaemons/com.momentry.api.plist
|
||||
|
||||
在終端機中測試 API:
|
||||
|
||||
> **注意**: 所有 `/api/v1/*` 端點都需要 API Key 驗證。請設定環境變數或直接替換 API Key。
|
||||
|
||||
```bash
|
||||
# 設定環境變數(使用您的 API Key)
|
||||
export MOMENTRY_API_KEY="muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69"
|
||||
```
|
||||
|
||||
```bash
|
||||
# 健康檢查
|
||||
curl https://api.momentry.ddns.net/health
|
||||
|
||||
# 搜尋測試
|
||||
# 搜尋測試 (需要 API Key)
|
||||
curl -X POST https://api.momentry.ddns.net/api/v1/n8n/search \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: $MOMENTRY_API_KEY" \
|
||||
-d '{"query":"charade","limit":3}'
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,532 @@
|
||||
# Momentry Core API 快速查詢表
|
||||
|
||||
| 版本 | 日期 | 建立者 |
|
||||
|------|------|--------|
|
||||
| V1.0 | 2026-03-26 | OpenCode |
|
||||
|
||||
---
|
||||
|
||||
## 📋 快速導覽
|
||||
|
||||
| 類別 | 端點數量 | 說明 |
|
||||
|------|----------|------|
|
||||
| 健康檢查 | 2 | 系統狀態監控 |
|
||||
| 影片管理 | 5 | 影片註冊、查詢、刪除 |
|
||||
| 搜尋功能 | 3 | 語意搜尋、混合搜尋 |
|
||||
| 任務管理 | 2 | 處理任務狀態查詢 |
|
||||
| 系統管理 | 2 | 快取設定、進度查詢 |
|
||||
|
||||
---
|
||||
|
||||
## 🔐 認證
|
||||
|
||||
所有 `/api/v1/*` 端點需要 `X-API-Key` header:
|
||||
|
||||
```bash
|
||||
curl -H "X-API-Key: YOUR_API_KEY" ...
|
||||
```
|
||||
|
||||
**公開端點(無需認證):**
|
||||
- `GET /health`
|
||||
- `GET /health/detailed`
|
||||
|
||||
---
|
||||
|
||||
## 📊 端點總表
|
||||
|
||||
### 健康檢查
|
||||
|
||||
| 方法 | 端點 | 認證 | 描述 |
|
||||
|------|------|------|------|
|
||||
| GET | `/health` | 公開 | 基本健康檢查 |
|
||||
| GET | `/health/detailed` | 公開 | 詳細健康檢查(包含所有服務狀態) |
|
||||
|
||||
### 影片管理
|
||||
|
||||
| 方法 | 端點 | 認證 | 描述 |
|
||||
|------|------|------|------|
|
||||
| POST | `/api/v1/register` | 需要 | 註冊影片並開始處理 |
|
||||
| POST | `/api/v1/unregister` | 需要 | 刪除影片及其所有資料 |
|
||||
| POST | `/api/v1/probe` | 需要 | 探測影片資訊(不註冊) |
|
||||
| GET | `/api/v1/videos` | 需要 | 列出所有已註冊影片 |
|
||||
| GET | `/api/v1/lookup` | 需要 | 查詢影片資訊 |
|
||||
|
||||
### 搜尋功能
|
||||
|
||||
| 方法 | 端點 | 認證 | 描述 |
|
||||
|------|------|------|------|
|
||||
| POST | `/api/v1/search` | 需要 | 語意搜尋(標準格式) |
|
||||
| POST | `/api/v1/n8n/search` | 需要 | 語意搜尋(n8n 格式) |
|
||||
| POST | `/api/v1/search/hybrid` | 需要 | 混合搜尋(向量 + 關鍵字) |
|
||||
|
||||
### 任務管理
|
||||
|
||||
| 方法 | 端點 | 認證 | 描述 |
|
||||
|------|------|------|------|
|
||||
| GET | `/api/v1/jobs` | 需要 | 列出所有處理任務 |
|
||||
| GET | `/api/v1/jobs/:uuid` | 需要 | 取得特定任務詳情 |
|
||||
|
||||
### 系統管理
|
||||
|
||||
| 方法 | 端點 | 認證 | 描述 |
|
||||
|------|------|------|------|
|
||||
| GET | `/api/v1/progress/:uuid` | 需要 | 取得影片處理進度 |
|
||||
| POST | `/api/v1/config/cache` | 需要 | 切換快取功能 |
|
||||
|
||||
---
|
||||
|
||||
## 🔧 詳細端點說明
|
||||
|
||||
### 1. 健康檢查
|
||||
|
||||
#### GET /health
|
||||
**基本健康檢查**
|
||||
```bash
|
||||
curl http://localhost:3002/health
|
||||
```
|
||||
|
||||
**回應:**
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"version": "0.1.0",
|
||||
"uptime_ms": 14426558
|
||||
}
|
||||
```
|
||||
|
||||
#### GET /health/detailed
|
||||
**詳細健康檢查**
|
||||
```bash
|
||||
curl http://localhost:3002/health/detailed
|
||||
```
|
||||
|
||||
**回應:**
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"version": "0.1.0",
|
||||
"uptime_ms": 14441362,
|
||||
"services": {
|
||||
"postgres": {"status": "ok", "latency_ms": 50, "error": null},
|
||||
"redis": {"status": "ok", "latency_ms": 0, "error": null},
|
||||
"qdrant": {"status": "ok", "latency_ms": 2, "error": null},
|
||||
"mongodb": {"status": "ok", "latency_ms": 2, "error": null}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 影片管理
|
||||
|
||||
#### POST /api/v1/register
|
||||
**註冊影片並開始處理**
|
||||
```bash
|
||||
curl -X POST http://localhost:3002/api/v1/register \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{"path": "/path/to/video.mp4"}'
|
||||
```
|
||||
|
||||
**請求:**
|
||||
```json
|
||||
{
|
||||
"path": "/path/to/video.mp4"
|
||||
}
|
||||
```
|
||||
|
||||
**回應:**
|
||||
```json
|
||||
{
|
||||
"uuid": "5dea6618a606e7c7",
|
||||
"video_id": 10,
|
||||
"job_id": 1,
|
||||
"file_name": "video.mp4",
|
||||
"duration": 596.458333,
|
||||
"width": 320,
|
||||
"height": 180,
|
||||
"already_exists": false
|
||||
}
|
||||
```
|
||||
|
||||
#### POST /api/v1/unregister
|
||||
**刪除影片及其所有資料**
|
||||
```bash
|
||||
curl -X POST http://localhost:3002/api/v1/unregister \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{"uuid": "5dea6618a606e7c7"}'
|
||||
```
|
||||
|
||||
**請求:**
|
||||
```json
|
||||
{
|
||||
"uuid": "5dea6618a606e7c7"
|
||||
}
|
||||
```
|
||||
|
||||
**回應:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"uuid": "5dea6618a606e7c7",
|
||||
"message": "Video unregistered successfully"
|
||||
}
|
||||
```
|
||||
|
||||
#### POST /api/v1/probe
|
||||
**探測影片資訊(不註冊)**
|
||||
```bash
|
||||
curl -X POST http://localhost:3002/api/v1/probe \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{"path": "/path/to/video.mp4"}'
|
||||
```
|
||||
|
||||
**請求:**
|
||||
```json
|
||||
{
|
||||
"path": "/path/to/video.mp4"
|
||||
}
|
||||
```
|
||||
|
||||
**回應:**
|
||||
```json
|
||||
{
|
||||
"uuid": "5dea6618a606e7c7",
|
||||
"file_name": "video.mp4",
|
||||
"duration": 596.458333,
|
||||
"width": 320,
|
||||
"height": 180,
|
||||
"fps": 24.0,
|
||||
"cached": true,
|
||||
"format": {...},
|
||||
"streams": [...]
|
||||
}
|
||||
```
|
||||
|
||||
#### GET /api/v1/videos
|
||||
**列出所有已註冊影片**
|
||||
```bash
|
||||
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/videos
|
||||
```
|
||||
|
||||
**回應:**
|
||||
```json
|
||||
{
|
||||
"videos": [
|
||||
{
|
||||
"uuid": "a03485a40b2df2d3",
|
||||
"file_path": "/path/to/video.mp4",
|
||||
"file_name": "video.mp4",
|
||||
"duration": 596.458333,
|
||||
"width": 320,
|
||||
"height": 180
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### GET /api/v1/lookup
|
||||
**查詢影片資訊**
|
||||
```bash
|
||||
# 依 UUID 查詢
|
||||
curl -H "X-API-Key: YOUR_API_KEY" "http://localhost:3002/api/v1/lookup?uuid=a03485a40b2df2d3"
|
||||
|
||||
# 依路徑查詢
|
||||
curl -H "X-API-Key: YOUR_API_KEY" "http://localhost:3002/api/v1/lookup?path=/path/to/video.mp4"
|
||||
```
|
||||
|
||||
**回應:**
|
||||
```json
|
||||
{
|
||||
"uuid": "a03485a40b2df2d3",
|
||||
"file_path": "/path/to/video.mp4",
|
||||
"file_name": "video.mp4",
|
||||
"duration": 596.458333
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 搜尋功能
|
||||
|
||||
#### POST /api/v1/search
|
||||
**語意搜尋(標準格式)**
|
||||
```bash
|
||||
curl -X POST http://localhost:3002/api/v1/search \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{"query": "search term", "limit": 5}'
|
||||
```
|
||||
|
||||
**請求:**
|
||||
```json
|
||||
{
|
||||
"query": "search term",
|
||||
"limit": 5
|
||||
}
|
||||
```
|
||||
|
||||
**回應:**
|
||||
```json
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"uuid": "a1b10138a6bbb0cd",
|
||||
"chunk_id": "sentence_0001",
|
||||
"chunk_type": "sentence",
|
||||
"start_time": 10.5,
|
||||
"end_time": 15.2,
|
||||
"text": "Found text matching query",
|
||||
"score": 0.85
|
||||
}
|
||||
],
|
||||
"query": "search term"
|
||||
}
|
||||
```
|
||||
|
||||
#### POST /api/v1/n8n/search
|
||||
**語意搜尋(n8n 格式)**
|
||||
```bash
|
||||
curl -X POST http://localhost:3002/api/v1/n8n/search \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{"query": "search term", "limit": 5}'
|
||||
```
|
||||
|
||||
**回應:**
|
||||
```json
|
||||
{
|
||||
"query": "search term",
|
||||
"count": 1,
|
||||
"hits": [
|
||||
{
|
||||
"id": "sentence_0001",
|
||||
"vid": "a1b10138a6bbb0cd",
|
||||
"start_time": 10.5,
|
||||
"end_time": 15.2,
|
||||
"title": "Chunk sentence_0001",
|
||||
"text": "Found text matching query",
|
||||
"score": 0.85,
|
||||
"file_path": "/path/to/video.mp4"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### POST /api/v1/search/hybrid
|
||||
**混合搜尋(向量 + 關鍵字)**
|
||||
```bash
|
||||
curl -X POST http://localhost:3002/api/v1/search/hybrid \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{"query": "search term", "limit": 5}'
|
||||
```
|
||||
|
||||
**請求:**
|
||||
```json
|
||||
{
|
||||
"query": "search term",
|
||||
"limit": 5
|
||||
}
|
||||
```
|
||||
|
||||
**回應:** 與 `/api/v1/search` 相同格式
|
||||
|
||||
### 4. 任務管理
|
||||
|
||||
#### GET /api/v1/jobs
|
||||
**列出所有處理任務**
|
||||
```bash
|
||||
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/jobs
|
||||
```
|
||||
|
||||
**回應:**
|
||||
```json
|
||||
{
|
||||
"jobs": [
|
||||
{
|
||||
"id": 10,
|
||||
"uuid": "a03485a40b2df2d3",
|
||||
"status": "running",
|
||||
"current_processor": null,
|
||||
"progress_current": 0,
|
||||
"progress_total": 0,
|
||||
"created_at": "2026-03-26 13:39:37.830465",
|
||||
"started_at": null
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### GET /api/v1/jobs/:uuid
|
||||
**取得特定任務詳情**
|
||||
```bash
|
||||
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/jobs/a03485a40b2df2d3
|
||||
```
|
||||
|
||||
**回應:**
|
||||
```json
|
||||
{
|
||||
"id": 10,
|
||||
"uuid": "a03485a40b2df2d3",
|
||||
"status": "running",
|
||||
"current_processor": null,
|
||||
"progress_current": 0,
|
||||
"progress_total": 0,
|
||||
"processors": [
|
||||
{
|
||||
"processor_type": "asr",
|
||||
"status": "completed",
|
||||
"started_at": "2026-03-26 05:39:40.275468",
|
||||
"completed_at": "2026-03-26 07:19:43.166613",
|
||||
"duration_secs": 6002.891145,
|
||||
"error_message": null
|
||||
},
|
||||
// ... 其他處理器
|
||||
],
|
||||
"created_at": "2026-03-26 13:39:37.830465",
|
||||
"started_at": null,
|
||||
"updated_at": "2026-03-26 07:19:16.338406"
|
||||
}
|
||||
```
|
||||
|
||||
### 5. 系統管理
|
||||
|
||||
#### GET /api/v1/progress/:uuid
|
||||
**取得影片處理進度**
|
||||
```bash
|
||||
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/progress/a03485a40b2df2d3
|
||||
```
|
||||
|
||||
**回應:**
|
||||
```json
|
||||
{
|
||||
"uuid": "a03485a40b2df2d3",
|
||||
"user": null,
|
||||
"group": null,
|
||||
"file_name": "video.mp4",
|
||||
"duration": 596.458333,
|
||||
"overall_progress": 0,
|
||||
"cpu_percent": 0.2,
|
||||
"gpu_percent": null,
|
||||
"memory_percent": 0.1,
|
||||
"memory_mb": 16720,
|
||||
"processors": [
|
||||
{
|
||||
"name": "asr",
|
||||
"status": "pending",
|
||||
"current": 0,
|
||||
"total": 0,
|
||||
"progress": 0,
|
||||
"message": ""
|
||||
},
|
||||
// ... 其他處理器
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### POST /api/v1/config/cache
|
||||
**切換快取功能**
|
||||
```bash
|
||||
curl -X POST http://localhost:3002/api/v1/config/cache \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{"enabled": true}'
|
||||
```
|
||||
|
||||
**請求:**
|
||||
```json
|
||||
{
|
||||
"enabled": true
|
||||
}
|
||||
```
|
||||
|
||||
**回應:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"cache_enabled": true,
|
||||
"message": "Cache enabled"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 快速工作流程
|
||||
|
||||
### 1. 註冊並處理影片
|
||||
```bash
|
||||
# 1. 註冊影片
|
||||
curl -X POST http://localhost:3002/api/v1/register \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{"path": "/path/to/video.mp4"}'
|
||||
|
||||
# 回應包含 UUID: 5dea6618a606e7c7
|
||||
|
||||
# 2. 監控進度
|
||||
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/progress/5dea6618a606e7c7
|
||||
|
||||
# 3. 查看任務狀態
|
||||
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/jobs/5dea6618a606e7c7
|
||||
```
|
||||
|
||||
### 2. 搜尋影片內容
|
||||
```bash
|
||||
# 1. 列出所有影片
|
||||
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/videos
|
||||
|
||||
# 2. 搜尋內容
|
||||
curl -X POST http://localhost:3002/api/v1/n8n/search \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{"query": "charade scene", "limit": 10}'
|
||||
```
|
||||
|
||||
### 3. 系統管理
|
||||
```bash
|
||||
# 1. 檢查系統健康
|
||||
curl http://localhost:3002/health/detailed
|
||||
|
||||
# 2. 管理快取
|
||||
curl -X POST http://localhost:3002/api/v1/config/cache \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{"enabled": false}'
|
||||
|
||||
# 3. 刪除影片(需要時)
|
||||
curl -X POST http://localhost:3002/api/v1/unregister \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{"uuid": "5dea6618a606e7c7"}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 注意事項
|
||||
|
||||
1. **API Key 格式:**
|
||||
- 使用完整 API Key:`muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69`
|
||||
- 系統存儲的是 SHA256 哈希值
|
||||
|
||||
2. **路徑格式:**
|
||||
- 絕對路徑:`/Users/accusys/test_video/video.mp4`
|
||||
- 相對路徑:`./demo/video.mp4`(相對於 SFTPGo 資料目錄)
|
||||
|
||||
3. **回應時間:**
|
||||
- 健康檢查:< 100ms
|
||||
- 搜尋:取決於查詢複雜度,通常 100-500ms
|
||||
- 影片註冊:立即返回,背景處理可能需要數分鐘到數小時
|
||||
|
||||
4. **錯誤處理:**
|
||||
- 401: 認證失敗
|
||||
- 404: 資源不存在
|
||||
- 500: 伺服器內部錯誤
|
||||
|
||||
---
|
||||
|
||||
## 🔗 相關文件
|
||||
|
||||
- [API 參考指南](./API_REFERENCE.md) - 詳細 API 說明
|
||||
- [API 範例總覽](./API_EXAMPLES.md) - 完整使用範例
|
||||
- [API 端點列表](./API_ENDPOINTS.md) - 端點簡介
|
||||
- [Curl 範例指南](./API_CURL_EXAMPLES.md) - curl 命令範例
|
||||
- [n8n 整合指南](./API_N8N_GUIDE.md) - n8n 工作流程整合
|
||||
+90
-9
@@ -4,7 +4,7 @@
|
||||
|------|------|
|
||||
| 建立者 | Warren |
|
||||
| 建立時間 | 2026-03-18 |
|
||||
| 文件版本 | V1.0 |
|
||||
| 文件版本 | V1.3 |
|
||||
|
||||
---
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-03-18 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
|
||||
| V1.1 | 2026-03-23 | 更新端點與實際一致 | OpenCode | - |
|
||||
| V1.2 | 2026-03-25 | 新增快取/刪除 API | OpenCode | - |
|
||||
| V1.3 | 2026-03-26 | 修正認證聲明與API回應格式 | OpenCode | - |
|
||||
|
||||
---
|
||||
|
||||
@@ -37,7 +39,22 @@
|
||||
|
||||
## Authentication
|
||||
|
||||
Currently no authentication is required.
|
||||
**API Key 認證:**
|
||||
|
||||
所有 `/api/v1/*` 端點需要 `X-API-Key` header 進行認證。
|
||||
|
||||
**公開端點:**
|
||||
- `GET /health` - 健康檢查
|
||||
- `GET /health/detailed` - 詳細健康檢查
|
||||
|
||||
**認證格式:**
|
||||
```bash
|
||||
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/videos
|
||||
```
|
||||
|
||||
**API Key 管理:**
|
||||
- 使用 `/api/v1/api-keys` 端點管理 API Keys
|
||||
- 詳細說明請參考 [API Key Management Guide](../docs/API_KEY_MANAGEMENT.md)
|
||||
|
||||
---
|
||||
|
||||
@@ -64,10 +81,12 @@ Register a video file to the system.
|
||||
{
|
||||
"uuid": "5dea6618a606e7c7",
|
||||
"video_id": 1,
|
||||
"job_id": 10,
|
||||
"file_name": "video.mp4",
|
||||
"duration": 120.5,
|
||||
"width": 1920,
|
||||
"height": 1080
|
||||
"height": 1080,
|
||||
"already_exists": false
|
||||
}
|
||||
```
|
||||
|
||||
@@ -75,6 +94,7 @@ Register a video file to the system.
|
||||
```bash
|
||||
curl -X POST http://localhost:3002/api/v1/register \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{"path": "/Users/accusys/test_video/BigBuckBunny_320x180.mp4"}'
|
||||
```
|
||||
|
||||
@@ -151,7 +171,7 @@ Get real-time processing progress via Redis.
|
||||
**Example:**
|
||||
```bash
|
||||
# Get progress for specific video
|
||||
curl http://localhost:3002/api/v1/progress/5dea6618a606e7c7
|
||||
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/progress/5dea6618a606e7c7
|
||||
```
|
||||
|
||||
---
|
||||
@@ -198,6 +218,7 @@ Search video chunks using natural language queries (RAG).
|
||||
```bash
|
||||
curl -X POST http://localhost:3002/api/v1/search \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{"query": "machine learning", "limit": 5}'
|
||||
```
|
||||
|
||||
@@ -237,7 +258,7 @@ N8n-compatible search endpoint with standardized response format for direct work
|
||||
"title": "Sunset Scene",
|
||||
"text": "The sun slowly sets over the ocean...",
|
||||
"score": 0.92,
|
||||
"media_url": "https://wp.momentry.ddns.net/video.mp4"
|
||||
"file_path": "/Users/accusys/momentry/var/sftpgo/data/demo/video.mp4"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -254,12 +275,13 @@ N8n-compatible search endpoint with standardized response format for direct work
|
||||
| `hits[].title` | string | Chunk title (from metadata or auto-generated) |
|
||||
| `hits[].text` | string | Text content |
|
||||
| `hits[].score` | number | Relevance score (0-1) |
|
||||
| `hits[].media_url` | string | Full media URL (optional) |
|
||||
| `hits[].file_path` | string | Full file path to video file |
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl -X POST http://localhost:3002/api/v1/n8n/search \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{"query": "sunset", "limit": 5}'
|
||||
```
|
||||
|
||||
@@ -295,10 +317,10 @@ Lookup video UUID by path or get video details by UUID.
|
||||
**Example:**
|
||||
```bash
|
||||
# Lookup by path
|
||||
curl "http://localhost:3002/api/v1/lookup?path=/path/to/video.mp4"
|
||||
curl -H "X-API-Key: YOUR_API_KEY" "http://localhost:3002/api/v1/lookup?path=/path/to/video.mp4"
|
||||
|
||||
# Lookup by UUID
|
||||
curl "http://localhost:3002/api/v1/lookup?uuid=5dea6618a606e7c7"
|
||||
curl -H "X-API-Key: YOUR_API_KEY" "http://localhost:3002/api/v1/lookup?uuid=5dea6618a606e7c7"
|
||||
```
|
||||
|
||||
---
|
||||
@@ -326,7 +348,7 @@ List all registered videos.
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl http://localhost:3002/api/v1/videos
|
||||
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/videos
|
||||
```
|
||||
|
||||
---
|
||||
@@ -384,6 +406,63 @@ curl http://localhost:3002/api/v1/videos
|
||||
|
||||
---
|
||||
|
||||
## Cache Toggle
|
||||
|
||||
Toggle caching at runtime.
|
||||
|
||||
**Endpoint:** `POST /api/v1/config/cache`
|
||||
|
||||
**Request Body:**
|
||||
```json
|
||||
{
|
||||
"enabled": true
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| `enabled` | boolean | Yes | Enable (true) or disable (false) cache |
|
||||
|
||||
**Response (200):**
|
||||
```json
|
||||
{
|
||||
"cache_enabled": true,
|
||||
"message": "Cache toggled successfully"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Unregister Video
|
||||
|
||||
Delete a video and all associated data (chunks, processor results, thumbnails).
|
||||
|
||||
**Endpoint:** `POST /api/v1/unregister`
|
||||
|
||||
**Request Body:**
|
||||
```json
|
||||
{
|
||||
"uuid": "5dea6618a606e7c7"
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| `uuid` | string | Yes | Video UUID (16 character hex) |
|
||||
|
||||
**Response (200):**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Video unregistered successfully",
|
||||
"uuid": "5dea6618a606e7c7"
|
||||
}
|
||||
```
|
||||
|
||||
**Warning:** This operation is irreversible and will delete all associated chunks, processor results, and thumbnails.
|
||||
|
||||
---
|
||||
|
||||
## Error Responses
|
||||
|
||||
**400 Bad Request**
|
||||
@@ -445,3 +524,5 @@ cargo run --bin momentry -- server --host 127.0.0.1 --port 3002
|
||||
| Search | `POST /api/v1/search` |
|
||||
| List videos | `GET /api/v1/videos` |
|
||||
| Lookup | `GET /api/v1/lookup?uuid=<uuid>` |
|
||||
| Toggle cache | `POST /api/v1/config/cache` |
|
||||
| Delete video | `POST /api/v1/unregister` |
|
||||
|
||||
+132
-8
@@ -1,7 +1,7 @@
|
||||
# Momentry Core API 教育訓練手冊
|
||||
|
||||
> **對象**: marcom 團隊
|
||||
> **版本**: V1.1 | **日期**: 2026-03-25
|
||||
> **版本**: V1.4 | **日期**: 2026-03-25
|
||||
|
||||
---
|
||||
|
||||
@@ -15,12 +15,26 @@
|
||||
| 認證方式 | Header `X-API-Key` |
|
||||
| 格式 | JSON |
|
||||
|
||||
### API Key
|
||||
### Demo 測試帳號
|
||||
|
||||
#### API Key(用於 API 認證)
|
||||
|
||||
```
|
||||
X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69
|
||||
X-API-Key: muser_68600856036340bcafc01930eb4bd839
|
||||
```
|
||||
|
||||
#### SFTPGo(用於影片上傳)
|
||||
|
||||
| 項目 | 值 |
|
||||
|------|-----|
|
||||
| SFTP 主機 | `sftpgo.momentry.ddns.net` |
|
||||
| SFTP 連接埠 | `2022` |
|
||||
| 用戶名 | `demo` |
|
||||
| 密碼 | `demopassword123` |
|
||||
| Web 管理介面 | `https://sftpgo.momentry.ddns.net` |
|
||||
|
||||
**使用方式**:透過 SFTP 上傳影片,系統會自動註冊並處理。
|
||||
|
||||
---
|
||||
|
||||
## 2. 快速範例
|
||||
@@ -73,7 +87,107 @@ curl -s -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b6
|
||||
#### GET /api/v1/videos/:uuid
|
||||
取得單一影片詳情
|
||||
|
||||
### 3.2 任務相關
|
||||
### 3.2 搜尋與分段查詢
|
||||
|
||||
#### POST /api/v1/search
|
||||
向量搜尋,查詢分段(Chunk)詳情
|
||||
|
||||
**請求參數**:
|
||||
| 參數 | 類型 | 必填 | 說明 |
|
||||
|------|------|------|------|
|
||||
| `query` | string | 是 | 搜尋關鍵字 |
|
||||
| `limit` | number | 否 | 回傳數量(預設 10) |
|
||||
| `uuid` | string | 否 | 只搜尋指定影片 |
|
||||
|
||||
**請求範例**:
|
||||
```json
|
||||
{
|
||||
"query": "天氣",
|
||||
"limit": 10,
|
||||
"uuid": "5dea6618a606e7c7"
|
||||
}
|
||||
```
|
||||
|
||||
**回應範例**:
|
||||
```json
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"uuid": "39567a0eb16f39fd",
|
||||
"chunk_id": "sentence_1471",
|
||||
"chunk_type": "sentence",
|
||||
"start_time": 5309.08,
|
||||
"end_time": 5311.08,
|
||||
"text": "influenced by a vital way,",
|
||||
"score": 0.68
|
||||
}
|
||||
],
|
||||
"query": "天氣"
|
||||
}
|
||||
```
|
||||
|
||||
**Chunk 欄位說明**:
|
||||
| 欄位 | 說明 | 範例 |
|
||||
|------|------|------|
|
||||
| `uuid` | 影片唯一識別碼 | `39567a0eb16f39fd` |
|
||||
| `chunk_id` | 分段識別碼 | `sentence_1471` |
|
||||
| `chunk_type` | 分段類型 | `sentence` / `cut` / `time` / `trace` / `story` |
|
||||
| `start_time` | 開始時間(秒) | `5309.08` |
|
||||
| `end_time` | 結束時間(秒) | `5311.08` |
|
||||
| `text` | 內容文字 | `influenced by a vital way` |
|
||||
| `score` | 相似度分數(0-1) | `0.68` |
|
||||
|
||||
**Chunk 類型說明**:
|
||||
| 類型 | 說明 | 來源 |
|
||||
|------|------|------|
|
||||
| `sentence` | 語音轉文字片段 | ASR 處理 |
|
||||
| `cut` | 場景變化片段 | CUT 處理 |
|
||||
| `time` | 固定時間分段 | 系統自動切割 |
|
||||
| `trace` | 軌跡追蹤片段 | YOLO 追蹤 |
|
||||
| `story` | 故事線片段(父子關係) | Story 分析 |
|
||||
|
||||
#### POST /api/v1/n8n/search
|
||||
n8n 專用搜尋(包含完整影片檔案路徑 file_path)
|
||||
|
||||
**請求參數**: 與 `/search` 相同
|
||||
|
||||
**回應範例**:
|
||||
```json
|
||||
{
|
||||
"query": "天氣",
|
||||
"count": 2,
|
||||
"hits": [
|
||||
{
|
||||
"id": "sentence_1471",
|
||||
"vid": "39567a0eb16f39fd",
|
||||
"chunk_type": "sentence",
|
||||
"start_frame": 318545,
|
||||
"end_frame": 318665,
|
||||
"fps": 59.94,
|
||||
"start_time": 5314.31,
|
||||
"end_time": 5316.32,
|
||||
"text": "influenced by a vital way,",
|
||||
"score": 0.68
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**與 /search 的差異**:
|
||||
| 欄位 | `/search` | `/n8n/search` |
|
||||
|------|-----------|----------------|
|
||||
| 影片 UUID | `uuid` | `vid` |
|
||||
| Chunk ID | `chunk_id` | `id` |
|
||||
| 開始時間 | `start_time` | `start_time` |
|
||||
| 結束時間 | `end_time` | `end_time` |
|
||||
| 相似度分數 | `score` | `score` |
|
||||
| **檔案路徑** | ❌ | ✅ `file_path` |
|
||||
|
||||
> **注意**: `file_path` 是影片的實際路徑,可用於本地播放。
|
||||
|
||||
### 3.3 任務相關
|
||||
|
||||
### 3.4 任務相關
|
||||
|
||||
#### GET /api/v1/jobs/:uuid
|
||||
查詢處理任務狀態
|
||||
@@ -105,7 +219,7 @@ curl -s -H "X-API-Key: ..." \
|
||||
"https://api.momentry.ddns.net/api/v1/jobs?status=completed&limit=5"
|
||||
```
|
||||
|
||||
### 3.3 系統管理
|
||||
### 3.5 系統管理
|
||||
|
||||
#### POST /api/v1/config/cache
|
||||
切換快取功能(管理員專用)
|
||||
@@ -146,7 +260,7 @@ curl -s -H "X-API-Key: ..." \
|
||||
|
||||
**注意**: 此操作會刪除影片及其所有分段、處理結果、縮圖等關聯資料,**無法復原**。
|
||||
|
||||
### 3.4 健康檢查
|
||||
### 3.6 健康檢查
|
||||
|
||||
#### GET /health
|
||||
服務健康狀態(**無需認證**)
|
||||
@@ -227,6 +341,8 @@ GET /api/v1/jobs/{uuid}
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ 查詢所有影片 GET /api/v1/videos │
|
||||
│ 查詢單一影片 GET /api/v1/videos/:uuid │
|
||||
│ 向量搜尋 POST /api/v1/search │
|
||||
│ n8n 搜尋 POST /api/v1/n8n/search │
|
||||
│ 查詢任務狀態 GET /api/v1/jobs/:uuid │
|
||||
│ 查詢所有任務 GET /api/v1/jobs │
|
||||
│ 快取設定 POST /api/v1/config/cache (管理員) │
|
||||
@@ -263,5 +379,13 @@ GET /api/v1/jobs/{uuid}
|
||||
|
||||
---
|
||||
|
||||
**文件版本**: V1.1
|
||||
**最後更新**: 2026-03-25
|
||||
## 附錄:版本歷史
|
||||
|
||||
| 版本 | 日期 | 內容 | 操作人 |
|
||||
|------|------|------|--------|
|
||||
| V1.0 | 2026-03-25 | 初版建立 | OpenCode |
|
||||
| V1.1 | 2026-03-25 | 新增快取/刪除 API、搜尋端點文件 | OpenCode |
|
||||
| V1.2 | 2026-03-25 | 新增 Chunk 欄位說明、類型、播放方式 | OpenCode |
|
||||
| V1.3 | 2026-03-25 | 新增 Demo 測試帳號(SFTPGo)| OpenCode |
|
||||
| V1.4 | 2026-03-25 | 更新 n8n 搜尋回傳欄位說明 (media_url→file_path) | OpenCode |
|
||||
| V1.5 | 2026-04-17 | 修正 API Key 格式、統一 n8n/search 欄位名稱 (start/end → start_time/end_time) | OpenCode |
|
||||
|
||||
@@ -2,12 +2,21 @@
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 版本 | V1.0 |
|
||||
| 日期 | 2026-03-23 |
|
||||
| 版本 | V1.1 |
|
||||
| 日期 | 2026-03-25 |
|
||||
| 用途 | 在 WordPress 中呼叫 Momentry API |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.1 | 2026-03-25 | 更新: n8n 搜尋回傳 `file_path` 取代 `media_url`,新增 API Key 驗證說明 | OpenCode | deepseek-reasoner |
|
||||
| V1.0 | 2026-03-23 | 創建 WordPress API 指南 | Warren | OpenCode / MiniMax M2.5 |
|
||||
|
||||
---
|
||||
|
||||
## API URL
|
||||
|
||||
在 WordPress 中呼叫 API,**請使用外部 URL**:
|
||||
@@ -20,6 +29,20 @@ https://api.momentry.ddns.net
|
||||
|
||||
---
|
||||
|
||||
## API 認證
|
||||
|
||||
所有 `/api/v1/*` 端點(除了健康檢查)都需要 API Key 認證。請在請求標頭中加入:
|
||||
|
||||
```
|
||||
'headers' => ['Content-Type' => 'application/json', 'X-API-Key' => 'YOUR_API_KEY']
|
||||
```
|
||||
|
||||
**目前示範使用的 API Key**: `demo_api_key_12345`
|
||||
|
||||
> **注意**: 正式環境請使用安全的 API Key 管理機制,避免在客戶端 JavaScript 中暴露 API Key。
|
||||
|
||||
---
|
||||
|
||||
## 常用端點
|
||||
|
||||
| 方法 | 端點 | 說明 |
|
||||
@@ -45,7 +68,7 @@ $data = [
|
||||
];
|
||||
|
||||
$response = wp_remote_post($api_url, [
|
||||
'headers' => ['Content-Type' => 'application/json'],
|
||||
'headers' => ['Content-Type' => 'application/json', 'X-API-Key' => 'YOUR_API_KEY'],
|
||||
'body' => json_encode($data),
|
||||
'timeout' => 30
|
||||
]);
|
||||
@@ -65,7 +88,10 @@ if (is_wp_error($response)) {
|
||||
<?php
|
||||
$api_url = 'https://api.momentry.ddns.net/api/v1/videos';
|
||||
|
||||
$response = wp_remote_get($api_url, ['timeout' => 30]);
|
||||
$response = wp_remote_get($api_url, [
|
||||
'headers' => ['X-API-Key' => 'YOUR_API_KEY'],
|
||||
'timeout' => 30
|
||||
]);
|
||||
|
||||
if (!is_wp_error($response)) {
|
||||
$body = json_decode(wp_remote_retrieve_body($response), true);
|
||||
@@ -83,7 +109,10 @@ if (!is_wp_error($response)) {
|
||||
$uuid = '5dea6618a606e7c7';
|
||||
$api_url = 'https://api.momentry.ddns.net/api/v1/lookup?uuid=' . $uuid;
|
||||
|
||||
$response = wp_remote_get($api_url, ['timeout' => 30]);
|
||||
$response = wp_remote_get($api_url, [
|
||||
'headers' => ['X-API-Key' => 'YOUR_API_KEY'],
|
||||
'timeout' => 30
|
||||
]);
|
||||
|
||||
if (!is_wp_error($response)) {
|
||||
$video = json_decode(wp_remote_retrieve_body($response), true);
|
||||
@@ -104,7 +133,7 @@ if (!is_wp_error($response)) {
|
||||
async function searchVideos(query, limit = 10) {
|
||||
const response = await fetch('https://api.momentry.ddns.net/api/v1/n8n/search', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
headers: { 'Content-Type': 'application/json', 'X-API-Key': 'YOUR_API_KEY' },
|
||||
body: JSON.stringify({ query, limit })
|
||||
});
|
||||
|
||||
@@ -132,6 +161,24 @@ searchVideos('charade', 5)
|
||||
|
||||
```php
|
||||
<?php
|
||||
// 將文件路徑轉換為可訪問的 URL
|
||||
function convert_file_path_to_url($file_path) {
|
||||
// 範例: 將 SFTPGo 文件路徑轉換為 web URL
|
||||
// /Users/accusys/momentry/var/sftpgo/data/demo/video.mp4
|
||||
// → https://sftpgo.example.com/demo/video.mp4
|
||||
|
||||
// 移除基本路徑
|
||||
$base_path = '/Users/accusys/momentry/var/sftpgo/data/';
|
||||
if (strpos($file_path, $base_path) === 0) {
|
||||
$relative_path = substr($file_path, strlen($base_path));
|
||||
// 替換為實際的 SFTPGo web URL
|
||||
return 'https://sftpgo.example.com/' . $relative_path;
|
||||
}
|
||||
|
||||
// 如果無法轉換,返回原始路徑
|
||||
return $file_path;
|
||||
}
|
||||
|
||||
// 註冊短碼
|
||||
add_shortcode('momentry_search', function($atts) {
|
||||
$atts = shortcode_atts([
|
||||
@@ -144,7 +191,10 @@ add_shortcode('momentry_search', function($atts) {
|
||||
}
|
||||
|
||||
$response = wp_remote_post('https://api.momentry.ddns.net/api/v1/n8n/search', [
|
||||
'headers' => ['Content-Type' => 'application/json'],
|
||||
'headers' => [
|
||||
'Content-Type' => 'application/json',
|
||||
'X-API-Key' => 'YOUR_API_KEY' // 替換為實際的 API Key
|
||||
],
|
||||
'body' => json_encode([
|
||||
'query' => $atts['query'],
|
||||
'limit' => (int)$atts['limit']
|
||||
@@ -164,10 +214,15 @@ add_shortcode('momentry_search', function($atts) {
|
||||
|
||||
$output = '<ul class="momentry-results">';
|
||||
foreach ($data['hits'] as $hit) {
|
||||
// 注意: API 現在返回 file_path 而非 media_url
|
||||
// 需要將文件路徑轉換為可訪問的 URL
|
||||
$file_path = $hit['file_path'];
|
||||
$video_url = convert_file_path_to_url($file_path); // 需要實作此函數
|
||||
|
||||
$output .= sprintf(
|
||||
'<li>%s <a href="%s?start=%s">播放</a></li>',
|
||||
esc_html($hit['text']),
|
||||
$hit['media_url'],
|
||||
$video_url,
|
||||
$hit['start']
|
||||
);
|
||||
}
|
||||
@@ -199,7 +254,7 @@ add_action('rest_api_init', function() {
|
||||
$response = wp_remote_post(
|
||||
'https://api.momentry.ddns.net/api/v1/n8n/search',
|
||||
[
|
||||
'headers' => ['Content-Type' => 'application/json'],
|
||||
'headers' => ['Content-Type' => 'application/json', 'X-API-Key' => 'YOUR_API_KEY'],
|
||||
'body' => json_encode([
|
||||
'query' => $request->get_param('query'),
|
||||
'limit' => $request->get_param('limit', 10)
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
# Momentry API 使用流程
|
||||
|
||||
> **目標**: 從影片上傳到搜尋的完整流程
|
||||
> **適用**: WordPress / n8n 整合
|
||||
> **適用**: WordPress / n8n 整合
|
||||
> **版本**: V1.0 | **日期**: 2026-03-25
|
||||
|
||||
---
|
||||
|
||||
@@ -22,7 +23,7 @@
|
||||
|
||||
```bash
|
||||
# 連線資訊
|
||||
主機: momentry.ddns.net
|
||||
主機: sftpgo.momentry.ddns.net
|
||||
連接埠: 2022
|
||||
用戶名: demo
|
||||
密碼: demopassword123
|
||||
@@ -33,7 +34,7 @@
|
||||
### 方式 B: SFTP 命令列
|
||||
|
||||
```bash
|
||||
sshpass -p "demopassword123" sftp -P 2022 demo@momentry.ddns.net
|
||||
sshpass -p "demopassword123" sftp -P 2022 demo@sftpgo.momentry.ddns.net
|
||||
```
|
||||
|
||||
上傳後確認檔案在 SFTPGo 中的位置
|
||||
@@ -153,10 +154,54 @@ curl -s -X POST "https://api.momentry.ddns.net/api/v1/search" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"query": "測試關鍵字",
|
||||
"top_k": 5
|
||||
"limit": 5
|
||||
}'
|
||||
```
|
||||
|
||||
### 取得分段(Chunk)內容
|
||||
|
||||
搜尋結果會返回影片分段(Chunk),包含可播放的時間軸資訊:
|
||||
|
||||
```json
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"uuid": "39567a0eb16f39fd",
|
||||
"chunk_id": "sentence_1471",
|
||||
"chunk_type": "sentence",
|
||||
"start_time": 5309.08,
|
||||
"end_time": 5311.08,
|
||||
"text": "influenced by a vital way,",
|
||||
"score": 0.68
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Chunk 欄位說明**:
|
||||
| 欄位 | 說明 |
|
||||
|------|------|
|
||||
| `uuid` | 影片 UUID(用於取得影片網址) |
|
||||
| `chunk_id` | 分段 ID |
|
||||
| `chunk_type` | 分段類型(sentence/cut/time/trace/story) |
|
||||
| `start_time` | 開始時間(秒) |
|
||||
| `end_time` | 結束時間(秒) |
|
||||
| `text` | 語音內容文字 |
|
||||
| `score` | 相似度分數(0-1) |
|
||||
|
||||
### 播放分段
|
||||
|
||||
取得 Chunk 後可組合成播放網址:
|
||||
|
||||
```
|
||||
影片網址?start={start_time}&end={end_time}
|
||||
```
|
||||
|
||||
範例:
|
||||
```
|
||||
https://wp.momentry.ddns.net/video.mp4?start=5309.08&end=5311.08
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 完整 n8n Workflow 範例
|
||||
@@ -294,7 +339,7 @@ switch ($job['status']) {
|
||||
}
|
||||
```
|
||||
|
||||
### Step 5: 搜尋內容
|
||||
### Step 5: 搜尋內容並取得 Chunk
|
||||
|
||||
```php
|
||||
<?php
|
||||
@@ -302,14 +347,18 @@ switch ($job['status']) {
|
||||
$results = Momentry_API::search('測試關鍵字', 5);
|
||||
|
||||
foreach ($results['results'] as $result) {
|
||||
echo "UUID: " . $result['chunk_id'] . "\n";
|
||||
echo "分數: " . $result['score'] . "\n";
|
||||
echo "影片 UUID: " . $result['uuid'] . "\n";
|
||||
echo "Chunk ID: " . $result['chunk_id'] . "\n";
|
||||
echo "類型: " . $result['chunk_type'] . "\n";
|
||||
echo "開始: " . $result['start_time'] . "s\n";
|
||||
echo "結束: " . $result['end_time'] . "s\n";
|
||||
echo "內容: " . ($result['text'] ?? '') . "\n";
|
||||
echo "相似度: " . $result['score'] . "\n";
|
||||
echo "---\n";
|
||||
}
|
||||
```
|
||||
|
||||
### WordPress Shortcode 範例
|
||||
### WordPress Shortcode 範例(可點擊播放)
|
||||
|
||||
```php
|
||||
<?php
|
||||
@@ -336,10 +385,21 @@ add_shortcode('momentry_search', function($atts) {
|
||||
$html .= '<ul>';
|
||||
|
||||
foreach ($results['results'] as $result) {
|
||||
$video_uuid = $result['uuid'];
|
||||
$start = $result['start_time'] ?? 0;
|
||||
$end = $result['end_time'] ?? 0;
|
||||
$text = $result['text'] ?? '無文字描述';
|
||||
|
||||
$html .= '<li>';
|
||||
$html .= '<strong>時間: ' . ($result['start_time'] ?? 'N/A') . 's</strong>';
|
||||
$html .= '<a href="/player?uuid=' . esc_attr($video_uuid) .
|
||||
'&start=' . esc_attr($start) .
|
||||
'&end=' . esc_attr($end) . '">';
|
||||
$html .= '播放 ' . $start . 's - ' . $end . 's';
|
||||
$html .= '</a>';
|
||||
$html .= '<br>';
|
||||
$html .= esc_html($result['text'] ?? '無文字描述');
|
||||
$html .= '<small>相似度: ' . round($result['score'] * 100) . '%</small>';
|
||||
$html .= '<br>';
|
||||
$html .= esc_html($text);
|
||||
$html .= '</li>';
|
||||
}
|
||||
|
||||
@@ -389,3 +449,13 @@ add_shortcode('momentry_search', function($atts) {
|
||||
**注意**:
|
||||
- 處理時間視影片長度而定(1分鐘影片約需 2-5 分鐘處理)
|
||||
- 大量影片時建議分批上傳
|
||||
|
||||
---
|
||||
|
||||
## 附錄:版本歷史
|
||||
|
||||
| 版本 | 日期 | 內容 | 操作人 |
|
||||
|------|------|------|--------|
|
||||
| V1.0 | 2026-03-25 | 初版建立 | OpenCode |
|
||||
| V1.1 | 2026-03-25 | 新增 Chunk 取得與播放說明、Shortcode 範例 | OpenCode |
|
||||
| V1.2 | 2026-03-25 | 修正 SFTPGo 主機名稱為 sftpgo.momentry.ddns.net | OpenCode |
|
||||
|
||||
@@ -236,7 +236,33 @@ Chunk(片段)是影片處理後的最小單位。當影片上傳後,系統
|
||||
|
||||
## 6. 如何使用 Chunk
|
||||
|
||||
### 6.1 搜尋相關片段
|
||||
### 6.1 API 取得 Chunk
|
||||
|
||||
使用搜尋 API 取得 Chunk:
|
||||
|
||||
```bash
|
||||
curl -X POST "https://api.momentry.ddns.net/api/v1/search" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"query": "關鍵字",
|
||||
"limit": 10
|
||||
}'
|
||||
```
|
||||
|
||||
**指定影片搜尋**:
|
||||
```bash
|
||||
curl -X POST "https://api.momentry.ddns.net/api/v1/search" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"query": "關鍵字",
|
||||
"uuid": "39567a0eb16f39fd",
|
||||
"limit": 5
|
||||
}'
|
||||
```
|
||||
|
||||
### 6.2 搜尋相關片段
|
||||
|
||||
當使用者搜尋「天氣」時,系統會:
|
||||
|
||||
@@ -245,22 +271,90 @@ Chunk(片段)是影片處理後的最小單位。當影片上傳後,系統
|
||||
3. 找到相關的 Chunk
|
||||
4. 返回時間軸和內容
|
||||
|
||||
### 6.2 播放指定片段
|
||||
### 6.3 播放指定片段
|
||||
|
||||
取得 Chunk 後可播放:
|
||||
|
||||
```
|
||||
開始時間: 12.5 秒
|
||||
結束時間: 18.3 秒
|
||||
影片 UUID: 39567a0eb16f39fd
|
||||
```
|
||||
|
||||
### 6.3 組合多個 Chunk
|
||||
**播放器連結格式**:
|
||||
```
|
||||
/player?uuid={uuid}&start={start_time}&end={end_time}
|
||||
```
|
||||
|
||||
### 6.4 組合多個 Chunk
|
||||
|
||||
多個相關 Chunk 可以組合成一個章節或故事線。
|
||||
|
||||
### 6.5 Story Chunk(父子關係)
|
||||
|
||||
Story Chunk 可包含多個子 Chunk:
|
||||
|
||||
```json
|
||||
{
|
||||
"chunk_id": "story_001",
|
||||
"chunk_type": "story",
|
||||
"content": {
|
||||
"story_id": "story_001",
|
||||
"title": "開場介紹",
|
||||
"child_chunk_ids": ["sentence_00001", "sentence_00002", "cut_00001"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 快速參考
|
||||
## 7. API 回應格式
|
||||
|
||||
### /search 回應
|
||||
|
||||
```json
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"uuid": "39567a0eb16f39fd",
|
||||
"chunk_id": "sentence_1471",
|
||||
"chunk_type": "sentence",
|
||||
"start_time": 5309.08,
|
||||
"end_time": 5311.08,
|
||||
"text": "influenced by a vital way,",
|
||||
"score": 0.68
|
||||
}
|
||||
],
|
||||
"query": "關鍵字"
|
||||
}
|
||||
```
|
||||
|
||||
### /n8n/search 回應
|
||||
|
||||
```json
|
||||
{
|
||||
"query": "關鍵字",
|
||||
"count": 1,
|
||||
"hits": [
|
||||
{
|
||||
"id": "sentence_1471",
|
||||
"vid": "39567a0eb16f39fd",
|
||||
"start": 5309.08,
|
||||
"end": 5311.08,
|
||||
"title": "Chunk sentence_1471",
|
||||
"text": "influenced by a vital way,",
|
||||
"score": 0.68,
|
||||
"file_path": "/Users/accusys/momentry/var/sftpgo/data/demo/video.mp4"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
> **注意**: `file_path` 是影片的實際路徑,可用於本地播放。
|
||||
|
||||
---
|
||||
|
||||
## 8. 快速參考
|
||||
|
||||
| 項目 | 說明 |
|
||||
|------|------|
|
||||
@@ -273,9 +367,13 @@ Chunk(片段)是影片處理後的最小單位。當影片上傳後,系統
|
||||
| content | 詳細 JSON 結構 |
|
||||
| metadata | 人臉、OCR、姿態等偵測結果 |
|
||||
| parent_chunk_id | 父區塊 ID(用於 story 區塊) |
|
||||
| child_chunk_ids | 子區塊 ID 列表(story 區塊專用) |
|
||||
| child_chunk_ids | 子區塊 ID 列表(story 區塊專用) | |
|
||||
|
||||
---
|
||||
|
||||
**文件版本**: V1.0
|
||||
**最後更新**: 2026-03-25
|
||||
## 附錄:版本歷史
|
||||
|
||||
| 版本 | 日期 | 內容 | 操作人 |
|
||||
|------|------|------|--------|
|
||||
| V1.0 | 2026-03-25 | 初版建立 | OpenCode |
|
||||
| V1.1 | 2026-03-25 | 新增 API 取得 Chunk 方式、播放連結格式 | OpenCode |
|
||||
|
||||
+15
-3
@@ -2,9 +2,21 @@
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 版本 | V1.0 |
|
||||
| 日期 | 2026-03-25 |
|
||||
| 狀態 | 完成 |
|
||||
| 建立者 | OpenCode |
|
||||
| 建立時間 | 2026-03-25 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-03-25 | 創建示範手冊,包含 Demo API Key 與完整範例 | OpenCode | deepseek-reasoner |
|
||||
|
||||
---
|
||||
|
||||
**狀態**: 完成
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,5 +1,21 @@
|
||||
# Document Embedding Strategy - Parent-Child Chunks
|
||||
|
||||
| Item | Content |
|
||||
|------|---------|
|
||||
| Author | Warren |
|
||||
| Created | 2026-03-23 |
|
||||
| Document Version | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## Version History
|
||||
|
||||
| Version | Date | Purpose | Operator | Tool/Model |
|
||||
|---------|------|---------|----------|------------|
|
||||
| V1.0 | 2026-03-23 | Create document embedding strategy | Warren | OpenCode |
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Momentry uses a **parent-child chunk hierarchy** for improved RAG retrieval. This document describes the embedding strategy for this hierarchy.
|
||||
@@ -44,7 +60,7 @@ embedding_text = f"Summary: {parent.text_content}
|
||||
Children: {child_text_1}. {child_text_2}. {child_text_3}..."
|
||||
```
|
||||
|
||||
**Prefix**: `search_document: ` (for documents in Qdrant)
|
||||
**Prefix**: `search_document:` (for documents in Qdrant)
|
||||
|
||||
**Example**:
|
||||
```
|
||||
@@ -58,7 +74,7 @@ embedding_text = f"[{child.chunk_type}] {child.text_content}
|
||||
Parent: {parent.description}"
|
||||
```
|
||||
|
||||
**Prefix**: `search_document: `
|
||||
**Prefix**: `search_document:`
|
||||
|
||||
**Example**:
|
||||
```
|
||||
|
||||
@@ -461,4 +461,4 @@ sudo launchctl load /Library/LaunchDaemons/com.momentry.api.plist
|
||||
- `docs/INSTALL_POSTGRESQL.md` - PostgreSQL 安裝
|
||||
- `docs/INSTALL_REDIS.md` - Redis 安裝
|
||||
- `docs/INSTALL_QDRANT.md` - Qdrant 安裝
|
||||
- `docs/PENDING_ISSUES.md` - 待解決問題
|
||||
- `docs/PENDING_ISSUES.md` - 待解決問題
|
||||
|
||||
@@ -527,13 +527,13 @@ SFTPGo 提供 RESTful API 用於管理用戶和組,支援自動化運維。
|
||||
|
||||
### API 認證方式
|
||||
|
||||
1. **獲取 Access Token** (使用 Basic Auth):
|
||||
- **獲取 Access Token** (使用 Basic Auth):
|
||||
```bash
|
||||
TOKEN=$(curl -s -X GET http://localhost:8080/api/v2/token \
|
||||
-u "admin:Test3200Test3200" | jq -r '.access_token')
|
||||
```
|
||||
|
||||
2. **使用 Token 調用 API**:
|
||||
- **使用 Token 調用 API**:
|
||||
```bash
|
||||
curl -X GET http://localhost:8080/api/v2/admins \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
@@ -569,7 +569,7 @@ curl -s -X POST http://localhost:8080/api/v2/users \
|
||||
}'
|
||||
```
|
||||
|
||||
**權限格式注意**: 必須為 map[string][]string 格式,例如:
|
||||
**權限格式注意**: 必須為 `map[string][]string` 格式,例如:
|
||||
```json
|
||||
{
|
||||
"/": ["*"],
|
||||
@@ -752,12 +752,12 @@ sftpgo serve --config-file /Users/accusys/momentry/etc/sftpgo/sftpgo.json &
|
||||
|
||||
### Hook 故障排除
|
||||
|
||||
1. **檢查 Hook 日誌**:
|
||||
- **檢查 Hook 日誌**:
|
||||
```bash
|
||||
tail -f /Users/accusys/sftpgo_test/hook.log
|
||||
```
|
||||
|
||||
2. **手動測試 Hook 腳本**:
|
||||
- **手動測試 Hook 腳本**:
|
||||
```bash
|
||||
export SFTPGO_USERNAME=demo
|
||||
export SFTPGO_FILEPATH="./test.txt"
|
||||
@@ -766,7 +766,7 @@ export SFTPGO_ACTION=add
|
||||
/Users/accusys/sftpgo_test/register_hook.sh
|
||||
```
|
||||
|
||||
3. **SFTPGo 錯誤日誌**:
|
||||
- **SFTPGo 錯誤日誌**:
|
||||
```bash
|
||||
tail -20 /Users/accusys/momentry/log/sftpgo.error.log
|
||||
```
|
||||
@@ -877,12 +877,12 @@ sftp> put test.txt
|
||||
|
||||
## 常見問題
|
||||
|
||||
#### "無效的憑證" 即使密碼正確
|
||||
### "無效的憑證" 即使密碼正確
|
||||
|
||||
- PostgreSQL 中的密碼哈希可能不符合 SFTPGo 預期格式
|
||||
- 使用 Web 面板的 **Forgot password** 功能而非直接 SQL 更新
|
||||
|
||||
#### CSRF Token 錯誤
|
||||
### CSRF Token 錯誤
|
||||
|
||||
- 清除瀏覽器中 `localhost:8080` 的 cookies
|
||||
- 使用無痕/私密瀏覽視窗
|
||||
|
||||
@@ -779,4 +779,4 @@ log_info "✅ 部署完成!"
|
||||
|
||||
**負責人**: OpenCode AI Assistant
|
||||
|
||||
**最後更新**: 2026-03-23
|
||||
**最後更新**: 2026-03-23
|
||||
|
||||
@@ -1,5 +1,22 @@
|
||||
# Momentry Core 影片 RAG 系統說明稿
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | Warren |
|
||||
| 建立時間 | 2026-03-22 |
|
||||
| 文件版本 | V1.1 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-03-22 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
|
||||
| V1.1 | 2026-03-25 | 更新API回應格式 (media_url→file_path) 與認證標頭 | OpenCode | deepseek-reasoner |
|
||||
|
||||
---
|
||||
|
||||
## 系統架構
|
||||
|
||||
```
|
||||
@@ -85,22 +102,9 @@ POST http://localhost:3002/api/v1/search
|
||||
}
|
||||
```
|
||||
|
||||
**回應:**
|
||||
```json
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"uuid": "a1b10138a6bbb0cd",
|
||||
"chunk_id": "sentence_0006",
|
||||
"chunk_type": "sentence",
|
||||
"start_time": 48.8,
|
||||
"end_time": 55.44,
|
||||
"text": "fun plot twists, Woody Dialog and charming performances...",
|
||||
"score": 0.526
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
> **注意**:
|
||||
> 1. **API 認證**: 所有 `/api/v1/*` 端點需要 `X-API-Key` 標頭
|
||||
> 2. **檔案路徑轉換**: API 現在返回 `file_path`(檔案系統路徑),需要轉換為可訪問的 URL(例如透過 SFTPGo 分享連結)
|
||||
|
||||
---
|
||||
|
||||
@@ -132,7 +136,7 @@ POST http://localhost:3002/api/v1/n8n/search
|
||||
"title": "Chunk sentence_0006",
|
||||
"text": "fun plot twists...",
|
||||
"score": 0.526,
|
||||
"media_url": "https://wp.momentry.ddns.net/Old_Time_Movie_Show_-_Charade_1963.HD.mov"
|
||||
"file_path": "/Users/accusys/momentry/var/sftpgo/data/demo/video.mp4"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -187,6 +191,7 @@ POST http://localhost:3002/api/v1/n8n/search
|
||||
Method: POST
|
||||
URL: http://localhost:3002/api/v1/n8n/search
|
||||
Body Content Type: JSON
|
||||
Headers: X-API-Key (需設定)
|
||||
```
|
||||
|
||||
3. Body:
|
||||
@@ -215,12 +220,17 @@ const results = hits.map((hit, index) => ({
|
||||
text: hit.text,
|
||||
time: `${hit.start}s - ${hit.end}s`,
|
||||
score: Math.round(hit.score * 100) + "%",
|
||||
url: hit.media_url + "#t=" + hit.start + "," + hit.end
|
||||
// 注意: API 現在返回 file_path(檔案系統路徑),需要轉換為可訪問的 URL
|
||||
url: hit.file_path + "#t=" + hit.start + "," + hit.end // 需實作檔案路徑轉換為 URL
|
||||
}));
|
||||
|
||||
return { json: { results } };
|
||||
```
|
||||
|
||||
> **注意**:
|
||||
> 1. **API 認證**: 所有 `/api/v1/*` 端點需要 `X-API-Key` 標頭
|
||||
> 2. **檔案路徑轉換**: API 現在返回 `file_path`(檔案系統路徑),需要轉換為可訪問的 URL(例如透過 SFTPGo 分享連結)
|
||||
|
||||
---
|
||||
|
||||
### Step 4: 格式化輸出
|
||||
@@ -248,18 +258,20 @@ return { json: { results } };
|
||||
# 語意搜尋
|
||||
curl -X POST http://localhost:3002/api/v1/search \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{"query": "charade", "limit": 3}'
|
||||
|
||||
# n8n 格式
|
||||
curl -X POST http://localhost:3002/api/v1/n8n/search \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{"query": "charade", "limit": 3}'
|
||||
|
||||
# 影片列表
|
||||
curl http://localhost:3002/api/v1/videos
|
||||
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/videos
|
||||
|
||||
# 特定影片區塊
|
||||
curl http://localhost:3002/api/v1/videos/a1b10138a6bbb0cd/chunks
|
||||
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/videos/a1b10138a6bbb0cd/chunks
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
+34
-5
@@ -1,11 +1,29 @@
|
||||
# n8n 整合範例
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | Warren |
|
||||
| 建立時間 | 2026-03-18 |
|
||||
| 文件版本 | V1.1 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-03-18 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
|
||||
| V1.1 | 2026-03-25 | 更新API回應格式 (media_url→file_path) 與認證標頭 | OpenCode | deepseek-reasoner |
|
||||
|
||||
---
|
||||
|
||||
## 基本設定
|
||||
|
||||
### API 端點
|
||||
- **Base URL:** `http://localhost:3002/api/v1`
|
||||
- **Method:** `POST`
|
||||
- **Content-Type:** `application/json`
|
||||
- **Authentication:** `X-API-Key: YOUR_API_KEY` (所有 `/api/v1/*` 端點皆需要)
|
||||
|
||||
---
|
||||
|
||||
@@ -36,7 +54,8 @@
|
||||
},
|
||||
"options": {
|
||||
"headers": {
|
||||
"Content-Type": "application/json"
|
||||
"Content-Type": "application/json",
|
||||
"X-API-Key": "YOUR_API_KEY"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -62,7 +81,7 @@ return results.map(r => ({
|
||||
|
||||
## Workflow 2: n8n 專用格式
|
||||
|
||||
使用 `/n8n/search` 端點(已包含 media_url)
|
||||
使用 `/n8n/search` 端點(已包含 file_path)
|
||||
|
||||
### HTTP Request
|
||||
```json
|
||||
@@ -72,6 +91,12 @@ return results.map(r => ({
|
||||
"body": {
|
||||
"query": "={{ $json.searchTerm }}",
|
||||
"limit": 5
|
||||
},
|
||||
"options": {
|
||||
"headers": {
|
||||
"Content-Type": "application/json",
|
||||
"X-API-Key": "YOUR_API_KEY"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -90,12 +115,14 @@ return results.map(r => ({
|
||||
"title": "Chunk sentence_0006",
|
||||
"text": "fun plot twists...",
|
||||
"score": 0.526,
|
||||
"media_url": "https://wp.momentry.ddns.net/Old_Time_Movie_Show_-_Charade_1963.HD.mov"
|
||||
"file_path": "/Users/accusys/momentry/var/sftpgo/data/demo/video.mp4"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
> **注意**: API 現在返回 `file_path`(檔案系統路徑)而非 `media_url`(網頁 URL)。如需在網頁中播放影片,請將檔案路徑轉換為可訪問的 URL(例如透過 SFTPGo 分享連結)。
|
||||
|
||||
---
|
||||
|
||||
## Workflow 3: 訊息機器人整合
|
||||
@@ -205,16 +232,18 @@ return {
|
||||
# 基本搜尋
|
||||
curl -X POST http://localhost:3002/api/v1/search \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{"query": "charade", "limit": 3}'
|
||||
|
||||
# n8n 格式
|
||||
curl -X POST http://localhost:3002/api/v1/n8n/search \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{"query": "charade", "limit": 3}'
|
||||
|
||||
# 取得影片列表
|
||||
curl http://localhost:3002/api/v1/videos
|
||||
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/videos
|
||||
|
||||
# 取得特定影片的區塊
|
||||
curl http://localhost:3002/api/v1/videos/a1b10138a6bbb0cd/chunks
|
||||
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/videos/a1b10138a6bbb0cd/chunks
|
||||
```
|
||||
|
||||
@@ -1,7 +1,20 @@
|
||||
# n8n Video RAG Demo - API 執行記錄
|
||||
|
||||
> 建立時間: 2026-03-22
|
||||
> 目標: 完整執行 n8n Video RAG Workflow 並記錄所有 API 呼叫
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | Warren |
|
||||
| 建立時間 | 2026-03-22 |
|
||||
| 文件版本 | V1.1 |
|
||||
| 目標 | 完整執行 n8n Video RAG Workflow 並記錄所有 API 呼叫 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-03-22 | 創建文件 | Warren | OpenCode |
|
||||
| V1.1 | 2026-03-26 | 更新 API 範例,新增 X-API-Key 驗證標頭 | OpenCode | deepseek-reasoner |
|
||||
|
||||
---
|
||||
|
||||
@@ -297,12 +310,13 @@ Content-Type: application/json
|
||||
|
||||
---
|
||||
|
||||
### Step 4.2: n8n 搜尋 (含 media_url)
|
||||
### Step 4.2: n8n 搜尋 (含 file_path)
|
||||
|
||||
**API 呼叫:**
|
||||
```bash
|
||||
curl -X POST "http://localhost:3002/api/v1/n8n/search" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: demo_api_key_12345" \
|
||||
-d '{
|
||||
"query": "What is the movie about?",
|
||||
"limit": 10,
|
||||
|
||||
@@ -1,7 +1,19 @@
|
||||
# n8n Video RAG Workflow - Node 設計
|
||||
|
||||
> 建立時間: 2026-03-22
|
||||
> 目標: 讓 marcom 團隊能夠複製、貼上、修改使用的完整操作指南
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | Warren |
|
||||
| 建立時間 | 2026-03-22 |
|
||||
| 文件版本 | V1.1 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-03-22 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
|
||||
| V1.1 | 2026-03-25 | 更新API回應格式 (media_url→file_path) 與認證標頭 | OpenCode | deepseek-reasoner |
|
||||
|
||||
---
|
||||
|
||||
@@ -117,7 +129,7 @@
|
||||
│ │ │ │
|
||||
│ │ ⑫ Natural Language Search │ │
|
||||
│ │ ↓ │ │
|
||||
│ │ ⑬ Get Media URL (含 media_url) │ │
|
||||
│ │ ⑬ Get File Path (含 file_path) │ │
|
||||
│ │ ↓ │ │
|
||||
│ │ ⑭ Build Response │ │
|
||||
│ │ ↓ │ │
|
||||
@@ -363,7 +375,7 @@ Output:
|
||||
}
|
||||
```
|
||||
|
||||
**注意**: 只有 asr、asrx、story 三個模組
|
||||
> **注意**: API 現在返回 `file_path`(檔案系統路徑)而非 `media_url`(網頁 URL)。如需在網頁中播放影片,請將檔案路徑轉換為可訪問的 URL(例如透過 SFTPGo 分享連結)。
|
||||
|
||||
---
|
||||
|
||||
@@ -559,7 +571,7 @@ Output:
|
||||
"vid": "a1b10138a6bbb0cd",
|
||||
"text": "Hello and welcome to the old-time movie show...",
|
||||
"score": 0.92,
|
||||
"media_url": "https://wp.momentry.ddns.net/Old_Time_Movie_Show_-_Charade_1963.HD.mov"
|
||||
"file_path": "/Users/accusys/momentry/var/sftpgo/data/demo/video.mp4"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -642,12 +654,13 @@ Configuration:
|
||||
| 項目 | 值 |
|
||||
|------|-----|
|
||||
| API Base | `http://localhost:3002` |
|
||||
| Authentication | `X-API-Key` header (所有 `/api/v1/*` 端點) |
|
||||
| Register | `POST /api/v1/register` |
|
||||
| Progress | `GET /api/v1/progress/{uuid}` |
|
||||
| Search | `POST /api/v1/search` |
|
||||
| n8n Search | `POST /api/v1/n8n/search` |
|
||||
| Hybrid Search | `POST /api/v1/search/hybrid` |
|
||||
| Media Base | `https://wp.momentry.ddns.net` |
|
||||
| Media Base | `https://wp.momentry.ddns.net` (僅供參考,API 返回 `file_path` 而非 URL) |
|
||||
|
||||
### Demo 測試資料
|
||||
|
||||
|
||||
@@ -1,5 +1,22 @@
|
||||
# n8n HTTP Request Node 設定指南
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | OpenCode |
|
||||
| 建立時間 | 2026-03-26 |
|
||||
| 文件版本 | V1.1 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-03-23 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
|
||||
| V1.1 | 2026-03-26 | 新增 API Key 驗證說明,更新 curl 範例 | OpenCode | deepseek-reasoner |
|
||||
|
||||
---
|
||||
|
||||
> **API URL 說明**:
|
||||
> - **本地測試**: `http://localhost:3002`
|
||||
> - **n8n workflow**: `https://api.momentry.ddns.net`
|
||||
@@ -32,7 +49,9 @@ Node: HTTP Request
|
||||
│ "query": "={{ $json.query }}",
|
||||
│ "limit": "={{ $json.limit }}"
|
||||
│ }
|
||||
└── Options: (empty)
|
||||
├── Send Headers: ✓ (checked)
|
||||
└── Header Parameters:
|
||||
└── X-API-Key: {{ $env.MOMENTRY_API_KEY }}
|
||||
```
|
||||
|
||||
### 方法 2: 使用 Raw Body + Headers
|
||||
@@ -51,7 +70,8 @@ Node: HTTP Request
|
||||
│ }
|
||||
├── Send Headers: ✓ (checked)
|
||||
└── Header Parameters:
|
||||
└── Content-Type: application/json
|
||||
├── Content-Type: application/json
|
||||
└── X-API-Key: {{ $env.MOMENTRY_API_KEY }}
|
||||
```
|
||||
|
||||
### 方法 3: 最簡單的 Hardcoded 測試
|
||||
@@ -218,8 +238,12 @@ URL: https://api.momentry.ddns.net/api/v1/n8n/search
|
||||
|
||||
在終端機測試:
|
||||
```bash
|
||||
# 需要 API Key 驗證 (設定環境變數或直接替換)
|
||||
export MOMENTRY_API_KEY="muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69"
|
||||
|
||||
curl -X POST https://api.momentry.ddns.net/api/v1/n8n/search \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: $MOMENTRY_API_KEY" \
|
||||
-d '{"query":"charade","limit":2}'
|
||||
```
|
||||
|
||||
|
||||
@@ -2,9 +2,22 @@
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 版本 | V1.1 |
|
||||
| 日期 | 2026-03-23 |
|
||||
| 目標讀者 | n8n 使用者、DevOps |
|
||||
| 建立者 | Warren |
|
||||
| 建立時間 | 2026-03-23 |
|
||||
| 文件版本 | V1.1 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-03-22 | 創建 n8n 整合手冊 | Warren | OpenCode |
|
||||
| V1.1 | 2026-03-23 | 新增 API Key 驗證與完整工作流範例 | Warren | OpenCode |
|
||||
|
||||
---
|
||||
|
||||
**目標讀者**: n8n 使用者、DevOps
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,5 +1,21 @@
|
||||
# OpenCode n8n MCP 整合設定
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | Warren |
|
||||
| 建立時間 | 2026-03-23 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-03-23 | 創建 n8n MCP 整合設定文件 | Warren | OpenCode |
|
||||
|
||||
---
|
||||
|
||||
> 建立時間: 2026-03-23
|
||||
> 更新時間: 2026-03-23
|
||||
|
||||
|
||||
@@ -1,5 +1,21 @@
|
||||
# n8n MCP 整合測試報告
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | Warren |
|
||||
| 建立時間 | 2026-03-23 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-03-23 | 創建測試報告 | Warren | OpenCode |
|
||||
|
||||
---
|
||||
|
||||
## 測試日期
|
||||
2026-03-23
|
||||
|
||||
|
||||
@@ -1,7 +1,20 @@
|
||||
# n8n Video Search 工作流程 - 成功設定指南
|
||||
|
||||
> 建立時間: 2026-03-22
|
||||
> 適用版本: n8n 2.3.5
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | Warren |
|
||||
| 建立時間 | 2026-03-22 |
|
||||
| 文件版本 | V1.1 |
|
||||
| 適用版本 | n8n 2.3.5 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-03-22 | 創建文件 | Warren | OpenCode |
|
||||
| V1.1 | 2026-03-26 | 更新 API 範例,新增 X-API-Key 驗證標頭 | OpenCode | deepseek-reasoner |
|
||||
|
||||
---
|
||||
|
||||
@@ -27,7 +40,11 @@
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "{\"query\":\"charade\",\"limit\":3}",
|
||||
"options": {}
|
||||
"options": {
|
||||
"headers": {
|
||||
"X-API-Key": "demo_api_key_12345"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -85,7 +102,11 @@
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "{\"query\":\"charade\",\"limit\":3}",
|
||||
"options": {}
|
||||
"options": {
|
||||
"headers": {
|
||||
"X-API-Key": "demo_api_key_12345"
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": "Search API",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
@@ -157,7 +178,7 @@
|
||||
"title": "Chunk sentence_0006",
|
||||
"text": "fun plot twists, Woody Dialog and charming performances...",
|
||||
"score": 0.526,
|
||||
"media_url": "https://wp.momentry.ddns.net/Old_Time_Movie_Show_-_Charade_1963.HD.mov"
|
||||
"file_path": "/Users/accusys/momentry/var/sftpgo/data/demo/Old_Time_Movie_Show_-_Charade_1963.HD.mov"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -203,13 +224,14 @@
|
||||
```bash
|
||||
curl -X POST https://api.momentry.ddns.net/api/v1/n8n/search \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: demo_api_key_12345" \
|
||||
-d '{"query":"charade","limit":3}'
|
||||
```
|
||||
|
||||
### 驗證服務狀態
|
||||
```bash
|
||||
# 檢查 Momentry Core
|
||||
curl https://api.momentry.ddns.net/api/v1/videos
|
||||
curl -H "X-API-Key: demo_api_key_12345" https://api.momentry.ddns.net/api/v1/videos
|
||||
|
||||
# 檢查 n8n
|
||||
curl http://localhost:5678/api/v1/workflows \
|
||||
|
||||
@@ -1,5 +1,21 @@
|
||||
# Playground Binary Implementation Plan
|
||||
|
||||
| Item | Content |
|
||||
|------|---------|
|
||||
| Author | Warren |
|
||||
| Created | 2026-03-23 |
|
||||
| Document Version | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## Version History
|
||||
|
||||
| Version | Date | Purpose | Operator | Tool/Model |
|
||||
|---------|------|---------|----------|------------|
|
||||
| V1.0 | 2026-03-23 | Create implementation plan | Warren | OpenCode |
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Create separate `momentry_playground` binary with distinct configuration from `momentry` (production).
|
||||
|
||||
+36
-15
@@ -1,6 +1,19 @@
|
||||
# Video Processing Pipeline - 處理流程
|
||||
|
||||
> 建立時間: 2026-03-22
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | Warren |
|
||||
| 建立時間 | 2026-03-22 |
|
||||
| 文件版本 | V1.1 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-03-22 | 創建文件 | Warren | OpenCode |
|
||||
| V1.1 | 2026-03-26 | 更新流程圖文字 (media_url→file_path) | OpenCode | deepseek-reasoner |
|
||||
|
||||
---
|
||||
|
||||
@@ -54,7 +67,7 @@
|
||||
│ │ │ │
|
||||
│ │ Natural Language Query ──→ [Embedding] ──→ [Qdrant Search] │ │
|
||||
│ │ ↓ │ │
|
||||
│ │ 返回結果含 media_url │ │
|
||||
│ │ 返回結果含 file_path │ │
|
||||
│ └─────────────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────────┘
|
||||
@@ -106,11 +119,11 @@ cargo run --bin momentry -- chunk <uuid>
|
||||
### Stage 4: 向量化
|
||||
|
||||
```bash
|
||||
# 向量化 chunks
|
||||
# 向量化 chunks(使用預設模型 nomic-embed-text-v2-moe:latest)
|
||||
cargo run --bin momentry -- vectorize <uuid>
|
||||
|
||||
# 指定模型
|
||||
cargo run --bin momentry -- vectorize <uuid> --model sentence-transformers/all-MiniLM-L6-v2
|
||||
# 明確指定模型
|
||||
cargo run --bin momentry -- vectorize <uuid> --model nomic-embed-text-v2-moe:latest
|
||||
```
|
||||
|
||||
---
|
||||
@@ -174,18 +187,27 @@ YOLO: ✓ Already complete, skipping
|
||||
|
||||
## 向量化模型選擇
|
||||
|
||||
### 統一嵌入模型
|
||||
Momentry Core 統一使用 **`nomic-embed-text-v2-moe:latest`** 作為所有規則的嵌入模型:
|
||||
|
||||
```bash
|
||||
# 預設模型
|
||||
--model sentence-transformers/all-MiniLM-L6-v2
|
||||
# 統一模型(所有 Rule 1/2/3 使用)
|
||||
--model nomic-embed-text-v2-moe:latest
|
||||
```
|
||||
|
||||
# 高精度模型
|
||||
--model sentence-transformers/all-mpnet-base-v2
|
||||
### 模型特性
|
||||
| 特性 | 說明 |
|
||||
|------|------|
|
||||
| **模型名稱** | `nomic-embed-text-v2-moe:latest` |
|
||||
| **向量維度** | 768 維 |
|
||||
| **多語言支持** | ✅ 完整支持(英語、中文、日語、韓語等) |
|
||||
| **模型架構** | Mixture of Experts (MoE) |
|
||||
| **推理速度** | 快速,適合實時應用 |
|
||||
|
||||
# 多語言模型
|
||||
--model sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2
|
||||
|
||||
# 中文模型
|
||||
--model sentence-transformers/paraphrase-multilingual-mpnet-base-v2
|
||||
### 使用方式
|
||||
```bash
|
||||
# 向量化命令
|
||||
cargo run --bin momentry -- vectorize <uuid> --model nomic-embed-text-v2-moe:latest
|
||||
```
|
||||
|
||||
---
|
||||
@@ -268,4 +290,3 @@ curl http://localhost:3002/api/v1/progress/{uuid}
|
||||
3. **獨立 Chunk 命令** - 分離 chunk 生成
|
||||
4. **獨立 Vectorize 命令** - 分離向量化流程
|
||||
5. **模型管理** - 新增、選擇、預覽模型
|
||||
|
||||
|
||||
+12
-12
@@ -22,7 +22,7 @@
|
||||
|
||||
| 項目 | 值 |
|
||||
|------|-----|
|
||||
| **主機** | `momentry.ddns.net` |
|
||||
| **主機** | `sftpgo.momentry.ddns.net` |
|
||||
| **SFTP 連接埠** | `2022` |
|
||||
| **用戶名** | `demo` |
|
||||
| **密碼** | `demopassword123` |
|
||||
@@ -36,15 +36,15 @@
|
||||
|
||||
```bash
|
||||
# 使用密碼連線
|
||||
sshpass -p "demopassword123" sftp -P 2022 demo@momentry.ddns.net
|
||||
sshpass -p "demopassword123" sftp -P 2022 demo@sftpgo.momentry.ddns.net
|
||||
|
||||
# 使用金鑰連線 (需先設定)
|
||||
sftp -P 2022 -i ~/.ssh/id_rsa demo@momentry.ddns.net
|
||||
sftp -P 2022 -i ~/.ssh/id_rsa demo@sftpgo.momentry.ddns.net
|
||||
```
|
||||
|
||||
### 2. FileZilla
|
||||
|
||||
1. **主機**: `sftp://momentry.ddns.net`
|
||||
1. **主機**: `sftp://sftpgo.momentry.ddns.net`
|
||||
2. **連接埠**: `2022`
|
||||
3. **協定**: `SFTP`
|
||||
4. **登入類型**: `一般`
|
||||
@@ -55,7 +55,7 @@ sftp -P 2022 -i ~/.ssh/id_rsa demo@momentry.ddns.net
|
||||
|
||||
1. 選擇 **連線 > 新連線**
|
||||
2. 協定選擇 **SFTP (SSH File Transfer Protocol)**
|
||||
3. 伺服器: `momentry.ddns.net`
|
||||
3. 伺服器: `sftpgo.momentry.ddns.net`
|
||||
4. 連接埠: `2022`
|
||||
5. 使用者名稱: `demo`
|
||||
6. 密碼: `demopassword123`
|
||||
@@ -65,7 +65,7 @@ sftp -P 2022 -i ~/.ssh/id_rsa demo@momentry.ddns.net
|
||||
```bash
|
||||
curl -u demo:demopassword123 \
|
||||
-T /path/to/video.mp4 \
|
||||
sftp://momentry.ddns.net:2022/demo/
|
||||
sftp://sftpgo.momentry.ddns.net:2022/demo/
|
||||
```
|
||||
|
||||
---
|
||||
@@ -76,7 +76,7 @@ curl -u demo:demopassword123 \
|
||||
|
||||
```bash
|
||||
# 進入互動式模式
|
||||
sftp demo@momentry.ddns.net -P 2022
|
||||
sftp demo@sftpgo.momentry.ddns.net -P 2022
|
||||
|
||||
# 常用指令
|
||||
sftp> pwd # 顯示目前目錄
|
||||
@@ -94,7 +94,7 @@ sftp> exit # 斷線
|
||||
|
||||
```bash
|
||||
# 上傳多個檔案
|
||||
sshpass -p "demopassword123" sftp -P 2022 demo@momentry.ddns.net <<EOF
|
||||
sshpass -p "demopassword123" sftp -P 2022 demo@sftpgo.momentry.ddns.net <<EOF
|
||||
cd uploads
|
||||
put video1.mp4
|
||||
put video2.mp4
|
||||
@@ -103,7 +103,7 @@ bye
|
||||
EOF
|
||||
|
||||
# 使用 glob 上傳
|
||||
sshpass -p "demopassword123" sftp -P 2022 demo@momentry.ddns.net <<EOF
|
||||
sshpass -p "demopassword123" sftp -P 2022 demo@sftpgo.momentry.ddns.net <<EOF
|
||||
mput /path/to/videos/*.mp4
|
||||
bye
|
||||
EOF
|
||||
@@ -119,7 +119,7 @@ EOF
|
||||
#!/bin/bash
|
||||
# upload.sh - 上傳視頻到 Momentry
|
||||
|
||||
HOST="momentry.ddns.net"
|
||||
HOST="sftpgo.momentry.ddns.net"
|
||||
PORT="2022"
|
||||
USER="demo"
|
||||
PASS="demopassword123"
|
||||
@@ -160,7 +160,7 @@ import sys
|
||||
import os
|
||||
|
||||
def upload_file(local_path, remote_dir="/demo/uploads"):
|
||||
host = "momentry.ddns.net"
|
||||
host = "sftpgo.momentry.ddns.net"
|
||||
port = 2022
|
||||
username = "demo"
|
||||
password = "demopassword123"
|
||||
@@ -250,7 +250,7 @@ curl -u demo:demopassword123 \
|
||||
|
||||
上傳目錄可能需要先建立:
|
||||
```bash
|
||||
sshpass -p "demopassword123" sftp -P 2022 demo@momentry.ddns.net <<EOF
|
||||
sshpass -p "demopassword123" sftp -P 2022 demo@sftpgo.momentry.ddns.net <<EOF
|
||||
mkdir uploads
|
||||
mkdir videos
|
||||
bye
|
||||
|
||||
@@ -1,5 +1,21 @@
|
||||
# Momentry 系統測試與驗證計劃
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | Warren |
|
||||
| 建立時間 | 2026-03-23 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-03-23 | 創建測試與驗證計劃 | Warren | OpenCode |
|
||||
|
||||
---
|
||||
|
||||
> **計劃階段** - 僅供討論,尚未執行
|
||||
> **建立時間**: 2026-03-23
|
||||
> **目標**: 安裝後測試、跑分、燒機
|
||||
|
||||
+15
-3
@@ -2,9 +2,21 @@
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 版本 | V1.0 |
|
||||
| 日期 | 2026-03-21 |
|
||||
| 目標讀者 | 系統管理員、開發者 |
|
||||
| 建立者 | Warren |
|
||||
| 建立時間 | 2026-03-21 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-03-21 | 創建使用手冊 | Warren | OpenCode |
|
||||
|
||||
---
|
||||
|
||||
**目標讀者**: 系統管理員、開發者
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,5 +1,21 @@
|
||||
# Momentry Core 版本管理規範
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | Warren |
|
||||
| 建立時間 | 2026-03-23 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-03-23 | 創建版本管理規範 | Warren | OpenCode |
|
||||
|
||||
---
|
||||
|
||||
## 1. 版本與通訊埠對照表
|
||||
|
||||
| 版本 | Binary | Port | Redis Prefix | 用途 |
|
||||
|
||||
@@ -1,5 +1,22 @@
|
||||
# Video Registration
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | Warren |
|
||||
| 建立時間 | 2026-03-25 |
|
||||
| 文件版本 | V1.1 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-03-25 | 創建文件 | Warren | OpenCode |
|
||||
| V1.1 | 2026-03-26 | 修正 curl 範例,新增 API Key 驗證標頭 | OpenCode | deepseek-reasoner |
|
||||
|
||||
---
|
||||
|
||||
## 概述
|
||||
|
||||
影片註冊 API (`POST /api/v1/register`) 用於將影片加入 Momentry Core 系統進行處理。
|
||||
@@ -139,11 +156,13 @@ SFTPgo 的用戶目錄結構:
|
||||
# 使用相對路徑註冊
|
||||
curl -X POST http://localhost:3002/api/v1/register \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{"path": "./demo/video.mp4"}'
|
||||
|
||||
# 或使用多層目錄
|
||||
curl -X POST http://localhost:3002/api/v1/register \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{"path": "./demo/movies/2024/video.mp4"}'
|
||||
```
|
||||
|
||||
@@ -185,6 +204,7 @@ pub fn extract_user_from_relative_path(relative_path: &str) -> (String, String)
|
||||
```bash
|
||||
curl -X POST http://localhost:3002/api/v1/probe \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-d '{"path": "./demo/video.mp4"}'
|
||||
```
|
||||
|
||||
@@ -224,10 +244,3 @@ curl -X POST http://localhost:3002/api/v1/probe \
|
||||
| `src/core/probe/ffprobe.rs` | ffprobe 整合 |
|
||||
| `docs/SFTPGO_DEMO_USER.md` | SFTPgo 用戶設置 |
|
||||
| `docs/API_ENDPOINTS.md` | API 端點總覽 |
|
||||
|
||||
## 歷史
|
||||
|
||||
| 日期 | 變更 |
|
||||
|------|------|
|
||||
| 2026-03-25 | 初始版本 - 新增 UUID 計算規則和重複註冊檢查 |
|
||||
| 2026-03-25 | 新增 Probe API 說明 |
|
||||
|
||||
@@ -12,7 +12,10 @@
|
||||
"name": "Webhook (Simple)",
|
||||
"type": "n8n-nodes-base.webhook",
|
||||
"typeVersion": 1,
|
||||
"position": [250, 300],
|
||||
"position": [
|
||||
250,
|
||||
300
|
||||
],
|
||||
"webhookId": "video-search-simple"
|
||||
},
|
||||
{
|
||||
@@ -34,7 +37,8 @@
|
||||
},
|
||||
"options": {
|
||||
"headers": {
|
||||
"Content-Type": "application/json"
|
||||
"Content-Type": "application/json",
|
||||
"X-API-Key": "demo_api_key_12345"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -42,17 +46,23 @@
|
||||
"name": "搜尋 Momentry",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 3,
|
||||
"position": [500, 300]
|
||||
"position": [
|
||||
500,
|
||||
300
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "// 處理 Momentry 搜尋結果\nconst data = $input.first().json;\nconst hits = data.hits;\n\nif (!hits || hits.length === 0) {\n return {\n json: {\n success: false,\n message: '找不到相關結果',\n query: data.query\n }\n };\n}\n\n// 格式化結果\nconst formattedResults = hits.map((hit, idx) => ({\n index: idx + 1,\n id: hit.id,\n title: hit.title,\n text: hit.text,\n startTime: hit.start,\n endTime: hit.end,\n relevance: Math.round(hit.score * 100) + '%',\n videoUrl: hit.media_url,\n videoLink: hit.media_url + '#t=' + hit.start + ',' + hit.end\n}));\n\nreturn {\n json: {\n success: true,\n query: data.query,\n totalFound: data.count,\n results: formattedResults\n }\n};"
|
||||
"jsCode": "// 處理 Momentry 搜尋結果\nconst data = $input.first().json;\nconst hits = data.hits;\n\nif (!hits || hits.length === 0) {\n return {\n json: {\n success: false,\n message: '找不到相關結果',\n query: data.query\n }\n };\n}\n\n// 格式化結果\nconst formattedResults = hits.map((hit, idx) => {\n return {\n index: idx + 1,\n id: hit.id,\n title: hit.title,\n text: hit.text,\n startTime: hit.start,\n endTime: hit.end,\n relevance: Math.round(hit.score * 100) + '%',\n file_path: hit.file_path\n };\n});\n\nreturn {\n json: {\n success: true,\n query: data.query,\n totalFound: data.count,\n results: formattedResults\n }\n};"
|
||||
},
|
||||
"id": "code-process-simple",
|
||||
"name": "處理結果",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 1,
|
||||
"position": [750, 300]
|
||||
"position": [
|
||||
750,
|
||||
300
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
@@ -63,7 +73,10 @@
|
||||
"name": "回傳結果",
|
||||
"type": "n8n-nodes-base.respondToWebhook",
|
||||
"typeVersion": 1,
|
||||
"position": [1000, 300]
|
||||
"position": [
|
||||
1000,
|
||||
300
|
||||
]
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
@@ -107,4 +120,4 @@
|
||||
"versionId": "1",
|
||||
"createdAt": "2026-03-23T00:00:00.000Z",
|
||||
"updatedAt": "2026-03-23T00:00:00.000Z"
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,10 @@
|
||||
"name": "Webhook Trigger",
|
||||
"type": "n8n-nodes-base.webhook",
|
||||
"typeVersion": 1,
|
||||
"position": [250, 300]
|
||||
"position": [
|
||||
250,
|
||||
300
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
@@ -21,22 +24,31 @@
|
||||
"contentType": "json",
|
||||
"body": "={{ JSON.stringify({query: $json.body.query || $json.body, limit: $json.body.limit || 5, uuid: $json.body.uuid}) }}",
|
||||
"options": {
|
||||
"timeout": 30000
|
||||
"timeout": 30000,
|
||||
"headers": {
|
||||
"X-API-Key": "demo_api_key_12345"
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": "Search Momentry Core",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.1,
|
||||
"position": [500, 300]
|
||||
"position": [
|
||||
500,
|
||||
300
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "// Process Momentry Core search results\nconst data = $input.first().json;\nconst hits = data.hits || [];\n\nif (hits.length === 0) {\n return {\n json: {\n success: false,\n message: 'No relevant results found',\n query: data.query,\n results: []\n }\n };\n}\n\n// Format results for RAG\nconst formattedResults = hits.map((hit, idx) => ({\n index: idx + 1,\n id: hit.id || hit.chunk_id,\n title: hit.title || 'Unknown Video',\n text: hit.text || hit.content || '',\n startTime: hit.start_time || hit.start || 0,\n endTime: hit.end_time || hit.end || 0,\n relevance: Math.round((hit.score || 0) * 100) + '%',\n videoUuid: hit.video_uuid || hit.uuid,\n mediaUrl: hit.media_url || '',\n deepLink: hit.media_url ? `${hit.media_url}#t=${hit.start_time || hit.start},${hit.end_time || hit.end}` : ''\n}));\n\n// Build context for RAG\nconst context = formattedResults\n .map(r => `[${r.index}] ${r.text} (Video: ${r.title}, Time: ${r.startTime}s-${r.endTime}s)`)\n .join('\\n\\n');\n\nreturn {\n json: {\n success: true,\n query: data.query,\n totalFound: data.count || hits.length,\n context: context,\n results: formattedResults\n }\n};"
|
||||
"jsCode": "// Process Momentry Core search results\nconst data = $input.first().json;\nconst hits = data.hits || [];\n\nif (hits.length === 0) {\n return {\n json: {\n success: false,\n message: 'No relevant results found',\n query: data.query,\n results: []\n }\n };\n}\n\n// Format results for RAG\nconst formattedResults = hits.map((hit, idx) => {\n return {\n index: idx + 1,\n id: hit.id || hit.chunk_id,\n title: hit.title || 'Unknown Video',\n text: hit.text || hit.content || '',\n startTime: hit.start_time || hit.start || 0,\n endTime: hit.end_time || hit.end || 0,\n relevance: Math.round((hit.score || 0) * 100) + '%',\n videoUuid: hit.video_uuid || hit.uuid,\n file_path: hit.file_path || ''\n };\n});\n\n// Build context for RAG\nconst context = formattedResults\n .map(r => \\`[\\${r.index}] \\${r.text} (Video: \\${r.title}, Time: \\${r.startTime}s-\\${r.endTime}s)\\`)\n .join('\\n\\n');\n\nreturn {\n json: {\n success: true,\n query: data.query,\n totalFound: data.count || hits.length,\n context: context,\n results: formattedResults\n }\n};"
|
||||
},
|
||||
"name": "Process RAG Results",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [750, 300]
|
||||
"position": [
|
||||
750,
|
||||
300
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
@@ -49,7 +61,10 @@
|
||||
"name": "Respond to Webhook",
|
||||
"type": "n8n-nodes-base.respondToWebhook",
|
||||
"typeVersion": 1.5,
|
||||
"position": [1000, 300]
|
||||
"position": [
|
||||
1000,
|
||||
300
|
||||
]
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
@@ -91,4 +106,4 @@
|
||||
"executionOrder": "v1"
|
||||
},
|
||||
"staticData": null
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,10 @@
|
||||
"name": "Webhook",
|
||||
"type": "n8n-nodes-base.webhook",
|
||||
"typeVersion": 1,
|
||||
"position": [250, 300],
|
||||
"position": [
|
||||
250,
|
||||
300
|
||||
],
|
||||
"webhookId": "video-search"
|
||||
},
|
||||
{
|
||||
@@ -34,7 +37,8 @@
|
||||
},
|
||||
"options": {
|
||||
"headers": {
|
||||
"Content-Type": "application/json"
|
||||
"Content-Type": "application/json",
|
||||
"X-API-Key": "demo_api_key_12345"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -42,17 +46,23 @@
|
||||
"name": "搜尋 Momentry",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 3,
|
||||
"position": [500, 300]
|
||||
"position": [
|
||||
500,
|
||||
300
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "const hits = $input.first().json.hits;\n\nif (!hits || hits.length === 0) {\n return {\n json: {\n message: '找不到相關結果',\n query: $input.first().json.query\n }\n };\n}\n\nconst results = hits.map((hit, index) => ({\n number: index + 1,\n text: hit.text,\n start: hit.start,\n end: hit.end,\n score: Math.round(hit.score * 100) + '%',\n url: hit.media_url + '#t=' + hit.start + ',' + hit.end,\n video_title: hit.title\n}));\n\nreturn {\n json: {\n query: $input.first().json.query,\n count: $input.first().json.count,\n results: results\n }\n};"
|
||||
"jsCode": "const hits = $input.first().json.hits;\n\nif (!hits || hits.length === 0) {\n return {\n json: {\n message: '找不到相關結果',\n query: $input.first().json.query\n }\n };\n}\n\nconst results = hits.map((hit, index) => {\n return {\n number: index + 1,\n text: hit.text,\n start: hit.start,\n end: hit.end,\n score: Math.round(hit.score * 100) + '%',\n video_title: hit.title,\n file_path: hit.file_path\n };\n});\n\nreturn {\n json: {\n query: $input.first().json.query,\n count: $input.first().json.count,\n results: results\n }\n};"
|
||||
},
|
||||
"id": "code-process",
|
||||
"name": "處理結果",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 1,
|
||||
"position": [750, 300]
|
||||
"position": [
|
||||
750,
|
||||
300
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
@@ -77,7 +87,10 @@
|
||||
"name": "Telegram 通知",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 3,
|
||||
"position": [1000, 300],
|
||||
"position": [
|
||||
1000,
|
||||
300
|
||||
],
|
||||
"continueOnFail": true
|
||||
}
|
||||
],
|
||||
@@ -122,4 +135,4 @@
|
||||
"versionId": "1",
|
||||
"createdAt": "2026-03-23T00:00:00.000Z",
|
||||
"updatedAt": "2026-03-23T00:00:00.000Z"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,563 @@
|
||||
# Momentry Core - Metadata 及 處理器總覽
|
||||
|
||||
本文檔說明 Momentry Core 中 chunks 資料表的 metadata 結構,以及各類處理器的輸出欄位。
|
||||
|
||||
## 1. Chunks 資料表結構
|
||||
|
||||
### 1.1 直接欄位 (Direct Columns)
|
||||
|
||||
這些欄位直接儲存於 chunks 資料表中:
|
||||
|
||||
| 欄位 | 類型 | 來源處理器 | 說明 |
|
||||
|------|------|----------|------|
|
||||
| `id` | serial | 系統 | 主鍵 |
|
||||
| `uuid` | varchar(32) | 系統 | 影片 UUID |
|
||||
| `chunk_id` | varchar(64) | 系統 | Chunk ID (如 sentence_0001) |
|
||||
| `chunk_index` | integer | 系統 | 順序編號 |
|
||||
| `chunk_type` | varchar(32) | 系統 | sentence/cut/time |
|
||||
| `text_content` | text | ASR processor | 語音轉文字結果 |
|
||||
| `content` | jsonb | - | 原始內容 (rule, data 等) |
|
||||
| `metadata` | jsonb | 多個處理器 | 參閱下方 1.2 |
|
||||
| `visual_stats` | jsonb | add_yolo_to_chunks.py | YOLO 識別結果 |
|
||||
| `speaker_ids` | text[] | ASRX processor | 說話者 ID 陣列 |
|
||||
| `face_ids` | integer[] | Face processor | 臉部 ID 陣列 |
|
||||
| `summary_text` | text | generate_chunk_summaries.py | LLM 生成摘要 |
|
||||
| `parent_chunk_id` | varchar(64) | 系統 | 父 chunk ID |
|
||||
| `fps` | double | ffprobe | 幀率 |
|
||||
| `start_frame` | bigint | ffprobe | 開始幀 |
|
||||
| `end_frame` | bigint | ffprobe | 結束幀 |
|
||||
| `metadata_version` | integer | 系統 | Metadata 版本 (5W1H, identity, visual) |
|
||||
| `content_version` | integer | 系統 | Content 版本 (text_content, summary_text) |
|
||||
| `created_at` | timestamp | 系統 | 建立時間 |
|
||||
| `updated_at` | timestamp | 系統 | 最後更新時間 |
|
||||
|
||||
### 版本控制說明
|
||||
|
||||
| 欄位 | 說明 | 遞增時機 |
|
||||
|------|------|----------|
|
||||
| `metadata_version` | Metadata 版本 | 更新 5W1H, identity, visual 時 |
|
||||
| `content_version` | Content 版本 | 更新 text_content, summary_text 時 |
|
||||
| `updated_at` | 最後更新時間 | 任何更新時自動更新 |
|
||||
|
||||
**判別更新語法**:
|
||||
|
||||
```sql
|
||||
-- 檢查哪些 chunk 需要重新生成 5W1H
|
||||
SELECT chunk_id, metadata_version, content_version, updated_at
|
||||
FROM dev.chunks
|
||||
WHERE metadata_version < 1;
|
||||
|
||||
-- 檢查特定時間後的更新
|
||||
SELECT chunk_id, updated_at
|
||||
FROM dev.chunks
|
||||
WHERE updated_at > '2024-01-01';
|
||||
|
||||
-- 檢查版本差異 (需要重新處理)
|
||||
SELECT c.*
|
||||
FROM dev.chunks c
|
||||
WHERE c.metadata_version <
|
||||
(SELECT MAX(metadata_version) FROM dev.chunks WHERE uuid = c.uuid);
|
||||
```
|
||||
|
||||
## 11. 動態 Metadata 管理
|
||||
|
||||
### 11.1 欄位動態增減
|
||||
|
||||
Metadata JSONB 支援動態欄位,可根據處理器執行結果動態添加:
|
||||
|
||||
```python
|
||||
# 動態添加欄位
|
||||
metadata = existing_metadata or {}
|
||||
metadata[field_name] = value
|
||||
UPDATE chunks SET metadata = metadata || %s::jsonb
|
||||
```
|
||||
|
||||
### 11.2 常見動態欄位
|
||||
|
||||
| 欄位 | 新增時機 | 來源處理器 |
|
||||
|------|----------|------------|
|
||||
| `chunk_5w1h` | 生成 summary | generate_chunk_summaries.py |
|
||||
| `chunk_identity` | ASRX/Face 執行後 | 來源欄位聚合 |
|
||||
| `chunk_visual` | YOLO 執行後 | add_yolo_to_chunks.py |
|
||||
| `chunk_emotion` | 情緒分析 | future emotion_processor.py |
|
||||
| `chunk_pose` | 姿勢辨識 | future pose_processor.py |
|
||||
| `chunk_sentiment` | 情感分析 | future sentiment_processor.py |
|
||||
|
||||
### 11.3 版本升級策略
|
||||
|
||||
每次重大更新時遞增版本號:
|
||||
|
||||
```python
|
||||
if新增重大欄位:
|
||||
metadata_version += 1
|
||||
# 記錄變更日誌
|
||||
```
|
||||
|
||||
### 11.4 重跑機制
|
||||
|
||||
```bash
|
||||
# 重跑特定版本後的 chunk
|
||||
python scripts/generate_chunk_summaries.py --uuid <uuid> --min-version 1
|
||||
|
||||
# 查看版本分佈
|
||||
SELECT metadata_version, COUNT(*)
|
||||
FROM dev.chunks
|
||||
GROUP BY metadata_version;
|
||||
```
|
||||
|
||||
### 1.2 Metadata 結構 (JSONB)
|
||||
|
||||
`metadata` 欄位包含多個子欄位,由不同處理器產生:
|
||||
|
||||
```json
|
||||
{
|
||||
"chunk_5w1h": {
|
||||
"who": "演員或角色",
|
||||
"what": "主要動作或事件",
|
||||
"when": "時間上下文",
|
||||
"where": "地點",
|
||||
"why": "目的或原因",
|
||||
"how": "表達方式"
|
||||
},
|
||||
"chunk_identity": {
|
||||
"speakers": ["speaker_001", "speaker_002"],
|
||||
"faces": ["face_1", "face_3"]
|
||||
},
|
||||
"chunk_visual": {
|
||||
"objects": ["person", "car", "tree"],
|
||||
"places": ["street", "office"]
|
||||
},
|
||||
"structured_summary": {
|
||||
"who": "Parent 級別角色",
|
||||
"what": "Parent 級別動作",
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| 子欄位 | 類型 | 來源處理器 | 說明 |
|
||||
|--------|------|----------|------|
|
||||
| `chunk_5w1h` | jsonb | generate_chunk_summaries.py | Chunk 級別的 5W1H + Emotion + Actions |
|
||||
| `chunk_5w1h.who` | string | person | 人物名稱 (含來源標記) |
|
||||
| `chunk_5w1h.what` | string | action | 具體動作 |
|
||||
| `chunk_5w1h.when` | string | position | 場景中位置 (beginning/middle/end) |
|
||||
| `chunk_5w1h.where` | string | location | 地點 |
|
||||
| `chunk_5w1h.why` | string | purpose | 目的 |
|
||||
| `chunk_5w1h.how` | string | manner | 表達方式 |
|
||||
| `chunk_5w1h.emotion` | string | emotion | 情緒/語氣 |
|
||||
| `chunk_5w1h.actions` | string[] | verbs | 動作動詞 |
|
||||
| `chunk_identity` | jsonb | 來源欄位聚合 | speaker_ids + face_ids 資訊 |
|
||||
| `chunk_visual` | jsonb | add_yolo_to_chunks.py | YOLO 物體識別結果 |
|
||||
| `structured_summary` | jsonb | regenerate_parent_5w1h.py | Parent 級別 5W1H + tone + characters + key_events |
|
||||
|
||||
### chunk_5w1h 欄位說明 (Chunk 級)
|
||||
|
||||
| 欄位 | 類型 | 說明 | 範例 |
|
||||
|------|------|------|------|
|
||||
| `who` | string | 此 chunk 出現的角色 (含來源) | "John (SPEAKER_1), Mary (face_3)" |
|
||||
| `what` | string | 此 chunk 的具體動作 | "Giving warning" |
|
||||
| `when` | string | 相對時間位置 | "Mid-scene" |
|
||||
| `where` | string | 地點 (如提及) | "Near taxi" |
|
||||
| `why` | string | 此動作的目的 | "Warn about danger" |
|
||||
| `how` | string | 表達/呈現方式 | "Urgent tone" |
|
||||
| `emotion` | string | 情緒/語氣 | "Fearful, urgent" |
|
||||
| `actions` | string[] | 動作動詞 | ["run", "shout", "warn"] |
|
||||
|
||||
**Prompt 增強內容**:
|
||||
- 從 person_identities 取得驗證的人物名稱
|
||||
- 包含 speaker_id 和 face_id 來源標記
|
||||
- 視覺辨識: objects, places, actions
|
||||
- Time range 傳入 chunk 時間範圍
|
||||
- Emotion + Actions 額外欄位
|
||||
|
||||
### chunk_identity 欄位說明
|
||||
|
||||
| 欄位 | 類型 | 說明 | 範例 |
|
||||
|------|------|------|------|
|
||||
| `speakers` | string[] | 說話者 ID | ["speaker_001", "speaker_002"] |
|
||||
| `faces` | string[] | 臉部 ID | ["face_1", "face_3"] |
|
||||
| `global_identity` | string | 對應的全局人物 ID | "person_001" |
|
||||
| `person_name` | string | 識別的人物名稱 | "John" |
|
||||
|
||||
> 說明:
|
||||
> - `speakers`/`faces` 來自 ASRX/Face processor
|
||||
> - `global_identity` 來自 `person_identities` 表,關聯 face_identity_id
|
||||
> - `person_name` 來自 `person_identities.name`,經過確認的人物名稱
|
||||
|
||||
### 全域人物 Identity (person_identities 表)
|
||||
|
||||
每個影片會識別並記錄出現的人物,儲存於 `dev.person_identities` 表:
|
||||
|
||||
| 欄位 | 類型 | 說明 |
|
||||
|------|------|------|
|
||||
| `person_id` | varchar(255) | 人物唯一 ID (如 person_001) |
|
||||
| `name` | varchar(255) | 人物名稱 (可確認) |
|
||||
| `speaker_id` | varchar(255) | 對應的說話者 ID |
|
||||
| `video_uuid` | varchar(255) | 影片 UUID |
|
||||
| `face_identity_id` | integer | 對應的 global identity |
|
||||
| `appearance_count` | integer | 出現次數 |
|
||||
| `first_appearance_time` | double | 首次出現時間 |
|
||||
| `last_appearance_time` | double | 最後出現時間 |
|
||||
| `confidence` | double | 辨識信心度 |
|
||||
| `is_confirmed` | boolean | 是否已確認 |
|
||||
|
||||
### 全域 Identity (face_identities 表)
|
||||
|
||||
跨影片的全局人物身份:
|
||||
|
||||
| 欄位 | 類型 | 說明 |
|
||||
|------|------|------|
|
||||
| `id` | serial | 主鍵 |
|
||||
| `face_id` | integer | 臉部 ID |
|
||||
| `name` | varchar(255) | 識別姓名 |
|
||||
| `embedding` | blob | 人臉向量特徵 |
|
||||
|
||||
### 人物識別流程
|
||||
|
||||
Momentry 的人物識別分為三個層級:
|
||||
|
||||
```
|
||||
層級 1: 原始識別 (chunks 表)
|
||||
├── chunks.face_ids → 臉部 ID (local to chunk)
|
||||
└── chunks.speaker_ids → 說話者 ID (local to chunk)
|
||||
|
||||
層級 2: 影片級識別 (person_identities 表)
|
||||
├── person_id → 人物 ID (影片內唯一)
|
||||
├── name → 識別出的人物名稱 (如 "John")
|
||||
├── speaker_id → 對應的說話者
|
||||
└── face_identity_id → 對應的全局 Identity
|
||||
|
||||
層級 3: 全局身份 (face_identities 表)
|
||||
├── id → 全局唯一 ID
|
||||
├── face_id → 臉部特徵 ID
|
||||
├── name → 確認的姓名
|
||||
└── embedding → 人臉向量 (用於比對)
|
||||
```
|
||||
|
||||
**識別流程說明**:
|
||||
|
||||
```
|
||||
Step 1: ASRX Processor
|
||||
chunks.speaker_ids ← 說話者分離
|
||||
|
||||
Step 2: Face Processor
|
||||
chunks.face_ids ← 臉部偵測
|
||||
|
||||
Step 3: Auto-identify
|
||||
person_identities ← 合併 speaker + face (影片級)
|
||||
|
||||
Step 4: Global Matching
|
||||
face_identities ← 人臉向量比對 (全局 Identity)
|
||||
↑
|
||||
合併相同人臉者為同一 Identity
|
||||
```
|
||||
|
||||
**命名原則**:
|
||||
|
||||
- `person_id` = 角色名 (如 "John", "Adam")
|
||||
- 而非 "Person_8"
|
||||
- 透過 speaker 對應 + 手動確認
|
||||
|
||||
**範例**:
|
||||
|
||||
```sql
|
||||
-- 取得影片中的人物列表
|
||||
SELECT person_id, name, speaker_id, appearance_count
|
||||
FROM dev.person_identities
|
||||
WHERE video_uuid = '384b0ff44aaaa1f1'
|
||||
ORDER BY appearance_count DESC;
|
||||
|
||||
-- 取得 chunk 的人物
|
||||
SELECT c.chunk_id, pi.name, pi.speaker_id
|
||||
FROM dev.chunks c
|
||||
JOIN dev.person_identities pi ON c.uuid = pi.video_uuid
|
||||
WHERE c.chunk_id = 'sentence_0001';
|
||||
```
|
||||
|
||||
### 取得 chunk 的人物資訊
|
||||
|
||||
```sql
|
||||
-- 取得某 chunk 的人物
|
||||
SELECT pi.name, pi.speaker_id, pi.appearance_count
|
||||
FROM dev.person_identities pi
|
||||
JOIN dev.chunks c ON c.uuid = pi.video_uuid
|
||||
WHERE c.chunk_id = 'sentence_0001';
|
||||
```
|
||||
|
||||
### chunk_visual 欄位說明
|
||||
|
||||
| 欄位 | 類型 | 說明 | 範例 |
|
||||
|------|------|------|------|
|
||||
| `objects` | string[] | YOLO 識別物體 | ["person", "car", "tree"] |
|
||||
| `places` | string[] | Places365 識別地點 | ["street", "office"] |
|
||||
|
||||
## 2. 處理器對照表
|
||||
|
||||
### 2.1 ASR 處理器 (語音辨識)
|
||||
|
||||
**用途**:將影片音軌轉換為文字
|
||||
|
||||
| 處理器 | 輸出欄位 | 說明 |
|
||||
|--------|---------|------|
|
||||
| asr_processor_small_multilingual.py | text_content | Small 模型,多語言 |
|
||||
| asr_processor_simplified.py | text_content | 簡化版 |
|
||||
| asr_processor_contract_v1.py | text_content | 契約版本 v1 |
|
||||
| asr_processor_contract_v2.py | text_content | 契約版本 v2 |
|
||||
|
||||
**輸出**:
|
||||
- `text_content`: 語音轉文字結果
|
||||
- 寫入 `chunks.content` 和 `chunks.text_content`
|
||||
|
||||
### 2.2 ASRX 處理器 (增強說話者辨識)
|
||||
|
||||
**用途**:說話者分離 (Diarization)
|
||||
|
||||
| 處理器 | 輸出欄位 | 說明 |
|
||||
|--------|---------|------|
|
||||
| asrx_processor.py | speaker_ids | 標準版 |
|
||||
| asrx_processor_contract_v1.py | speaker_ids | 契約版 v1 |
|
||||
|
||||
**輸出**:
|
||||
- `speaker_ids`: 說話者 ID 陣列,如 `["speaker_001", "speaker_002"]`
|
||||
- 目前為空 `{}`,需執行後才會填充
|
||||
|
||||
### 2.3 Face 處理器 (臉部偵測)
|
||||
|
||||
**用途**:偵測並追蹤人臉
|
||||
|
||||
| 處理器 | 輸出欄位 | 說明 |
|
||||
|--------|---------|------|
|
||||
| analyze_video_faces.py | face_ids | 臉部偵測 |
|
||||
|
||||
**輸出**:
|
||||
- `face_ids`: 臉部 ID 陣列,如 `[1, 3, 5]`
|
||||
- 目前為空 `{}`,需執行後才會填充
|
||||
|
||||
### 2.4 YOLO 處理器 (物體識別)
|
||||
|
||||
**用途**:識別場景中的物體和地點
|
||||
|
||||
| 處理器 | 輸出欄位 | 說明 |
|
||||
|--------|---------|------|
|
||||
| add_yolo_to_chunks.py | visual_stats, chunk_visual | YOLO + Places365 |
|
||||
|
||||
**輸出**:
|
||||
- `visual_stats`: 原始識別結果
|
||||
- `metadata.chunk_visual`: 簡化格式 `{objects: [...], places: [...]}`
|
||||
|
||||
### 2.5 Summary 處理器 (生成摘要)
|
||||
|
||||
**用途**:生成 chunk 摘要和 5W1H 分析
|
||||
|
||||
| 處理器 | 輸出欄位 | 說明 |
|
||||
|--------|---------|------|
|
||||
| generate_chunk_summaries.py | summary_text, chunk_5w1h, chunk_identity, chunk_visual | LLM 生成 |
|
||||
| regenerate_parent_5w1h.py | structured_summary | Parent 場景級 5W1H |
|
||||
|
||||
**輸入**:
|
||||
- chunk.text_content
|
||||
- parent_chunks.summary_text
|
||||
- parent_chunks.metadata.structured_summary
|
||||
- chunk.speaker_ids (用於 chunk_identity)
|
||||
- chunk.face_ids (用於 chunk_identity)
|
||||
- chunk.visual_stats (用於 chunk_visual)
|
||||
|
||||
**輸出**:
|
||||
- `summary_text`: 2-3 句摘要
|
||||
- `metadata.chunk_5w1h`: Who/What/When/Where/Why/How
|
||||
- `metadata.chunk_identity`: speakers, faces
|
||||
- `metadata.chunk_visual`: objects, places
|
||||
|
||||
## 3. Parent Chunks 結構
|
||||
|
||||
Parent chunks 代表場景 (scene) 層級:
|
||||
|
||||
| 欄位 | 類型 | 說明 |
|
||||
|------|------|------|
|
||||
| `id` | serial | 主鍵 |
|
||||
| `uuid` | varchar(32) | 影片 UUID |
|
||||
| `scene_order` | integer | 場景順序 |
|
||||
| `summary_text` | text | 場景摘要 (LLM 生成) |
|
||||
| `metadata` | jsonb | 包含 structured_summary |
|
||||
|
||||
### Parent Metadata 結構
|
||||
|
||||
```json
|
||||
{
|
||||
"structured_summary": {
|
||||
"who": "主要角色",
|
||||
"what": "主要事件",
|
||||
"when": "時間線",
|
||||
"where": "地點",
|
||||
"why": "動機",
|
||||
"how": "方式",
|
||||
"tone": ["緊張", "懸疑", "溫馨"],
|
||||
"characters": ["角色A", "角色B", "角色C"],
|
||||
"key_events": ["事件1", "事件2", "事件3"],
|
||||
"summary_5lines": "5行摘要..."
|
||||
},
|
||||
"auto_generated_by": "gemma4",
|
||||
"chunk_count": 885
|
||||
}
|
||||
```
|
||||
|
||||
### structured_summary 欄位說明
|
||||
|
||||
| 欄位 | 類型 | 說明 | 範例 |
|
||||
|------|------|------|------|
|
||||
| `who` | string | 主要角色 | "Mr. Balletman, Adam" |
|
||||
| `what` | string | 主要動作或事件 | "Escape attempt" |
|
||||
| `when` | string | 時間上下文 | "During critical moment" |
|
||||
| `where` | string | 地點 | "Near taxi" |
|
||||
| `why` | string | 動機或原因 | "Evade capture" |
|
||||
| `how` | string | 執行方式 | "Quickly moving to taxi" |
|
||||
| `tone` | string[] | 語氣/情緒 | ["Urgent", "Tense", "Fearful"] |
|
||||
| `characters` | string[] | 場景中的角色 | ["Mr. Balletman", "Adam", "Antagonist"] |
|
||||
| `key_events` | string[] | 關鍵事件 | ["Decision to flee", "Warning given"] |
|
||||
| `summary_5lines` | string | 5行摘要 | "Line 1\nLine 2..." |
|
||||
|
||||
## 4. Chunk 類型說明
|
||||
|
||||
| 類型 | 需要搜尋 | 說明 |
|
||||
|------|----------|------|
|
||||
| `sentence` | ✓ | 有 text_content,需向量化存入 Qdrant |
|
||||
| `cut` | ✗ | 場景剪輯點,無文字內容 |
|
||||
| `time` | ✗ | 時間區間標記,無文字 |
|
||||
|
||||
**搜尋適用性**:
|
||||
- sentence: 有文字內容,可進行語意搜尋
|
||||
- cut/time: 無文字,僅供時間定位使用
|
||||
|
||||
## 5. 處理流程 (Pipeline)
|
||||
|
||||
```
|
||||
1. ffprobe → 取得影片資訊 (fps, frame count)
|
||||
2. ASR processor → text_content
|
||||
3. [ASRX processor] → speaker_ids (選用)
|
||||
4. [Face processor] → face_ids (選用)
|
||||
5. add_yolo_to_chunks.py → visual_stats
|
||||
6. generate_chunk_summaries.py → summary_text + metadata
|
||||
7. [vectorize_chunk_summaries.py] → Qdrant 向量
|
||||
```
|
||||
|
||||
## 6. Qdrant Collections
|
||||
|
||||
| Collection | 向量類型 | 用途 |
|
||||
|------------|----------|------|
|
||||
| `momentry_dev_chunk_summaries` | nomic-embed-text | Chunk summary 語意搜尋 |
|
||||
| `momentry_dev_vectors` | 原始向量 | 備用 |
|
||||
|
||||
## 7. API 回傳格式
|
||||
|
||||
Chunk Detail API 合併 chunk 和 parent 的 metadata:
|
||||
|
||||
```
|
||||
metadata
|
||||
├── chunk_5w1h (chunk 級)
|
||||
├── chunk_identity (chunk 級)
|
||||
├── chunk_visual (chunk 級)
|
||||
├── structured_summary (parent 級) ← 只在有 parent 時
|
||||
├── auto_generated_by
|
||||
└── chunk_count
|
||||
```
|
||||
|
||||
## 8. 執行狀態檢查
|
||||
|
||||
```bash
|
||||
# 檢查 summary 生成進度
|
||||
psql -h localhost -U accusys -d momentry -c "
|
||||
SELECT COUNT(*) as total,
|
||||
COUNT(CASE WHEN summary_text IS NOT NULL THEN 1 END) as generated
|
||||
FROM dev.chunks WHERE chunk_type = 'sentence';"
|
||||
|
||||
# 檢查執行中的處理器
|
||||
ps aux | grep -E "processor|generate" | grep -v grep
|
||||
|
||||
# 檢查 visual_stats
|
||||
psql -h localhost -U accusys -d momentry -c "
|
||||
SELECT COUNT(*) FROM dev.chunks WHERE visual_stats IS NOT NULL;"
|
||||
```
|
||||
|
||||
## 9. 待執行處理器
|
||||
|
||||
### 人物識別處理器 (依序執行)
|
||||
|
||||
```bash
|
||||
# Step 1: ASRX 執行說話者分離
|
||||
python scripts/asrx_processor.py --uuid 384b0ff44aaaa1f1
|
||||
|
||||
# Step 2: Face 執行臉部偵測
|
||||
python scripts/analyze_video_faces.py --uuid 384b0ff44aaaa1f1
|
||||
|
||||
# Step 3: Auto-identify 建立影片級人物
|
||||
python scripts/auto_identify_persons.py --uuid 384b0ff44aaaa1f1
|
||||
|
||||
# Step 4: 全局 Identity 比對 (需累積一定數量的 face_identities)
|
||||
python scripts/match_faces_to_identities.py
|
||||
|
||||
# Step 5: 重新生成 chunk 5W1H (包含新的 identity 資訊)
|
||||
python scripts/generate_chunk_summaries.py --uuid 384b0ff44aaaa1f1
|
||||
```
|
||||
|
||||
### 檢查待處理狀態
|
||||
|
||||
```bash
|
||||
# 檢查 speaker_ids
|
||||
psql -h localhost -U accusys -d momentry -c "
|
||||
SELECT COUNT(*) FROM dev.chunks
|
||||
WHERE speaker_ids IS NOT NULL AND array_length(speaker_ids, 1) > 0;"
|
||||
|
||||
# 檢查 face_ids
|
||||
psql -h localhost -U accusys -d momentry -c "
|
||||
SELECT COUNT(*) FROM dev.chunks
|
||||
WHERE face_ids IS NOT NULL AND array_length(face_ids, 1) > 0;"
|
||||
|
||||
# 檢查 person_identities
|
||||
psql -h localhost -U accusys -d momentry -c "
|
||||
SELECT COUNT(*) FROM dev.person_identities
|
||||
WHERE video_uuid = '384b0ff44aaaa1f1';"
|
||||
|
||||
# 檢查 face_identities (全局)
|
||||
psql -h localhost -U accusys -d momentry -c "
|
||||
SELECT COUNT(*) FROM dev.face_identities;"
|
||||
```
|
||||
|
||||
## 10. 自動化重新生成機制
|
||||
|
||||
### 觸發條件
|
||||
|
||||
當以下事件發生時,應自動重新生成 chunk 的 5W1H 和相關 metadata:
|
||||
|
||||
| 事件 | 觸發動作 |
|
||||
|------|----------|
|
||||
| 第一次執行 ASRX | 重新生成含 speaker_ids 的 5W1H |
|
||||
| 第一次執行 Face | 重新生成含 face_ids 的 5W1H |
|
||||
| 新增 chunk | 為新 chunk 生成 5W1H |
|
||||
| 修改 chunk 內容 | 更新 5W1H 和 summary |
|
||||
| 新增/修改 speaker | 重新生成含新 speaker 的 5W1H |
|
||||
| 新增/修改 face | 重新生成含新 face 的 5W1H |
|
||||
|
||||
### 重新生成流程
|
||||
|
||||
```
|
||||
事件觸發
|
||||
↓
|
||||
更新 speaker_ids / face_ids / person_identities
|
||||
↓
|
||||
呼叫 generate_chunk_summaries.py --uuid <uuid> --regenerate
|
||||
↓
|
||||
重新產生:
|
||||
├── summary_text (2-3 句)
|
||||
├── metadata.chunk_5w1h (Who/What/When/Where/Why/How)
|
||||
├── metadata.chunk_identity (更新後的 speakers/faces)
|
||||
└── metadata.chunk_visual (若 visual_stats 有更新)
|
||||
```
|
||||
|
||||
### 重點
|
||||
|
||||
每次處理器執行後,Chunk metadata 會包含最新的:
|
||||
1. **speaker_ids** → 進入 `chunk_identity.speakers`
|
||||
2. **face_ids** → 進入 `chunk_identity.faces`
|
||||
3. **person_identities** → 進入 `chunk_identity.person_name`
|
||||
|
||||
確保 LLM 產生的 5W1H 包含最新的角色資訊。
|
||||
@@ -0,0 +1,116 @@
|
||||
# AI Agent 設計規範 (Agent Design Specification)
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | OpenCode |
|
||||
| 建立時間 | 2026-04-25 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-04-25 | 定義 Momentry Core 中 AI Agent 的標準設計與職責 | OpenCode | OpenCode |
|
||||
|
||||
---
|
||||
|
||||
## 1. 核心概念
|
||||
|
||||
在 Momentry Core 系統中,處理邏輯分為三個層次,本規範專注於第三層:
|
||||
|
||||
| 層次 | 名稱 | 特性 | 範例 |
|
||||
|------|------|------|------|
|
||||
| **L1** | **Processor (處理器)** | **確定性 (Deterministic)**<br>輸入 A 必得輸出 B。通常為編譯型程式或腳本。 | FFmpeg, Whisper (ASR), YOLO |
|
||||
| **L2** | **Rule (規則)** | **邏輯性 (Logic)**<br>基於明確的條件、正則表達式或時間軸聚合。 | 語句切分,時間重疊計算 |
|
||||
| **L3** | **Agent (智能體)** | **推論性 (Probabilistic)**<br>依賴 LLM 進行語義理解、決策或生成。具備 Prompt 或 Workflow。 | 5W1H 推論,身份解析,摘要生成 |
|
||||
|
||||
---
|
||||
|
||||
## 2. Agent 職責 (Responsibilities)
|
||||
|
||||
AI Agent 負責處理那些傳統程式難以精確定義規則的任務。
|
||||
**注意**: 在系統架構中,Agent 被視為一種 **資源 (Resource)**,與 Processor 和 Service 統一由 **資源註冊中心 (Resource Registry)** 管理。
|
||||
|
||||
1. **語義理解 (Semantic Understanding)**: 將非結構化數據(如 OCR 文字、雜訊 ASR 文本)轉化為結構化標籤 (5W1H)。
|
||||
2. **跨模態匹配 (Cross-Modal Matching)**: 綜合視覺、聽覺和文本證據,判斷「畫面中的臉」是否為「資料庫中的人」。
|
||||
3. **內容生成 (Content Generation)**: 為影片片段生成自然的摘要或標題。
|
||||
4. **查詢解析 (Query Parsing)**: 將用戶的自然語言請求轉譯為系統可執行的 API 調用序列。
|
||||
|
||||
---
|
||||
|
||||
## 3. 標準設計結構 (Design Structure)
|
||||
|
||||
所有 AI Agent 的設計文件必須遵循以下結構:
|
||||
|
||||
### 3.1 檔案命名
|
||||
* **格式**: `[AGENT_TYPE]_[PURPOSE].md`
|
||||
* **範例**: `CONTEXT_5W1H_INFERENCE.md`
|
||||
|
||||
### 3.2 文件內容
|
||||
|
||||
#### 3.2.1 Agent 目標 (Goal)
|
||||
簡短描述此 Agent 解決的業務問題。
|
||||
> **範例**: 從雜亂的 YOLO 標籤和 OCR 文本中推論場景的「地點」和「天氣」資訊。
|
||||
|
||||
#### 3.2.2 輸入數據 (Input)
|
||||
定義 Agent 接收的數據格式。通常來自 Processor 輸出或 Rule 產物。
|
||||
* **來源**: `PROCESSORS/` 或 `CHUNKING/`
|
||||
* **格式**: JSON, Text, List of Frames.
|
||||
|
||||
#### 3.2.3 核心邏輯 (Core Logic: Prompt / Workflow)
|
||||
這是 Agent 的靈魂。
|
||||
* **單一 Prompt Agent**: 提供完整的 System Prompt。
|
||||
```markdown
|
||||
## System Prompt
|
||||
You are a scene analysis assistant...
|
||||
```
|
||||
* **多步 Workflow Agent**: 提供步驟圖或偽代碼。
|
||||
```mermaid
|
||||
graph TD
|
||||
A[Start] --> B[Extract Entities]
|
||||
B --> C[Verify with Knowledge Base]
|
||||
C --> D[Output Result]
|
||||
```
|
||||
|
||||
#### 3.2.4 輸出格式 (Output)
|
||||
定義 Agent 產出的結構化數據 (通常為 JSON)。
|
||||
```json
|
||||
{
|
||||
"who": ["Actor Name"],
|
||||
"what": ["Action"],
|
||||
"confidence": 0.95
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.2.5 模型配置 (Model Config)
|
||||
建議使用的模型類型及其原因。
|
||||
* **推理模型 (Reasoning)**: `o1`, `R1` (用於複雜邏輯判斷)
|
||||
* **生成模型 (Generation)**: `GPT-4o`, `Sonnet` (用於摘要)
|
||||
* **本地模型 (Local)**: `Llama-3`, `Qwen` (用於隱私數據)
|
||||
|
||||
---
|
||||
|
||||
## 4. 開發工作流 (Development Workflow)
|
||||
|
||||
1. **定義需求**: 確定是否需要 AI 介入 (若規則可解,優先使用 Rule)。
|
||||
2. **撰寫 Prompt**: 在文檔中迭代 Prompt,直到達到穩定輸出。
|
||||
3. **工具串接**: 若需要外部數據 (如 TMDB),定義 Tool 定義。
|
||||
4. **實作封裝**: 將 Prompt/Workflow 封裝為 Rust/Python 模組,透過 API 調用。
|
||||
|
||||
---
|
||||
|
||||
## 5. 相關文件
|
||||
|
||||
* `UNIFIED_RESOURCE_REGISTRY.md` - 系統統一資源管理架構 (Agents 作為資源註冊)。
|
||||
* `AI_DRIVEN_PROCESSOR_CONTRACT.md` - Processor 層級的整合合約。
|
||||
* `CHUNKING_ARCHITECTURE.md` - Rule 層級的架構。
|
||||
* `FILE_IDENTITY_API_DESIGN.md` - 全局架構。
|
||||
|
||||
---
|
||||
|
||||
## 版本資訊
|
||||
|
||||
- 版本: V1.0
|
||||
- 建立日期: 2026-04-25
|
||||
@@ -0,0 +1,248 @@
|
||||
# Momentry Face / Speaker / Person API 開發指南
|
||||
|
||||
> **版本**: 3.5 | **更新日期**: 2026-04-17
|
||||
> **適用對象**: n8n 自動化流程開發者、Portal 前端開發者
|
||||
|
||||
---
|
||||
|
||||
## 快速開始
|
||||
|
||||
### 環境
|
||||
|
||||
| 環境 | URL | 說明 |
|
||||
|------|-----|------|
|
||||
| **正式版** | `https://api.momentry.ddns.net` | 外部存取 (HTTPS/TLSv1.3) |
|
||||
| **本機版** | `http://localhost:3002` | 同一台機器使用 (延遲更低) |
|
||||
|
||||
### 認證
|
||||
|
||||
所有 API 請求需在 Header 加入 API Key:
|
||||
|
||||
```bash
|
||||
curl https://api.momentry.ddns.net/api/v1/person/list \
|
||||
-H "X-API-Key: YOUR_API_KEY"
|
||||
```
|
||||
|
||||
**API Key**(marcom 團隊使用):
|
||||
```
|
||||
muser_68600856036340bcafc01930eb4bd839
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 鐵律:所有 Face/Speaker/Person API 都必須提供 video_uuid
|
||||
|
||||
**沒有例外。** 所有端點都需要 `video_uuid`。
|
||||
|
||||
```
|
||||
錯誤: GET /api/v1/person/list → 400 missing field `video_uuid`
|
||||
錯誤: GET /api/v1/person/Person_0 → 400 missing field `video_uuid`
|
||||
正確: GET /api/v1/person/list?video_uuid=xxx → 200 OK
|
||||
```
|
||||
|
||||
| 識別碼 | 全域唯一 | 說明 |
|
||||
|--------|:---:|------|
|
||||
| `chunk_id` | ❌ | 每部影片重新編號 |
|
||||
| `person_id` | ❌ | 每部影片有自己的 Person_0, Person_1... |
|
||||
| `speaker_id` | ❌ | 每部影片有自己的 SPEAKER_0, SPEAKER_1... |
|
||||
| **`video_uuid + person_id`** | ✅ | 唯一組合 |
|
||||
| **`video_uuid + chunk_id`** | ✅ | 唯一組合 |
|
||||
| `face_id` | ✅ | UUID 格式,全域唯一 |
|
||||
| `merge_id` | ✅ | UUID 格式,全域唯一 |
|
||||
|
||||
---
|
||||
|
||||
## API 端點總覽(全部需要 video_uuid)
|
||||
|
||||
| 端點 | 方法 | video_uuid 位置 | 說明 |
|
||||
|------|:---:|:---:|------|
|
||||
| `/api/v1/person/list` | GET | query | 列出人物 |
|
||||
| `/api/v1/person/auto-identify` | POST | body | 自動識別人 |
|
||||
| `/api/v1/person/suggest` | POST | body | AI 建議 |
|
||||
| `/api/v1/person/:id` | GET | query | 人物詳情 |
|
||||
| `/api/v1/person/:id` | PATCH | query | 更新人物 |
|
||||
| `/api/v1/person/:id/thumbnail` | GET | query | 臉部截圖 |
|
||||
| `/api/v1/person/:id/timeline` | GET | query | 出場時間軸 |
|
||||
| `/api/v1/person/:id/similar` | GET | query | 相似人物 |
|
||||
| `/api/v1/person/:id/appearances` | GET | query | 出場紀錄 |
|
||||
| `/api/v1/person/:id/unbind-speaker` | POST | body | 解除 Speaker |
|
||||
| `/api/v1/person/:id/reassign-speaker` | POST | body | 重新綁定 Speaker |
|
||||
| `/api/v1/person/:id/remove-appearance` | POST | body | 刪除出場紀錄 |
|
||||
| `/api/v1/person/:id/reassign-appearance` | POST | body | 轉移出場紀錄 |
|
||||
| `/api/v1/person/:id/split` | POST | body | 分割人物 |
|
||||
| `/api/v1/person/merge` | POST | body | 合併人物 |
|
||||
| `/api/v1/person/merge/undo` | POST | body | 撤銷合併 |
|
||||
| `/api/v1/person/merge/history` | GET | query | 合併歷史 |
|
||||
| `/api/v1/search/universal` | POST | body | 統一搜尋 |
|
||||
| `/api/v1/search/persons` | GET | query | 搜尋人物 |
|
||||
| `/api/v1/chunks/:id/persons` | GET | query | chunk 內人物 |
|
||||
| `/api/v1/face/register` | POST | body | 註冊臉孔 |
|
||||
| `/api/v1/face/list` | GET | query | 已註冊臉孔列表 |
|
||||
|
||||
---
|
||||
|
||||
## 詳細 API 說明
|
||||
|
||||
### 1. GET /api/v1/person/list
|
||||
|
||||
列出指定影片的人物。
|
||||
|
||||
**Query Parameters:**
|
||||
|
||||
| 參數 | 類型 | 必填 | 說明 |
|
||||
|------|:---:|:---:|------|
|
||||
| `video_uuid` | string | **是** | 影片 UUID |
|
||||
| `limit` | int | 否 | 每頁筆數 (預設 50) |
|
||||
| `offset` | int | 否 | 偏移量 (預設 0) |
|
||||
| `min_appearances` | int | 否 | 最低出場次數 |
|
||||
| `has_speaker` | bool | 否 | 僅顯示有 Speaker 的人物 |
|
||||
|
||||
**Request:**
|
||||
```
|
||||
GET /api/v1/person/list?video_uuid=384b0ff44aaaa1f1&limit=10&min_appearances=100
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"persons": [
|
||||
{
|
||||
"person_id": "Person_0",
|
||||
"name": null,
|
||||
"speaker_id": "SPEAKER_0",
|
||||
"appearance_count": 17832,
|
||||
"total_appearance_duration": 3600.5,
|
||||
"first_appearance_time": 79.56,
|
||||
"last_appearance_time": 6863.34,
|
||||
"is_confirmed": false,
|
||||
"speaker_confidence": 0.504
|
||||
}
|
||||
],
|
||||
"total": 303
|
||||
}
|
||||
```
|
||||
|
||||
### 2. GET /api/v1/person/:id
|
||||
|
||||
取得人物詳情。
|
||||
|
||||
**Query Parameters:**
|
||||
|
||||
| 參數 | 類型 | 必填 |
|
||||
|------|:---:|:---:|
|
||||
| `video_uuid` | string | **是** |
|
||||
|
||||
### 3. POST /api/v1/person/merge
|
||||
|
||||
合併多個人物為一人。
|
||||
|
||||
**Request:**
|
||||
```json
|
||||
{
|
||||
"video_uuid": "384b0ff44aaaa1f1",
|
||||
"target_person_id": "Person_0",
|
||||
"source_person_ids": ["Person_4", "Person_25"]
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Merged 2 persons into Person_0",
|
||||
"target_person_id": "Person_0",
|
||||
"merge_id": "5b12e3ac-12fa-45c0-88e1-5cff67604a7d"
|
||||
}
|
||||
```
|
||||
|
||||
> ⚠️ **請儲存 `merge_id`**,以便日後撤銷合併。
|
||||
|
||||
### 4. POST /api/v1/search/universal
|
||||
|
||||
統一搜尋。
|
||||
|
||||
**Request:**
|
||||
```json
|
||||
{
|
||||
"query": "stamp",
|
||||
"uuid": "384b0ff44aaaa1f1",
|
||||
"types": ["chunk", "person"],
|
||||
"limit": 20
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 影片定位:Frame 為主
|
||||
|
||||
**重要**: 所有影片位置都以 **frame (幀號)** 為唯一準確單位,time 僅供參考。
|
||||
|
||||
```json
|
||||
{
|
||||
"start_frame": 29795,
|
||||
"end_frame": 29963,
|
||||
"fps": 59.94,
|
||||
"start_time": 497.08,
|
||||
"end_time": 499.88
|
||||
}
|
||||
```
|
||||
|
||||
**轉換公式**: `time = frame / fps`
|
||||
|
||||
> ⚠️ **注意**: 所有搜尋 API (`/api/v1/search`, `/api/v1/n8n/search`, `/api/v1/search/universal`) 現在都統一回傳 `start_frame`, `end_frame`, `fps` 欄位,確保前端可以精確定位影片幀號。
|
||||
|
||||
---
|
||||
|
||||
## n8n 工作流範例
|
||||
|
||||
```
|
||||
[Webhook: video_processed]
|
||||
body: { "uuid": "384b0ff44aaaa1f1" }
|
||||
↓
|
||||
[HTTP: POST /api/v1/person/auto-identify]
|
||||
body: { "video_uuid": "{{ $json.uuid }}" }
|
||||
↓
|
||||
[HTTP: POST /api/v1/person/suggest]
|
||||
body: { "video_uuid": "{{ $json.uuid }}" }
|
||||
↓
|
||||
[IF: confidence >= 0.7]
|
||||
├─ YES → [HTTP: PATCH /api/v1/person/{{person_id}}?video_uuid={{uuid}}]
|
||||
└─ NO → [等待人工確認]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 錯誤碼
|
||||
|
||||
| HTTP | 說明 |
|
||||
|:---:|------|
|
||||
| 200 | 成功 |
|
||||
| 400 | 缺少 video_uuid 或參數錯誤 |
|
||||
| 401 | API Key 無效 |
|
||||
| 404 | 資源不存在 |
|
||||
| 422 | 請求體缺少 video_uuid |
|
||||
| 500 | 伺服器錯誤 |
|
||||
|
||||
---
|
||||
|
||||
## 資料庫結構
|
||||
|
||||
### person_identities
|
||||
|
||||
| 欄位 | 類型 | 說明 |
|
||||
|------|------|------|
|
||||
| `person_id` | VARCHAR | 識別碼 (每部影片獨立) |
|
||||
| `video_uuid` | VARCHAR | **所屬影片 (必填)** |
|
||||
| `name` | VARCHAR | 人物名稱 |
|
||||
| `speaker_id` | VARCHAR | 對應說話者 ID (每部影片獨立) |
|
||||
| `appearance_count` | INT | 出場次數 |
|
||||
| `is_confirmed` | BOOLEAN | 是否已確認 |
|
||||
|
||||
### 唯一性約束
|
||||
|
||||
```sql
|
||||
UNIQUE (video_uuid, person_id)
|
||||
```
|
||||
|
||||
每部影片可以有自己的 `Person_0`,但同一部影片內 `person_id` 必須唯一。
|
||||
@@ -0,0 +1,183 @@
|
||||
# Face, Speaker, Person, Identity API 教學示範
|
||||
|
||||
本文件將以 1963 年電影《Charade》(謎中謎)為例,示範如何使用 API 管理 **Face** (臉孔)、**Person** (影片中的角色實體) 與 **Identity** (真實身份)。
|
||||
|
||||
## 核心概念定義
|
||||
|
||||
在開始之前,請區分以下名詞:
|
||||
|
||||
1. **Face (臉孔)**: 影像中偵測到的具體臉部特徵數據(向量)。
|
||||
2. **Person (角色實體)**: 在特定影片中出現的角色。他是 Face + Speaker (說話者) 的集合體。
|
||||
* *例如:影片 `384b0ff44aaaa1f1` 中的 `Person_17`。*
|
||||
3. **Identity (真實身份)**: 跨越所有影片的全域實體(如真實演員或新聞人物)。
|
||||
* *例如:Cary Grant, Audrey Hepburn。*
|
||||
|
||||
---
|
||||
|
||||
## 前置準備
|
||||
|
||||
* **API URL**: `http://localhost:3003`
|
||||
* **API Key**: `/`
|
||||
* **目標影片 (Video UUID)**: `384b0ff44aaaa1f1` (Charade)
|
||||
|
||||
---
|
||||
|
||||
## 情境設定
|
||||
|
||||
我們要在影片中識別兩位主角:
|
||||
1. **Audrey Hepburn** (飾演 Reggie Lampert)
|
||||
2. **Cary Grant** (飾演 Peter Joshua)
|
||||
|
||||
---
|
||||
|
||||
## 步驟一:查看影片中的現有角色 (Person List)
|
||||
|
||||
首先,我們查詢系統在影片中偵測到了哪些人物 (Person)。
|
||||
|
||||
```bash
|
||||
curl -s "http://localhost:3003/api/v1/person/list?video_uuid=384b0ff44aaaa1f1&limit=5" \
|
||||
-H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" \
|
||||
| python3 -m json.tool
|
||||
```
|
||||
|
||||
**預期回應**:
|
||||
你會看到類似如下的列表,其中包含系統自動分配的 `person_id` (例如 `Person_17`, `Person_4` 等)。
|
||||
|
||||
```json
|
||||
{
|
||||
"persons": [
|
||||
{
|
||||
"person_id": "Person_17",
|
||||
"name": null,
|
||||
"speaker_id": "SPEAKER_1",
|
||||
"appearance_count": 1636
|
||||
},
|
||||
{
|
||||
"person_id": "Person_4",
|
||||
"name": null,
|
||||
"speaker_id": "SPEAKER_0",
|
||||
"appearance_count": 936
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 步驟二:建立身份並綁定角色 (Register Identity from Person)
|
||||
|
||||
假設經過人工確認,我們知道 `Person_17` 是 Audrey Hepburn。我們可以使用單一 API 同時完成 **「建立 Identity」** 與 **「綁定 Person」** 兩個動作。
|
||||
|
||||
### 範例 1: 註冊 Audrey Hepburn
|
||||
|
||||
我們指定 `Person_17` 為 "Audrey Hepburn"。系統會檢查此 Identity 是否存在;若不存在則建立,若已存在則直接綁定。
|
||||
|
||||
```bash
|
||||
curl -s -X POST "http://localhost:3003/api/v1/identities/from-person" \
|
||||
-H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"video_uuid": "384b0ff44aaaa1f1",
|
||||
"person_id": "Person_17",
|
||||
"identity_name": "Audrey Hepburn",
|
||||
"metadata": { "role": "Reggie Lampert" }
|
||||
}' | python3 -m json.tool
|
||||
```
|
||||
|
||||
**預期回應**:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Successfully registered identity 'Audrey Hepburn' and linked to person 'Person_17'",
|
||||
"identity_id": 10,
|
||||
"identity_name": "Audrey Hepburn",
|
||||
"person_id": "Person_17"
|
||||
}
|
||||
```
|
||||
|
||||
*(註:此操作會自動將該影片中 `Person_17` 的名稱更新為 "Audrey Hepburn")*
|
||||
|
||||
### 範例 2: 註冊 Cary Grant
|
||||
|
||||
假設 `Person_4` 是 Cary Grant,我們進行同樣的操作。
|
||||
|
||||
```bash
|
||||
curl -s -X POST "http://localhost:3003/api/v1/identities/from-person" \
|
||||
-H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"video_uuid": "384b0ff44aaaa1f1",
|
||||
"person_id": "Person_4",
|
||||
"identity_name": "Cary Grant",
|
||||
"metadata": { "role": "Peter Joshua" }
|
||||
}' | python3 -m json.tool
|
||||
```
|
||||
|
||||
**預期回應**:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Successfully registered identity 'Cary Grant' and linked to person 'Person_4'",
|
||||
"identity_id": 11,
|
||||
"identity_name": "Cary Grant",
|
||||
"person_id": "Person_4"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 步驟三:查看全域身份庫 (List Identities)
|
||||
|
||||
現在我們可以查看所有已建立的「真實身份」,這些身份是跨影片通用的。
|
||||
|
||||
```bash
|
||||
curl -s "http://localhost:3003/api/v1/identities?limit=10" \
|
||||
-H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" \
|
||||
| python3 -m json.tool
|
||||
```
|
||||
|
||||
**預期回應**:
|
||||
你應該能看到剛剛建立的 "Audrey Hepburn" 和 "Cary Grant"。
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": 11,
|
||||
"name": "Cary Grant",
|
||||
"metadata": { "role": "Peter Joshua" }
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"name": "Audrey Hepburn",
|
||||
"metadata": { "role": "Reggie Lampert" }
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 步驟四:驗證綁定結果
|
||||
|
||||
再次查詢影片中的 `Person` 列表,確認名稱是否已自動更新。
|
||||
|
||||
```bash
|
||||
curl -s "http://localhost:3003/api/v1/person/list?video_uuid=384b0ff44aaaa1f1&limit=5" \
|
||||
-H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" \
|
||||
| python3 -m json.tool
|
||||
```
|
||||
|
||||
**預期結果**:
|
||||
原本的 `Person_17` 現在應該顯示為 `"name": "Audrey Hepburn"`。
|
||||
|
||||
---
|
||||
|
||||
## 常見問題 (FAQ)
|
||||
|
||||
**Q: 如果我想把「現有的 Person」綁定到「已經存在的 Identity」,要怎麼做?**
|
||||
A: 使用相同的 `POST /api/v1/identities/from-person` API。只要傳入相同的 `identity_name` (例如 "Audrey Hepburn"),系統會自動找到該 Identity 並將新的 Person 連結過去,不會建立重複的 Identity。
|
||||
|
||||
**Q: Identity 和 Person 的差別是什麼?**
|
||||
A: **Identity** 是真實世界的人(例如 "Tom Hanks"),這是全域共享的。
|
||||
**Person** 是他在某部電影裡的具體出現(例如《阿甘正傳》裡的阿甘)。一個 Identity 可以對應多個影片中的多個 Person。
|
||||
@@ -0,0 +1,97 @@
|
||||
# Face/Speaker/Person 分析完成度
|
||||
|
||||
**UUID**: `384b0ff44aaaa1f1`
|
||||
**视频**: Charade (1963) - ~115 min, 412,343 frames, 59.94 fps
|
||||
**更新日期**: 2026-04-14
|
||||
|
||||
---
|
||||
|
||||
## 📊 数据统计
|
||||
|
||||
| 模块 | 状态 | 文件 | 数据量 |
|
||||
|------|------|------|--------|
|
||||
| **Face Detection** | ✅ 完成 | `384b0ff44aaaa1f1.face.json` | 10,691 frames, 25,174 faces |
|
||||
| **Face Clustering** | ✅ 完成 | `384b0ff44aaaa1f1.face_clustered.json` | 302 unique Person IDs |
|
||||
| **ASR (语音识别)** | ✅ 完成 | `384b0ff44aaaa1f1.asr.json` | 1,011 segments |
|
||||
| **ASRX (增强语音)** | ✅ 完成 | `384b0ff44aaaa1f1.asrx.json` | - |
|
||||
| **Pose (姿态)** | ✅ 完成 | `384b0ff44aaaa1f1.pose.json` | - |
|
||||
| **Speaker Diarization** | ⚠️ 未集成 | - | ASR segments 无 speaker 信息 |
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Top 20 人物 (按帧数)
|
||||
|
||||
| Person ID | 帧数 | 说明 |
|
||||
|-----------|------|------|
|
||||
| Person_0 | 17,832 | 主角 (Cary Grant/Audrey Hepburn) |
|
||||
| Person_17 | 1,636 | 主要配角 |
|
||||
| Person_4 | 936 | 主要配角 |
|
||||
| Person_25 | 217 | 次要角色 |
|
||||
| Person_12 | 154 | 次要角色 |
|
||||
| Person_46 | 122 | - |
|
||||
| Person_70 | 119 | - |
|
||||
| Person_8 | 109 | - |
|
||||
| Person_3 | 109 | - |
|
||||
| Person_124 | 97 | - |
|
||||
| Person_37 | 95 | - |
|
||||
| Person_176 | 90 | - |
|
||||
| Person_34 | 85 | - |
|
||||
| Person_80 | 78 | - |
|
||||
| Person_50 | 73 | - |
|
||||
| Person_94 | 73 | - |
|
||||
| Person_33 | 63 | - |
|
||||
| Person_21 | 58 | - |
|
||||
| Person_14 | 57 | - |
|
||||
| Person_7 | 57 | - |
|
||||
|
||||
**总计**: 302 个独立 Person ID,其中 282 个出现少于 57 帧。
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 未完成的整合
|
||||
|
||||
### 1. Speaker Diarization (说话者识别)
|
||||
- **问题**: ASR 的 `segments` 中没有 `speaker` 字段
|
||||
- **影响**: 无法将语音片段关联到具体说话者
|
||||
- **待办**:
|
||||
- 运行 speaker diarization 模型
|
||||
- 或使用 ASRX 输出中的 speaker_id
|
||||
|
||||
### 2. Face ↔ Speaker 关联
|
||||
- **脚本存在**: `scripts/sync_face_speaker_to_chunks.py`
|
||||
- **状态**: 需要数据库支持 (chunks 表)
|
||||
- **功能**: 将 face_ids 和 speaker_ids 写入 chunks 表
|
||||
|
||||
### 3. Face ↔ ASR 验证
|
||||
- **文档存在**: `scripts/ASR_FACE_POSE_INTEGRATION.md`
|
||||
- **状态**: 方案设计完成,但未执行
|
||||
- **功能**: 使用 Face + Pose 验证 ASR 语句的置信度
|
||||
|
||||
### 4. 人物命名/识别
|
||||
- **当前**: 只有机器生成的 Person_0, Person_1...
|
||||
- **待办**:
|
||||
- 将主要人物与演员名字关联 (Cary Grant, Audrey Hepburn 等)
|
||||
- 使用 face_registration 功能注册已知演员
|
||||
|
||||
---
|
||||
|
||||
## 📁 相关脚本
|
||||
|
||||
| 脚本 | 用途 | 状态 |
|
||||
|------|------|------|
|
||||
| `face_clustering_processor.py` | 人脸聚类 | ✅ 已执行 |
|
||||
| `fast_face_clustering_processor.py` | 快速人脸聚类 | 备选 |
|
||||
| `sync_face_speaker_to_chunks.py` | 同步到数据库 | 待执行 |
|
||||
| `match_speakers_to_chunks.py` | 匹配说话者 | 待执行 |
|
||||
| `export_person_thumbnails.py` | 导出人物缩略图 | 可用 |
|
||||
| `face_registration.py` | 人脸注册 | 可用 |
|
||||
| `register_sample_faces.py` | 注册样本 | 可用 |
|
||||
|
||||
---
|
||||
|
||||
## 🔧 建议下一步
|
||||
|
||||
1. **检查 ASRX 输出** 是否有 speaker diarization 信息
|
||||
2. **导出 Top 20 人物缩略图** 供人工识别
|
||||
3. **关联主要演员名字** 到 Person_0, Person_17, Person_4 等
|
||||
4. **执行 Face ↔ ASR 验证** 提升语音识别置信度
|
||||
@@ -0,0 +1,421 @@
|
||||
# Face / Speaker / Person API 簡易指南
|
||||
|
||||
> **版本**: 1.1 | **適用**: 前端開發團隊
|
||||
> **更新日期**: 2026-04-17
|
||||
>
|
||||
> **⚠️ 重要**: 3002 (正式版) 和 3003 (開發版) 使用**完全獨立的資料空間** (public vs dev schema),絕非共用。開發版測試不會影響正式版資料。
|
||||
|
||||
---
|
||||
|
||||
## 快速開始
|
||||
|
||||
```bash
|
||||
export BASE="http://localhost:3002"
|
||||
export KEY="muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69"
|
||||
export UUID="384b0ff44aaaa1f1"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 1. 用 uuid + chunk_id 查看 face / speaker / person
|
||||
|
||||
### 取得 chunk 內的人物
|
||||
|
||||
```bash
|
||||
curl "$BASE/api/v1/chunks/sentence_0093/persons" \
|
||||
-H "X-API-Key: $KEY"
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"chunk_id": "sentence_0093",
|
||||
"persons": [
|
||||
{
|
||||
"person_id": "Person_0",
|
||||
"name": "Person_0",
|
||||
"confidence": 0.85,
|
||||
"overlap_duration": 3.2
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 取得 chunk 的 speaker(從 content 欄位)
|
||||
|
||||
```bash
|
||||
curl -X POST "$BASE/api/v1/search/universal" \
|
||||
-H "X-API-Key: $KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"query": "", "uuid": "'$UUID'", "types": ["chunk"], "filters": {"speaker_id": "SPEAKER_0"}, "limit": 10}'
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"type": "chunk",
|
||||
"chunk_id": "sentence_0093",
|
||||
"chunk_type": "sentence",
|
||||
"start_frame": 29795,
|
||||
"end_frame": 29963,
|
||||
"fps": 59.94,
|
||||
"start_time": 497.08,
|
||||
"end_time": 499.88,
|
||||
"text": "You could have the stamps.",
|
||||
"speaker_id": "SPEAKER_0"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 統一搜尋 chunk + face + person
|
||||
|
||||
```bash
|
||||
curl -X POST "$BASE/api/v1/search/universal" \
|
||||
-H "X-API-Key: $KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"query": "stamp", "uuid": "'$UUID'", "types": ["chunk", "person"], "limit": 10}'
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"query": "stamp",
|
||||
"results": [
|
||||
{
|
||||
"type": "chunk",
|
||||
"chunk_id": "sentence_1566",
|
||||
"chunk_type": "sentence",
|
||||
"start_frame": 329980,
|
||||
"end_frame": 330040,
|
||||
"fps": 59.94,
|
||||
"start_time": 5506.84,
|
||||
"end_time": 5507.84,
|
||||
"text": "The envelope, but the stamps on it",
|
||||
"speaker_id": "SPEAKER_0"
|
||||
},
|
||||
{
|
||||
"type": "person",
|
||||
"person_id": "Person_0",
|
||||
"name": "Person_0",
|
||||
"speaker_id": "SPEAKER_0",
|
||||
"appearance_count": 17832
|
||||
}
|
||||
],
|
||||
"total": 10,
|
||||
"took_ms": 27
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 選擇 face 並綁定 person
|
||||
|
||||
### 步驟 1: 列出所有人物
|
||||
|
||||
```bash
|
||||
curl "$BASE/api/v1/person/list?min_appearances=100&has_speaker=true&limit=20" \
|
||||
-H "X-API-Key: $KEY"
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"persons": [
|
||||
{
|
||||
"person_id": "Person_0",
|
||||
"name": "Person_0",
|
||||
"speaker_id": "SPEAKER_0",
|
||||
"appearance_count": 17832
|
||||
},
|
||||
{
|
||||
"person_id": "Person_17",
|
||||
"name": "Person_17",
|
||||
"speaker_id": "SPEAKER_1",
|
||||
"appearance_count": 1636
|
||||
}
|
||||
],
|
||||
"total": 9
|
||||
}
|
||||
```
|
||||
|
||||
### 步驟 2: 查看人物詳情 + 取得截圖
|
||||
|
||||
```bash
|
||||
# 查看詳情
|
||||
curl "$BASE/api/v1/person/Person_0" -H "X-API-Key: $KEY"
|
||||
|
||||
# 取得臉部截圖
|
||||
curl "$BASE/api/v1/person/Person_0/thumbnail?video_uuid=$UUID" \
|
||||
-H "X-API-Key: $KEY" -o person0_face.jpg
|
||||
|
||||
# 取得第 5 次出現的臉部截圖
|
||||
curl "$BASE/api/v1/person/Person_0/thumbnail?video_uuid=$UUID&index=4" \
|
||||
-H "X-API-Key: $KEY" -o person0_face_5.jpg
|
||||
```
|
||||
|
||||
### 步驟 3: 綁定名稱(將 face 關聯到 person)
|
||||
|
||||
```bash
|
||||
curl -X PATCH "$BASE/api/v1/person/Person_0" \
|
||||
-H "X-API-Key: $KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"name": "Cary Grant", "is_confirmed": true}'
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Person 'Cary Grant' updated successfully",
|
||||
"person_id": "Person_0"
|
||||
}
|
||||
```
|
||||
|
||||
### 步驟 4: 註冊新臉孔(建立參考樣本)
|
||||
|
||||
```bash
|
||||
curl -X POST "$BASE/api/v1/face/register" \
|
||||
-H "X-API-Key: $KEY" \
|
||||
-F "image=@known_face.jpg" \
|
||||
-F "name=Cary Grant" \
|
||||
-F 'metadata={"imdb_id": "nm0000001"}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 合併前檢視:取得臉部截圖
|
||||
|
||||
### 取得單張截圖
|
||||
|
||||
```bash
|
||||
# 預設:第一次出現的臉部
|
||||
curl "$BASE/api/v1/person/Person_0/thumbnail?video_uuid=$UUID" \
|
||||
-H "X-API-Key: $KEY" -o face.jpg
|
||||
|
||||
# 指定第 N 次出現
|
||||
curl "$BASE/api/v1/person/Person_0/thumbnail?video_uuid=$UUID&index=10" \
|
||||
-H "X-API-Key: $KEY" -o face_10.jpg
|
||||
```
|
||||
|
||||
### 找出相似人物(可能為同一人)
|
||||
|
||||
```bash
|
||||
curl "$BASE/api/v1/person/Person_0/similar?threshold=0.5&limit=10" \
|
||||
-H "X-API-Key: $KEY"
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"person_id": "Person_0",
|
||||
"similar_persons": [
|
||||
{
|
||||
"person_id": "Person_4",
|
||||
"name": "Person_4",
|
||||
"speaker_id": "SPEAKER_0",
|
||||
"similarity": 0.7
|
||||
},
|
||||
{
|
||||
"person_id": "Person_25",
|
||||
"name": "Person_25",
|
||||
"speaker_id": "SPEAKER_0",
|
||||
"similarity": 0.7
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 取得 AI 合併建議
|
||||
|
||||
```bash
|
||||
curl -X POST "$BASE/api/v1/person/suggest" \
|
||||
-H "X-API-Key: $KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"video_uuid": "'$UUID'"}'
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"merge_suggestions": [
|
||||
{
|
||||
"person_id": "Person_0",
|
||||
"merge_with": ["Person_4", "Person_25"],
|
||||
"confidence": 0.65,
|
||||
"reasons": [
|
||||
"All share speaker_id: SPEAKER_0",
|
||||
"Primary Person_0 has 17832 appearances (89% of group)"
|
||||
],
|
||||
"action": "needs_review"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 統一搜尋
|
||||
|
||||
### ⚠️ 重要:搜尋 chunks 時 uuid 為必填
|
||||
|
||||
**只有 `uuid + chunk_id` 組合才是唯一識別碼。** 單獨 `chunk_id` 在不同影片中可能重複。
|
||||
|
||||
```bash
|
||||
# ✅ 正確:包含 uuid
|
||||
curl -X POST "$BASE/api/v1/search/universal" \
|
||||
-H "X-API-Key: $KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"query": "stamp", "uuid": "'$UUID'", "types": ["chunk"]}'
|
||||
|
||||
# ❌ 錯誤:缺少 uuid
|
||||
curl -X POST "$BASE/api/v1/search/universal" \
|
||||
-H "X-API-Key: $KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"query": "stamp", "types": ["chunk"]}'
|
||||
# 回傳: {"error": "uuid is required for chunk search"}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 使用 API 合併 face / speaker / person
|
||||
|
||||
### ⚠️ 重要:合併撤銷限制
|
||||
|
||||
**合併撤銷完全依賴 `merge_history` 記錄。**
|
||||
|
||||
| 情況 | 可否撤銷 |
|
||||
|------|:---:|
|
||||
| 使用 `POST /api/v1/person/merge` API 合併 | ✅ 可以(自動記錄歷史) |
|
||||
| 手動修改資料庫合併 | ❌ 不可以(無歷史記錄) |
|
||||
| 舊版程式碼合併(無 merge_history 表) | ❌ 不可以 |
|
||||
| 已撤銷過的合併 | ❌ 不可以(防止重複撤銷) |
|
||||
|
||||
**每次合併 API 都會回傳 `merge_id`,請務必儲存以便日後撤銷。**
|
||||
|
||||
### 執行合併
|
||||
|
||||
```bash
|
||||
curl -X POST "$BASE/api/v1/person/merge" \
|
||||
-H "X-API-Key: $KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"target_person_id": "Person_0",
|
||||
"source_person_ids": ["Person_4", "Person_25"]
|
||||
}'
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Merged 2 persons into Person_0",
|
||||
"target_person_id": "Person_0",
|
||||
"merge_id": "5b12e3ac-12fa-45c0-88e1-5cff67604a7d"
|
||||
}
|
||||
```
|
||||
|
||||
### 合併做了什麼?
|
||||
|
||||
```
|
||||
合併前:
|
||||
Person_0 (17832 幀, SPEAKER_0)
|
||||
Person_4 (936 幀, SPEAKER_0)
|
||||
Person_25 (217 幀, SPEAKER_0)
|
||||
|
||||
合併後:
|
||||
Person_0 (17832+936+217=18985 幀, SPEAKER_0) ← 保留
|
||||
Person_4 ← 刪除
|
||||
Person_25 ← 刪除
|
||||
```
|
||||
|
||||
### 撤銷合併
|
||||
|
||||
```bash
|
||||
# 使用合併時回傳的 merge_id
|
||||
curl -X POST "$BASE/api/v1/person/merge/undo" \
|
||||
-H "X-API-Key: $KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"merge_id": "5b12e3ac-12fa-45c0-88e1-5cff67604a7d"}'
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Undo merge completed. Restored 2 source persons",
|
||||
"merge_id": "5b12e3ac-12fa-45c0-88e1-5cff67604a7d",
|
||||
"target_person_id": "Person_0",
|
||||
"restored_persons": ["Person_4", "Person_25"]
|
||||
}
|
||||
```
|
||||
|
||||
**⚠️ 如果沒有 merge_id(手動合併/舊版合併),無法撤銷。**
|
||||
|
||||
### 查看合併歷史
|
||||
|
||||
```bash
|
||||
curl "$BASE/api/v1/person/merge/history" -H "X-API-Key: $KEY"
|
||||
```
|
||||
|
||||
### 完整合併流程
|
||||
|
||||
```
|
||||
1. 取得建議 → POST /api/v1/person/suggest
|
||||
2. 檢視截圖 → GET /api/v1/person/:id/thumbnail
|
||||
3. 檢視相似 → GET /api/v1/person/:id/similar
|
||||
4. 執行合併 → POST /api/v1/person/merge ← 儲存 merge_id!
|
||||
5. 確認結果 → GET /api/v1/person/list
|
||||
6. 如需撤銷 → POST /api/v1/person/merge/undo ← 需要 merge_id
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API 速查表
|
||||
|
||||
| 用途 | 方法 | 端點 |
|
||||
|------|:---:|------|
|
||||
| **查看 chunk 內人物** | GET | `/api/v1/chunks/:chunk_id/persons` |
|
||||
| **搜尋人物** | GET | `/api/v1/search/persons?query=Person` |
|
||||
| **列出人物** | GET | `/api/v1/person/list?limit=20` |
|
||||
| **人物詳情** | GET | `/api/v1/person/:id` |
|
||||
| **人物截圖** | GET | `/api/v1/person/:id/thumbnail?video_uuid=...` |
|
||||
| **相似人物** | GET | `/api/v1/person/:id/similar` |
|
||||
| **AI 建議** | POST | `/api/v1/person/suggest` |
|
||||
| **綁定名稱** | PATCH | `/api/v1/person/:id` |
|
||||
| **合併人物** | POST | `/api/v1/person/merge` |
|
||||
| **撤銷合併** | POST | `/api/v1/person/merge/undo` |
|
||||
| **合併歷史** | GET | `/api/v1/person/merge/history` |
|
||||
| **統一搜尋** | POST | `/api/v1/search/universal` |
|
||||
| **註冊臉孔** | POST | `/api/v1/face/register` |
|
||||
|
||||
---
|
||||
|
||||
## 錯誤處理
|
||||
|
||||
```bash
|
||||
# 錯誤回應
|
||||
curl -X POST "$BASE/api/v1/person/merge" \
|
||||
-H "X-API-Key: $KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"target_person_id": "Person_0", "source_person_ids": []}'
|
||||
# → "source_person_ids cannot be empty"
|
||||
```
|
||||
|
||||
| 狀態碼 | 說明 |
|
||||
|:---:|------|
|
||||
| 200 | 成功 |
|
||||
| 400 | 參數錯誤 |
|
||||
| 401 | API Key 無效 |
|
||||
| 404 | 找不到 |
|
||||
| 500 | 伺服器錯誤 |
|
||||
|
||||
---
|
||||
|
||||
## 資料修正
|
||||
|
||||
發現綁定錯誤時,參考 [人物資料修正機制指南](./PERSON_CORRECTION_GUIDE.md)
|
||||
|
||||
| 錯誤類型 | 修正方式 |
|
||||
|---------|---------|
|
||||
| Speaker 綁錯 | `POST /person/:id/reassign-speaker` |
|
||||
| 不該綁 Speaker | `POST /person/:id/unbind-speaker` |
|
||||
| Appearance 分錯人 | `POST /person/:id/reassign-appearance` |
|
||||
| 錯誤 Appearance | `POST /person/:id/remove-appearance` |
|
||||
| 兩人被合併為一 | `POST /person/:id/split` |
|
||||
| 錯誤合併 | `POST /person/merge/undo` |
|
||||
| 錯誤命名 | `PATCH /person/:id` |
|
||||
@@ -0,0 +1,167 @@
|
||||
# Face / Speaker / Person / Identity Workflow Guide
|
||||
|
||||
This document describes the end-to-end workflow for managing characters in Momentry Core, from raw detection to a clean, aggregated identity database.
|
||||
|
||||
## 📊 1. Workflow Visualization
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
%% Nodes
|
||||
Start((Start Analysis))
|
||||
ListPersons[List Persons]
|
||||
|
||||
subgraph "Phase 1: Registration"
|
||||
CheckIdentity{Identity Exists?}
|
||||
Register[Register Identity]
|
||||
Link[Link Person to Identity]
|
||||
end
|
||||
|
||||
subgraph "Phase 2: Aggregation"
|
||||
Suggest[Get AI Suggestions]
|
||||
Review[Review Suggestions]
|
||||
Merge[Execute Merge]
|
||||
Confirm[Confirm Result]
|
||||
end
|
||||
|
||||
End((Database Clean))
|
||||
|
||||
%% Flow
|
||||
Start --> ListPersons
|
||||
ListPersons --> CheckIdentity
|
||||
|
||||
CheckIdentity -- No --> Register
|
||||
Register --> Link
|
||||
Link --> Suggest
|
||||
|
||||
CheckIdentity -- Yes --> Suggest
|
||||
|
||||
Suggest --> Review
|
||||
Review -- Merge Recommended --> Merge
|
||||
Review -- Naming Recommended --> Rename[Update Name]
|
||||
Rename --> Confirm
|
||||
|
||||
Merge --> Confirm
|
||||
Confirm --> End
|
||||
|
||||
style Start fill:#f9f,stroke:#333
|
||||
style End fill:#bbf,stroke:#333
|
||||
style Register fill:#dfd,stroke:#333
|
||||
style Merge fill:#dfd,stroke:#333
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ 2. Step-by-Step API Operations
|
||||
|
||||
### Phase 1: Registration (Creating Identities)
|
||||
**Scenario**: You see `Person_17` is Audrey Hepburn. You want to create a global record for her.
|
||||
|
||||
1. **Find the Person**:
|
||||
```bash
|
||||
curl -s "http://localhost:3003/api/v1/person/list?video_uuid=...&limit=5" ...
|
||||
# Output: Person_17 (1636 frames, null name)
|
||||
```
|
||||
|
||||
2. **Register Identity**:
|
||||
```bash
|
||||
curl -X POST "http://localhost:3003/api/v1/identities/from-person" ... \
|
||||
-d '{
|
||||
"video_uuid": "...",
|
||||
"person_id": "Person_17",
|
||||
"identity_name": "Audrey Hepburn"
|
||||
}'
|
||||
```
|
||||
*Result: `Person_17` is now named "Audrey Hepburn". A global `identity_id` is created.*
|
||||
|
||||
---
|
||||
|
||||
### Phase 2: Suggestion (AI Analysis)
|
||||
**Scenario**: You suspect `Person_25` might also be Audrey Hepburn, or you just want to clean up the data.
|
||||
|
||||
1. **Ask for Suggestions**:
|
||||
```bash
|
||||
curl -X POST "http://localhost:3003/api/v1/person/suggest" ... \
|
||||
-d '{"video_uuid": "..."}'
|
||||
```
|
||||
*Response*:
|
||||
```json
|
||||
{
|
||||
"merge_suggestions": [
|
||||
{
|
||||
"person_id": "Person_17",
|
||||
"merge_with": ["Person_25"],
|
||||
"reasons": ["All share speaker_id: SPEAKER_1", "Person_17 has 88% of frames"],
|
||||
"action": "auto_apply"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Phase 3: Review & Execution
|
||||
**Scenario**: You verify the suggestion. The AI logic (Shared Speaker + Frame dominance) seems correct.
|
||||
|
||||
1. **Execute the Merge**:
|
||||
```bash
|
||||
curl -X POST "http://localhost:3003/api/v1/person/merge" ... \
|
||||
-d '{
|
||||
"video_uuid": "...",
|
||||
"target_person_id": "Person_17",
|
||||
"source_person_ids": ["Person_25"]
|
||||
}'
|
||||
```
|
||||
*Result*: `Person_25` is deleted. All 217 frames of `Person_25` are added to `Person_17`.
|
||||
|
||||
---
|
||||
|
||||
## 🚀 3. Automated Demo Script
|
||||
|
||||
Run the following script to see the entire process in action automatically.
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# scripts/demo_identity_workflow.sh
|
||||
# Usage: chmod +x scripts/demo_identity_workflow.sh && ./scripts/demo_identity_workflow.sh
|
||||
|
||||
API_URL="http://localhost:3002"
|
||||
API_KEY="muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69"
|
||||
UUID="384b0ff44aaaa1f1"
|
||||
|
||||
echo "🎬 === MOMENTRY IDENTITY WORKFLOW DEMO ==="
|
||||
|
||||
# 1. Registration
|
||||
echo "👉 STEP 1: Registering Person_17 as Audrey Hepburn..."
|
||||
curl -s -X POST "$API_URL/api/v1/identities/from-person" \
|
||||
-H "X-API-Key: $API_KEY" -H "Content-Type: application/json" \
|
||||
-d "{\"video_uuid\":\"$UUID\", \"person_id\":\"Person_17\", \"identity_name\":\"Audrey Hepburn\"}" \
|
||||
| python3 -m json.tool
|
||||
|
||||
# 2. Suggestion
|
||||
echo ""
|
||||
echo "👉 STEP 2: Asking AI for cleaning suggestions..."
|
||||
curl -s -X POST "$API_URL/api/v1/person/suggest" \
|
||||
-H "X-API-Key: $API_KEY" -H "Content-Type: application/json" \
|
||||
-d "{\"video_uuid\":\"$UUID\"}" \
|
||||
| python3 -c "
|
||||
import sys, json
|
||||
d = json.load(sys.stdin)
|
||||
sugs = d.get('naming_suggestions', []) + d.get('merge_suggestions', [])
|
||||
if sugs:
|
||||
print(f' Found {len(sugs)} suggestions.')
|
||||
for s in sugs:
|
||||
print(f' - {s}')
|
||||
else:
|
||||
print(' No suggestions (Data is already clean!).')
|
||||
"
|
||||
|
||||
# 3. Execution (Example Merge if Person_25 existed)
|
||||
echo ""
|
||||
echo "👉 STEP 3: Simulating a merge (Merging hypothetical Person_25 -> Person_17)..."
|
||||
# Note: In a real scenario, Person_25 would exist.
|
||||
# Here we just show the command structure.
|
||||
echo " Command: POST /api/v1/person/merge { target: 'Person_17', sources: ['Person_25'] }"
|
||||
echo " Result: Person_25 frames added to Person_17. Person_25 deleted."
|
||||
|
||||
echo ""
|
||||
echo "✅ Demo Complete."
|
||||
@@ -0,0 +1,214 @@
|
||||
# 📘 Momentry 身份管理 (Identity Management) API 實作指南
|
||||
|
||||
本文件示範如何透過 API 完成「從影片選擇 → 臉部分析 → 全域身份註冊」的完整流程。
|
||||
|
||||
## 1. 選擇目標影片
|
||||
|
||||
**目標**: 獲取系統中已註冊的影片列表,選擇要進行管理的影片。
|
||||
|
||||
**API**: `GET /api/v1/videos`
|
||||
|
||||
```bash
|
||||
curl -s "http://127.0.0.1:3002/api/v1/videos" \
|
||||
-H "x-api-key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" | jq .
|
||||
```
|
||||
|
||||
**回應範例**:
|
||||
```json
|
||||
{
|
||||
"videos": [
|
||||
{
|
||||
"uuid": "384b0ff44aaaa1f1",
|
||||
"file_name": "Old_Time_Movie_Show_-_Charade_1963.HD.mov",
|
||||
"duration": 6879.33
|
||||
},
|
||||
{
|
||||
"uuid": "9760d0820f0cf9a7",
|
||||
"file_name": "ExaSAN PCIe series - Director Ou.mp4",
|
||||
"duration": 159.64
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
> **決策**: 我們選擇 `Charade 1963` (UUID: `384b0ff44aaaa1f1`) 進行管理。
|
||||
|
||||
---
|
||||
|
||||
## 2. 分析影片內的所有人物 (Faces / Persons / Speakers)
|
||||
|
||||
**目標**: 查看該影片內所有偵測到的「臉群 (Clusters)」。區分**已命名 (Named)**、**待命名 (Unregistered)** 與 **AI 建議**。
|
||||
|
||||
**API**: `GET /api/v1/videos/{uuid}/faces`
|
||||
|
||||
```bash
|
||||
curl -s "http://127.0.0.1:3002/api/v1/videos/384b0ff44aaaa1f1/faces" \
|
||||
-H "x-api-key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" | jq .
|
||||
```
|
||||
|
||||
**回應範例**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"video_uuid": "384b0ff44aaaa1f1",
|
||||
"total_faces": 6,
|
||||
"registered_count": 0,
|
||||
"unregistered_count": 6,
|
||||
"clusters": [
|
||||
{
|
||||
"cluster_id": "Person_4",
|
||||
"face_count": 45,
|
||||
"status": "unregistered",
|
||||
"identity": {
|
||||
"name": "Cary Grant",
|
||||
"is_confirmed": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"cluster_id": "Person_17",
|
||||
"face_count": 32,
|
||||
"status": "unregistered",
|
||||
"identity": {
|
||||
"name": "Audrey Hepburn",
|
||||
"is_confirmed": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"cluster_id": "Person_12",
|
||||
"face_count": 10,
|
||||
"status": "unregistered",
|
||||
"identity": { "name": "Person_12" }
|
||||
},
|
||||
{
|
||||
"cluster_id": "Person_124",
|
||||
"face_count": 5,
|
||||
"status": "unregistered",
|
||||
"identity": null
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 如何解讀結果?
|
||||
|
||||
| 欄位 | 說明 | 狀態 |
|
||||
| :--- | :--- | :--- |
|
||||
| **`identity.name`** | 若顯示具體人名 (如 "Audrey Hepburn"),代表 **已命名**。 | ✅ 待註冊 |
|
||||
| **`identity.name`** | 若顯示 `Person_XX` (系統預設名),代表 **待命名**。 | 🔄 等待 AI 或人工命名 |
|
||||
| **`identity: null`** | 代表完全 **未識別**,通常數量較少。 | ❓ 待處理 |
|
||||
|
||||
---
|
||||
|
||||
## 3. 註冊全域身份 (Register Identity)
|
||||
|
||||
**目標**: 將已命名的人物升級為 **全域身份 (Global Identity)**。這能讓系統在其他影片中自動認出他們。
|
||||
|
||||
**API**: `POST /api/v1/person/{person_id}/register?video_uuid={uuid}`
|
||||
|
||||
### 3.1 註冊 Audrey Hepburn
|
||||
|
||||
```bash
|
||||
curl -s -X POST "http://127.0.0.1:3002/api/v1/person/Person_17/register?video_uuid=384b0ff44aaaa1f1" \
|
||||
-H "x-api-key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" | jq .
|
||||
```
|
||||
|
||||
**回應**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Successfully registered as global identity",
|
||||
"person_id": "Person_17",
|
||||
"name": "Audrey Hepburn",
|
||||
"face_identity_id": 12
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 註冊 Cary Grant
|
||||
|
||||
```bash
|
||||
curl -s -X POST "http://127.0.0.1:3002/api/v1/person/Person_4/register?video_uuid=384b0ff44aaaa1f1" \
|
||||
-H "x-api-key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" | jq .
|
||||
```
|
||||
|
||||
**回應**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"face_identity_id": 13,
|
||||
"name": "Cary Grant"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ 驗證成果
|
||||
|
||||
現在可以使用全域搜尋 API 確認身份是否註冊成功:
|
||||
|
||||
```bash
|
||||
curl -s -X POST "http://127.0.0.1:3002/api/v1/identities/search" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "x-api-key: muser_..." \
|
||||
-d '{"query": "Audrey"}' | jq '.identities[] | {name: .profile.name, identity_id: .face_identity_id}'
|
||||
```
|
||||
|
||||
**結果**:
|
||||
```json
|
||||
{
|
||||
"name": "Audrey Hepburn",
|
||||
"identity_id": 12
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 擷取身份 / 人物 / 臉部 截圖
|
||||
|
||||
**目標**: 取得特定人物的臉部特寫截圖。
|
||||
由於「Identity (全域身份)」是由多個影片中的「Person (區域人物)」組成,而「Person」是由多個「Face (臉部偵測點)」聚合而成,因此擷取截圖的核心是取得 **該人物在某部影片中的某幀臉部影像**。
|
||||
|
||||
**API**: `GET /api/v1/person/{person_id}/thumbnail`
|
||||
|
||||
### 參數說明
|
||||
|
||||
| 參數 | 類型 | 必填 | 說明 |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| `person_id` | Path | ✅ | 人物 ID (例如: `Person_17`) |
|
||||
| `video_uuid` | Query | ✅ | 影片 UUID (用來定位影像源) |
|
||||
| `index` | Query | ❌ | 指定第幾張臉 (預設 `0`) |
|
||||
|
||||
### 4.1 擷取 Audrey Hepburn 的臉部截圖 (預設第一張)
|
||||
|
||||
此指令會自動從 `Charade 1963` 影片中擷取 Audrey Hepburn 最清晰的一張臉,並儲存為 `audrey.jpg`。
|
||||
|
||||
```bash
|
||||
curl -s -o audrey.jpg \
|
||||
"http://127.0.0.1:3002/api/v1/person/Person_17/thumbnail?video_uuid=384b0ff44aaaa1f1" \
|
||||
-H "x-api-key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69"
|
||||
```
|
||||
|
||||
> **注意**: 回應是 **圖片二進位資料 (JPG)**,請使用 `-o filename.jpg` 儲存,**不要**使用 `| jq`。
|
||||
|
||||
### 4.2 擷取 Cary Grant 的其他臉部截圖 (指定 Index)
|
||||
|
||||
若你想看同一人物的其他角度,可以調整 `index` 參數。
|
||||
假設 Cary Grant (`Person_4`) 在影片中出現了 45 次:
|
||||
|
||||
```bash
|
||||
# 擷取第 5 次出現的臉部截圖 (index 從 0 開始)
|
||||
curl -s -o cary_face_5.jpg \
|
||||
"http://127.0.0.1:3002/api/v1/person/Person_4/thumbnail?video_uuid=384b0ff44aaaa1f1&index=4" \
|
||||
-H "x-api-key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69"
|
||||
```
|
||||
|
||||
### 4.3 Identity (全域身份) 的截圖策略
|
||||
|
||||
由於全域 Identity (`face_identity_id: 12`) 跨越多部影片,要取得它的截圖,請先查詢它所屬的影片:
|
||||
|
||||
1. **查詢 Identity 所在的影片**:
|
||||
```bash
|
||||
curl -s "http://127.0.0.1:3002/api/v1/identities/12/videos" \
|
||||
-H "x-api-key: muser_..." | jq '.videos[0].video_uuid'
|
||||
```
|
||||
2. **取得該影片中的對應 Person ID**: 從上一步結果中找到 `person_id` (例如 `Person_17`)。
|
||||
3. **呼叫截圖 API**: 使用該 `video_uuid` 和 `person_id` 呼叫上述截圖 API。
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
---
|
||||
document_type: "reference_doc"
|
||||
service: "MOMENTRY_CORE"
|
||||
title: "搜尋範例 Prompt"
|
||||
date: "2026-04-25"
|
||||
version: "V1.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
tags:
|
||||
- "prompt"
|
||||
- "搜尋範例"
|
||||
ai_query_hints:
|
||||
- "查詢 搜尋範例 Prompt 的內容"
|
||||
- "搜尋範例 Prompt 的主要目的是什麼?"
|
||||
- "如何操作或實施 搜尋範例 Prompt?"
|
||||
---
|
||||
|
||||
# 搜尋範例 Prompt
|
||||
|
||||
## 基本搜尋測試
|
||||
|
||||
### 1. 簡單關鍵字搜尋
|
||||
```bash
|
||||
curl -X POST http://localhost:3002/api/v1/search \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"query": "charade", "limit": 5}'
|
||||
```
|
||||
|
||||
### 2. 電影相關詞
|
||||
```
|
||||
charade
|
||||
woody allen
|
||||
audrey hepburn
|
||||
classic movie
|
||||
old time movie
|
||||
romantic comedy
|
||||
```
|
||||
|
||||
### 3. 場景描述
|
||||
```
|
||||
widowed woman
|
||||
secret agent
|
||||
chase scene
|
||||
paris
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 進階搜尋測試
|
||||
|
||||
### 4. 短語搜尋
|
||||
```bash
|
||||
curl -X POST http://localhost:3002/api/v1/search \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"query": "fun plot twists", "limit": 3}'
|
||||
```
|
||||
|
||||
### 5. 情感/描述詞
|
||||
```
|
||||
charming performances
|
||||
hilarious
|
||||
suspenseful
|
||||
dramatic
|
||||
```
|
||||
|
||||
### 6. 動作場景
|
||||
```
|
||||
running
|
||||
chase
|
||||
fighting
|
||||
dancing
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 整合範例
|
||||
|
||||
### n8n Workflow
|
||||
```
|
||||
搜尋詞: "charade"
|
||||
→ 取得 chunk 的 start_time, end_time
|
||||
→ 組裝成影片 URL
|
||||
→ 回傳給用戶
|
||||
```
|
||||
|
||||
### PHP 範例
|
||||
```php
|
||||
$searchTerms = ['charade', 'woody', 'audrey', 'classic'];
|
||||
|
||||
// 搜尋每個詞
|
||||
foreach ($searchTerms as $term) {
|
||||
$ch = curl_init('http://localhost:3002/api/v1/search');
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
|
||||
'query' => $term,
|
||||
'limit' => 5
|
||||
]));
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
|
||||
$response = curl_exec($ch);
|
||||
$data = json_decode($response, true);
|
||||
|
||||
// 處理結果
|
||||
foreach ($data['results'] as $result) {
|
||||
echo "{$result['text']} (score: {$result['score']})\n";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 預期回傳格式
|
||||
|
||||
```json
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"uuid": "a1b10138a6bbb0cd",
|
||||
"chunk_id": "sentence_0006",
|
||||
"chunk_type": "sentence",
|
||||
"start_time": 48.8,
|
||||
"end_time": 55.44,
|
||||
"text": "fun plot twists, Woody Dialog and charming performances...",
|
||||
"score": 0.526
|
||||
}
|
||||
],
|
||||
"query": "charade"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 測試檢查清單
|
||||
|
||||
- [ ] 基本關鍵字搜尋
|
||||
- [ ] n8n 整合格式
|
||||
- [ ] 影片時戳取得
|
||||
- [ ] 多筆結果排序
|
||||
- [ ] 不同 chunk_type 搜尋
|
||||
@@ -0,0 +1,231 @@
|
||||
---
|
||||
document_type: "architecture_design"
|
||||
service: "MOMENTRY_CORE"
|
||||
title: "Momentry Core Chunk Rule 4: 摘要分析級檢索 (Summary 5W1H Chunk) (v1.0)"
|
||||
date: "2026-04-21"
|
||||
version: "V1.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
tags:
|
||||
- "momentry"
|
||||
- "core"
|
||||
- "摘要分析級檢索"
|
||||
- "rule"
|
||||
ai_query_hints:
|
||||
- "查詢 Momentry Core Chunk Rule 4: 摘要分析級檢索 (Summary 5W1H Chunk) (v1.0) 的內容"
|
||||
- "Momentry Core Chunk Rule 4: 摘要分析級檢索 (Summary 5W1H Chunk) (v1.0) 的主要目的是什麼?"
|
||||
- "如何操作或實施 Momentry Core Chunk Rule 4: 摘要分析級檢索 (Summary 5W1H Chunk) (v1.0)?"
|
||||
---
|
||||
|
||||
# Momentry Core Chunk Rule 4: 摘要分析級檢索 (Summary 5W1H Chunk) (v1.0)
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | OpenCode |
|
||||
| 建立時間 | 2026-04-21 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-04-21 | 定義 Rule 4: 基於 LLM 5W1H 分析的最高層級摘要結構 | OpenCode | OpenCode / Qwen3.6-Plus |
|
||||
|
||||
---
|
||||
|
||||
## 0. 設計目標
|
||||
|
||||
**Rule 4** 的核心概念是**「情節理解」(Storyline Understanding)**。透過將多個場景 (Rule 3) 聚合,並利用大型語言模型 (Gemma4) 進行深度分析,提取 5W1H 結構化資訊,使系統能夠回答複雜的「情節相關問題」。
|
||||
|
||||
- **核心原則**: 5-10 個場景 (Rule 3) = 1 個摘要區塊 (Summary Chunk)。
|
||||
- **結構**: 頂層 Parent Chunk。
|
||||
- **特徵**: 包含 LLM 生成的完整摘要與 **5W1H** (Who, What, When, Where, Why, How) 分析結果。
|
||||
- **優勢**: 支援宏觀劇情檢索、人物動線追蹤與複雜問答 (RAG)。
|
||||
|
||||
---
|
||||
|
||||
## 1. 數據源與聚合邏輯
|
||||
|
||||
Rule 4 是處理管線的終點,依賴 **Rule 3** 的產出以及 **LLM 服務**。
|
||||
|
||||
1. **Rule 3 Chunks (Primary)**: 提供場景級的文本摘要與元數據。
|
||||
- *聚合策略*: 將連續的 5-10 個 Rule 3 Chunks 視為一個「敘事區塊」。
|
||||
2. **LLM Processor (Gemma4)**:
|
||||
- *任務*: 讀取該區塊內所有 Rule 3 的摘要與 ASR 文本。
|
||||
- *輸出*:
|
||||
- **Summary**: 流暢的劇情描述。
|
||||
- **5W1H**: 結構化的關鍵要素提取。
|
||||
3. **Visual/Audio Retention**:
|
||||
- 保留區塊內所有出現過的 `face_ids` (Who) 和 `objects` (What/Where)。
|
||||
|
||||
---
|
||||
|
||||
## 2. Chunk 結構定義
|
||||
|
||||
### 2.1 資料庫結構 (PostgreSQL)
|
||||
|
||||
```sql
|
||||
CREATE TABLE chunks_rule4 (
|
||||
id UUID PRIMARY KEY,
|
||||
asset_uuid UUID NOT NULL,
|
||||
chunk_type VARCHAR(20) DEFAULT 'summary',
|
||||
|
||||
-- 時間軸 (繼承自第一個與最後一個 Rule 3 子區塊)
|
||||
start_frame INT NOT NULL,
|
||||
end_frame INT NOT NULL,
|
||||
start_time_sec DOUBLE PRECISION,
|
||||
end_time_sec DOUBLE PRECISION,
|
||||
|
||||
-- LLM 生成內容
|
||||
summary TEXT NOT NULL, -- 劇情摘要
|
||||
analysis_5w1h JSONB, -- 結構化分析結果
|
||||
|
||||
-- 聚合元數據
|
||||
faces JSONB, -- 區塊內所有人物
|
||||
objects JSONB, -- 區塊內重要物件
|
||||
|
||||
-- 向量索引
|
||||
embedding vector(768), -- 摘要與 5W1H 的混合向量
|
||||
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- 關聯子區塊
|
||||
ALTER TABLE parent_chunks ADD COLUMN rule4_parent_id UUID REFERENCES chunks_rule4(id);
|
||||
```
|
||||
|
||||
### 2.2 5W1H 結構 (JSONB)
|
||||
|
||||
```json
|
||||
{
|
||||
"who": ["Cary Grant", "Audrey Hepburn"], // 主要人物 (對應 Face ID)
|
||||
"what": ["Searching for the stamps", "Car chase"], // 核心事件
|
||||
"where": ["Paris", "Bank", "Car"], // 地點/場景 (對應 Visual Objects)
|
||||
"when": "Night", // 時間背景 (對應 Time of day)
|
||||
"why": "To pay off a debt", // 動機
|
||||
"how": "By sneaking into the vault" // 手段/過程
|
||||
}
|
||||
```
|
||||
|
||||
### 2.3 JSON 產出範例
|
||||
|
||||
```json
|
||||
{
|
||||
"chunk_id": "550e...0004",
|
||||
"type": "summary",
|
||||
"summary": "Peter 和 Regina 計劃潛入銀行金庫尋找郵票。他們在夜間開車前往,途中遭遇巡邏隊盤查,但最終利用機智脫身。",
|
||||
"start_frame": 5000,
|
||||
"end_frame": 8000,
|
||||
"analysis_5w1h": {
|
||||
"who": ["peter_joshua", "regina_lampert"],
|
||||
"what": ["heist_planning", "evasion"],
|
||||
"where": ["car", "street", "bank_exterior"],
|
||||
"when": "night",
|
||||
"why": "retrieve_stamps",
|
||||
"how": "stealth_deception"
|
||||
},
|
||||
"metadata": {
|
||||
"rule3_count": 7
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 搜尋能力定義
|
||||
|
||||
Rule 4 是 **RAG (Retrieval-Augmented Generation)** 的核心數據源。
|
||||
|
||||
### 3.1 劇情摘要搜尋 (Plot Search)
|
||||
* **場景**: "這部片在講什麼?"、"他們找到郵票了嗎?"
|
||||
* **邏輯**:
|
||||
1. 搜尋 `summary` 向量。
|
||||
2. 返回包含該情節的完整摘要區塊。
|
||||
|
||||
### 3.2 5W1H 結構化查詢 (Structured Query)
|
||||
* **場景**: "找出所有 **Cary Grant (Who)** 在 **車上 (Where)** 的片段"。
|
||||
* **邏輯**:
|
||||
1. 過濾 `analysis_5w1h` JSONB 欄位。
|
||||
2. `who` 包含 "Cary Grant" **AND** `where` 包含 "car"。
|
||||
3. 這種查詢比傳統關鍵字搜索更精準,因為它是經過 LLM 理解後的結構化數據。
|
||||
|
||||
### 3.3 動機與原因搜尋 (Why/How)
|
||||
* **場景**: "他為什麼要偷東西?"
|
||||
* **邏輯**:
|
||||
1. 針對 `analysis_5w1h.why` 進行語意比對。
|
||||
|
||||
---
|
||||
|
||||
## 4. 處理流程 (LLM Pipeline)
|
||||
|
||||
Rule 4 的生成需要呼叫 `llm_engine` (Gemma4) 服務。
|
||||
|
||||
### 4.1 演算法邏輯 (Pseudocode)
|
||||
|
||||
```python
|
||||
# 輸入: rule3_chunks (List of Scene Chunks)
|
||||
|
||||
# 1. 分組 (每 5-10 個場景一組)
|
||||
for group in chunks(rule3_chunks, size=7):
|
||||
|
||||
# 2. 準備 LLM 上下文
|
||||
context_text = "\n".join([chunk.summary for chunk in group])
|
||||
context_objects = aggregate_objects(group)
|
||||
|
||||
prompt = f"""
|
||||
Analyze the following video scenes and extract the 5W1H information.
|
||||
Scenes:
|
||||
{context_text}
|
||||
|
||||
Return JSON format:
|
||||
{{
|
||||
"summary": "A brief summary of these scenes.",
|
||||
"5w1h": {{
|
||||
"who": ["List of characters"],
|
||||
"what": ["Main events"],
|
||||
...
|
||||
}}
|
||||
}}
|
||||
"""
|
||||
|
||||
# 3. 呼叫 LLM (Gemma4 via Service Registry)
|
||||
response = llm_service.chat(prompt)
|
||||
result = parse_json(response)
|
||||
|
||||
# 4. 建立 Rule 4 Chunk
|
||||
rule4_chunk = {
|
||||
"summary": result["summary"],
|
||||
"analysis_5w1h": result["5w1h"],
|
||||
"start_frame": group[0].start_frame,
|
||||
"end_frame": group[-1].end_frame,
|
||||
"faces": aggregate_faces(group),
|
||||
"objects": aggregate_objects(group)
|
||||
}
|
||||
|
||||
# 5. 儲存並關聯
|
||||
rule4_id = store_rule4_chunk(rule4_chunk)
|
||||
for chunk in group:
|
||||
link_rule3_to_rule4(chunk.id, rule4_id)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 總結
|
||||
|
||||
Rule 4 將 Momentry 從「影片搜尋引擎」提升為**「影片知識圖譜」**。
|
||||
|
||||
| 特性 | 實作方式 |
|
||||
|------|----------|
|
||||
| **粒度** | 情節/敘事區塊 (5-10 場景) |
|
||||
| **核心技術** | LLM 5W1H 提取 (Gemma4) |
|
||||
| **數據結構** | 摘要文本 + JSONB 5W1H 結構 |
|
||||
| **向量內容** | 混合向量 (Summary + 5W1H) |
|
||||
| **適用場景** | 問答系統 (RAG)、劇情回顧、複雜條件過濾 |
|
||||
|
||||
**四層架構總覽:**
|
||||
1. **Rule 1 (Sentence)**: 精確台詞檢索。
|
||||
2. **Rule 2 (Visual)**: 畫面物件檢索。
|
||||
3. **Rule 3 (Scene)**: 場景上下文檢索。
|
||||
4. **Rule 4 (Summary)**: 劇情理解與知識問答。
|
||||
@@ -0,0 +1,166 @@
|
||||
# 翻譯 Agent (Translation Agent) 設計文件
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | OpenCode |
|
||||
| 建立時間 | 2026-04-25 |
|
||||
| 文件版本 | V1.0 |
|
||||
| 用途 | 提供多語言文本翻譯服務 (應用於 Portal Chunk Detail) |
|
||||
|
||||
---
|
||||
|
||||
## 1. Agent 概覽
|
||||
|
||||
Translation Agent 負責將系統中的非結構化文本(如 Chunk 內容、摘要、5W1H 推論結果)翻譯為使用者指定的語言。
|
||||
在 Portal 的 **Chunk Search Detail** 頁面,當使用者瀏覽不同語言的影片內容時,此 Agent 提供即時翻譯支援。
|
||||
|
||||
### 1.1 資源註冊資訊 (Resource Registry)
|
||||
|
||||
當 Agent 啟動時,將向 **Resource Registry** 註冊以下資訊:
|
||||
|
||||
```json
|
||||
{
|
||||
"resource_id": "agent_text_translation_v1",
|
||||
"resource_type": "agent",
|
||||
"capabilities": ["translate_text", "detect_language", "batch_translate"],
|
||||
"category": "text_processing",
|
||||
"config": {
|
||||
"default_model": "gpt-4o-mini",
|
||||
"fallback_model": "local-llama-3-8b",
|
||||
"max_tokens": 4096,
|
||||
"supported_languages": ["zh-TW", "en-US", "ja-JP", "ko-KR"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 核心設計
|
||||
|
||||
### 2.1 輸入格式 (Input)
|
||||
|
||||
Agent 接收來自 Portal 或內部 API 的 JSON 請求:
|
||||
|
||||
```json
|
||||
{
|
||||
"text": "He walked into the room and saw a large red car.",
|
||||
"target_language": "zh-TW",
|
||||
"source_language": "auto",
|
||||
"context": {
|
||||
"domain": "movie_subtitle",
|
||||
"glossary": {
|
||||
"red car": "紅色跑車"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- `text`: 待翻譯文本。
|
||||
- `target_language`: 目標語言 (BCP 47 格式)。
|
||||
- `context` (可選): 提供領域資訊或專有名詞對照表 (Glossary) 以提高準確度。
|
||||
|
||||
### 2.2 輸出格式 (Output)
|
||||
|
||||
Agent 回傳標準化 JSON:
|
||||
|
||||
```json
|
||||
{
|
||||
"translated_text": "他走進房間,看到一輛紅色跑車。",
|
||||
"source_language_detected": "en-US",
|
||||
"confidence": 0.98,
|
||||
"usage": {
|
||||
"input_tokens": 12,
|
||||
"output_tokens": 15
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Prompt 設計 (System Prompt)
|
||||
|
||||
為了確保翻譯風格符合 Momentry Core 的專業性(如準確的影視術語),我們使用以下 System Prompt:
|
||||
|
||||
```text
|
||||
You are a professional translator for Momentry Core, a digital asset management system specializing in video analysis.
|
||||
|
||||
## Guidelines:
|
||||
1. **Accuracy**: Translate the meaning accurately, maintaining the original tone.
|
||||
2. **Context Awareness**: If a glossary is provided in the context, strictly follow it.
|
||||
3. **Style**:
|
||||
- For subtitles: Keep it concise and natural for reading.
|
||||
- For technical terms (e.g., 5W1H, metadata): Use standard industry translations.
|
||||
4. **Format**: Preserve any JSON structure, markdown, or timestamps present in the input text. Do not translate code blocks.
|
||||
5. **Output**: Return ONLY the translated text in the requested format unless asked otherwise.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. API 端點設計
|
||||
|
||||
### 4.1 單一翻譯
|
||||
|
||||
```http
|
||||
POST /api/v1/agents/translate
|
||||
Content-Type: application/json
|
||||
X-Resource-Id: agent_text_translation_v1
|
||||
|
||||
{
|
||||
"text": "...",
|
||||
"target_language": "zh-TW"
|
||||
}
|
||||
```
|
||||
|
||||
### 4.2 批次翻譯 (Batch Translation)
|
||||
|
||||
針對 Chunk Detail 頁面可能一次顯示多個段落,支援批次翻譯:
|
||||
|
||||
```http
|
||||
POST /api/v1/agents/translate/batch
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"items": [
|
||||
{ "id": "chunk_001", "text": "..." },
|
||||
{ "id": "chunk_002", "text": "..." }
|
||||
],
|
||||
"target_language": "zh-TW"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 錯誤處理與容錯
|
||||
|
||||
- **模型降級 (Fallback)**: 若 `gpt-4o-mini` 超時或不可用,自動切換至本地模型 `local-llama-3-8b`。
|
||||
- **Token 超長**: 若文本超過 `max_tokens`,自動進行分段翻譯 (Split & Translate)。
|
||||
- **無效語言**: 若 `target_language` 不在支援列表中,回傳 `400 Bad Request`。
|
||||
|
||||
---
|
||||
|
||||
## 6. Portal 整合範例 (Chunk Detail)
|
||||
|
||||
在 Portal 的 `ChunkDetailView.vue` 中,翻譯功能的調用流程如下:
|
||||
|
||||
1. 使用者點擊「翻譯為 繁體中文」按鈕。
|
||||
2. Portal 發送 POST 請求至 `/api/v1/agents/translate`。
|
||||
3. 取得結果後,在不重新整理頁面的情況下更新 UI (顯示 `translated_text`)。
|
||||
|
||||
```typescript
|
||||
// Portal 前端調用範例
|
||||
async function translateChunkText(text: string, targetLang: string) {
|
||||
const response = await fetch('/api/v1/agents/translate', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ text, target_language: targetLang })
|
||||
});
|
||||
return response.json();
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 版本資訊
|
||||
|
||||
- 版本: V1.0
|
||||
- 建立日期: 2026-04-25
|
||||
@@ -0,0 +1,215 @@
|
||||
---
|
||||
document_type: "architecture_design"
|
||||
service: "MOMENTRY_CORE"
|
||||
title: "API Key Management System Architecture"
|
||||
date: "2026-03-20"
|
||||
version: "V1.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
tags:
|
||||
- "api-key"
|
||||
- "security"
|
||||
- "authentication"
|
||||
- "architecture"
|
||||
ai_query_hints:
|
||||
- "API Key 管理系統架構是什麼?"
|
||||
- "如何設計 API Key 驗證流程?"
|
||||
- "API Key 異常檢測機制如何運作?"
|
||||
---
|
||||
|
||||
# API Key Management System Architecture
|
||||
|
||||
## System Overview
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ API Key Management System │
|
||||
├─────────────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
||||
│ │ CLI │ │ HTTP API │ │ Service │ │ External │ │
|
||||
│ │ Layer │────▶│ Layer │────▶│ Layer │────▶│ Services │ │
|
||||
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │
|
||||
│ │ │ │ │ │
|
||||
│ │ │ │ │ │
|
||||
│ ▼ ▼ ▼ ▼ │
|
||||
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ Core Modules │ │
|
||||
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
|
||||
│ │ │ Service │ │Validator│ │ Anomaly │ │Rotation │ │ Cleanup │ │ │
|
||||
│ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │
|
||||
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
|
||||
│ │ │ Webhook │ │Encrypt │ │Blacklist│ │ Report │ │ Error │ │ │
|
||||
│ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │
|
||||
│ └─────────────────────────────────────────────────────────────────────────┘ │
|
||||
│ │ │ │ │
|
||||
│ ▼ ▼ ▼ │
|
||||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
||||
│ │ PostgreSQL │ │ Redis │ │ External │ │
|
||||
│ │ (Storage) │ │ (Cache) │ │ (Gitea/n8n)│ │
|
||||
│ └─────────────┘ └─────────────┘ └─────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Module Dependencies
|
||||
|
||||
```
|
||||
┌──────────────┐
|
||||
│ models.rs │
|
||||
│ (Types) │
|
||||
└──────┬───────┘
|
||||
│
|
||||
┌──────────────────┼──────────────────┐
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
|
||||
│ service.rs │ │ error.rs │ │ validator.rs │
|
||||
│ (Core CRUD) │ │ (Errors) │ │ (Cache+Rate) │
|
||||
└───────┬───────┘ └───────────────┘ └───────────────┘
|
||||
│
|
||||
│ ┌───────────────────────────────┐
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
|
||||
│ anomaly.rs │ │ rotation.rs │ │ blacklist.rs │
|
||||
│ (Detection) │ │ (Rotation) │ │ (IP Block) │
|
||||
└───────────────┘ └───────────────┘ └───────────────┘
|
||||
```
|
||||
|
||||
## Request Flow
|
||||
|
||||
```
|
||||
Client Request
|
||||
│
|
||||
▼
|
||||
┌─────────────┐
|
||||
│ CLI/API │
|
||||
└──────┬──────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────┐ ┌─────────────┐
|
||||
│ Rate Limit │────▶│ IP Blacklist│
|
||||
│ Check │ │ Check │
|
||||
└──────┬──────┘ └──────┬──────┘
|
||||
│ │
|
||||
└─────────┬─────────┘
|
||||
│
|
||||
▼
|
||||
┌───────────────┐
|
||||
│ Hash API Key │
|
||||
└───────┬───────┘
|
||||
│
|
||||
▼
|
||||
┌───────────────┐ ┌───────────────┐
|
||||
│ Cache Lookup │────▶│ PostgreSQL │
|
||||
└───────┬───────┘ │ Lookup │
|
||||
│ └───────┬───────┘
|
||||
│ │
|
||||
└──────────┬──────────┘
|
||||
│
|
||||
▼
|
||||
┌───────────────┐
|
||||
│ Validate │
|
||||
│ (Status, │
|
||||
│ Expiry) │
|
||||
└───────┬───────┘
|
||||
│
|
||||
┌─────────────┼─────────────┐
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌──────────┐ ┌──────────┐ ┌──────────┐
|
||||
│ Valid │ │ Invalid │ │ Error │
|
||||
│ Response│ │ Response │ │ Response │
|
||||
└──────────┘ └──────────┘ └──────────┘
|
||||
```
|
||||
|
||||
## Database Schema
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ PostgreSQL │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─────────────────┐ ┌─────────────────┐ │
|
||||
│ │ api_keys │ │ api_key_audit_ │ │
|
||||
│ ├─────────────────┤ │ log │ │
|
||||
│ │ id │ ├─────────────────┤ │
|
||||
│ │ key_id │─────▶│ id │ │
|
||||
│ │ key_hash │ │ key_id (FK) │ │
|
||||
│ │ name │ │ action │ │
|
||||
│ │ key_type │ │ ip_address │ │
|
||||
│ │ status │ │ details │ │
|
||||
│ │ expires_at │ └─────────────────┘ │
|
||||
│ │ ... │ │
|
||||
│ └─────────────────┘ ┌─────────────────┐ │
|
||||
│ │ api_key_anomalies│ │
|
||||
│ ┌─────────────────┐ ├─────────────────┤ │
|
||||
│ │ gitea_tokens │ │ id │ │
|
||||
│ ├─────────────────┤ │ key_id (FK) │ │
|
||||
│ │ id │ │ anomaly_type │ │
|
||||
│ │ gitea_token_id │ │ severity │ │
|
||||
│ │ token_name │ │ details │ │
|
||||
│ │ scopes │ └─────────────────┘ │
|
||||
│ └─────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────────┐ │
|
||||
│ │ n8n_api_keys │ │
|
||||
│ ├─────────────────┤ │
|
||||
│ │ id │ │
|
||||
│ │ n8n_key_id │ │
|
||||
│ │ label │ │
|
||||
│ └─────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## External Integrations
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ External Integrations │
|
||||
├─────────────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
|
||||
│ │ Gitea │ │ n8n │ │ Webhook │ │
|
||||
│ ├─────────────────┤ ├─────────────────┤ ├─────────────────┤ │
|
||||
│ │ • Create Token │ │ • Create API Key│ │ • Key Created │ │
|
||||
│ │ • List Tokens │ │ • List API Keys │ │ • Key Revoked │ │
|
||||
│ │ • Delete Token │ │ • Delete API Key│ │ • Anomaly │ │
|
||||
│ │ • Verify Token │ │ • Verify │ │ • Rate Limited │ │
|
||||
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Security Layers
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ Security Layers │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ Layer 1: Network │
|
||||
│ ┌─────────────────────────────────────────────────────────┐ │
|
||||
│ │ • IP Blacklist │ │
|
||||
│ │ • Rate Limiting │ │
|
||||
│ └─────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ Layer 2: Authentication │
|
||||
│ ┌─────────────────────────────────────────────────────────┐ │
|
||||
│ │ • API Key Hash (SHA256) │ │
|
||||
│ │ • Constant-time Comparison │ │
|
||||
│ │ • Key Validation (Status, Expiry) │ │
|
||||
│ └─────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ Layer 3: Monitoring │
|
||||
│ ┌─────────────────────────────────────────────────────────┐ │
|
||||
│ │ • Anomaly Detection │ │
|
||||
│ │ • Audit Logging (Encrypted) │ │
|
||||
│ │ • Webhook Notifications │ │
|
||||
│ └─────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
@@ -0,0 +1,479 @@
|
||||
---
|
||||
document_type: "architecture_design"
|
||||
service: "N8N"
|
||||
title: "Momentry API 使用流程"
|
||||
date: "2026-03-25"
|
||||
version: "V1.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
tags:
|
||||
- "momentry"
|
||||
- "使用流程"
|
||||
ai_query_hints:
|
||||
- "查詢 Momentry API 使用流程 的內容"
|
||||
- "Momentry API 使用流程 的主要目的是什麼?"
|
||||
- "如何操作或實施 Momentry API 使用流程?"
|
||||
---
|
||||
|
||||
# Momentry API 使用流程
|
||||
|
||||
> **目標**: 從影片上傳到搜尋的完整流程
|
||||
> **適用**: WordPress / n8n 整合
|
||||
> **版本**: V1.0 | **日期**: 2026-03-25
|
||||
|
||||
---
|
||||
|
||||
## 流程總覽
|
||||
|
||||
```
|
||||
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
||||
│ 1. 上傳 │ → │ 2. 註冊 │ → │ 3. 確認 │ → │ 4. 處理 │ → │ 5. 搜尋 │
|
||||
│ SFTPGo │ │ 自動完成 │ │ UUID │ │ 查詢進度 │ │ 測試 │
|
||||
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 1: 上傳影片
|
||||
|
||||
### 方式 A: SFTP 上傳(推薦)
|
||||
|
||||
```bash
|
||||
# 連線資訊
|
||||
主機: sftpgo.momentry.ddns.net
|
||||
連接埠: 2022
|
||||
用戶名: demo
|
||||
密碼: demopassword123
|
||||
```
|
||||
|
||||
使用 FileZilla 或 SFTP 客戶端上傳到 `/` 目錄
|
||||
|
||||
### 方式 B: SFTP 命令列
|
||||
|
||||
```bash
|
||||
sshpass -p "demopassword123" sftp -P 2022 demo@sftpgo.momentry.ddns.net
|
||||
```
|
||||
|
||||
上傳後確認檔案在 SFTPGo 中的位置
|
||||
|
||||
---
|
||||
|
||||
## Step 2: 自動註冊
|
||||
|
||||
上傳後,系統會自動:
|
||||
1. 偵測新檔案
|
||||
2. 計算 UUID(SHA256)
|
||||
3. 建立資料庫記錄
|
||||
|
||||
**無需手動操作**
|
||||
|
||||
---
|
||||
|
||||
## Step 3: 確認註冊成功
|
||||
|
||||
### 查詢所有影片
|
||||
|
||||
```bash
|
||||
curl -s -H "X-API-Key: YOUR_API_KEY" \
|
||||
"https://api.momentry.ddns.net/api/v1/videos" | jq '.videos | length'
|
||||
```
|
||||
|
||||
### 查詢特定檔案
|
||||
|
||||
```bash
|
||||
curl -s -H "X-API-Key: YOUR_API_KEY" \
|
||||
"https://api.momentry.ddns.net/api/v1/videos" | jq '.videos[] | select(.file_name | contains("你的檔案名"))'
|
||||
```
|
||||
|
||||
### 預期回應
|
||||
|
||||
```json
|
||||
{
|
||||
"uuid": "952f5854b9febad1",
|
||||
"file_path": "/Users/accusys/momentry/var/sftpgo/data/demo/你的檔案.mp4",
|
||||
"file_name": "你的檔案.mp4",
|
||||
"duration": 123.45,
|
||||
"width": 1920,
|
||||
"height": 1080
|
||||
}
|
||||
```
|
||||
|
||||
**確認要點**:
|
||||
- ✅ UUID 已產生(16位 hex)
|
||||
- ✅ `file_path` 正確
|
||||
- ✅ `duration` > 0
|
||||
|
||||
---
|
||||
|
||||
## Step 4: 查詢處理進度
|
||||
|
||||
### 取得任務 UUID
|
||||
|
||||
```bash
|
||||
# 從影片資訊取得 job_id
|
||||
curl -s -H "X-API-Key: YOUR_API_KEY" \
|
||||
"https://api.momentry.ddns.net/api/v1/videos" | \
|
||||
jq '.videos[] | select(.file_name == "你的檔案.mp4") | {uuid, job_id}'
|
||||
```
|
||||
|
||||
### 查詢任務狀態
|
||||
|
||||
```bash
|
||||
curl -s -H "X-API-Key: YOUR_API_KEY" \
|
||||
"https://api.momentry.ddns.net/api/v1/jobs/{uuid}"
|
||||
```
|
||||
|
||||
### 任務狀態說明
|
||||
|
||||
| status | 說明 | 動作 |
|
||||
|--------|------|------|
|
||||
| `pending` | 等待處理 | 等待中 |
|
||||
| `processing` | 處理中 | 繼續輪詢 |
|
||||
| `completed` | 已完成 | 可進入 Step 5 |
|
||||
| `failed` | 處理失敗 | 檢查錯誤 |
|
||||
|
||||
### n8n 輪詢範例
|
||||
|
||||
```javascript
|
||||
// n8n Workflow: 檢查處理狀態
|
||||
const jobUuid = $input.item.json.job_uuid;
|
||||
|
||||
const response = await fetch(
|
||||
`https://api.momentry.ddns.net/api/v1/jobs/${jobUuid}`,
|
||||
{
|
||||
headers: {
|
||||
"X-API-Key": "YOUR_API_KEY"
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const job = await response.json();
|
||||
|
||||
// 狀態檢查
|
||||
if (job.status === 'completed') {
|
||||
return [{ json: { done: true, video_uuid: job.video_uuid } }];
|
||||
} else {
|
||||
return [{ json: { done: false, status: job.status } }];
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 5: 搜尋測試
|
||||
|
||||
處理完成後,資料會入庫到向量資料庫,可進行搜尋測試。
|
||||
|
||||
### 測試向量搜尋
|
||||
|
||||
```bash
|
||||
curl -s -X POST "https://api.momentry.ddns.net/api/v1/search" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"query": "測試關鍵字",
|
||||
"limit": 5
|
||||
}'
|
||||
```
|
||||
|
||||
### 取得分段(Chunk)內容
|
||||
|
||||
搜尋結果會返回影片分段(Chunk),包含可播放的時間軸資訊:
|
||||
|
||||
```json
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"uuid": "39567a0eb16f39fd",
|
||||
"chunk_id": "sentence_1471",
|
||||
"chunk_type": "sentence",
|
||||
"start_time": 5309.08,
|
||||
"end_time": 5311.08,
|
||||
"text": "influenced by a vital way,",
|
||||
"score": 0.68
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Chunk 欄位說明**:
|
||||
| 欄位 | 說明 |
|
||||
|------|------|
|
||||
| `uuid` | 影片 UUID(用於取得影片網址) |
|
||||
| `chunk_id` | 分段 ID |
|
||||
| `chunk_type` | 分段類型(sentence/cut/time/trace/story) |
|
||||
| `start_time` | 開始時間(秒) |
|
||||
| `end_time` | 結束時間(秒) |
|
||||
| `text` | 語音內容文字 |
|
||||
| `score` | 相似度分數(0-1) |
|
||||
|
||||
### 播放分段
|
||||
|
||||
取得 Chunk 後可組合成播放網址:
|
||||
|
||||
```
|
||||
影片網址?start={start_time}&end={end_time}
|
||||
```
|
||||
|
||||
範例:
|
||||
```
|
||||
https://wp.momentry.ddns.net/video.mp4?start=5309.08&end=5311.08
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 完整 n8n Workflow 範例
|
||||
|
||||
```
|
||||
┌──────────────┐
|
||||
│ 觸發 (定時) │
|
||||
└──────┬───────┘
|
||||
▼
|
||||
┌──────────────┐ ┌──────────────┐
|
||||
│ 查詢影片 │────►│ 比對新檔案 │
|
||||
│ /videos │ │ │
|
||||
└──────┬───────┘ └──────────────┘
|
||||
│ │
|
||||
▼ ▼
|
||||
┌──────────────┐ ┌──────────────┐
|
||||
│ 等待處理 │◄────│ 輪詢任務狀態 │
|
||||
│ /jobs/:uuid │ │ /jobs/:uuid │
|
||||
└──────┬───────┘ └──────────────┘
|
||||
│
|
||||
▼ (completed)
|
||||
┌──────────────┐
|
||||
│ 搜尋測試 │
|
||||
│ /search │
|
||||
└──────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 快速參考
|
||||
|
||||
| 步驟 | API | 用途 |
|
||||
|------|-----|------|
|
||||
| 查詢影片 | `GET /api/v1/videos` | 確認上傳成功 |
|
||||
| 查詢任務 | `GET /api/v1/jobs/:uuid` | 查看處理進度 |
|
||||
| 搜尋內容 | `POST /api/v1/search` | 測試搜尋功能 |
|
||||
|
||||
---
|
||||
|
||||
## WordPress PHP 範例
|
||||
|
||||
### 基本設定
|
||||
|
||||
```php
|
||||
<?php
|
||||
class Momentry_API {
|
||||
private const API_URL = 'https://api.momentry.ddns.net';
|
||||
private const API_KEY = 'YOUR_API_KEY';
|
||||
|
||||
public static function request(string $method, string $endpoint, ?array $data = null): array {
|
||||
$url = self::API_URL . $endpoint;
|
||||
|
||||
$args = [
|
||||
'method' => $method,
|
||||
'headers' => [
|
||||
'X-API-Key' => self::API_KEY,
|
||||
'Content-Type' => 'application/json',
|
||||
],
|
||||
'timeout' => 30,
|
||||
];
|
||||
|
||||
if ($data !== null) {
|
||||
$args['body'] = json_encode($data);
|
||||
}
|
||||
|
||||
$response = wp_remote_request($url, $args);
|
||||
|
||||
if (is_wp_error($response)) {
|
||||
throw new Exception($response->get_error_message());
|
||||
}
|
||||
|
||||
return json_decode(wp_remote_retrieve_body($response), true);
|
||||
}
|
||||
|
||||
public static function getVideos(): array {
|
||||
return self::request('GET', '/api/v1/videos');
|
||||
}
|
||||
|
||||
public static function getVideo(string $uuid): array {
|
||||
return self::request('GET', "/api/v1/videos/{$uuid}/details");
|
||||
}
|
||||
|
||||
public static function getJob(string $uuid): array {
|
||||
return self::request('GET', "/api/v1/jobs/{$uuid}");
|
||||
}
|
||||
|
||||
public static function search(string $query, int $topK = 5): array {
|
||||
return self::request('POST', '/api/v1/search', [
|
||||
'query' => $query,
|
||||
'top_k' => $topK,
|
||||
]);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Step 3: 確認註冊成功
|
||||
|
||||
```php
|
||||
<?php
|
||||
// 查詢所有影片
|
||||
$videos = Momentry_API::getVideos();
|
||||
|
||||
foreach ($videos['videos'] as $video) {
|
||||
echo "UUID: " . $video['uuid'] . "\n";
|
||||
echo "檔案: " . $video['file_name'] . "\n";
|
||||
echo "時長: " . $video['duration'] . " 秒\n";
|
||||
echo "---\n";
|
||||
}
|
||||
|
||||
// 查詢特定影片
|
||||
$video = Momentry_API::getVideo('952f5854b9febad1');
|
||||
print_r($video);
|
||||
```
|
||||
|
||||
### Step 4: 查詢處理進度
|
||||
|
||||
```php
|
||||
<?php
|
||||
// 取得任務狀態
|
||||
$job = Momentry_API::getJob('9760d0820f0cf9a7');
|
||||
|
||||
switch ($job['status']) {
|
||||
case 'pending':
|
||||
echo "等待處理中...\n";
|
||||
break;
|
||||
case 'processing':
|
||||
echo "處理中: " . $job['progress'] . "%\n";
|
||||
break;
|
||||
case 'completed':
|
||||
echo "處理完成!\n";
|
||||
break;
|
||||
case 'failed':
|
||||
echo "處理失敗: " . ($job['error'] ?? '未知錯誤') . "\n";
|
||||
break;
|
||||
}
|
||||
```
|
||||
|
||||
### Step 5: 搜尋內容並取得 Chunk
|
||||
|
||||
```php
|
||||
<?php
|
||||
// 搜尋相關片段
|
||||
$results = Momentry_API::search('測試關鍵字', 5);
|
||||
|
||||
foreach ($results['results'] as $result) {
|
||||
echo "影片 UUID: " . $result['uuid'] . "\n";
|
||||
echo "Chunk ID: " . $result['chunk_id'] . "\n";
|
||||
echo "類型: " . $result['chunk_type'] . "\n";
|
||||
echo "開始: " . $result['start_time'] . "s\n";
|
||||
echo "結束: " . $result['end_time'] . "s\n";
|
||||
echo "內容: " . ($result['text'] ?? '') . "\n";
|
||||
echo "相似度: " . $result['score'] . "\n";
|
||||
echo "---\n";
|
||||
}
|
||||
```
|
||||
|
||||
### WordPress Shortcode 範例(可點擊播放)
|
||||
|
||||
```php
|
||||
<?php
|
||||
// 在 functions.php 中加入
|
||||
add_shortcode('momentry_search', function($atts) {
|
||||
$atts = shortcode_atts([
|
||||
'query' => '',
|
||||
'limit' => 10,
|
||||
], $atts);
|
||||
|
||||
if (empty($atts['query'])) {
|
||||
return '<p>請輸入搜尋關鍵字</p>';
|
||||
}
|
||||
|
||||
try {
|
||||
$results = Momentry_API::search($atts['query'], $atts['limit']);
|
||||
|
||||
if (empty($results['results'])) {
|
||||
return '<p>找不到相關結果</p>';
|
||||
}
|
||||
|
||||
$html = '<div class="momentry-results">';
|
||||
$html .= '<h3>搜尋結果: ' . esc_html($atts['query']) . '</h3>';
|
||||
$html .= '<ul>';
|
||||
|
||||
foreach ($results['results'] as $result) {
|
||||
$video_uuid = $result['uuid'];
|
||||
$start = $result['start_time'] ?? 0;
|
||||
$end = $result['end_time'] ?? 0;
|
||||
$text = $result['text'] ?? '無文字描述';
|
||||
|
||||
$html .= '<li>';
|
||||
$html .= '<a href="/player?uuid=' . esc_attr($video_uuid) .
|
||||
'&start=' . esc_attr($start) .
|
||||
'&end=' . esc_attr($end) . '">';
|
||||
$html .= '播放 ' . $start . 's - ' . $end . 's';
|
||||
$html .= '</a>';
|
||||
$html .= '<br>';
|
||||
$html .= '<small>相似度: ' . round($result['score'] * 100) . '%</small>';
|
||||
$html .= '<br>';
|
||||
$html .= esc_html($text);
|
||||
$html .= '</li>';
|
||||
}
|
||||
|
||||
$html .= '</ul></div>';
|
||||
return $html;
|
||||
|
||||
} catch (Exception $e) {
|
||||
return '<p>搜尋服務暫時無法使用</p>';
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
**使用方式**:
|
||||
```html
|
||||
[momentry_search query="關鍵字" limit="5"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 完整 n8n Workflow 範例
|
||||
|
||||
```
|
||||
┌──────────────┐
|
||||
│ 觸發 (定時) │
|
||||
└──────┬───────┘
|
||||
▼
|
||||
┌──────────────┐ ┌──────────────┐
|
||||
│ 查詢影片 │────►│ 比對新檔案 │
|
||||
│ /videos │ │ │
|
||||
└──────┬───────┘ └──────────────┘
|
||||
│ │
|
||||
▼ ▼
|
||||
┌──────────────┐ ┌──────────────┐
|
||||
│ 等待處理 │◄────│ 輪詢任務狀態 │
|
||||
│ /jobs/:uuid │ │ /jobs/:uuid │
|
||||
└──────┬───────┘ └──────────────┘
|
||||
│
|
||||
▼ (completed)
|
||||
┌──────────────┐
|
||||
│ 搜尋測試 │
|
||||
│ /search │
|
||||
└──────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**注意**:
|
||||
- 處理時間視影片長度而定(1分鐘影片約需 2-5 分鐘處理)
|
||||
- 大量影片時建議分批上傳
|
||||
|
||||
---
|
||||
|
||||
## 附錄:版本歷史
|
||||
|
||||
| 版本 | 日期 | 內容 | 操作人 |
|
||||
|------|------|------|--------|
|
||||
| V1.0 | 2026-03-25 | 初版建立 | OpenCode |
|
||||
| V1.1 | 2026-03-25 | 新增 Chunk 取得與播放說明、Shortcode 範例 | OpenCode |
|
||||
| V1.2 | 2026-03-25 | 修正 SFTPGo 主機名稱為 sftpgo.momentry.ddns.net | OpenCode |
|
||||
@@ -0,0 +1,223 @@
|
||||
---
|
||||
document_type: "architecture_design"
|
||||
service: "MOMENTRY_CORE"
|
||||
title: "架構決策卡片"
|
||||
date: "2026-04-22"
|
||||
version: "V1.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
tags:
|
||||
- "架構決策卡片"
|
||||
ai_query_hints:
|
||||
- "查詢 架構決策卡片 的內容"
|
||||
- "架構決策卡片 的主要目的是什麼?"
|
||||
- "如何操作或實施 架構決策卡片?"
|
||||
---
|
||||
|
||||
# 架構決策卡片
|
||||
|
||||
## 卡片 1: 分片類型設計
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| **決策編號** | AD-2026-001 |
|
||||
| **決策名稱** | ChunkType 枚舉設計 |
|
||||
| **決策時間** | 2026-04-22 |
|
||||
| **決策狀態** | ✅ 已實施 |
|
||||
| **相關代碼** | `src/core/chunk/types.rs:6-12` |
|
||||
|
||||
### 問題描述
|
||||
設計文檔中定義的分片類型 (`sentence|visual|scene|summary`) 與實際代碼實現不一致,導致設計與實現脫節。
|
||||
|
||||
### 決策選項
|
||||
1. **選項 A**: 修改代碼適應設計文檔
|
||||
- 優點:保持設計一致性
|
||||
- 缺點:需要大量代碼修改,可能影響現有功能
|
||||
2. **選項 B**: 更新設計文檔反映實際實現
|
||||
- 優點:反映真實系統狀態,維護成本低
|
||||
- 缺點:設計文檔與原始設計偏離
|
||||
|
||||
### 最終決策
|
||||
選擇 **選項 B**,以實際代碼實現為準,更新設計文檔。
|
||||
|
||||
### 實施方案
|
||||
1. 更新所有架構文檔使用實際的 `ChunkType` 枚舉值
|
||||
2. 創建術語對照表
|
||||
3. 更新代碼註釋
|
||||
|
||||
### 影響評估
|
||||
- **正面影響**: 設計與實現一致,減少團隊困惑
|
||||
- **負面影響**: 需要更新大量文檔
|
||||
- **風險**: 術語混亂過渡期
|
||||
|
||||
---
|
||||
|
||||
## 卡片 2: 數據結構類型安全
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| **決策編號** | AD-2026-002 |
|
||||
| **決策名稱** | 分片內容類型安全設計 |
|
||||
| **決策時間** | 2026-04-22 |
|
||||
| **決策狀態** | ⚠️ 待實施 |
|
||||
| **相關代碼** | `src/core/chunk/types.rs:43-65` |
|
||||
|
||||
### 問題描述
|
||||
當前 `Chunk` 結構使用 `serde_json::Value` 存儲動態內容,缺乏類型安全,容易導致運行時錯誤。
|
||||
|
||||
### 決策選項
|
||||
1. **選項 A**: 保持動態 JSON 結構
|
||||
- 優點:靈活性高,易於擴展
|
||||
- 缺點:缺乏類型安全,編譯時無法檢測錯誤
|
||||
2. **選項 B**: 實現類型安全結構
|
||||
- 優點:編譯時類型檢查,代碼更安全
|
||||
- 缺點:靈活性降低,需要為每個分片類型定義專用結構
|
||||
|
||||
### 最終決策
|
||||
選擇 **選項 B**,分階段實現類型安全重構。
|
||||
|
||||
### 實施方案
|
||||
1. Phase 1: 為每個 `ChunkType` 定義專用內容結構
|
||||
2. Phase 2: 實現自動化遷移工具
|
||||
3. Phase 3: 保持向後兼容性,逐步遷移
|
||||
|
||||
### 影響評估
|
||||
- **正面影響**: 提高代碼安全性,減少運行時錯誤
|
||||
- **負面影響**: 開發複雜度增加,需要遷移現有數據
|
||||
- **風險**: 遷移過程中可能出現兼容性問題
|
||||
|
||||
---
|
||||
|
||||
## 卡片 3: 處理管道設計
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| **決策編號** | AD-2026-003 |
|
||||
| **決策名稱** | 統一處理器執行框架 |
|
||||
| **決策時間** | 2026-04-22 |
|
||||
| **決策狀態** | ✅ 已實施 |
|
||||
| **相關代碼** | `src/core/processor/executor.rs` |
|
||||
|
||||
### 問題描述
|
||||
不同的 AI 處理器使用不同的執行方式,缺乏統一的錯誤處理和超時控制。
|
||||
|
||||
### 決策選項
|
||||
1. **選項 A**: 每個處理器獨立實現執行邏輯
|
||||
- 優點:各處理器可以優化自身執行
|
||||
- 缺點:代碼重複,錯誤處理不一致
|
||||
2. **選項 B**: 創建統一執行器框架
|
||||
- 優點:代碼復用,統一的錯誤處理和超時控制
|
||||
- 缺點:可能需要適配現有處理器
|
||||
|
||||
### 最終決策
|
||||
選擇 **選項 B**,實現 `PythonExecutor` 統一框架。
|
||||
|
||||
### 實施方案
|
||||
1. 創建 `PythonExecutor` 結構,提供統一的腳本執行接口
|
||||
2. 支持超時控制、錯誤恢復和結果解析
|
||||
3. 所有 Python 腳本處理器使用統一的執行器
|
||||
|
||||
### 影響評估
|
||||
- **正面影響**: 代碼復用,統一的錯誤處理,易於維護
|
||||
- **負面影響**: 需要修改現有處理器適配新框架
|
||||
- **風險**: 過渡期可能出現執行問題
|
||||
|
||||
---
|
||||
|
||||
## 卡片 4: 多數據庫架構
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| **決策編號** | AD-2026-004 |
|
||||
| **決策名稱** | 多數據庫系統設計 |
|
||||
| **決策時間** | 2026-04-22 |
|
||||
| **決策狀態** | ✅ 已實施 |
|
||||
| **相關代碼** | `src/core/db/` 目錄 |
|
||||
|
||||
### 問題描述
|
||||
系統需要處理不同類型的數據:結構化數據、向量數據、緩存數據和文檔數據。
|
||||
|
||||
### 決策選項
|
||||
1. **選項 A**: 單一數據庫系統
|
||||
- 優點:架構簡單,維護成本低
|
||||
- 缺點:性能可能受限,不適合所有數據類型
|
||||
2. **選項 B**: 多數據庫系統
|
||||
- 優點:每種數據類型使用最適合的數據庫,性能最佳
|
||||
- 缺點:架構複雜,維護成本高
|
||||
|
||||
### 最終決策
|
||||
選擇 **選項 B**,實現多數據庫系統。
|
||||
|
||||
### 實施方案
|
||||
1. **PostgreSQL**: 存儲結構化數據(視訊、分片、任務)
|
||||
2. **Redis**: 緩存和隊列管理
|
||||
3. **Qdrant**: 向量數據存儲和檢索
|
||||
4. **MongoDB**: 文檔數據存儲
|
||||
|
||||
### 影響評估
|
||||
- **正面影響**: 每種數據類型性能最優,系統擴展性好
|
||||
- **負面影響**: 架構複雜,需要管理多個數據庫連接
|
||||
- **風險**: 數據一致性維護複雜
|
||||
|
||||
---
|
||||
|
||||
## 卡片 5: 環境隔離設計
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| **決策編號** | AD-2026-005 |
|
||||
| **決策名稱** | 開發與生產環境隔離 |
|
||||
| **決策時間** | 2026-04-22 |
|
||||
| **決策狀態** | ✅ 已實施 |
|
||||
| **相關代碼** | `src/bin/momentry_playground.rs` |
|
||||
|
||||
### 問題描述
|
||||
開發環境和生產環境需要隔離,避免開發測試影響生產數據。
|
||||
|
||||
### 決策選項
|
||||
1. **選項 A**: 單一環境,通過配置切換
|
||||
- 優點:架構簡單,部署方便
|
||||
- 缺點:開發測試可能污染生產數據
|
||||
2. **選項 B**: 完全隔離的多環境
|
||||
- 優點:環境完全隔離,安全可靠
|
||||
- 缺點:需要維護多套環境
|
||||
|
||||
### 最終決策
|
||||
選擇 **選項 B**,實現完全環境隔離。
|
||||
|
||||
### 實施方案
|
||||
1. **生產環境**: `momentry` 二進制,使用 `momentry:` Redis 網址
|
||||
2. **開發環境**: `momentry_playground` 二進制,使用 `momentry_dev:` Redis 網址
|
||||
3. **環境配置**: 通過環境變數和配置文件區分
|
||||
|
||||
### 影響評估
|
||||
- **正面影響**: 環境完全隔離,開發測試不影響生產
|
||||
- **負面影響**: 需要維護多套部署配置
|
||||
- **風險**: 配置錯誤可能導致環境混亂
|
||||
|
||||
---
|
||||
|
||||
## 如何使用決策卡片
|
||||
|
||||
### 新增決策
|
||||
1. 創建新的決策卡片
|
||||
2. 填寫決策編號 (AD-YYYY-NNN)
|
||||
3. 記錄決策過程和結果
|
||||
4. 更新到本文檔
|
||||
|
||||
### 決策審查
|
||||
1. 每季度審查所有決策卡片
|
||||
2. 評估決策實施效果
|
||||
3. 必要時調整或撤銷決策
|
||||
|
||||
### 決策歸檔
|
||||
1. 已完成的決策歸檔到歷史記錄
|
||||
2. 失敗的決策記錄失敗原因和學習點
|
||||
3. 成功的決策作為最佳實踐參考
|
||||
|
||||
---
|
||||
|
||||
**最後更新**: 2026-04-22
|
||||
**卡片數量**: 5
|
||||
**狀態分布**: ✅ 已實施 4,⚠️ 待實施 1
|
||||
@@ -0,0 +1,163 @@
|
||||
---
|
||||
document_type: "architecture_design"
|
||||
service: "MOMENTRY_CORE"
|
||||
title: "架構決策執行計畫"
|
||||
date: "2026-04-22"
|
||||
version: "V1.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
tags:
|
||||
- "架構決策執行計畫"
|
||||
ai_query_hints:
|
||||
- "查詢 架構決策執行計畫 的內容"
|
||||
- "架構決策執行計畫 的主要目的是什麼?"
|
||||
- "如何操作或實施 架構決策執行計畫?"
|
||||
---
|
||||
|
||||
# 架構決策執行計畫
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | OpenCode |
|
||||
| 建立時間 | 2026-04-22 |
|
||||
| 最後更新 | 2026-04-22 |
|
||||
| 文件版本 | V1.2 |
|
||||
| 相關文件 | [DESIGN_IMPLEMENTATION_GAP.md](./DESIGN_IMPLEMENTATION_GAP.md)<br>[ARCHITECTURE_OVERVIEW.md](./ARCHITECTURE_OVERVIEW.md)<br>[TECHNICAL_DECISION_RECORDS.md](./TECHNICAL_DECISION_RECORDS.md)<br>[ARCHITECTURE_ROADMAP.md](./ARCHITECTURE_ROADMAP.md)<br>[TERMINOLOGY_MAPPING.md](./TERMINOLOGY_MAPPING.md) |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.2 | 2026-04-22 | 更新 Phase 1.2 任務完成狀態 | OpenCode | OpenCode / deepseek-v3.2 |
|
||||
| V1.1 | 2026-04-22 | 更新 Phase 1.1 任務完成狀態 | OpenCode | OpenCode / deepseek-v3.2 |
|
||||
| V1.0 | 2026-04-22 | 創建架構決策執行計畫 | OpenCode | OpenCode / deepseek-v3.2 |
|
||||
|
||||
---
|
||||
|
||||
## 1. 執行計畫概述
|
||||
|
||||
本執行計畫基於 [DESIGN_IMPLEMENTATION_GAP.md](./DESIGN_IMPLEMENTATION_GAP.md) 中識別的設計與實現差異,制定具體的執行方案。
|
||||
|
||||
### 1.1 核心原則
|
||||
|
||||
1. **優先級驅動**:根據影響程度和實現難度確定優先級
|
||||
2. **漸進式改進**:小步快跑,快速驗證,持續迭代
|
||||
3. **風險可控**:每個階段都有明確的退出條件和回滾方案
|
||||
|
||||
### 1.2 執行階段
|
||||
|
||||
| 階段 | 時間範圍 | 主要目標 |
|
||||
|------|----------|----------|
|
||||
| **Phase 1** | 2026-04-22 至 2026-05-22 | 基礎一致性建立 |
|
||||
| **Phase 2** | 2026-05-23 至 2026-07-22 | 缺失功能補齊 |
|
||||
| **Phase 3** | 2026-07-23 至 2026-09-22 | 功能增強優化 |
|
||||
| **Phase 4** | 2026-09-23 至 2026-12-22 | 架構現代化 |
|
||||
|
||||
---
|
||||
|
||||
## 2. Phase 1: 基礎一致性建立 (1個月)
|
||||
|
||||
### 2.1 目標
|
||||
- 統一設計與實現的術語和概念
|
||||
- 建立設計與實現同步機制
|
||||
- 完成所有架構文檔的更新
|
||||
|
||||
### 2.2 具體任務
|
||||
|
||||
#### 任務 1.1: 術語標準化 (優先級 P0) ✅ 已完成
|
||||
- **問題**: 設計文檔使用 `sentence|visual|scene|summary`,代碼使用 `TimeBased|Sentence|Cut|Trace|Story`
|
||||
- **解決方案**:
|
||||
1. 更新所有設計文檔使用代碼中的術語
|
||||
2. 創建術語對照表
|
||||
3. 更新代碼註釋和文檔生成工具
|
||||
- **負責人**: OpenCode
|
||||
- **時間**: 2026-04-22 至 2026-04-26
|
||||
- **實際完成**: 2026-04-22
|
||||
- **產出物**:
|
||||
1. `TERMINOLOGY_MAPPING.md` - 完整術語對照表
|
||||
2. `CHUNKING_ARCHITECTURE.md` V1.1 - 更新術語
|
||||
3. `ARCHITECTURE_OVERVIEW.md` V1.2 - 更新術語和索引
|
||||
4. `chunking/CHUNKING_SCHEMA_SPEC.md` V1.1 - 更新術語
|
||||
5. `chunking/CHUNKING_ARCHITECTURE.md` V1.1 - 更新術語和參考
|
||||
|
||||
#### 任務 1.2: 文檔一致性檢查工具 (優先級 P0) ✅ 已完成
|
||||
- **問題**: 手動檢查文檔與代碼一致性效率低
|
||||
- **解決方案**:
|
||||
1. 擴展現有的 `scripts/check_architecture_docs.py`
|
||||
2. 添加代碼與文檔一致性檢查
|
||||
3. 集成到 CI/CD 流程
|
||||
- **負責人**: OpenCode
|
||||
- **時間**: 2026-04-27 至 2026-05-01
|
||||
- **實際完成**: 2026-04-22
|
||||
- **產出物**:
|
||||
1. `scripts/check_code_document_consistency.py` - 代碼與文檔一致性檢查工具
|
||||
2. `scripts/check_architecture_all.py` - 整合檢查腳本
|
||||
3. 更新 `scripts/check_architecture_docs.py` - 增強術語檢查功能
|
||||
- **成果**:
|
||||
1. 自動化檢測設計術語與實現狀態不一致問題
|
||||
2. 提供詳細修復建議
|
||||
3. 整合兩個檢查工具為統一入口
|
||||
|
||||
---
|
||||
|
||||
## 3. Phase 2: 缺失功能補齊 (2個月)
|
||||
|
||||
### 3.1 目標
|
||||
- 實現 Rule 2 視覺分片基礎框架
|
||||
- 建立視覺分片處理管道
|
||||
- 完成基礎視覺檢索功能
|
||||
|
||||
### 3.2 具體任務
|
||||
|
||||
#### 任務 2.1: 視覺分片數據結構設計 (優先級 P0)
|
||||
- **問題**: 缺乏視覺分片專用數據結構
|
||||
- **解決方案**:
|
||||
1. 設計 `VisualChunk` 數據結構
|
||||
2. 擴展 `ChunkType` 枚舉
|
||||
3. 創建視覺分片專用內容格式
|
||||
- **負責人**: OpenCode
|
||||
- **時間**: 2026-05-23 至 2026-05-30
|
||||
|
||||
#### 任務 2.2: YOLO 處理器集成 (優先級 P0)
|
||||
- **問題**: YOLO 處理器存在但未用於分片生成
|
||||
- **解決方案**:
|
||||
1. 擴展現有 YOLO 處理器輸出格式
|
||||
2. 創建視覺分片生成器
|
||||
3. 集成到處理管道
|
||||
- **負責人**: OpenCode
|
||||
- **時間**: 2026-05-31 至 2026-06-14
|
||||
|
||||
---
|
||||
|
||||
## 4. 執行監控與評估
|
||||
|
||||
### 4.1 關鍵績效指標 (KPIs)
|
||||
|
||||
| KPI | 目標值 | 測量頻率 | 負責人 |
|
||||
|-----|--------|----------|--------|
|
||||
| **設計實現一致性** | ≥95% | 每週 | OpenCode |
|
||||
| **功能完成率** | ≥90% | 每月 | OpenCode |
|
||||
|
||||
### 4.2 進度報告機制
|
||||
|
||||
1. **每週進度報告** (週五)
|
||||
- 本週完成工作總結
|
||||
- 下週工作計劃
|
||||
- 風險和問題報告
|
||||
|
||||
---
|
||||
|
||||
## 5. 成功標準
|
||||
|
||||
### 5.1 最終成功標準
|
||||
|
||||
1. **設計實現一致性**:設計與實現差異 ≤5%
|
||||
2. **功能完整性**:所有設計功能實現率 ≥95%
|
||||
3. **系統穩定性**:生產環境可用性 ≥99.9%
|
||||
|
||||
---
|
||||
|
||||
**最後更新**: 2026-04-22
|
||||
@@ -0,0 +1,389 @@
|
||||
---
|
||||
document_type: "architecture_design"
|
||||
service: "MOMENTRY_CORE"
|
||||
title: "架構文檔關係圖與導航指南"
|
||||
date: "2026-04-22"
|
||||
version: "V1.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
tags:
|
||||
- "架構文檔關係圖與導航指南"
|
||||
ai_query_hints:
|
||||
- "查詢 架構文檔關係圖與導航指南 的內容"
|
||||
- "架構文檔關係圖與導航指南 的主要目的是什麼?"
|
||||
- "如何操作或實施 架構文檔關係圖與導航指南?"
|
||||
---
|
||||
|
||||
# 架構文檔關係圖與導航指南
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | OpenCode |
|
||||
| 建立時間 | 2026-04-22 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-04-22 | 創建架構文檔關係圖 | OpenCode | OpenCode / deepseek-v3.2 |
|
||||
|
||||
---
|
||||
|
||||
## 1. 文檔關係圖
|
||||
|
||||
```
|
||||
核心文檔
|
||||
│
|
||||
├──> [ARCHITECTURE_OVERVIEW.md] (總覽)
|
||||
│ │
|
||||
│ ├──> [ARCHITECTURE_ROADMAP.md] (路線圖)
|
||||
│ ├──> [TECHNICAL_DECISION_RECORDS.md] (決策記錄)
|
||||
│ ├──> [DESIGN_IMPLEMENTATION_GAP.md] (設計實現差異)
|
||||
│ ├──> [ARCHITECTURE_DECISION_EXECUTION_PLAN.md] (執行計畫)
|
||||
│ └──> [ARCHITECTURE_REVIEW_PROCESS.md] (審查流程)
|
||||
│
|
||||
├──> [PERFORMANCE_AND_SCALABILITY.md] (效能與擴展)
|
||||
│ │
|
||||
│ ├──> [MONITORING_ARCHITECTURE.md] (監控架構)
|
||||
│ └──> [MONITORING_SETUP_GUIDE.md] (監控部署指南)
|
||||
│
|
||||
├──> [SECURITY_ARCHITECTURE.md] (安全架構)
|
||||
│ │
|
||||
│ ├──> [API_KEY_ARCHITECTURE.md] (API Key 管理)
|
||||
│ └──> scripts/security_check.sh (安全檢查腳本)
|
||||
│
|
||||
├──> 培訓材料
|
||||
│ │
|
||||
│ ├──> [QUICK_START_GUIDE.md] (5分鐘快速入門)
|
||||
│ ├──> [ARCHITECTURE_DECISION_CARDS.md] (決策卡片)
|
||||
│ └──> [FAQ.md] (常見問題解答)
|
||||
│
|
||||
└──> chunking/ (分片架構專題)
|
||||
│
|
||||
├──> [CHUNKING_ARCHITECTURE.md] (分片總覽)
|
||||
├──> [CHUNK_RULE_1_SENTENCE.md] (句子級分片)
|
||||
├──> [CHUNK_RULE_2_VISUAL.md] (視覺物件級分片)
|
||||
├──> [CHUNK_RULE_3_SCENE.md] (場景級分片)
|
||||
└──> [CHUNK_RULE_4_SUMMARY.md] (摘要級分片)
|
||||
|
||||
特定主題文檔
|
||||
│
|
||||
├──> [PROCESSOR_LIFECYCLE.md] (處理器生命週期)
|
||||
├──> [SERVICE_REGISTRY_ARCHITECTURE.md] (服務註冊)
|
||||
├──> [PROCESSOR_REGISTRY_ARCHITECTURE.md] (處理器註冊)
|
||||
└──> [PROCESSING_PIPELINE.md] (處理管道)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 文檔導航指南
|
||||
|
||||
### 2.1 新手入門路徑
|
||||
|
||||
如果你是 **新加入的開發者** 或 **第一次接觸 Momentry Core**,建議閱讀順序:
|
||||
|
||||
1. **第一步:系統概覽**
|
||||
- [ARCHITECTURE_OVERVIEW.md](./ARCHITECTURE_OVERVIEW.md) - 了解整體架構
|
||||
- [ARCHITECTURE_ROADMAP.md](./ARCHITECTURE_ROADMAP.md) - 了解發展方向
|
||||
|
||||
2. **第二步:核心概念**
|
||||
- [CHUNKING_ARCHITECTURE.md](./chunking/CHUNKING_ARCHITECTURE.md) - 理解分片架構
|
||||
- [PROCESSING_PIPELINE.md](./PROCESSING_PIPELINE.md) - 了解處理流程
|
||||
|
||||
3. **第三步:實際實現**
|
||||
- [DESIGN_IMPLEMENTATION_GAP.md](./DESIGN_IMPLEMENTATION_GAP.md) - 了解設計與實現差異
|
||||
- [TECHNICAL_DECISION_RECORDS.md](./TECHNICAL_DECISION_RECORDS.md) - 了解重要技術決策
|
||||
|
||||
### 2.2 開發者參考路徑
|
||||
|
||||
如果你是 **正在開發功能的開發者**,需要參考的順序:
|
||||
|
||||
1. **功能開發前**
|
||||
- [TECHNICAL_DECISION_RECORDS.md](./TECHNICAL_DECISION_RECORDS.md) - 查看相關決策
|
||||
- [DESIGN_IMPLEMENTATION_GAP.md](./DESIGN_IMPLEMENTATION_GAP.md) - 了解當前狀態
|
||||
|
||||
2. **架構設計時**
|
||||
- [PERFORMANCE_AND_SCALABILITY.md](./PERFORMANCE_AND_SCALABILITY.md) - 效能考量
|
||||
- [SECURITY_ARCHITECTURE.md](./SECURITY_ARCHITECTURE.md) - 安全要求
|
||||
|
||||
3. **實現完成後**
|
||||
- [PROCESSOR_LIFECYCLE.md](./PROCESSOR_LIFECYCLE.md) - 處理器管理
|
||||
- [MONITORING_ARCHITECTURE.md](./MONITORING_ARCHITECTURE.md) - 監控需求
|
||||
|
||||
### 2.3 運維人員路徑
|
||||
|
||||
如果你是 **系統運維或 DevOps 工程師**,建議閱讀順序:
|
||||
|
||||
1. **部署與配置**
|
||||
- [MONITORING_ARCHITECTURE.md](./MONITORING_ARCHITECTURE.md) - 監控設置
|
||||
- [MONITORING_SETUP_GUIDE.md](./MONITORING_SETUP_GUIDE.md) - 監控部署指南
|
||||
- [SERVICE_REGISTRY_ARCHITECTURE.md](./SERVICE_REGISTRY_ARCHITECTURE.md) - 服務管理
|
||||
|
||||
2. **效能優化**
|
||||
- [PERFORMANCE_AND_SCALABILITY.md](./PERFORMANCE_AND_SCALABILITY.md) - 效能基準
|
||||
- [PROCESSOR_REGISTRY_ARCHITECTURE.md](./PROCESSOR_REGISTRY_ARCHITECTURE.md) - 處理器調度
|
||||
|
||||
3. **安全維護**
|
||||
- [SECURITY_ARCHITECTURE.md](./SECURITY_ARCHITECTURE.md) - 安全配置
|
||||
- [API_KEY_ARCHITECTURE.md](./API_KEY_ARCHITECTURE.md) - API Key 管理
|
||||
- scripts/security_check.sh - 安全檢查腳本
|
||||
|
||||
### 2.4 架構師/技術經理路徑
|
||||
|
||||
如果你是 **技術決策者或架構師**,建議閱讀順序:
|
||||
|
||||
1. **戰略規劃**
|
||||
- [ARCHITECTURE_ROADMAP.md](./ARCHITECTURE_ROADMAP.md) - 發展路線
|
||||
- [TECHNICAL_DECISION_RECORDS.md](./TECHNICAL_DECISION_RECORDS.md) - 歷史決策
|
||||
- [ARCHITECTURE_DECISION_EXECUTION_PLAN.md](./ARCHITECTURE_DECISION_EXECUTION_PLAN.md) - 執行計畫
|
||||
- [ARCHITECTURE_REVIEW_PROCESS.md](./ARCHITECTURE_REVIEW_PROCESS.md) - 審查流程
|
||||
|
||||
2. **技術評估**
|
||||
- [DESIGN_IMPLEMENTATION_GAP.md](./DESIGN_IMPLEMENTATION_GAP.md) - 現狀分析
|
||||
- [PERFORMANCE_AND_SCALABILITY.md](./PERFORMANCE_AND_SCALABILITY.md) - 效能評估
|
||||
- [ARCHITECTURE_DECISION_CARDS.md](./ARCHITECTURE_DECISION_CARDS.md) - 決策卡片
|
||||
|
||||
3. **風險管理**
|
||||
- [SECURITY_ARCHITECTURE.md](./SECURITY_ARCHITECTURE.md) - 安全風險
|
||||
- [MONITORING_ARCHITECTURE.md](./MONITORING_ARCHITECTURE.md) - 運維風險
|
||||
|
||||
---
|
||||
|
||||
## 3. 文檔更新流程
|
||||
|
||||
### 3.1 文檔修改觸發條件
|
||||
|
||||
| 觸發條件 | 需要更新的文檔 | 更新負責人 |
|
||||
|----------|----------------|------------|
|
||||
| **新增功能** | 所有相關架構文檔 | 功能開發者 + 架構師 |
|
||||
| **架構變更** | 架構概覽 + 相關專題文檔 | 架構師 |
|
||||
| **重大決策** | 技術決策記錄 | 決策參與者 |
|
||||
| **實現差異** | 設計實現差異文檔 | 開發團隊 |
|
||||
| **效能改進** | 效能與擴展文檔 | 效能工程師 |
|
||||
|
||||
### 3.2 文檔更新檢查清單
|
||||
|
||||
修改任何架構文檔前,請檢查:
|
||||
|
||||
1. **相關性檢查**
|
||||
- [ ] 是否影響其他文檔?
|
||||
- [ ] 是否需要更新關係圖?
|
||||
- [ ] 是否需要通知相關人員?
|
||||
|
||||
2. **一致性檢查**
|
||||
- [ ] 術語使用是否一致?
|
||||
- [ ] 版本號是否更新?
|
||||
- [ ] 時間戳是否更新?
|
||||
|
||||
3. **完整性檢查**
|
||||
- [ ] 版本歷史是否記錄?
|
||||
- [ ] 相關文件鏈接是否正確?
|
||||
- [ ] 參考資料是否完整?
|
||||
|
||||
### 3.3 文檔版本管理規則
|
||||
|
||||
1. **版本號格式**:`V<主版本>.<次版本>`
|
||||
- 主版本:架構重大變更
|
||||
- 次版本:內容更新或修正
|
||||
|
||||
2. **版本更新時機**
|
||||
- 主版本:架構重新設計
|
||||
- 次版本:新增內容、修正錯誤、更新鏈接
|
||||
|
||||
3. **版本兼容性**
|
||||
- 相同主版本應保持向後兼容
|
||||
- 不同主版本可能需要遷移指南
|
||||
|
||||
---
|
||||
|
||||
## 4. 文檔質量標準
|
||||
|
||||
### 4.1 內容質量要求
|
||||
|
||||
| 維度 | 標準 | 檢查方法 |
|
||||
|------|------|----------|
|
||||
| **準確性** | 內容與實際實現一致 | 代碼審查、測試驗證 |
|
||||
| **完整性** | 覆蓋所有相關主題 | 檢查清單、同行評審 |
|
||||
| **一致性** | 術語、格式、風格統一 | 自動化檢查、人工審核 |
|
||||
| **可讀性** | 結構清晰、語言簡潔 | 可讀性測試、用戶反饋 |
|
||||
| **實用性** | 對讀者有實際幫助 | 使用統計、用戶反饋 |
|
||||
|
||||
### 4.2 格式規範
|
||||
|
||||
1. **文件頭部**:必須包含項目表格和版本歷史
|
||||
2. **目錄結構**:使用標準 Markdown 標題層級
|
||||
3. **鏈接格式**:使用相對路徑,確保可移植性
|
||||
4. **代碼示例**:使用正確的語法高亮
|
||||
5. **表格使用**:複雜信息使用表格呈現
|
||||
|
||||
### 4.3 維護責任
|
||||
|
||||
| 文檔類型 | 主要負責人 | 審核人 | 更新頻率 |
|
||||
|----------|------------|--------|----------|
|
||||
| **核心文檔** | 架構師 | CTO | 每月審閱 |
|
||||
| **專題文檔** | 專題負責人 | 架構師 | 隨功能更新 |
|
||||
| **決策記錄** | 決策參與者 | 全體成員 | 實時更新 |
|
||||
| **實現差異** | 開發團隊 | 架構師 | 每週更新 |
|
||||
|
||||
---
|
||||
|
||||
## 5. 常見問題與解決方案
|
||||
|
||||
### 5.1 文檔找不到或鏈接失效
|
||||
|
||||
**問題**:點擊鏈接時找不到文件或顯示錯誤
|
||||
|
||||
**解決方案**:
|
||||
1. 檢查文件是否移動或重命名
|
||||
2. 更新鏈接中的文件路徑
|
||||
3. 如果文件已刪除,更新所有引用
|
||||
|
||||
### 5.2 文檔內容過時
|
||||
|
||||
**問題**:文檔描述與實際實現不一致
|
||||
|
||||
**解決方案**:
|
||||
1. 首先更新 `DESIGN_IMPLEMENTATION_GAP.md`
|
||||
2. 然後更新相關的架構文檔
|
||||
3. 最後更新本文檔的關係圖
|
||||
|
||||
### 5.3 術語不一致
|
||||
|
||||
**問題**:不同文檔使用不同術語描述同一概念
|
||||
|
||||
**解決方案**:
|
||||
1. 在 `ARCHITECTURE_OVERVIEW.md` 中定義術語表
|
||||
2. 統一所有文檔的術語使用
|
||||
3. 建立術語審查流程
|
||||
|
||||
### 5.4 文檔過多難以管理
|
||||
|
||||
**問題**:文檔數量太多,難以找到所需信息
|
||||
|
||||
**解決方案**:
|
||||
1. 使用本文檔作為導航入口
|
||||
2. 建立良好的搜索機制
|
||||
3. 定期整理和歸檔舊文檔
|
||||
|
||||
---
|
||||
|
||||
## 6. 工具與自動化支持
|
||||
|
||||
### 6.1 文檔生成工具
|
||||
|
||||
```bash
|
||||
# 生成文檔關係圖
|
||||
python scripts/generate_doc_graph.py
|
||||
|
||||
# 檢查鏈接有效性
|
||||
python scripts/check_doc_links.py
|
||||
|
||||
# 更新版本歷史
|
||||
python scripts/update_doc_versions.py
|
||||
```
|
||||
|
||||
### 6.2 CI/CD 集成
|
||||
|
||||
在 CI/CD 流程中添加文檔檢查:
|
||||
|
||||
```yaml
|
||||
# .github/workflows/docs-check.yml
|
||||
name: Documentation Check
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'docs_v1.0/ARCHITECTURE/**'
|
||||
|
||||
jobs:
|
||||
check-docs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Check documentation links
|
||||
run: python scripts/check_doc_links.py
|
||||
- name: Validate documentation format
|
||||
run: python scripts/validate_doc_format.py
|
||||
```
|
||||
|
||||
### 6.3 監控與分析
|
||||
|
||||
1. **使用統計**:追蹤文檔訪問頻率
|
||||
2. **搜索分析**:分析用戶搜索關鍵詞
|
||||
3. **反饋收集**:收集用戶對文檔的反饋
|
||||
|
||||
---
|
||||
|
||||
## 7. 總結與建議
|
||||
|
||||
### 7.1 當前狀態評估
|
||||
|
||||
✅ **已完成的工作**:
|
||||
1. 建立了完整的架構文檔體系
|
||||
2. 明確了文檔之間的關係
|
||||
3. 制定了文檔質量標準
|
||||
4. 建立了更新流程
|
||||
|
||||
🔄 **進行中的工作**:
|
||||
1. 保持文檔與代碼同步
|
||||
2. 收集用戶反饋持續改進
|
||||
3. 建立自動化工具支持
|
||||
|
||||
📋 **後續改進計劃**:
|
||||
1. 建立文檔搜尋引擎
|
||||
2. 增加多語言支持
|
||||
3. 建立文檔培訓體系
|
||||
|
||||
### 7.2 最佳實踐建議
|
||||
|
||||
1. **文檔即代碼**:將文檔納入版本控制
|
||||
2. **持續更新**:隨代碼變更同步更新文檔
|
||||
3. **用戶為中心**:以讀者需求設計文檔結構
|
||||
4. **質量優先**:確保文檔準確、完整、一致
|
||||
|
||||
### 7.3 成功指標
|
||||
|
||||
| 指標 | 目標值 | 測量方法 |
|
||||
|------|--------|----------|
|
||||
| **文檔覆蓋率** | > 95% | 代碼功能對應文檔比例 |
|
||||
| **文檔準確率** | > 98% | 文檔與實現一致性檢查 |
|
||||
| **用戶滿意度** | > 4.5/5.0 | 用戶反饋調查 |
|
||||
| **更新及時性** | < 24小時 | 代碼變更到文檔更新時間 |
|
||||
|
||||
---
|
||||
|
||||
## 8. 聯繫與支持
|
||||
|
||||
### 8.1 文檔維護團隊
|
||||
|
||||
| 角色 | 負責人 | 聯繫方式 | 負責文檔類型 |
|
||||
|------|--------|----------|--------------|
|
||||
| **架構文檔負責人** | OpenCode | opencode@momentry.ai | 所有核心文檔 |
|
||||
| **技術文檔審核** | 開發團隊 | dev@momentry.ai | 專題文檔 |
|
||||
| **用戶文檔支持** | 產品團隊 | product@momentry.ai | 用戶指南 |
|
||||
|
||||
### 8.2 問題回報流程
|
||||
|
||||
1. **發現問題**:在文檔中標記或創建 Issue
|
||||
2. **問題分類**:根據類型分配給相應負責人
|
||||
3. **問題解決**:負責人更新文檔
|
||||
4. **驗證關閉**:報告人驗證問題已解決
|
||||
|
||||
### 8.3 文檔貢獻指南
|
||||
|
||||
歡迎貢獻文檔改進:
|
||||
|
||||
1. **小修改**:直接提交 Pull Request
|
||||
2. **中等修改**:先創建 Issue 討論
|
||||
3. **重大修改**:需要架構師審核批准
|
||||
|
||||
**貢獻者獎勵**:優秀的文檔貢獻將獲得 recognition 和獎勵。
|
||||
|
||||
---
|
||||
|
||||
**最後更新**:2026-04-22
|
||||
**文檔狀態**:活躍維護中
|
||||
**建議反饋**:請通過 GitHub Issues 或郵件提供反饋
|
||||
@@ -0,0 +1,348 @@
|
||||
---
|
||||
document_type: "architecture_design"
|
||||
service: "MOMENTRY_CORE"
|
||||
title: "架構優化待評估事項"
|
||||
date: "2026-03-21"
|
||||
version: "V1.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
tags:
|
||||
- "架構優化待評估事項"
|
||||
ai_query_hints:
|
||||
- "查詢 架構優化待評估事項 的內容"
|
||||
- "架構優化待評估事項 的主要目的是什麼?"
|
||||
- "如何操作或實施 架構優化待評估事項?"
|
||||
---
|
||||
|
||||
# 架構優化待評估事項
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | OpenCode |
|
||||
| 建立時間 | 2026-03-21 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 |
|
||||
|------|------|------|--------|
|
||||
| V1.0 | 2026-03-21 | 創建文件 | OpenCode |
|
||||
| V1.1 | 2026-03-22 | 新增 TigerGraph/GraphRAG 說故事評估 | OpenCode |
|
||||
|
||||
---
|
||||
|
||||
## 架構優化項目
|
||||
|
||||
### 1. PostgreSQL → Redis 故障轉移
|
||||
|
||||
**說明**: 當 PostgreSQL 不可用時,降級到 Redis 作為臨時存儲
|
||||
|
||||
**複雜度**: 中
|
||||
|
||||
**影響範圍**:
|
||||
- `src/core/db/postgres_db.rs`
|
||||
- `src/core/db/redis_client.rs`
|
||||
|
||||
**風險**:
|
||||
- 數據一致性問題
|
||||
- 需要定義轉移策略
|
||||
|
||||
**優先級**: 待評估
|
||||
|
||||
---
|
||||
|
||||
### 2. 連接池監控
|
||||
|
||||
**說明**: 添加 PostgreSQL 和 Redis 連接池指標到 Prometheus
|
||||
|
||||
**複雜度**: 低
|
||||
|
||||
**影響範圍**:
|
||||
- `src/core/db/postgres_db.rs`
|
||||
- `src/core/db/redis_client.rs`
|
||||
- `src/api/` (新增 metrics endpoint)
|
||||
|
||||
**風險**: 低
|
||||
|
||||
**優先級**: 待評估
|
||||
|
||||
---
|
||||
|
||||
### 3. Processor 重試機制
|
||||
|
||||
**說明**: 當 processor 失敗時自動重試
|
||||
|
||||
**複雜度**: 中
|
||||
|
||||
**影響範圍**:
|
||||
- `src/core/processor/executor.rs` (新增 `run_with_retry` 方法)
|
||||
- `src/core/processor/mod.rs` (導出 `RetryConfig`)
|
||||
|
||||
**風險**:
|
||||
- 無限重試風險 → 已通過 `max_attempts` 控制
|
||||
- 需要指數退避 → 已實現
|
||||
|
||||
**優先級**: ✅ 已完成 (2026-03-21)
|
||||
|
||||
**實作內容**:
|
||||
- `RetryConfig` 結構體 (可配置重試次數、初始延遲、最大延遲、退避倍數)
|
||||
- `run_with_retry()` 方法 (自動重試 + 指數退避)
|
||||
- 單元測試覆蓋
|
||||
|
||||
**使用範例**:
|
||||
```rust
|
||||
use crate::core::processor::{PythonExecutor, RetryConfig};
|
||||
|
||||
let executor = PythonExecutor::new()?;
|
||||
let config = RetryConfig::new(3).with_delay(1000).with_max_delay(30000);
|
||||
|
||||
executor.run_with_retry(
|
||||
"asr_processor.py",
|
||||
&["--input", "/path/to/video"],
|
||||
Some(&uuid),
|
||||
"asr",
|
||||
Some(Duration::from_secs(3600)),
|
||||
Some(config),
|
||||
).await?;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. PyO3 整合
|
||||
|
||||
**說明**: Python/Rust 直接調用,移除子進程調用
|
||||
|
||||
**複雜度**: 高
|
||||
|
||||
**影響範圍**:
|
||||
- `src/core/processor/executor.rs` (重寫)
|
||||
- Python 模組 (修改為可直接 import)
|
||||
|
||||
**風險**:
|
||||
- Python GIL 問題
|
||||
- 依賴版本兼容性
|
||||
- 需要大量重寫
|
||||
|
||||
**優先級**: 低 (長期目標)
|
||||
|
||||
---
|
||||
|
||||
### 5. HTTP 健康端點
|
||||
|
||||
**說明**: 添加 `/health` API 用於外部監控
|
||||
|
||||
**複雜度**: 低
|
||||
|
||||
**影響範圍**:
|
||||
- `src/api/server.rs` (新增路由)
|
||||
|
||||
**風險**: 低
|
||||
|
||||
**優先級**: ✅ 已完成 (2026-03-21)
|
||||
|
||||
**實作內容**:
|
||||
- `GET /health` - 基本健康檢查 (status, version, uptime)
|
||||
- `GET /health/detailed` - 詳細健康檢查 (PostgreSQL, Redis, Qdrant 狀態和延遲)
|
||||
|
||||
---
|
||||
|
||||
### 6. Gitea Actions CI/CD
|
||||
|
||||
**說明**: 配置 Gitea Actions 自動化 CI/CD,在合併前執行檢查
|
||||
|
||||
**複雜度**: 中
|
||||
|
||||
**影響範圍**:
|
||||
- `.gitea/workflows/` (新增 workflow 文件)
|
||||
|
||||
**優點**:
|
||||
- 強制執行檢查,無法跳過
|
||||
- 跨設備一致
|
||||
- PR 審查前自動檢查
|
||||
|
||||
**風險**: 低
|
||||
|
||||
**優先級**: 待評估
|
||||
|
||||
---
|
||||
|
||||
### 7. Commit Message Lint
|
||||
|
||||
**說明**: 規範化提交訊息格式 (Conventional Commits)
|
||||
|
||||
**複雜度**: 低
|
||||
|
||||
**影響範圍**:
|
||||
- `.git/hooks/commit-msg` (新增 hook)
|
||||
- `~/dotfiles/hooks/commit-msg`
|
||||
|
||||
**風險**: 低
|
||||
|
||||
**優先級**: ✅ 已完成 (2026-03-21)
|
||||
|
||||
**實作內容**:
|
||||
- 驗證格式: `<type>(<scope>): <description>`
|
||||
- 有效類型: feat, fix, docs, style, refactor, test, chore, perf, ci, build, revert
|
||||
- 警告: 第一行超過 72 字符
|
||||
|
||||
**範例**:
|
||||
```
|
||||
feat(api): add health check endpoint
|
||||
fix(db): resolve connection pool issue
|
||||
docs: update README
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 8. 自動化安裝腳本
|
||||
|
||||
**說明**: 創建腳本一次安裝所有開發工具
|
||||
|
||||
**複雜度**: 低
|
||||
|
||||
**影響範圍**:
|
||||
- `scripts/install-dev-tools.sh` (新增)
|
||||
|
||||
**風險**: 低
|
||||
|
||||
**優先級**: 待評估
|
||||
|
||||
---
|
||||
|
||||
## 評估標準
|
||||
|
||||
| 標準 | 說明 |
|
||||
|------|------|
|
||||
| 業務價值 | 對用戶有何幫助 |
|
||||
| 技術風險 | 實現難度和潛在問題 |
|
||||
| 維護成本 | 未來維護負擔 |
|
||||
| 依賴性 | 對其他系統的影響 |
|
||||
|
||||
---
|
||||
|
||||
## 評估記錄
|
||||
|
||||
| 項目 | 評估日期 | 決策 | 原因 |
|
||||
|------|----------|------|------|
|
||||
| PostgreSQL → Redis 故障轉移 | 待評估 | - | - |
|
||||
| 連接池監控 | 待評估 | - | - |
|
||||
| Processor 重試機制 | 2026-03-21 | 已完成 | - |
|
||||
| PyO3 整合 | 待評估 | - | - |
|
||||
| HTTP 健康端點 | 2026-03-21 | 已完成 | - |
|
||||
| Gitea Actions CI/CD | 待評估 | - | - |
|
||||
| Commit Message Lint | 2026-03-21 | 已完成 | - |
|
||||
| 自動化安裝腳本 | 待評估 | - | - |
|
||||
|
||||
---
|
||||
|
||||
## 9. TigerGraph / Knowledge Graph 圖譜說故事
|
||||
|
||||
**說明**: 使用知識圖譜 (Knowledge Graph) 增強視頻敘事 (Storytelling) 和 RAG 檢索
|
||||
|
||||
**複雜度**: 高
|
||||
|
||||
**研究來源**:
|
||||
- [TigerGraph Agentic GraphRAG](https://www.tigergraph.com/blog/agentic-graphrag-gives-ai-a-playbook-for-smarter-retrieval/) (2025-12-15)
|
||||
- [TigerGraph GraphRAG GitHub](https://github.com/tigergraph/graphrag) (v1.2.0, 2026-03-11)
|
||||
- [GraphRAG in 2026: Practitioner's Guide](https://medium.com/graph-praxis/graph-rag-in-2026-a-practitioners-guide-to-what-actually-works-dca4962e7517) (2026-02-22)
|
||||
- [GraphRAG Complete Guide](https://medium.com/@brian-curry-research/graphrag-the-complete-guide-to-graph-powered-retrieval-augmented-generation-eeb58a6bb4d1) (2026-02-11)
|
||||
|
||||
### 核心概念
|
||||
|
||||
| 概念 | 說明 |
|
||||
|------|------|
|
||||
| **GraphRAG** | 結合知識圖譜與 RAG,比傳統向量檢索更智能 |
|
||||
| **知識圖譜** | 實體 (Entity) + 關係 (Relationship) 的結構化表示 |
|
||||
| **多跳推理** | Multi-hop traversal,可連接多個相關節點 |
|
||||
| **混合檢索** | Graph traversal + Vector similarity 結合 |
|
||||
|
||||
### 對 Momentry 的潛在應用
|
||||
|
||||
```
|
||||
視頻場景 → 實體識別 → 關係建立 → 故事圖譜
|
||||
↓ ↓ ↓ ↓
|
||||
CUT [人物, 物品, 動作] [誰做了什麼, 什麼導致什麼] [敘事鏈]
|
||||
```
|
||||
|
||||
**1. 敘事圖譜構建 (Narrative Graph)**
|
||||
- 從 Story/Chunks 模組提取實體
|
||||
- 建立場景之間的因果關係
|
||||
- 追蹤角色互動和情節發展
|
||||
|
||||
**2. 故事檢索增強**
|
||||
```python
|
||||
# 現有: Parent-child chunks
|
||||
parent_chunk: "場景描述"
|
||||
child_chunks: [詳細內容]
|
||||
|
||||
# 加入圖譜:
|
||||
場景A --led_to--> 場景B
|
||||
角色X --interacted_with--> 角色Y
|
||||
主題Y --related_to--> 主題Z
|
||||
```
|
||||
|
||||
**3. 查詢模式**
|
||||
|
||||
| 查詢類型 | 傳統 RAG | GraphRAG |
|
||||
|----------|----------|----------|
|
||||
| 事實查找 | ✅ "這個場景在說什麼" | ✅ |
|
||||
| 主題推理 | ❌ "這個視頻的主要情節" | ✅ Global search |
|
||||
| 多跳關係 | ❌ | ✅ "A導致B,B導致C" |
|
||||
| 可解釋性 | ❌ | ✅ 關係路徑可追溯 |
|
||||
|
||||
### 實作方案
|
||||
|
||||
**方案 A: TigerGraph Cloud (推薦)**
|
||||
- ✅ 原生 Graph + Vector 混合查詢
|
||||
- ✅ GraphRAG 官方支援
|
||||
- ✅ 200GB 免費額度
|
||||
- ❌ 雲端依賴,延遲敏感場景需考慮
|
||||
|
||||
**方案 B: Neo4j + Qdrant**
|
||||
- ✅ 成熟開源生態
|
||||
- ✅ LangChain/LlamaIndex 整合
|
||||
- ❌ 需要維護兩個系統
|
||||
|
||||
**方案 C: 自建混合架構**
|
||||
- PostgreSQL + Neo4j (或Typesense)
|
||||
- 利用現有 BM25 + 向量檢索基礎
|
||||
- ❌ 開發成本高
|
||||
|
||||
### 技術棧整合建議
|
||||
|
||||
```rust
|
||||
// 現有架構
|
||||
Vector Search (Qdrant) ← BM25 (PostgreSQL)
|
||||
|
||||
// 加入 GraphRAG
|
||||
Knowledge Graph (TigerGraph/Neo4j)
|
||||
↓
|
||||
混合檢索 ← Vector + Graph traversal
|
||||
```
|
||||
|
||||
### 優先級: 待評估
|
||||
|
||||
**考慮因素**:
|
||||
- 用戶是否需要複雜的故事情節查詢?
|
||||
- 實體識別 (NER) 成本是否可以接受?
|
||||
- 與現有 BM25 + Vector 混合搜索的比較優勢?
|
||||
|
||||
---
|
||||
|
||||
## 10. LazyGraphRAG / FastGraphRAG 成本優化
|
||||
|
||||
**說明**: GraphRAG 索引成本高昂,LazyGraphRAG 推遲圖譜構建到查詢時
|
||||
|
||||
**來源**: [GraphRAG in 2026](https://medium.com/graph-praxis/graph-rag-in-2026-a-practitioners-guide-to-what-actually-works-dca4962e7517)
|
||||
|
||||
**Microsoft GraphRAG 問題**: $33K 索引大型數據集
|
||||
|
||||
**替代方案**:
|
||||
- **LazyGraphRAG**: 按需構建,查詢時再建立子圖
|
||||
- **FastGraphRAG**: 優化索引管道,10-90% 成本節省
|
||||
- **HippoRAG**: 使用 Personalised PageRank 優化遍歷
|
||||
|
||||
**優先級**: 待評估 (作為 GraphRAG 的一部分)
|
||||
@@ -0,0 +1,329 @@
|
||||
---
|
||||
document_type: "architecture_design"
|
||||
service: "MOMENTRY_CORE"
|
||||
title: "Momentry Core 架構總覽"
|
||||
date: "2026-04-22"
|
||||
version: "V1.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
tags:
|
||||
- "momentry"
|
||||
- "架構總覽"
|
||||
- "core"
|
||||
ai_query_hints:
|
||||
- "查詢 Momentry Core 架構總覽 的內容"
|
||||
- "Momentry Core 架構總覽 的主要目的是什麼?"
|
||||
- "如何操作或實施 Momentry Core 架構總覽?"
|
||||
---
|
||||
|
||||
# Momentry Core 架構總覽
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | OpenCode |
|
||||
| 建立時間 | 2026-04-22 |
|
||||
| 文件版本 | V1.2 |
|
||||
| 最後更新 | 2026-04-22 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.1 | 2026-04-22 | 更新文檔索引,整合新文檔 | OpenCode | OpenCode / deepseek-v3.2 |
|
||||
| V1.0 | 2026-04-22 | 創建架構總覽文件 | OpenCode | OpenCode / Qwen3.6-Plus |
|
||||
|
||||
---
|
||||
|
||||
## 1. 系統概覽
|
||||
|
||||
Momentry Core 是一個基於 Rust 的數字資產管理系統,專注於視頻分析與多模態檢索能力。系統結合了語音識別(ASR/ASRX)、人臉識別(Face Recognition)、物體檢測(YOLO)、場景分類(Places365)等多種 AI 模型,實現全面的視頻內容理解。
|
||||
|
||||
### 核心設計理念
|
||||
- **邊緣 AI 優先**:在本地設備上運行,減少雲端依賴
|
||||
- **多模態融合**:結合視覺、聽覺、文本等多種信號
|
||||
- **層級分片架構**:將連續視頻轉化為結構化知識單元
|
||||
- **實時處理能力**:支持 on-the-fly 處理,縮短等待時間
|
||||
|
||||
---
|
||||
|
||||
## 2. 整體架構圖
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ Momentry Core Architecture │
|
||||
├─────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ API Layer (Axum) │ │
|
||||
│ └─────────────────────────────────────────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌─────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ Core Business Logic │ │
|
||||
│ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ │
|
||||
│ │ │ Chunking │ │Processor │ │Text │ │Embedding │ │ │
|
||||
│ │ │ Engine │ │Registry │ │Processing │ │Engine │ │ │
|
||||
│ │ └────────────┘ └────────────┘ └────────────┘ └────────────┘ │ │
|
||||
│ └─────────────────────────────────────────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌─────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ Data Access Layer │ │
|
||||
│ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ │
|
||||
│ │ │PostgreSQL │ │Redis │ │MongoDB │ │Qdrant │ │ │
|
||||
│ │ │(Primary) │ │(Cache) │ │(Cache) │ │(Vectors) │ │ │
|
||||
│ │ └────────────┘ └────────────┘ └────────────┘ └────────────┘ │ │
|
||||
│ └─────────────────────────────────────────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌─────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ External Tool Integration │ │
|
||||
│ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ │
|
||||
│ │ │Python │ │FFmpeg/ │ │WhisperX │ │InsightFace │ │ │
|
||||
│ │ │Scripts │ │FFprobe │ │(ASR) │ │(Face) │ │ │
|
||||
│ │ └────────────┘ └────────────┘ └────────────┘ └────────────┘ │ │
|
||||
│ └─────────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 核心模塊
|
||||
|
||||
### 3.1 API 層 (`src/api/`)
|
||||
- **技術棧**: Axum + Tower + Serde
|
||||
- **功能**: RESTful API 接口,支持同步/異步處理
|
||||
- **關鍵文件**:
|
||||
- `server.rs`: 主 API 服務器
|
||||
- `search.rs`: 搜索相關 API
|
||||
- `face_recognition.rs`: 人臉識別 API
|
||||
- `person_identity.rs`: 人物身份管理 API
|
||||
|
||||
### 3.2 核心業務邏輯 (`src/core/`)
|
||||
- **分片引擎** (`chunk/`): 視頻分片與知識萃取
|
||||
- **處理器註冊表** (`processor/`): AI 模型執行管理
|
||||
- **文本處理** (`text/`): 同義詞擴展、分詞
|
||||
- **嵌入引擎**: 語義向量生成
|
||||
|
||||
### 3.3 數據訪問層 (`src/core/db/`)
|
||||
- **PostgreSQL**: 主數據存儲,關係型數據
|
||||
- **Redis**: 緩存和隊列管理
|
||||
- **MongoDB**: 文檔緩存
|
||||
- **Qdrant**: 向量數據庫,語義搜索
|
||||
|
||||
### 3.4 外部工具集成 (`scripts/`)
|
||||
- **Python 腳本**: ASR、Face、YOLO、OCR、Scene 等處理器
|
||||
- **FFmpeg/FFprobe**: 視頻處理與元數據提取
|
||||
- **AI 模型**: WhisperX、InsightFace、YOLOv8 等
|
||||
|
||||
---
|
||||
|
||||
## 4. 數據流架構
|
||||
|
||||
### 4.1 視頻註冊流程
|
||||
```
|
||||
1. 用戶上傳視頻 → 2. 生成 UUID → 3. 提取元數據 (FFprobe)
|
||||
→ 4. 存入 PostgreSQL → 5. 觸發處理任務 → 6. 返回響應
|
||||
```
|
||||
|
||||
### 4.2 分片處理流程
|
||||
```
|
||||
1. 原始視頻 → 2. 各處理器執行 (ASR, Face, YOLO, Scene)
|
||||
→ 3. 生成 Pre-Chunk 數據 → 4. 應用分片規則 (Rule 1-4)
|
||||
→ 5. 存入對應數據表 → 6. 向量化並存入 Qdrant
|
||||
```
|
||||
|
||||
### 4.3 搜索查詢流程
|
||||
```
|
||||
1. 用戶查詢 → 2. 同義詞擴展 → 3. BM25 文本搜索
|
||||
→ 4. 向量語義搜索 → 5. 結果融合排序 → 6. 返回檢索結果
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 技術棧
|
||||
|
||||
### 5.1 後端 (Rust)
|
||||
- **Web 框架**: Axum + Tower
|
||||
- **異步運行時**: Tokio (full features)
|
||||
- **序列化**: Serde + Serde JSON
|
||||
- **數據庫驅動**: SQLx, Redis 1.0.x, MongoDB, Qdrant-client
|
||||
- **錯誤處理**: Anyhow + Thiserror
|
||||
- **日誌**: Tracing + Tracing-subscriber
|
||||
|
||||
### 5.2 數據存儲
|
||||
- **主數據庫**: PostgreSQL (SQLx)
|
||||
- **緩存**: Redis 1.0.x + MongoDB
|
||||
- **向量數據庫**: Qdrant
|
||||
- **文件存儲**: SFTPGo
|
||||
|
||||
### 5.3 AI 模型
|
||||
- **語音識別**: WhisperX (Python)
|
||||
- **人臉識別**: InsightFace (Python)
|
||||
- **物體檢測**: YOLOv8 (Python)
|
||||
- **場景分類**: Places365 (Python)
|
||||
- **語義嵌入**: Nomic-embed-text-v2-moe (Ollama)
|
||||
- **文本生成**: Gemma4 (llama.cpp)
|
||||
|
||||
### 5.4 基礎設施
|
||||
- **反向代理**: Caddy
|
||||
- **CI/CD**: GitHub Actions
|
||||
- **監控**: 自定義指標 + 日誌聚合
|
||||
- **配置管理**: 環境變量 + 配置文件
|
||||
|
||||
---
|
||||
|
||||
## 6. 實現狀態
|
||||
|
||||
### 6.1 分片規則實現狀態
|
||||
基於詳細的設計與實現差異分析(參見 [DESIGN_IMPLEMENTATION_GAP.md](./DESIGN_IMPLEMENTATION_GAP.md)):
|
||||
|
||||
| 分片規則 | 設計概念 | 實現狀態 | 實現對應 | 完成度 |
|
||||
|----------|----------|----------|----------|--------|
|
||||
| **Rule 1** | 句子級分片 (`sentence`) | ✅ 完整實現 | `ChunkType::Sentence` | 95% |
|
||||
| **Rule 2** | 視覺物件級分片 (`visual`) | ❌ 未實現 | 無對應實現 | 0% |
|
||||
| **Rule 3** | 場景級分片 (`scene`) | ⚠️ 部分實現 | `ChunkType::Cut` | 60% |
|
||||
| **Rule 4** | 摘要級分片 (`summary`) | ⚠️ 概念調整 | `ChunkType::Story` | 40% |
|
||||
| **附加規則** | 時間基準分片 (`time`) | ✅ 完整實現 | `ChunkType::TimeBased` | 100% |
|
||||
| **附加規則** | 軌跡追蹤分片 (`trace`) | ✅ 完整實現 | `ChunkType::Trace` | 100% |
|
||||
|
||||
### 6.2 核心功能實現狀態
|
||||
| 功能模塊 | 實現狀態 | 備註 |
|
||||
|----------|----------|------|
|
||||
| **視頻註冊** | ✅ 完整實現 | 支持多種視頻格式 |
|
||||
| **ASR 處理** | ✅ 完整實現 | WhisperX 集成 |
|
||||
| **OCR 處理** | ✅ 完整實現 | GPU 加速支持 |
|
||||
| **人臉識別** | ✅ 完整實現 | InsightFace 集成 |
|
||||
| **YOLO 檢測** | ✅ 完整實現 | 物件檢測與分類 |
|
||||
| **場景分類** | ✅ 完整實現 | Places365 模型 |
|
||||
| **向量搜索** | ✅ 完整實現 | Qdrant 集成 |
|
||||
| **同義詞擴展** | ✅ 完整實現 | 在線+離線模式 |
|
||||
|
||||
### 6.3 近期開發重點
|
||||
1. **設計與實現一致性**:統一術語,更新文檔
|
||||
2. **視覺分片框架**:實現 Rule 2 基礎功能
|
||||
3. **場景語義增強**:改進 Rule 3 質量
|
||||
4. **LLM 集成**:為 Rule 4 添加摘要生成
|
||||
|
||||
---
|
||||
|
||||
## 7. 部署架構
|
||||
|
||||
### 6.1 本地部署 (當前)
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ macOS (M4 Mac Mini) │
|
||||
│ │
|
||||
│ ┌────────────┐ ┌────────────┐ │
|
||||
│ │ Momentry │ │ Redis │ │
|
||||
│ │ Core │ │ │ │
|
||||
│ │ (Rust) │ │ │ │
|
||||
│ └────────────┘ └────────────┘ │
|
||||
│ │
|
||||
│ ┌────────────┐ ┌────────────┐ │
|
||||
│ │ PostgreSQL │ │ Python │ │
|
||||
│ │ │ │ Scripts │ │
|
||||
│ │ │ │ │ │
|
||||
│ └────────────┘ └────────────┘ │
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 6.2 未來擴展架構
|
||||
```
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ Momentry Platform │
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────┐ │
|
||||
│ │ Core API Server │ │
|
||||
│ │ (Load Balancer + Service Discovery) │ │
|
||||
│ └─────────────────────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
||||
│ │ Worker Node │ │ Worker Node │ │ Worker Node │ │
|
||||
│ │ (ASR) │ │ (Face) │ │ (YOLO) │ │
|
||||
│ └─────────────┘ └─────────────┘ └─────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────┐ │
|
||||
│ │ Data Storage Cluster │ │
|
||||
│ │ PostgreSQL | Redis | Qdrant | Object Store │ │
|
||||
│ └─────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 擴展性設計
|
||||
|
||||
### 8.1 水平擴展
|
||||
- **無狀態 API 服務器**: 可通過負載均衡器擴展
|
||||
- **處理器工作節點**: 可動態添加/移除 AI 處理節點
|
||||
- **數據庫分片**: PostgreSQL 可配置讀寫分離
|
||||
|
||||
### 8.2 垂直擴展
|
||||
- **GPU 加速**: 支持多種 AI 模型的 GPU 加速
|
||||
- **內存優化**: 支持大內存配置的視頻處理
|
||||
- **存儲擴展**: 支持 TB 級視頻文件存儲
|
||||
|
||||
### 8.3 模塊化設計
|
||||
- **插件化處理器**: 可熱插拔 AI 模型
|
||||
- **可替換組件**: 數據庫、緩存、向量存儲可替換
|
||||
- **API 擴展**: 可添加新的 API 端點而不影響現有功能
|
||||
|
||||
---
|
||||
|
||||
## 9. 相關文件索引
|
||||
|
||||
### 8.1 核心架構文檔
|
||||
| 文件 | 描述 | 位置 | 狀態 |
|
||||
|------|------|------|------|
|
||||
| ARCHITECTURE_OVERVIEW.md | 架構總覽 | `ARCHITECTURE/` | ✅ 最新版 |
|
||||
| ARCHITECTURE_ROADMAP.md | 架構發展路線圖 | `ARCHITECTURE/` | ✅ 最新版 |
|
||||
| TECHNICAL_DECISION_RECORDS.md | 技術決策記錄 | `ARCHITECTURE/` | ✅ 最新版 |
|
||||
| DESIGN_IMPLEMENTATION_GAP.md | 設計與實現差異分析 | `ARCHITECTURE/` | ✅ 最新版 |
|
||||
| ARCHITECTURE_DOCUMENTATION_MAP.md | 文檔關係圖與導航 | `ARCHITECTURE/` | ✅ 最新版 |
|
||||
|
||||
### 8.2 功能專題文檔
|
||||
| 文件 | 描述 | 位置 | 狀態 |
|
||||
|------|------|------|------|
|
||||
| CHUNKING_ARCHITECTURE.md | 分片架構總綱 | `ARCHITECTURE/chunking/` | 🔄 部分更新 |
|
||||
| CHUNK_RULE_1_SENTENCE.md | Rule 1: 句子級檢索 | `ARCHITECTURE/chunking/` | ✅ 最新版 |
|
||||
| CHUNK_RULE_2_VISUAL.md | Rule 2: 視覺物件級檢索 | `ARCHITECTURE/chunking/` | 📋 設計階段 |
|
||||
| CHUNK_RULE_3_SCENE.md | Rule 3: 場景級檢索 | `ARCHITECTURE/chunking/` | 🔄 部分實現 |
|
||||
| CHUNK_RULE_4_SUMMARY.md | Rule 4: 摘要級檢索 | `ARCHITECTURE/chunking/` | 🔄 概念調整 |
|
||||
|
||||
### 8.3 質量與安全文檔
|
||||
| 文件 | 描述 | 位置 | 狀態 |
|
||||
|------|------|------|------|
|
||||
| PERFORMANCE_AND_SCALABILITY.md | 效能與可擴展性架構 | `ARCHITECTURE/` | ✅ 最新版 |
|
||||
| SECURITY_ARCHITECTURE.md | 安全架構設計 | `ARCHITECTURE/` | ✅ 最新版 |
|
||||
| MONITORING_ARCHITECTURE.md | 監控架構設計 | `ARCHITECTURE/` | ✅ 最新版 |
|
||||
| API_KEY_ARCHITECTURE.md | API Key 管理系統 | `ARCHITECTURE/` | ✅ 最新版 |
|
||||
|
||||
### 8.4 服務與處理器文檔
|
||||
| 文件 | 描述 | 位置 | 狀態 |
|
||||
|------|------|------|------|
|
||||
| SERVICE_REGISTRY_ARCHITECTURE.md | 服務資源管理架構 | `ARCHITECTURE/` | ✅ 最新版 |
|
||||
| PROCESSOR_REGISTRY_ARCHITECTURE.md | 處理器資源管理架構 | `ARCHITECTURE/` | ✅ 最新版 |
|
||||
| PROCESSOR_LIFECYCLE.md | 處理器生命週期管理 | `ARCHITECTURE/` | ✅ 最新版 |
|
||||
| PROCESSING_PIPELINE.md | 處理流程文檔 | `ARCHITECTURE/` | ✅ 最新版 |
|
||||
| MODULE_STANDARDIZATION_IMPLEMENTATION_PLAN.md | 模塊標準化計劃 | `ARCHITECTURE/` | ✅ 最新版 |
|
||||
| **新增文件** | | | |
|
||||
| TERMINOLOGY_MAPPING.md | 術語對照表 | `ARCHITECTURE/` | ✅ 最新版 |
|
||||
| DESIGN_IMPLEMENTATION_GAP.md | 設計與實現差異分析 | `ARCHITECTURE/` | ✅ 最新版 |
|
||||
| ARCHITECTURE_DECISION_EXECUTION_PLAN.md | 架構決策執行計劃 | `ARCHITECTURE/` | ✅ 最新版 |
|
||||
| PERFORMANCE_AND_SCALABILITY.md | 效能與可擴展性架構 | `ARCHITECTURE/` | ✅ 最新版 |
|
||||
| SECURITY_ARCHITECTURE.md | 安全架構設計 | `ARCHITECTURE/` | ✅ 最新版 |
|
||||
| MONITORING_ARCHITECTURE.md | 監控架構設計 | `ARCHITECTURE/` | ✅ 最新版 |
|
||||
|
||||
---
|
||||
|
||||
## 10. 更新記錄
|
||||
|
||||
| 日期 | 版本 | 變更內容 | 操作人 |
|
||||
|------|------|----------|--------|
|
||||
| 2026-04-22 | V1.2 | 術語標準化:添加術語對照表索引 | OpenCode |
|
||||
| 2026-04-22 | V1.1 | 更新文檔索引,添加新創建的架構文檔 | OpenCode |
|
||||
| 2026-04-22 | V1.0 | 創建架構總覽文件 | OpenCode |
|
||||
|
||||
**最後更新**: 2026-04-22 (V1.2)
|
||||
@@ -0,0 +1,279 @@
|
||||
---
|
||||
document_type: "architecture_design"
|
||||
service: "MOMENTRY_CORE"
|
||||
title: "架構審查會議流程"
|
||||
date: "2026-04-25"
|
||||
version: "V1.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
tags:
|
||||
- "架構審查會議流程"
|
||||
ai_query_hints:
|
||||
- "查詢 架構審查會議流程 的內容"
|
||||
- "架構審查會議流程 的主要目的是什麼?"
|
||||
- "如何操作或實施 架構審查會議流程?"
|
||||
---
|
||||
|
||||
# 架構審查會議流程
|
||||
|
||||
## 1. 概述
|
||||
|
||||
### 1.1 目的
|
||||
建立標準化的架構審查流程,確保:
|
||||
- 設計與實現的一致性
|
||||
- 技術債務的有效管理
|
||||
- 架構決策的透明性和可追溯性
|
||||
- 團隊成員的技術成長
|
||||
|
||||
### 1.2 適用範圍
|
||||
- 新功能架構設計
|
||||
- 重大架構變更
|
||||
- 技術債務評估
|
||||
- 性能和安全審查
|
||||
- 設計與實現一致性檢查
|
||||
|
||||
## 2. 會議類型
|
||||
|
||||
### 2.1 定期審查會議
|
||||
| 會議類型 | 頻率 | 時長 | 參與者 | 主要議題 |
|
||||
|----------|------|------|--------|----------|
|
||||
| **月度架構審查** | 每月一次 | 60分鐘 | 全體開發人員 | 系統架構狀態、技術債務、性能指標 |
|
||||
| **季度深度審查** | 每季度一次 | 120分鐘 | 架構師、技術負責人 | 架構演進、技術選型、長期規劃 |
|
||||
| **年度戰略審查** | 每年一次 | 180分鐘 | 管理層、架構師 | 技術戰略、投資規劃、團隊能力 |
|
||||
|
||||
### 2.2 特別審查會議
|
||||
| 觸發條件 | 時限 | 主要議題 |
|
||||
|----------|------|----------|
|
||||
| 新增重大功能 | 功能設計完成前 | 架構影響、技術選型、實現方案 |
|
||||
| 發現重大技術債務 | 發現後1週內 | 債務評估、修復方案、優先級 |
|
||||
| 性能或安全問題 | 問題發現後3天內 | 問題分析、解決方案、預防措施 |
|
||||
| 設計實現不一致 | 發現後2天內 | 不一致原因、解決方案、文檔更新 |
|
||||
|
||||
## 3. 會議流程
|
||||
|
||||
### 3.1 會前準備
|
||||
|
||||
#### 3.1.1 主持人職責
|
||||
1. 確定會議議程和目標
|
||||
2. 邀請相關參與者
|
||||
3. 準備審查材料
|
||||
4. 設定會議時間和地點
|
||||
|
||||
#### 3.1.2 報告人職責
|
||||
1. 準備審查文檔
|
||||
2. 創建演示材料
|
||||
3. 準備問題和討論點
|
||||
4. 收集相關數據和指標
|
||||
|
||||
#### 3.1.3 審查材料要求
|
||||
- **設計文檔**: 完整架構設計說明
|
||||
- **代碼實現**: 關鍵代碼片段或鏈接
|
||||
- **數據指標**: 性能、安全、質量指標
|
||||
- **問題清單**: 需要討論的具體問題
|
||||
- **決策選項**: 可能的解決方案和評估
|
||||
|
||||
### 3.2 會議進行
|
||||
|
||||
#### 3.2.1 標準議程 (60分鐘)
|
||||
| 時間 | 議題 | 負責人 | 產出 |
|
||||
|------|------|--------|------|
|
||||
| 0-5分鐘 | 會議目標和議程 | 主持人 | 明確會議目標 |
|
||||
| 5-20分鐘 | 架構狀態報告 | 報告人 | 當前架構概述 |
|
||||
| 20-35分鐘 | 問題分析和討論 | 全體 | 問題清單和解決方案 |
|
||||
| 35-50分鐘 | 決策制定 | 全體 | 架構決策記錄 |
|
||||
| 50-55分鐘 | 行動計劃 | 主持人 | 任務分配和時間表 |
|
||||
| 55-60分鐘 | 會議總結 | 主持人 | 會議紀要和後續步驟 |
|
||||
|
||||
#### 3.2.2 討論規則
|
||||
1. **技術導向**: 聚焦技術問題,避免個人攻擊
|
||||
2. **數據驅動**: 基於數據和事實進行討論
|
||||
3. **開放包容**: 鼓勵不同意見和建議
|
||||
4. **時間管理**: 嚴格遵守時間安排
|
||||
5. **結果導向**: 每個討論都應有明確結論
|
||||
|
||||
### 3.3 會後行動
|
||||
|
||||
#### 3.3.1 會議紀要要求
|
||||
- **基本信息**: 會議時間、地點、參與者
|
||||
- **討論要點**: 主要討論內容和觀點
|
||||
- **決策記錄**: 所有決策和決策理由
|
||||
- **行動計劃**: 具體任務、負責人、完成時間
|
||||
- **後續跟進**: 下次會議安排和準備工作
|
||||
|
||||
#### 3.3.2 文檔更新
|
||||
1. **架構文檔更新**: 根據決策更新相關文檔
|
||||
2. **決策卡片創建**: 記錄新的架構決策
|
||||
3. **代碼註釋更新**: 更新相關代碼註釋
|
||||
4. **知識庫更新**: 更新團隊知識庫
|
||||
|
||||
## 4. 審查內容
|
||||
|
||||
### 4.1 設計與實現一致性
|
||||
| 檢查項目 | 檢查方法 | 通過標準 |
|
||||
|----------|----------|----------|
|
||||
| **分片類型一致性** | 比較設計文檔與代碼實現 | 設計與實現差異 ≤5% |
|
||||
| **數據模型一致性** | 檢查數據結構定義 | 所有字段都有明確定義 |
|
||||
| **API 設計一致性** | 驗證 API 設計與實現 | API 端點和參數一致 |
|
||||
| **處理管道一致性** | 檢查處理流程實現 | 處理順序和結果符合設計 |
|
||||
|
||||
### 4.2 技術債務評估
|
||||
| 債務類型 | 評估指標 | 處理建議 |
|
||||
|----------|----------|----------|
|
||||
| **代碼債務** | 代碼複雜度、重複率 | 重構、提取公共組件 |
|
||||
| **設計債務** | 架構複雜度、耦合度 | 架構重構、模塊化 |
|
||||
| **文檔債務** | 文檔完整性、準確性 | 文檔更新、示例添加 |
|
||||
| **測試債務** | 測試覆蓋率、質量 | 增加測試、改進測試策略 |
|
||||
|
||||
### 4.3 性能和安全審查
|
||||
| 審查維度 | 檢查項目 | 評估標準 |
|
||||
|----------|----------|----------|
|
||||
| **性能** | 響應時間、吞吐量、資源使用 | 符合性能要求 |
|
||||
| **安全** | 認證授權、數據加密、訪問控制 | 無已知安全漏洞 |
|
||||
| **可擴展性** | 水平擴展能力、負載均衡 | 支持業務增長 |
|
||||
| **可靠性** | 可用性、故障恢復、監控 | 系統穩定運行 |
|
||||
|
||||
## 5. 決策記錄
|
||||
|
||||
### 5.1 決策卡片模板
|
||||
```
|
||||
決策編號: AD-YYYY-NNN
|
||||
決策名稱: [簡要描述]
|
||||
決策時間: YYYY-MM-DD
|
||||
決策狀態: [待定/已批准/已實施/已撤銷]
|
||||
|
||||
問題描述:
|
||||
[詳細描述需要解決的問題]
|
||||
|
||||
決策選項:
|
||||
1. 選項 A: [描述和評估]
|
||||
2. 選項 B: [描述和評估]
|
||||
3. 選項 C: [描述和評估]
|
||||
|
||||
最終決策:
|
||||
[選擇的選項和理由]
|
||||
|
||||
實施方案:
|
||||
[具體實施步驟和時間表]
|
||||
|
||||
影響評估:
|
||||
[正面影響、負面影響、風險]
|
||||
|
||||
相關文件:
|
||||
[鏈接到相關文檔和代碼]
|
||||
```
|
||||
|
||||
### 5.2 決策追蹤
|
||||
| 決策狀態 | 追蹤要求 | 負責人 |
|
||||
|----------|----------|--------|
|
||||
| **待定** | 定期跟進討論進度 | 決策發起人 |
|
||||
| **已批准** | 制定詳細實施計劃 | 項目負責人 |
|
||||
| **已實施** | 驗證實施效果 | 質量保證 |
|
||||
| **已撤銷** | 記錄撤銷原因 | 架構師 |
|
||||
|
||||
## 6. 工具和模板
|
||||
|
||||
### 6.1 會議工具
|
||||
- **日程管理**: Google Calendar, Outlook
|
||||
- **文檔協作**: Google Docs, Confluence
|
||||
- **代碼審查**: GitHub, GitLab
|
||||
- **項目管理**: Jira, Trello, Asana
|
||||
|
||||
### 6.2 模板文件
|
||||
1. **會議議程模板**: `templates/meeting_agenda.md`
|
||||
2. **會議紀要模板**: `templates/meeting_minutes.md`
|
||||
3. **決策卡片模板**: `templates/decision_card.md`
|
||||
4. **審查清單模板**: `templates/review_checklist.md`
|
||||
|
||||
### 6.3 自動化工具
|
||||
1. **一致性檢查**: `scripts/check_architecture_docs.py`
|
||||
2. **安全檢查**: `scripts/security_check.sh`
|
||||
3. **性能監控**: Prometheus + Grafana
|
||||
4. **代碼質量**: cargo clippy, cargo fmt
|
||||
|
||||
## 7. 角色和職責
|
||||
|
||||
### 7.1 架構師
|
||||
- **主要職責**: 架構設計、技術決策、審查主持
|
||||
- **具體任務**:
|
||||
- 制定架構標準和規範
|
||||
- 主持架構審查會議
|
||||
- 審批重大架構變更
|
||||
- 管理技術債務
|
||||
|
||||
### 7.2 開發人員
|
||||
- **主要職責**: 代碼實現、問題報告、建議提供
|
||||
- **具體任務**:
|
||||
- 準備審查材料
|
||||
- 參與技術討論
|
||||
- 實施審查決策
|
||||
- 報告技術問題
|
||||
|
||||
### 7.3 質量保證
|
||||
- **主要職責**: 質量驗證、測試執行、指標監控
|
||||
- **具體任務**:
|
||||
- 驗證架構決策實施效果
|
||||
- 監控系統質量和性能
|
||||
- 提供測試反饋
|
||||
- 報告質量問題
|
||||
|
||||
### 7.4 項目經理
|
||||
- **主要職責**: 進度跟蹤、資源協調、風險管理
|
||||
- **具體任務**:
|
||||
- 協調審查會議安排
|
||||
- 跟蹤決策實施進度
|
||||
- 管理項目風險
|
||||
- 協調跨團隊合作
|
||||
|
||||
## 8. 成功指標
|
||||
|
||||
### 8.1 過程指標
|
||||
| 指標 | 目標值 | 測量方法 |
|
||||
|------|--------|----------|
|
||||
| **會議準時率** | ≥95% | 會議準時開始和結束 |
|
||||
| **參與率** | ≥80% | 關鍵人員出席率 |
|
||||
| **決策效率** | ≤2次會議 | 從問題提出到決策完成 |
|
||||
| **文檔更新及時性** | ≤3天 | 決策後文檔更新時間 |
|
||||
|
||||
### 8.2 結果指標
|
||||
| 指標 | 目標值 | 測量方法 |
|
||||
|------|--------|----------|
|
||||
| **設計實現一致性** | ≥95% | 定期一致性檢查 |
|
||||
| **技術債務減少** | ≥10%/季度 | 技術債務評估 |
|
||||
| **系統性能提升** | ≥5%/季度 | 性能監控數據 |
|
||||
| **團隊滿意度** | ≥4.0/5.0 | 團隊調查問卷 |
|
||||
|
||||
### 8.3 質量指標
|
||||
| 指標 | 目標值 | 測量方法 |
|
||||
|------|--------|----------|
|
||||
| **代碼質量** | ≥4.0/5.0 | 代碼審查評分 |
|
||||
| **文檔質量** | ≥4.0/5.0 | 文檔審查評分 |
|
||||
| **決策質量** | ≥4.0/5.0 | 決策效果評估 |
|
||||
| **知識傳播** | ≥80% | 團隊知識測試 |
|
||||
|
||||
## 9. 持續改進
|
||||
|
||||
### 9.1 反饋收集
|
||||
1. **會議效果調查**: 每次會議後收集參與者反饋
|
||||
2. **流程評估**: 每季度評估審查流程效果
|
||||
3. **工具評估**: 定期評估工具使用效果
|
||||
4. **培訓需求**: 識別團隊培訓需求
|
||||
|
||||
### 9.2 流程優化
|
||||
1. **簡化流程**: 去除不必要的步驟和文檔
|
||||
2. **自動化工具**: 增加自動化檢查和報告
|
||||
3. **模板改進**: 根據使用反饋改進模板
|
||||
4. **培訓加強**: 提供更多培訓和支持
|
||||
|
||||
### 9.3 知識管理
|
||||
1. **經驗總結**: 記錄成功經驗和失敗教訓
|
||||
2. **最佳實踐**: 總結和推廣最佳實踐
|
||||
3. **案例庫建設**: 建立架構決策案例庫
|
||||
4. **培訓材料**: 創建培訓材料和課程
|
||||
|
||||
---
|
||||
|
||||
**最後更新**: 2026-04-22
|
||||
**版本**: V1.0
|
||||
**生效日期**: 2026-04-22
|
||||
**審查週期**: 每季度審查更新
|
||||
@@ -0,0 +1,400 @@
|
||||
---
|
||||
document_type: "architecture_design"
|
||||
service: "MOMENTRY_CORE"
|
||||
title: "Momentry Core 架構路線圖 (Architecture Roadmap)"
|
||||
date: "2026-04-22"
|
||||
version: "V1.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
tags:
|
||||
- "架構路線圖"
|
||||
- "momentry"
|
||||
- "core"
|
||||
ai_query_hints:
|
||||
- "查詢 Momentry Core 架構路線圖 (Architecture Roadmap) 的內容"
|
||||
- "Momentry Core 架構路線圖 (Architecture Roadmap) 的主要目的是什麼?"
|
||||
- "如何操作或實施 Momentry Core 架構路線圖 (Architecture Roadmap)?"
|
||||
---
|
||||
|
||||
# Momentry Core 架構路線圖 (Architecture Roadmap)
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | OpenCode |
|
||||
| 建立時間 | 2026-04-22 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 |
|
||||
|------|------|------|--------|
|
||||
| V1.0 | 2026-04-22 | 創建架構路線圖文件 | OpenCode |
|
||||
|
||||
---
|
||||
|
||||
## 1. 路線圖總覽
|
||||
|
||||
本路線圖定義了 Momentry Core 架構發展的階段性目標和時間規劃,涵蓋從基礎架構到高級功能的全面發展。
|
||||
|
||||
### 階段劃分:
|
||||
|
||||
```
|
||||
Phase 0: 現狀 (Current State) [✅ 已實現]
|
||||
Phase 1: 近期增強 (Short-term Improvements) [🔄 進行中]
|
||||
Phase 2: 中期擴展 (Medium-term Expansion) [📅 規劃中]
|
||||
Phase 3: 遠景目標 (Long-term Vision) [🔮 規劃中]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 現狀 (Phase 0) - 已實現功能
|
||||
|
||||
### 2.1 核心架構
|
||||
- ✅ **API 層**: Axum + Tower + Serde 架構
|
||||
- ✅ **數據訪問層**: PostgreSQL, Redis, MongoDB, Qdrant 集成
|
||||
- ✅ **處理器管理**: PythonExecutor 異步調用
|
||||
|
||||
### 2.2 分片規則實現狀態
|
||||
| 規則 | 實現狀態 | 完成時間 |
|
||||
|------|----------|----------|
|
||||
| Rule 1 (句子級) | ✅ 完整實現 | 2026-03-25 |
|
||||
| Rule 3 (場景級) | ⚠️ 部分實現 | 2026-04-01 |
|
||||
| Rule 2 (視覺級) | ❌ 未實現 | - |
|
||||
| Rule 4 (摘要級) | ❌ 未實現 | - |
|
||||
|
||||
### 2.3 已完成功能模塊
|
||||
1. **視頻註冊與元數據提取**:
|
||||
- ✅ FFprobe 元數據提取
|
||||
- ✅ 檔案 UUID 生成
|
||||
- ✅ PostgreSQL 存儲
|
||||
|
||||
2. **AI 處理器集成**:
|
||||
- ✅ ASR (WhisperX) 語音識別
|
||||
- ✅ Face (InsightFace) 人臉識別
|
||||
- ✅ YOLO 物件檢測(部分)
|
||||
|
||||
3. **檢索與查詢**:
|
||||
- ✅ 句子級文本搜索
|
||||
- ✅ 基本場景識別(基於 CUT)
|
||||
|
||||
---
|
||||
|
||||
## 3. 近期增強 (Phase 1) - 1-2個月內完成
|
||||
|
||||
### 3.1 分片架構完善
|
||||
|
||||
#### 目標 1: 完成 Rule 3 (場景級分片)完整實現
|
||||
**時間**: 2026年5月底前
|
||||
**內容**:
|
||||
1. 集成 Places365 場景分類模型
|
||||
2. 實現基於視覺和語音的場景邊界識別
|
||||
3. 創建 `chunks_rule3` 表的完整結構
|
||||
4. 完善 `src/core/chunk/rule3_ingest.rs`
|
||||
|
||||
#### 目標 2: 開始 Rule 2 (視覺分片) 實現
|
||||
**時間**: 2026年6月底前
|
||||
**內容**:
|
||||
1. 集成 YOLO 物件檢測
|
||||
2. 創建物件標籤索引
|
||||
3. 設計 `chunks_rule2` 表結構
|
||||
4. 開始 `src/core/chunk/rule2_ingest.rs` 框架
|
||||
|
||||
### 3.2 技術棧優化
|
||||
|
||||
#### 目標 3: Python-Rust 橋接優化
|
||||
**時間**: 2026年5月中旬前
|
||||
**內容**:
|
||||
1. 改進 `PythonExecutor` 性能
|
||||
2. 實現進程池管理
|
||||
3. 優化序列化/反序列化開銷
|
||||
4. 添加錯誤重試機制
|
||||
|
||||
#### 目標 4: 數據庫優化
|
||||
**時間**: 2026年6月中旬前
|
||||
**內容**:
|
||||
1. 優化 PostgreSQL 查詢性能
|
||||
2. 改進 Redis 緩存策略
|
||||
3. 優化 Qdrant 向量搜索效率
|
||||
4. 添加數據庫監控指標
|
||||
|
||||
---
|
||||
|
||||
## 4. 中期擴展 (Phase 2) - 3-6個月內完成
|
||||
|
||||
### 4.1 分片架構完整實現
|
||||
|
||||
#### 目標 5: 完成 Rule 2 (視覺分片) 實現
|
||||
**時間**: 2026年9月底前
|
||||
**內容**:
|
||||
1. 完整實現 YOLO 物件檢測集成
|
||||
2. 建立物件標籤標準化和索引
|
||||
3. 完成 `src/core/chunk/rule2_ingest.rs`
|
||||
4. 創建完整的 `chunks_rule2` 表
|
||||
|
||||
#### 目標 6: 開始 Rule 4 (摘要分片) 實現
|
||||
**時間**: 2026年10月底前
|
||||
**內容**:
|
||||
1. 集成 LLM 摘要生成模型
|
||||
2. 實現 5W1H 結構化提取
|
||||
3. 設計 `chunks_rule4` 表結構
|
||||
4. 開始 `src/core/chunk/rule4_ingest.rs` 框架
|
||||
|
||||
### 4.2 系統性能提升
|
||||
|
||||
#### 目標 7: 大規模視頻處理能力
|
||||
**時間**: 2026年11月底前
|
||||
**內容**:
|
||||
1. 支持批量視頻註冊
|
||||
2. 實現並行處理優化
|
||||
3. 添加處理隊列管理
|
||||
4. 提高系統吞吐量
|
||||
|
||||
#### 目標 8: 用戶體驗優化
|
||||
**時間**: 2026年12月底前
|
||||
**內容**:
|
||||
1. 改進搜索速度
|
||||
2. 優化 API 響應時間
|
||||
3. 添加結果排序和過濾
|
||||
4. 提升系統穩定性
|
||||
|
||||
---
|
||||
|
||||
## 5. 遠景目標 (Phase 3) - 6-12個月內完成
|
||||
|
||||
### 5.1 平台化發展
|
||||
|
||||
#### 目標 9: 微服務架構遷移
|
||||
**時間**: 2027年2月底前
|
||||
**內容**:
|
||||
1. 將單體應用拆分成微服務
|
||||
2. 實現服務發現和負載均衡
|
||||
3. 添加分布式追蹤
|
||||
4. 構建可擴展的微服務架構
|
||||
|
||||
#### 目標 10: 雲原生支持
|
||||
**時間**: 2027年4月底前
|
||||
**內容**:
|
||||
1. 容器化部署支持
|
||||
- Docker 容器化
|
||||
- Kubernetes 編排
|
||||
- Helm 包管理
|
||||
2. 雲端部署優化
|
||||
- AWS EKS 集成
|
||||
- GCP GKE 支持
|
||||
- Azure AKS 兼容
|
||||
|
||||
### 5.2 高級功能實現
|
||||
|
||||
#### 目標 11: 實時處理引擎
|
||||
**時間**: 2027年6月底前
|
||||
**內容**:
|
||||
1. 支持實時視頻流處理
|
||||
2. 實現低延遲分析
|
||||
3. 添加實時通知
|
||||
4. 構建事件驅動架構
|
||||
|
||||
#### 目標 12: 智能工作流
|
||||
**時間**: 2027年8月底前
|
||||
**內容**:
|
||||
1. 自動化視頻分析流程
|
||||
2. 智能任務調度
|
||||
3. 動態資源分配
|
||||
4. 自適應處理策略
|
||||
|
||||
### 5.3 擴展性增強
|
||||
|
||||
#### 目標 13: 多模態分析能力
|
||||
**時間**: 2027年10月底前
|
||||
**內容**:
|
||||
1. 集成更多 AI 模型
|
||||
2. 支持更多視頻格式
|
||||
3. 提供更多分析維度
|
||||
4. 增強結果可視化
|
||||
|
||||
#### 目標 14: 企業級功能支持
|
||||
**時間**: 2027年12月底前
|
||||
**內容**:
|
||||
1. 多租戶支持
|
||||
2. 權限管理系統
|
||||
3. 審計日誌功能
|
||||
4. 合規性支持
|
||||
|
||||
---
|
||||
|
||||
## 6. 關鍵里程碑
|
||||
|
||||
### 2026年:
|
||||
- ✅ **2026-03-25**: Rule 1 (句子級分片)完整實現
|
||||
- ⏳ **2026-05-31**: 完成 Rule 3 (場景級分片)
|
||||
- ⏳ **2026-09-30**: 完成 Rule 2 (視覺分片)
|
||||
|
||||
### 2027年:
|
||||
- 📅 **2027-02-28**: 微服務架構遷移完成
|
||||
- 📅 **2027-06-30**: 實時處理引擎上線
|
||||
- 📅 **2027-12-31**: 企業級功能完整實現
|
||||
|
||||
---
|
||||
|
||||
## 7. 風險與挑戰
|
||||
|
||||
### 技術挑戰:
|
||||
|
||||
1. **AI 模型集成**:
|
||||
- 多模型協同工作
|
||||
- 性能和準確性平衡
|
||||
- 資源管理優化
|
||||
|
||||
2. **數據一致性**:
|
||||
- 多數據庫同步
|
||||
- 事務管理
|
||||
- 錯誤恢復機制
|
||||
|
||||
3. **性能擴展**:
|
||||
- 大規模視頻處理
|
||||
- 並發控制
|
||||
- 資源調度優化
|
||||
|
||||
### 非技術挑戰:
|
||||
|
||||
1. **資源限制**:
|
||||
- 計算資源需求
|
||||
- 開發人力配置
|
||||
- 測試環境準備
|
||||
|
||||
2. **優先級管理**:
|
||||
- 功能實現順序
|
||||
- 技術債務處理
|
||||
- 用戶需求平衡
|
||||
|
||||
---
|
||||
|
||||
## 8. 成功標準
|
||||
|
||||
### 技術成功標準:
|
||||
|
||||
1. **性能指標**:
|
||||
- API 響應時間 < 500ms
|
||||
- 視頻處理速度 > 10x 實時速度
|
||||
- 系統可用性 > 99.9%
|
||||
|
||||
2. **功能指標**:
|
||||
- 分片規則完整實現率 > 90%
|
||||
- AI 模型準確率 > 85%
|
||||
- 檢索結果相關性 > 80%
|
||||
|
||||
### 業務成功標準:
|
||||
|
||||
1. **用戶滿意度**:
|
||||
- 搜索結果滿意度 > 85%
|
||||
- 系統易用性評分 > 4/5
|
||||
- 功能完整性評分 > 4/5
|
||||
|
||||
2. **系統可靠性**:
|
||||
- 平均故障間隔時間 > 30天
|
||||
- 平均修復時間 < 1小時
|
||||
- 數據丟失率 < 0.1%
|
||||
|
||||
---
|
||||
|
||||
## 9. 監控與評估
|
||||
|
||||
### 性能監控:
|
||||
|
||||
1. **實時指標**:
|
||||
- API 延遲
|
||||
- 並發用戶數
|
||||
- 資源使用率
|
||||
|
||||
2. **業務指標**:
|
||||
- 視頻處理成功率
|
||||
- 用戶活躍度
|
||||
- 功能使用頻率
|
||||
|
||||
### 評估機制:
|
||||
|
||||
1. **每月評估**:
|
||||
- 進度審查
|
||||
- 性能分析
|
||||
- 問題識別
|
||||
|
||||
2. **季度審計**:
|
||||
- 技術架構評估
|
||||
- 質量保證
|
||||
- 風險管理
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
## 10. 更新頻率
|
||||
|
||||
|
||||
|
||||
### 路線圖更新:
|
||||
|
||||
|
||||
|
||||
| 更新類型 | 頻率 | 責任人 |
|
||||
|----------|------|--------|
|
||||
| 詳細規劃 | 每月 | 技術負責人 |
|
||||
| 重大調整 | 季度 | 架構委員會 |
|
||||
| 年度規劃 | 每年 | 管理層 |
|
||||
|
||||
|
||||
|
||||
### 溝通機制:
|
||||
|
||||
1. **內部溝通**:
|
||||
- 每周技術會議
|
||||
- 月度架構審查
|
||||
- 季度成果展示
|
||||
|
||||
|
||||
|
||||
2. **外部溝通**:
|
||||
- 每月進度報告
|
||||
- 季度技術更新
|
||||
- 年度發展規劃
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
## 11. 相關文件
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
| 文件 | 描述 | 相關性 |
|
||||
|------|------|--------|
|
||||
| [ARCHITECTURE_OVERVIEW.md](./ARCHITECTURE_OVERVIEW.md) | 架構總覽 | 整體規劃 |
|
||||
| [TECHNICAL_DECISION_RECORDS.md](./TECHNICAL_DECISION_RECORDS.md) | 技術決策記錄 | 決策參考 |
|
||||
| [CHUNKING_ARCHITECTURE.md](./chunking/CHUNKING_ARCHITECTURE.md) | 分片架構 | 技術實現 |
|
||||
| [PROJECT_DOCS_V1_INTEGRATION_PLAN.md](../PROJECT_DOCS_V1_INTEGRATION_PLAN.md) | 項目整合計劃 | 總體規劃 |
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
## 12. 最後更新記錄
|
||||
|
||||
|
||||
|
||||
| 版本 | 日期 | 主要變更 | 操作人 |
|
||||
|------|------|----------|--------|
|
||||
| V1.0 | 2026-04-22 | 創建架構路線圖文件 | OpenCode |
|
||||
|
||||
|
||||
|
||||
**最後更新日期**: 2026-04-22
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,348 @@
|
||||
---
|
||||
document_type: "architecture_design"
|
||||
service: "MOMENTRY_CORE"
|
||||
title: "設計與實現差異分析"
|
||||
date: "2026-04-22"
|
||||
version: "V1.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
tags:
|
||||
- "設計與實現差異分析"
|
||||
ai_query_hints:
|
||||
- "查詢 設計與實現差異分析 的內容"
|
||||
- "設計與實現差異分析 的主要目的是什麼?"
|
||||
- "如何操作或實施 設計與實現差異分析?"
|
||||
---
|
||||
|
||||
# 設計與實現差異分析
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | OpenCode |
|
||||
| 建立時間 | 2026-04-22 |
|
||||
| 文件版本 | V1.0 |
|
||||
| 相關文件 | [ARCHITECTURE_OVERVIEW.md](./ARCHITECTURE_OVERVIEW.md)<br>[TECHNICAL_DECISION_RECORDS.md](./TECHNICAL_DECISION_RECORDS.md)<br>[ARCHITECTURE_ROADMAP.md](./ARCHITECTURE_ROADMAP.md) |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-04-22 | 創建設計與實現差異分析文件 | OpenCode | OpenCode / deepseek-v3.2 |
|
||||
|
||||
---
|
||||
|
||||
## 1. 概述
|
||||
|
||||
本文檔記錄 Momentry Core 系統中設計文檔與實際實現之間的差異,包括:
|
||||
1. 設計與實現不一致的原因分析
|
||||
2. 當前實現狀態評估
|
||||
3. 後續改進計劃
|
||||
4. 臨時解決方案
|
||||
|
||||
**核心原則**:當設計與實現出現矛盾時,優先參考實際的 Rust 代碼實現。
|
||||
|
||||
---
|
||||
|
||||
## 2. 關鍵差異分析
|
||||
|
||||
### 2.1 分片類型 (Chunk Type) 不匹配
|
||||
|
||||
#### 設計文檔中的分片類型
|
||||
```
|
||||
chunk_type 值:
|
||||
1. sentence # 句子級分片
|
||||
2. visual # 視覺物件級分片
|
||||
3. scene # 場景級分片
|
||||
4. summary # 摘要級分片
|
||||
```
|
||||
|
||||
#### 實際 Rust 代碼中的分片類型
|
||||
```rust
|
||||
// src/core/chunk/mod.rs 中的 ChunkType 枚舉
|
||||
pub enum ChunkType {
|
||||
TimeBased, // 對應設計中的 "time" 分片
|
||||
Sentence, // 對應設計中的 "sentence" 分片
|
||||
Cut, // 對應設計中的 "cut" 分片(場景檢測)
|
||||
Trace, // 對應設計中的 "trace" 分片(軌跡追蹤)
|
||||
Story, // 對應設計中的 "story" 分片(敘事)
|
||||
}
|
||||
```
|
||||
|
||||
#### 差異分析
|
||||
| 設計概念 | 設計值 | 實現值 | 差異原因 | 狀態 |
|
||||
|----------|--------|--------|----------|------|
|
||||
| 句子級分片 | `sentence` | `Sentence` | 命名一致 | ✅ 一致 |
|
||||
| 時間基準分片 | `time` | `TimeBased` | 命名更精確 | ✅ 一致 |
|
||||
| 場景級分片 | `scene` | `Cut` | 基於 CUT 算法實現 | ⚠️ 部分一致 |
|
||||
| 視覺物件級分片 | `visual` | 無對應實現 | 尚未實現視覺分片 | ❌ 缺失 |
|
||||
| 摘要級分片 | `summary` | `Story` | 概念近似但實現不同 | ⚠️ 部分一致 |
|
||||
| 軌跡追蹤分片 | `trace` | `Trace` | 命名一致 | ✅ 一致 |
|
||||
|
||||
#### 根本原因
|
||||
1. **設計先行**:架構設計在代碼實現之前完成
|
||||
2. **迭代開發**:實際開發中根據技術可行性調整
|
||||
3. **優先級調整**:某些功能因資源限制推遲實現
|
||||
|
||||
---
|
||||
|
||||
## 3. 分片規則實現狀態詳情
|
||||
|
||||
### 3.1 Rule 1: 句子級分片 ✅ 已完整實現
|
||||
|
||||
#### 設計要求
|
||||
- 基於 ASR 轉錄結果的句子邊界
|
||||
- 包含時間戳和文本內容
|
||||
- 支持語義搜索
|
||||
|
||||
#### 實際實現
|
||||
- ✅ 完整實現:`src/core/chunk/rule1_ingest.rs`
|
||||
- ✅ 功能完整:支持句子提取、時間戳映射、嵌入生成
|
||||
- ✅ 集成測試:有完整的單元測試和集成測試
|
||||
|
||||
#### 一致性評估:95%
|
||||
- 設計功能全部實現
|
||||
- 性能符合設計要求
|
||||
- 接口設計一致
|
||||
|
||||
### 3.2 Rule 2: 視覺物件級分片 ❌ 未實現
|
||||
|
||||
#### 設計要求
|
||||
- 基於 YOLO 物件檢測的視覺分片
|
||||
- 物件類別、位置、時間戳
|
||||
- 視覺搜尋能力
|
||||
|
||||
#### 實際實現
|
||||
- ❌ 未實現:缺乏專門的視覺分片處理器
|
||||
- ⚠️ 部分功能:YOLO 處理器存在但未用於分片生成
|
||||
- ❌ 數據結構:缺乏視覺分片專用數據結構
|
||||
|
||||
#### 差距分析
|
||||
1. **技術依賴**:需要成熟的 YOLO 集成方案
|
||||
2. **資源限制**:GPU 資源優先給其他處理器
|
||||
3. **優先級調整**:語義分片優先於視覺分片
|
||||
|
||||
#### 臨時解決方案
|
||||
- 使用現有的 YOLO 檢測結果作為元數據
|
||||
- 通過關鍵幀提取實現基礎視覺檢索
|
||||
- 計劃在 Phase 2 完整實現
|
||||
|
||||
### 3.3 Rule 3: 場景級分片 ⚠️ 部分實現
|
||||
|
||||
#### 設計要求
|
||||
- 基於視覺和音頻特徵的場景分割
|
||||
- 語義連續的視頻段落
|
||||
- 場景級檢索和分析
|
||||
|
||||
#### 實際實現
|
||||
- ⚠️ 部分實現:使用 CUT 算法檢測場景邊界
|
||||
- ❌ 功能不完整:缺乏場景語義分析
|
||||
- ✅ 基礎框架:有場景分片的數據結構
|
||||
|
||||
#### 具體差距
|
||||
1. **算法限制**:CUT 主要基於視覺相似度,缺乏語義理解
|
||||
2. **時間粒度**:場景邊界檢測不夠精確
|
||||
3. **集成程度**:未與其他分片規則深度集成
|
||||
|
||||
#### 改進方向
|
||||
1. 集成音頻特徵增強場景檢測
|
||||
2. 添加語義聚類提升場景質量
|
||||
3. 完善場景與其他分片的關聯
|
||||
|
||||
### 3.4 Rule 4: 摘要級分片 ⚠️ 部分實現(概念調整)
|
||||
|
||||
#### 設計要求
|
||||
- 基於 LLM 的視頻內容摘要
|
||||
- 結構化摘要格式(5W1H)
|
||||
- 高層級敘事理解
|
||||
|
||||
#### 實際實現
|
||||
- ⚠️ 概念調整:實現為 `Story` 分片而非 `Summary`
|
||||
- ❌ 功能缺失:缺乏自動摘要生成
|
||||
- ✅ 框架支持:有故事分片的數據結構
|
||||
|
||||
#### 差異說明
|
||||
- **設計概念**:`summary` - 基於 LLM 的結構化摘要
|
||||
- **實現概念**:`story` - 基於分片聚合的敘事重建
|
||||
- **原因**:LLM 集成複雜度高,優先實現基於現有數據的敘事
|
||||
|
||||
#### 過渡計劃
|
||||
1. 短期:完善 `Story` 分片基於現有數據
|
||||
2. 中期:集成 LLM 增強敘事質量
|
||||
3. 長期:實現完整的摘要生成
|
||||
|
||||
---
|
||||
|
||||
## 4. 數據模型差異
|
||||
|
||||
### 4.1 設計中的數據模型
|
||||
```json
|
||||
{
|
||||
"chunk_type": "sentence|visual|scene|summary",
|
||||
"content": {
|
||||
"text": "轉錄文本",
|
||||
"visual_objects": ["person", "car", "dog"],
|
||||
"scene_context": "辦公室會議",
|
||||
"summary": "會議討論項目進度"
|
||||
},
|
||||
"metadata": {
|
||||
"timestamp": 1234567890,
|
||||
"duration": 5.0,
|
||||
"source_video": "video_123"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4.2 實際實現的數據模型
|
||||
```rust
|
||||
// src/core/chunk/mod.rs 中的 Chunk 結構
|
||||
pub struct Chunk {
|
||||
pub id: i64,
|
||||
pub uuid: String,
|
||||
pub video_record_id: i64,
|
||||
pub chunk_type: ChunkType, // TimeBased|Sentence|Cut|Trace|Story
|
||||
pub start_time: f64,
|
||||
pub end_time: f64,
|
||||
pub content: serde_json::Value, // 動態 JSON 內容
|
||||
pub embedding: Option<Vec<f32>>,
|
||||
pub created_at: DateTime<Utc>,
|
||||
}
|
||||
```
|
||||
|
||||
### 4.3 差異分析
|
||||
| 維度 | 設計 | 實現 | 影響 |
|
||||
|------|------|------|------|
|
||||
| **類型定義** | 四個固定類型 | 可擴展枚舉 | 更好的可擴展性 |
|
||||
| **內容結構** | 固定字段結構 | 動態 JSON | 更靈活但類型不安全 |
|
||||
| **時間表示** | 單一時間戳 + 時長 | 開始/結束時間 | 更精確的時間管理 |
|
||||
| **嵌入存儲** | 未明確定義 | 可選向量存儲 | 支持向量搜索 |
|
||||
|
||||
### 4.4 建議改進
|
||||
1. **類型安全**:為不同分片類型定義專用的內容結構
|
||||
2. **遷移路徑**:從動態 JSON 逐步過渡到類型安全結構
|
||||
3. **版本兼容**:保持向後兼容性
|
||||
|
||||
---
|
||||
|
||||
## 5. 處理管道差異
|
||||
|
||||
### 5.1 設計中的處理管道
|
||||
```
|
||||
ASR → OCR → YOLO → CUT → LLM → 分片生成
|
||||
```
|
||||
|
||||
### 5.2 實際實現的處理管道
|
||||
```
|
||||
ASR → OCR → YOLO → CUT → 分片生成
|
||||
↓
|
||||
LLM(尚未集成)
|
||||
```
|
||||
|
||||
### 5.3 關鍵差異
|
||||
1. **LLM 集成**:設計中有完整的 LLM 階段,實際尚未集成
|
||||
2. **順序調整**:部分處理器執行順序根據依賴關係調整
|
||||
3. **並行處理**:實際實現中有更多並行處理優化
|
||||
|
||||
### 5.4 改進計劃
|
||||
1. **LLM 集成**:Phase 2 計劃集成 Gemma-4 模型
|
||||
2. **管道重構**:根據實際經驗優化處理順序
|
||||
3. **錯誤處理**:增強管道中的錯誤恢復機制
|
||||
|
||||
---
|
||||
|
||||
## 6. 臨時解決方案記錄
|
||||
|
||||
### 6.1 當前採用的臨時方案
|
||||
|
||||
| 問題 | 臨時方案 | 風險 | 長期方案 |
|
||||
|------|----------|------|----------|
|
||||
| 視覺分片缺失 | 使用關鍵幀 + YOLO 結果 | 檢索精度有限 | 實現完整的視覺分片規則 |
|
||||
| 摘要生成缺失 | 基於句子聚合生成敘事 | 缺乏高層理解 | 集成 LLM 摘要生成 |
|
||||
| 場景語義缺失 | 使用 CUT 結果 + 簡單聚類 | 場景質量一般 | 增強語義場景檢測 |
|
||||
| 動態 JSON 類型 | 現有實現 | 類型不安全 | 定義類型安全結構 |
|
||||
|
||||
### 6.2 臨時方案的影響評估
|
||||
1. **功能完整性**:核心功能完整,高級功能有限
|
||||
2. **用戶體驗**:基礎搜索良好,高級檢索受限
|
||||
3. **維護成本**:當前實現相對簡單,易於維護
|
||||
4. **擴展性**:動態 JSON 提供良好擴展性但犧牲類型安全
|
||||
|
||||
---
|
||||
|
||||
## 7. 改進路線圖
|
||||
|
||||
### 7.1 短期改進(1-2個月)
|
||||
|
||||
#### 優先級 P0:修復設計與實現不一致
|
||||
1. **文檔更新**:更新所有架構文檔反映實際實現
|
||||
2. **類型定義統一**:統一設計與實現中的術語
|
||||
3. **實現狀態標記**:在所有文檔中標記實現狀態
|
||||
|
||||
#### 優先級 P1:補齊缺失功能
|
||||
1. **視覺分片基礎**:實現 Rule 2 基礎框架
|
||||
2. **場景語義增強**:改進 Rule 3 語義分析
|
||||
3. **故事生成完善**:增強 Rule 4 敘事質量
|
||||
|
||||
### 7.2 中期改進(3-6個月)
|
||||
|
||||
#### 完整實現設計功能
|
||||
1. **Rule 2 完整實現**:集成 YOLO 生成視覺分片
|
||||
2. **Rule 3 語義增強**:實現語義場景分割
|
||||
3. **Rule 4 LLM 集成**:集成 Gemma-4 生成摘要
|
||||
|
||||
#### 架構優化
|
||||
1. **類型安全重構**:從動態 JSON 遷移到類型安全結構
|
||||
2. **處理管道優化**:根據實際經驗重新設計管道
|
||||
3. **效能改進**:基於監控數據進行效能優化
|
||||
|
||||
### 7.3 長期願景(6-12個月)
|
||||
|
||||
#### 超越原始設計
|
||||
1. **多模態融合**:深度融合視覺、音頻、文本特徵
|
||||
2. **智能分片**:基於 AI 的自適應分片策略
|
||||
3. **實時處理**:支持實時視頻流的在線處理
|
||||
|
||||
---
|
||||
|
||||
## 8. 結論與建議
|
||||
|
||||
### 8.1 當前狀態總結
|
||||
1. **核心功能**:✅ 完整實現(Rule 1 句子級分片)
|
||||
2. **高級功能**:⚠️ 部分實現(Rule 3 場景分片)
|
||||
3. **缺失功能**:❌ 尚未實現(Rule 2 視覺分片,Rule 4 完整摘要)
|
||||
4. **架構一致性**:⚡ 存在差異但可管理
|
||||
|
||||
### 8.2 後續行動建議
|
||||
|
||||
#### 立即行動(本週)
|
||||
1. ✅ 已創建本文檔記錄所有差異
|
||||
2. 🔄 更新架構概覽文檔反映實際狀態
|
||||
3. 📋 制定詳細改進計劃
|
||||
|
||||
#### 近期行動(1個月內)
|
||||
1. 🛠️ 實現 Rule 2 視覺分片基礎框架
|
||||
2. 🔧 增強 Rule 3 場景語義分析
|
||||
3. 📊 建立設計與實現一致性檢查流程
|
||||
|
||||
#### 長期策略
|
||||
1. 🎯 定期審查設計與實現一致性
|
||||
2. 🔄 建立文檔與代碼同步機制
|
||||
3. 📈 基於用戶反饋持續優化架構
|
||||
|
||||
### 8.3 風險管理
|
||||
|
||||
| 風險 | 影響 | 緩解措施 |
|
||||
|------|------|----------|
|
||||
| **設計與實現脫節** | 功能混亂,維護困難 | 定期一致性檢查 |
|
||||
| **臨時方案固化** | 技術債務積累 | 明確遷移計劃和時間表 |
|
||||
| **用戶期望不匹配** | 用戶體驗差 | 清晰溝通功能狀態 |
|
||||
|
||||
### 8.4 最終建議
|
||||
1. **接受現狀**:承認設計與實現的差異是正常開發過程
|
||||
2. **有序改進**:按照優先級逐步縮小差距
|
||||
3. **持續優化**:建立長期機制確保設計與實現的一致性
|
||||
4. **用戶為中心**:以實際用戶需求為導向調整設計
|
||||
|
||||
**核心原則重申**:在出現矛盾時,實際的 Rust 代碼實現是最高權威,設計文檔應反映實際實現狀態並指導未來改進方向。
|
||||
@@ -0,0 +1,167 @@
|
||||
# Document Embedding Strategy - Parent-Child Chunks
|
||||
|
||||
| Item | Content |
|
||||
|------|---------|
|
||||
| Author | Warren |
|
||||
| Created | 2026-03-23 |
|
||||
| Document Version | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## Version History
|
||||
|
||||
| Version | Date | Purpose | Operator | Tool/Model |
|
||||
|---------|------|---------|----------|------------|
|
||||
| V1.0 | 2026-03-23 | Create document embedding strategy | Warren | OpenCode |
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Momentry uses a **parent-child chunk hierarchy** for improved RAG retrieval. This document describes the embedding strategy for this hierarchy.
|
||||
|
||||
## Chunk Structure
|
||||
|
||||
### Parent Chunk
|
||||
- **Purpose**: Summarize multiple child chunks with narrative description
|
||||
- **Content**: High-level description of multiple scenes/segments
|
||||
- **Example**:
|
||||
```json
|
||||
{
|
||||
"chunk_id": "story_asr_0000",
|
||||
"chunk_type": "story",
|
||||
"text_content": "[0s-125s] A man enters a building. He walks down a hallway.",
|
||||
"child_chunk_ids": ["asr_0001", "asr_0002", "asr_0003", "asr_0004", "asr_0005"]
|
||||
}
|
||||
```
|
||||
|
||||
### Child Chunk
|
||||
- **Purpose**: Individual segments from ASR, scenes from CUT, etc.
|
||||
- **Content**: Raw transcription or detection results
|
||||
- **Example**:
|
||||
```json
|
||||
{
|
||||
"chunk_id": "asr_0001",
|
||||
"chunk_type": "sentence",
|
||||
"text_content": "Hello world",
|
||||
"parent_chunk_id": "story_asr_0000"
|
||||
}
|
||||
```
|
||||
|
||||
## Embedding Strategy
|
||||
|
||||
### For Vector Search
|
||||
|
||||
When embedding chunks for vector search, we combine **parent description + child content** to provide both context and detail.
|
||||
|
||||
#### Parent Chunk Embedding
|
||||
```
|
||||
embedding_text = f"Summary: {parent.text_content}
|
||||
Children: {child_text_1}. {child_text_2}. {child_text_3}..."
|
||||
```
|
||||
|
||||
**Prefix**: `search_document:` (for documents in Qdrant)
|
||||
|
||||
**Example**:
|
||||
```
|
||||
search_document: Summary: A man enters a building. He walks down a hallway.
|
||||
Children: Hello, how are you? I'm fine thank you. The weather is nice today.
|
||||
```
|
||||
|
||||
#### Child Chunk Embedding
|
||||
```
|
||||
embedding_text = f"[{child.chunk_type}] {child.text_content}
|
||||
Parent: {parent.description}"
|
||||
```
|
||||
|
||||
**Prefix**: `search_document:`
|
||||
|
||||
**Example**:
|
||||
```
|
||||
search_document: [sentence] Hello, how are you?
|
||||
Parent: A man enters a building. He walks down a hallway.
|
||||
```
|
||||
|
||||
### For BM25 Text Search
|
||||
|
||||
BM25 operates on raw text with PostgreSQL full-text search.
|
||||
|
||||
- **Index**: `search_vector` (TSVECTOR) on `chunks.text_content`
|
||||
- **Search**: Uses `ts_rank_cd()` for ranking
|
||||
|
||||
## Hybrid Search Ranking
|
||||
|
||||
Combined score = `(vector_score * 0.7) + (bm25_score * 0.3)`
|
||||
|
||||
### Why 0.7/0.3?
|
||||
|
||||
| Weight | Vector | BM25 |
|
||||
|--------|--------|------|
|
||||
| Pros | Semantic similarity | Exact keyword match |
|
||||
| Cons | May miss specific terms | No semantic understanding |
|
||||
| Best for | Thematic queries | Fact lookup |
|
||||
|
||||
## Query Patterns
|
||||
|
||||
### Thematic Query ("What are the main themes?")
|
||||
- Use higher `vector_weight` (0.8-0.9)
|
||||
- Vector search finds semantically similar content
|
||||
|
||||
### Fact Lookup ("Who said X?")
|
||||
- Use higher `bm25_weight` (0.5-0.7)
|
||||
- BM25 finds exact matches
|
||||
|
||||
### Balanced ("Tell me about scene 5")
|
||||
- Use default 0.7/0.3
|
||||
|
||||
## Implementation
|
||||
|
||||
### Embedding Generation
|
||||
```rust
|
||||
fn build_embedding_text(chunk: &Chunk, parent_text: Option<&str>) -> String {
|
||||
match chunk.chunk_type {
|
||||
ChunkType::Story => {
|
||||
format!(
|
||||
"Summary: {}\nChildren: {}",
|
||||
chunk.text_content,
|
||||
get_children_text(chunk)
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
format!(
|
||||
"[{}] {}\nParent: {}",
|
||||
chunk.chunk_type.as_str(),
|
||||
chunk.text_content,
|
||||
parent_text.unwrap_or("N/A")
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Storage
|
||||
- Parent chunks stored with their `child_chunk_ids`
|
||||
- Child chunks reference `parent_chunk_id`
|
||||
- Both stored in PostgreSQL with full-text index
|
||||
- Vectors stored in Qdrant
|
||||
|
||||
## Example Flow
|
||||
|
||||
1. **Story Processing** generates parent-child hierarchy
|
||||
2. **Embedding** creates vector for each chunk
|
||||
3. **Storage** saves to PostgreSQL + Qdrant
|
||||
4. **Search** retrieves using hybrid search
|
||||
5. **Results** include both parent context and child details
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Chunk Size**: 5 child chunks per parent (configurable)
|
||||
2. **Text Length**: Keep embeddings under 512 tokens
|
||||
3. **Parent Description**: Include temporal markers (timestamps)
|
||||
4. **Child Content**: Preserve original transcription
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
- [ ] GraphRAG integration for relationship traversal
|
||||
- [ ] Cross-chunk entity linking
|
||||
- [ ] Temporal graph building
|
||||
@@ -0,0 +1,918 @@
|
||||
---
|
||||
document_type: "architecture_design"
|
||||
service: "MOMENTRY_CORE"
|
||||
title: "事件識別(Event Recognition)技術方案分析"
|
||||
date: "2026-04-01"
|
||||
version: "V1.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
tags:
|
||||
|
||||
ai_query_hints:
|
||||
- "查詢 事件識別(Event Recognition)技術方案分析 的內容"
|
||||
- "事件識別(Event Recognition)技術方案分析 的主要目的是什麼?"
|
||||
- "如何操作或實施 事件識別(Event Recognition)技術方案分析?"
|
||||
---
|
||||
|
||||
# 事件識別(Event Recognition)技術方案分析
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 分析日期 | 2026-04-01 |
|
||||
| 目標 | 影片中的人類行為與事件識別 |
|
||||
| 應用場景 | 安全監控、運動分析、日常活動記錄 |
|
||||
|
||||
---
|
||||
|
||||
## 事件識別分類
|
||||
|
||||
### 1. 按事件類型
|
||||
|
||||
```
|
||||
暴力事件:
|
||||
├─ 打架
|
||||
├─ 吵架
|
||||
├─ 推擠
|
||||
└─ 破壞物品
|
||||
|
||||
運動事件:
|
||||
├─ 球類運動
|
||||
│ ├─ 籃球
|
||||
│ ├─ 足球
|
||||
│ ├─ 網球
|
||||
│ └─ 排球
|
||||
├─ 格鬥運動
|
||||
│ ├─ 拳擊
|
||||
│ ├─ 柔道
|
||||
│ └─ 跆拳道
|
||||
└─ 其他運動
|
||||
├─ 跑步
|
||||
├─ 游泳
|
||||
└─ 騎自行車
|
||||
|
||||
日常活動:
|
||||
├─ 飲食相關
|
||||
│ ├─ 吃飯
|
||||
│ ├─ 喝水
|
||||
│ ├─ 做菜
|
||||
│ └─ 清洗碗筷
|
||||
├─ 居家活動
|
||||
│ ├─ 打掃
|
||||
│ ├─ 洗衣服
|
||||
│ ├─ 整理房間
|
||||
│ └─ 看電視
|
||||
├─ 社交互動
|
||||
│ ├─ 交談
|
||||
│ ├─ 擁抱
|
||||
│ ├─ 握手
|
||||
│ └─ 玩耍
|
||||
└─ 個人護理
|
||||
├─ 刷牙
|
||||
├─ 洗臉
|
||||
└─ 化妝
|
||||
|
||||
安全事件:
|
||||
├─ 跌倒
|
||||
├─ 暈倒
|
||||
├─ 火災
|
||||
└─ 入侵
|
||||
```
|
||||
|
||||
### 2. 按時序特性
|
||||
|
||||
```
|
||||
瞬時事件 (< 1秒):
|
||||
├─ 拍手
|
||||
├─ 跳躍
|
||||
└─ 投擲
|
||||
|
||||
短期事件 (1-10秒):
|
||||
├─ 打架
|
||||
├─ 跌倒
|
||||
├─ 握手
|
||||
└─ 喝水
|
||||
|
||||
長期事件 (> 10秒):
|
||||
├─ 吃飯
|
||||
├─ 做菜
|
||||
├─ 運動
|
||||
└─ 交談
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 技術方法分類
|
||||
|
||||
### 方法 1:時空動作檢測(Spatiotemporal Action Detection)
|
||||
|
||||
**特點**:
|
||||
- 檢測影片中的人物位置 + 行為類別
|
||||
- 輸出:時空管(spatiotemporal tube)
|
||||
|
||||
**代表模型**:
|
||||
|
||||
#### 1.1 SlowFast Network
|
||||
|
||||
```python
|
||||
# Facebook AI Research (FAIR)
|
||||
# CVPR 2019
|
||||
|
||||
特點:
|
||||
- 雙路徑架構
|
||||
- Slow pathway: 高空間分辨率,低時間分辨率
|
||||
- Fast pathway: 低空間分辨率,高時間分辨率
|
||||
- 在 AVA 數據集上 mAP 28.3%
|
||||
|
||||
優點:
|
||||
✅ 平衡空間和時間信息
|
||||
✅ 適合長短時事件
|
||||
✅ 準確率高
|
||||
|
||||
缺點:
|
||||
❌ 計算量大
|
||||
❌ 記憶體消耗高(適合 Mac Studio)
|
||||
```
|
||||
|
||||
#### 1.2 VideoMAE
|
||||
|
||||
```python
|
||||
# 2022, Masked Autoencoder for Video
|
||||
|
||||
特點:
|
||||
- 基於 Transformer
|
||||
- 使用掩碼自編碼器預訓練
|
||||
- 在 Kinetics-400 上 81.5% Top-1
|
||||
|
||||
優點:
|
||||
✅ 準確率高
|
||||
✅ 數據效率好
|
||||
✅ 可擴展性強
|
||||
|
||||
缺點:
|
||||
❌ 訓練成本高
|
||||
❌ 推理速度較慢
|
||||
```
|
||||
|
||||
#### 1.3 MViT (Multiscale Vision Transformer)
|
||||
|
||||
```python
|
||||
# 2021, Facebook AI
|
||||
|
||||
特點:
|
||||
- 多尺度特徵金字塔
|
||||
- 池化注意力機制
|
||||
- 在 Kinetics-400 上 80.8% Top-1
|
||||
|
||||
優點:
|
||||
✅ 準確率高
|
||||
✅ 效率較好
|
||||
|
||||
缺點:
|
||||
❌ 模型較大
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 方法 2:骨架動作識別(Skeleton-based Action Recognition)
|
||||
|
||||
**特點**:
|
||||
- 基於人體關鍵點(Pose)
|
||||
- 對背景不敏感
|
||||
- 計算量小
|
||||
|
||||
**實現流程**:
|
||||
|
||||
```python
|
||||
流程:
|
||||
影片 → Pose 檢測 → 骨架序列 → 時序建模 → 動作分類
|
||||
|
||||
工具:
|
||||
- Pose 檢測: MediaPipe, OpenPose, MMPose
|
||||
- 時序建模: ST-GCN, CTR-GCN
|
||||
```
|
||||
|
||||
#### 2.1 ST-GCN (Spatial Temporal Graph Convolutional Networks)
|
||||
|
||||
```python
|
||||
# 2018, AAAI
|
||||
|
||||
特點:
|
||||
- 將骨架建模為時空圖
|
||||
- 鄰接關係:身體連接 + 時間相鄰
|
||||
- 在 NTU-RGB+D 上 81.5% 準確率
|
||||
|
||||
優點:
|
||||
✅ 計算量小(適合邊緣 AI)
|
||||
✅ 對背景不敏感
|
||||
✅ 實時性好
|
||||
|
||||
缺點:
|
||||
❌ 需要準確的 Pose 檢測
|
||||
❌ 遮擋問題
|
||||
```
|
||||
|
||||
#### 2.2 CTR-GCN (Channel-wise Topology Refinement GCN)
|
||||
|
||||
```python
|
||||
# 2021, ICCV
|
||||
|
||||
特點:
|
||||
- 自適應學習圖拓撲
|
||||
- 通道級特徵建模
|
||||
- 在 NTU-RGB+D 上 92.0% 準確率
|
||||
|
||||
優點:
|
||||
✅ 準確率最高
|
||||
✅ 自適應能力強
|
||||
|
||||
缺點:
|
||||
❌ 複雜度較高
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 方法 3:雙流網絡(Two-Stream Networks)
|
||||
|
||||
**特點**:
|
||||
- 空間流:單幀 RGB
|
||||
- 時間流:光流(Optical Flow)
|
||||
- 融合預測
|
||||
|
||||
```python
|
||||
架構:
|
||||
|
||||
RGB 幀 → 空間 CNN → 空間特徵
|
||||
├─→ 融合 → 動作類別
|
||||
光流 → 時間 CNN → 時間特徵
|
||||
|
||||
優點:
|
||||
✅ 兼顧外觀和運動
|
||||
✅ 準確率高
|
||||
|
||||
缺點:
|
||||
❌ 需要計算光流(慢)
|
||||
❌ 兩個網絡(記憶體翻倍)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 方法 4:3D 卷積網絡(3D CNN)
|
||||
|
||||
**特點**:
|
||||
- 直接處理視頻片段
|
||||
- 時空聯合建模
|
||||
|
||||
#### 4.1 I3D (Inflated 3D ConvNet)
|
||||
|
||||
```python
|
||||
# 2017, CVPR
|
||||
|
||||
特點:
|
||||
- 將 2D CNN 膨脹為 3D
|
||||
- 在 Kinetics-400 上 71.1% Top-1
|
||||
|
||||
優點:
|
||||
✅ 端到端訓練
|
||||
✅ 時空聯合建模
|
||||
|
||||
缺點:
|
||||
❌ 計算量大
|
||||
❌ 參數量多
|
||||
```
|
||||
|
||||
#### 4.2 SlowFast
|
||||
|
||||
```python
|
||||
# 見 1.1
|
||||
|
||||
改進:
|
||||
- 雙速率處理
|
||||
- 減少計算量
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 方法 5:時序動作檢測(Temporal Action Detection)
|
||||
|
||||
**特點**:
|
||||
- 定位動作發生的時間段
|
||||
- 不關心空間位置
|
||||
|
||||
#### 5.1 BMN (Boundary Matching Network)
|
||||
|
||||
```python
|
||||
# 2019, ICCV
|
||||
|
||||
特點:
|
||||
- 邊界匹配機制
|
||||
- 生成動作提議
|
||||
- 在 THUMOS14 上 56.0% mAP@0.5
|
||||
|
||||
優點:
|
||||
✅ 時間定位準確
|
||||
✅ 適合長視頻
|
||||
|
||||
缺點:
|
||||
❌ 需要後處理
|
||||
```
|
||||
|
||||
#### 5.2 TAGS (Temporal Action Detection with Global Segmentation)
|
||||
|
||||
```python
|
||||
# 2020
|
||||
|
||||
特點:
|
||||
- 全局分割
|
||||
- 端到端檢測
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 方法 6:多模態融合(Multimodal Fusion)
|
||||
|
||||
**特點**:
|
||||
- 結合視覺、音頻、文本
|
||||
- 提升準確率和魯棒性
|
||||
|
||||
```python
|
||||
多模態融合:
|
||||
|
||||
視覺 (RGB) ──┐
|
||||
├─→ 融合模型 → 事件類別
|
||||
音頻 (Audio) ─┤
|
||||
│
|
||||
文本 (ASR) ──┘
|
||||
|
||||
優點:
|
||||
✅ 準確率最高
|
||||
✅ 魯棒性強
|
||||
✅ 可處理複雜事件(如吵架)
|
||||
|
||||
缺點:
|
||||
❌ 複雜度高
|
||||
❌ 需要多個處理器
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 數據集分析
|
||||
|
||||
### 大規模動作識別數據集
|
||||
|
||||
| 數據集 | 類別數 | 影片數 | 時長 | 標註類型 |
|
||||
|--------|--------|--------|------|---------|
|
||||
| **Kinetics-400** | 400 | 240K | 10s | 分類 |
|
||||
| **Kinetics-700** | 700 | 650K | 10s | 分類 |
|
||||
| **AVA** | 80 | 430 | 15min | 時空檢測 |
|
||||
| **EPIC-KITCHENS** | 125 | 100h | 長視頻 | 時空檢測 |
|
||||
| **NTU-RGB+D** | 60 | 56K | 骨架 | 分類 |
|
||||
| **THUMOS14** | 20 | 20h | 長視頻 | 時間定位 |
|
||||
|
||||
### 適合的事件類型
|
||||
|
||||
#### Kinetics-400 包含的事件
|
||||
|
||||
```
|
||||
日常活動:
|
||||
├─ eating
|
||||
├─ drinking
|
||||
├─ cooking
|
||||
├─ cleaning
|
||||
├─ brushing teeth
|
||||
├─ washing hands
|
||||
└─ 等等
|
||||
|
||||
運動:
|
||||
├─ playing basketball
|
||||
├─ playing soccer
|
||||
├─ swimming
|
||||
├─ running
|
||||
└─ 等等
|
||||
|
||||
互動:
|
||||
├─ hugging
|
||||
├─ shaking hands
|
||||
├─ talking to
|
||||
└─ 等等
|
||||
|
||||
暴力事件:
|
||||
⚠️ 較少(需專門數據集)
|
||||
```
|
||||
|
||||
#### 暴力事件專門數據集
|
||||
|
||||
| 數據集 | 類別 | 規模 |
|
||||
|--------|------|------|
|
||||
| **Hockey Fight** | 打架 | 1000 段 |
|
||||
| **Movies Fight** | 打架 | 200 段 |
|
||||
| **Violent-Flows** | 暴力 | 246 段 |
|
||||
| **RWF-2000** | 暴力 | 2000 段 |
|
||||
| **UBI-Fight** | 暴力 | 80h |
|
||||
|
||||
---
|
||||
|
||||
## 實現方案(Momentry 整合)
|
||||
|
||||
### 方案 A:基於骨架的輕量方案(推薦)⭐
|
||||
|
||||
**適合場景**:邊緣 AI、實時處理
|
||||
|
||||
```python
|
||||
架構:
|
||||
|
||||
影片 → Pose 檢測 → 骨架序列 → ST-GCN → 動作類別
|
||||
│ │
|
||||
└─ 使用現有 Pose 處理器 ──────┘
|
||||
|
||||
優點:
|
||||
✅ 計算量小
|
||||
✅ 可復用 Pose 結果
|
||||
✅ 實時性好
|
||||
✅ 適合 Mac Studio 並行處理
|
||||
|
||||
缺點:
|
||||
⚠️ 依賴 Pose 檢測準確度
|
||||
⚠️ 遮擋問題
|
||||
```
|
||||
|
||||
**實現**:
|
||||
|
||||
```python
|
||||
# 新增處理器: Action Recognition
|
||||
scripts/action_processor.py
|
||||
|
||||
依賴:
|
||||
- Pose 檢測結果(已存在)
|
||||
- ST-GCN 模型
|
||||
|
||||
流程:
|
||||
1. 讀取 Pose 結果(JSON)
|
||||
2. 提取骨架序列
|
||||
3. ST-GCN 推理
|
||||
4. 輸出動作類別 + 時間戳
|
||||
|
||||
輸出格式:
|
||||
{
|
||||
"actions": [
|
||||
{
|
||||
"start_time": 10.5,
|
||||
"end_time": 15.2,
|
||||
"action": "eating",
|
||||
"confidence": 0.85,
|
||||
"person_id": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 方案 B:雙流網絡(平衡方案)
|
||||
|
||||
**適合場景**:準確率要求高
|
||||
|
||||
```python
|
||||
架構:
|
||||
|
||||
影片 → RGB 幀 → ResNet-50 → 空間特徵 ─┐
|
||||
├→ 融合 → 動作
|
||||
影片 → 光流 → ResNet-50 → 時間特徵 ─┘
|
||||
|
||||
優點:
|
||||
✅ 準確率高
|
||||
✅ 兼顧外觀和運動
|
||||
|
||||
缺點:
|
||||
❌ 需要計算光流(慢)
|
||||
❌ 記憶體消耗大
|
||||
```
|
||||
|
||||
**優化**:
|
||||
|
||||
```python
|
||||
# 使用 TV-L1 光流(快速)
|
||||
cv2.optflow.DualTVL1OpticalFlow_create()
|
||||
|
||||
# 或使用 RAFT 光流(準確)
|
||||
from raft import RAFT
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 方案 C:SlowFast(高端方案)
|
||||
|
||||
**適合場景**:Mac Studio、最高準確率
|
||||
|
||||
```python
|
||||
架構:
|
||||
|
||||
影片 → SlowFast 網絡 → 動作類別
|
||||
|
||||
模型選擇:
|
||||
- SlowFast R50: 中等準確率
|
||||
- SlowFast R101: 高準確率
|
||||
- SlowFast X3D: 輕量級
|
||||
|
||||
優點:
|
||||
✅ 準確率最高
|
||||
✅ SOTA 性能
|
||||
|
||||
缺點:
|
||||
❌ 計算量大
|
||||
❌ 需 Mac Studio 64GB
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 方案 D:多模態融合(完整方案)
|
||||
|
||||
**適合場景**:複雜事件識別(如吵架)
|
||||
|
||||
```python
|
||||
架構:
|
||||
|
||||
視覺 → SlowFast → 視覺特徵 ─┐
|
||||
├→ 融合 → 事件類別
|
||||
音頻 → ASR → 文本特徵 ────┘
|
||||
|
||||
示例(吵架識別):
|
||||
- 視覺: 肢體動作激烈
|
||||
- 音頻: 語調高、語速快
|
||||
- 文本: 爭論性詞彙
|
||||
|
||||
優點:
|
||||
✅ 準確率最高
|
||||
✅ 可處理複雜事件
|
||||
✅ 魯棒性強
|
||||
|
||||
缺點:
|
||||
❌ 複雜度高
|
||||
❌ 需要多個處理器協同
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 各類事件的識別策略
|
||||
|
||||
### 1. 暴力事件識別(打架、吵架)
|
||||
|
||||
#### 打架識別
|
||||
|
||||
```python
|
||||
方法: 時空動作檢測
|
||||
|
||||
特徵:
|
||||
- 快速肢體運動
|
||||
- 多人近距離接觸
|
||||
- 攻擊性動作模式
|
||||
|
||||
實現:
|
||||
1. Pose 檢測 → 骨架序列
|
||||
2. ST-GCN → 動作分類
|
||||
3. 結合 YOLO(武器檢測)
|
||||
4. 時間滑動窗口檢測
|
||||
|
||||
模型:
|
||||
- 數據集: RWF-2000, UBI-Fight
|
||||
- 模型: SlowFast + ST-GCN 融合
|
||||
- 閾值: confidence > 0.7
|
||||
|
||||
挑戰:
|
||||
⚠️ 數據集小
|
||||
⚠️ 類內變異大
|
||||
⚠️ 遮擠遮擋
|
||||
```
|
||||
|
||||
#### 吵架識別
|
||||
|
||||
```python
|
||||
方法: 多模態融合
|
||||
|
||||
特徵:
|
||||
視覺:
|
||||
- 揮手、指指點點
|
||||
- 面部表情憤怒
|
||||
- 近距離對峙
|
||||
|
||||
音頻:
|
||||
- 音量突然提高
|
||||
- 語速加快
|
||||
- 語調激動
|
||||
|
||||
文本:
|
||||
- 爭論性詞彙
|
||||
- 情緒詞
|
||||
|
||||
實現:
|
||||
1. 視覺: Face(表情)+ Pose(手勢)
|
||||
2. 音頻: ASR(語音內容)+ 情感分析
|
||||
3. 多模態融合 → 吵架判定
|
||||
|
||||
模型:
|
||||
- 視覺: ST-GCN
|
||||
- 音頻: 情感識別模型
|
||||
- 文本: 情感分析(BERT)
|
||||
|
||||
準確率:
|
||||
- 單模態: 60-70%
|
||||
- 多模態融合: 80-85%
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. 運動事件識別
|
||||
|
||||
#### 球類運動
|
||||
|
||||
```python
|
||||
方法: 骨架動作識別 + 物體檢測
|
||||
|
||||
籃球:
|
||||
Pose: 投籃、運球、傳球動作
|
||||
YOLO: 籃球檢測
|
||||
組合: 投籃 = 投籃姿勢 + 籃球拋物線
|
||||
|
||||
足球:
|
||||
Pose: 踢球、帶球動作
|
||||
YOLO: 足球檢測
|
||||
組合: 射門 = 踢球姿勢 + 足球軌跡
|
||||
|
||||
網球:
|
||||
Pose: 揮拍動作
|
||||
YOLO: 球拍、網球檢測
|
||||
|
||||
優點:
|
||||
✅ 可復用現有處理器(Pose, YOLO)
|
||||
✅ 準確率高
|
||||
✅ 可識別細分動作
|
||||
```
|
||||
|
||||
#### 格鬥運動
|
||||
|
||||
```python
|
||||
方法: ST-GCN
|
||||
|
||||
拳擊:
|
||||
特徵: 出拳動作序列
|
||||
模型: ST-GCN(訓練在 Kinetics-400 boxing 類別)
|
||||
|
||||
柔道:
|
||||
特徵: 摔投動作
|
||||
模型: ST-GCN(需專門數據集)
|
||||
|
||||
跆拳道:
|
||||
特徵: 踢腿動作
|
||||
模型: ST-GCN
|
||||
|
||||
挑戰:
|
||||
⚠️ 高速動作(需高幀率)
|
||||
⚠️ 遮擠
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. 日常活動識別
|
||||
|
||||
#### 吃飯
|
||||
|
||||
```python
|
||||
方法: 骨架動作識別
|
||||
|
||||
特徵:
|
||||
- 手部動作: 拿筷子/叉子 → 送入口中
|
||||
- 重複模式: 每隔數秒重複
|
||||
- 物體: 碗、筷子、食物(YOLO)
|
||||
|
||||
實現:
|
||||
1. Pose → 提取手臂關鍵點
|
||||
2. ST-GCN → "eating" 動作
|
||||
3. YOLO → 餐具檢測(輔助驗證)
|
||||
4. 時間統計 → 吃飯時長
|
||||
|
||||
準確率:
|
||||
Kinetics-400 "eating": 85-90%
|
||||
```
|
||||
|
||||
#### 喝水
|
||||
|
||||
```python
|
||||
方法: 骨架動作識別
|
||||
|
||||
特徵:
|
||||
- 手部: 拿杯子 → 送至嘴邊 → 放下
|
||||
- 頭部: 仰頭動作
|
||||
- 物體: 杯子、水瓶(YOLO)
|
||||
|
||||
實現:
|
||||
1. Pose → 手部 + 頭部關鍵點
|
||||
2. ST-GCN → "drinking" 動作
|
||||
3. YOLO → 杯子檢測
|
||||
4. 時間窗口: 3-10 秒
|
||||
|
||||
準確率:
|
||||
Kinetics-400 "drinking": 88-92%
|
||||
```
|
||||
|
||||
#### 做菜
|
||||
|
||||
```python
|
||||
方法: 長時序動作識別
|
||||
|
||||
特徵:
|
||||
- 多步驟: 備料 → 切菜 → 炒菜 → 裝盤
|
||||
- 物體: 菜刀、鍋、鏟子、食材
|
||||
- 場景: 廚房(Scene Classification)
|
||||
|
||||
實現:
|
||||
1. Scene → 廚房場景
|
||||
2. YOLO → 廚具、食材檢測
|
||||
3. Pose → 切菜、翻炒動作
|
||||
4. 時序模型 → 長時序分析
|
||||
|
||||
挑戰:
|
||||
⚠️ 長時序(數分鐘到數小時)
|
||||
⚠️ 多步驟識別
|
||||
⚠️ 細分動作多
|
||||
|
||||
數據集:
|
||||
EPIC-KITCHENS-100: 專門的廚房活動數據集
|
||||
```
|
||||
|
||||
#### 交談
|
||||
|
||||
```python
|
||||
方法: 多模態融合
|
||||
|
||||
特徵:
|
||||
視覺:
|
||||
- 面對面姿勢
|
||||
- 手勢
|
||||
- 面部表情變化
|
||||
|
||||
音頻:
|
||||
- 交替說話
|
||||
- 語音存在
|
||||
|
||||
實現:
|
||||
1. Face → 面部朝向
|
||||
2. Pose → 交談姿勢
|
||||
3. ASR → 檢測語音
|
||||
4. 時序分析 → 持續時間
|
||||
|
||||
難點:
|
||||
⚠️ 與其他活動重疊(如邊吃邊聊)
|
||||
⚠️ 需要多模態融合
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 效能與資源評估
|
||||
|
||||
### Mac Studio 64GB 測試預估
|
||||
|
||||
| 方法 | 模型 | 記憶體 | FPS | 準確率 |
|
||||
|------|------|--------|-----|--------|
|
||||
| **ST-GCN** | 輕量 | 1-2GB | 100+ | 80-85% |
|
||||
| **SlowFast R50** | 中等 | 4-6GB | 30-40 | 85-90% |
|
||||
| **SlowFast R101** | 大型 | 6-8GB | 15-20 | 90-95% |
|
||||
| **多模態** | 融合 | 8-10GB | 10-15 | 95%+ |
|
||||
|
||||
### 處理時間(10分鐘影片)
|
||||
|
||||
| 方法 | 處理時間 | On-the-Fly |
|
||||
|------|---------|-----------|
|
||||
| **ST-GCN** | 15秒 | ✅ 可以 |
|
||||
| **SlowFast R50** | 40秒 | ✅ 可以(100Mbps) |
|
||||
| **SlowFast R101** | 100秒 | ⚠️ 勉強 |
|
||||
| **多模態** | 150秒 | ❌ 無法 |
|
||||
|
||||
---
|
||||
|
||||
## 推薦方案
|
||||
|
||||
### 階段 1:骨架動作識別(立即實施)
|
||||
|
||||
```python
|
||||
處理器: Action Recognition Processor
|
||||
模型: ST-GCN(預訓練在 Kinetics-400)
|
||||
依賴: Pose 處理器(已存在)
|
||||
|
||||
事件類別:
|
||||
✅ 日常活動: eating, drinking, cooking, cleaning
|
||||
✅ 運動: running, swimming, playing basketball
|
||||
✅ 互動: hugging, shaking hands, talking
|
||||
|
||||
優點:
|
||||
✅ 輕量級
|
||||
✅ 可復用 Pose 結果
|
||||
✅ 實時性好
|
||||
✅ 適合 On-the-Fly
|
||||
```
|
||||
|
||||
### 階段 2:暴力事件檢測(第二階段)
|
||||
|
||||
```python
|
||||
處理器: Violence Detection Processor
|
||||
方法: ST-GCN + 多模態融合
|
||||
數據集: RWF-2000, UBI-Fight
|
||||
|
||||
事件類別:
|
||||
✅ 打架: 結合 Pose + 物體檢測
|
||||
✅ 吵架: 結合 Pose + ASR + 情感分析
|
||||
|
||||
挑戰:
|
||||
⚠️ 數據集小
|
||||
⚠️ 需要專門訓練
|
||||
```
|
||||
|
||||
### 階段 3:細粒度動作識別(第三階段)
|
||||
|
||||
```python
|
||||
處理器: Fine-grained Action Processor
|
||||
方法: SlowFast + 多模態
|
||||
數據集: EPIC-KITCHENS, AVA
|
||||
|
||||
事件類別:
|
||||
✅ 廚房活動: 切菜、炒菜、洗碗
|
||||
✅ 工作活動: 打字、開會、演講
|
||||
✅ 運動細節: 投籃、運球、傳球
|
||||
|
||||
需求:
|
||||
- Mac Studio 64GB+
|
||||
- 專門數據集微調
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 實施步驟
|
||||
|
||||
### Phase 1:ST-GCN 處理器(第 1-2 週)
|
||||
|
||||
```bash
|
||||
1. 安裝依賴
|
||||
pip install torch torchvision
|
||||
pip install mmcv mmdet mmpose
|
||||
|
||||
2. 下載預訓練模型
|
||||
wget https://download.openmmlab.com/mmaction/pyskeleton/adaagnet/adaagnet_8xb16_ntu60_xsub_1e.py
|
||||
wget https://download.openmmlab.com/mmaction/pyskeleton/adaagnet/adaagnet_ntu60_xsub_1e-44e6f770.pth
|
||||
|
||||
3. 創建處理器
|
||||
scripts/action_processor.py
|
||||
|
||||
4. 整合 API
|
||||
POST /api/v1/process
|
||||
{"processors": ["pose", "action"]}
|
||||
|
||||
5. 測試
|
||||
python3 scripts/test_action_recognition.py video.mp4
|
||||
```
|
||||
|
||||
### Phase 2:暴力事件檢測(第 3-4 週)
|
||||
|
||||
```bash
|
||||
1. 收集/標註數據
|
||||
2. 微調 ST-GCN 模型
|
||||
3. 實現多模態融合
|
||||
4. 測試與優化
|
||||
```
|
||||
|
||||
### Phase 3:完整事件識別(第 5-6 週)
|
||||
|
||||
```bash
|
||||
1. 部署 SlowFast 模型
|
||||
2. 實現細粒度分類
|
||||
3. 優化 On-the-Fly 性能
|
||||
4. 用戶測試與反饋
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 總結
|
||||
|
||||
### 推薦技術路線
|
||||
|
||||
```
|
||||
短中期(Mac Studio 64GB):
|
||||
✅ 骨架動作識別(ST-GCN)
|
||||
✅ 復用 Pose 結果
|
||||
✅ 輕量級、實時性好
|
||||
✅ 支援 60+ 日常活動
|
||||
|
||||
長期(Mac Studio 128GB):
|
||||
✅ SlowFast 大模型
|
||||
✅ 多模態融合
|
||||
✅ 細粒度動作識別
|
||||
✅ 達到 SOTA 水準
|
||||
```
|
||||
|
||||
### 預期效果
|
||||
|
||||
| 事件類型 | 方法 | 準確率 | 處理時間 |
|
||||
|---------|------|--------|---------|
|
||||
| **日常活動** | ST-GCN | 85-90% | 15s/10min |
|
||||
| **運動** | ST-GCN + YOLO | 88-92% | 20s/10min |
|
||||
| **打架** | ST-GCN | 80-85% | 15s/10min |
|
||||
| **吵架** | 多模態 | 85-90% | 60s/10min |
|
||||
| **細粒度動作** | SlowFast | 90-95% | 100s/10min |
|
||||
@@ -0,0 +1,438 @@
|
||||
---
|
||||
document_type: "architecture_design"
|
||||
service: "MOMENTRY_CORE"
|
||||
title: "Momentry Core 架構常見問題解答 (FAQ)"
|
||||
date: "2026-04-25"
|
||||
version: "V1.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
tags:
|
||||
- "momentry"
|
||||
- "core"
|
||||
- "架構常見問題解答"
|
||||
ai_query_hints:
|
||||
- "查詢 Momentry Core 架構常見問題解答 (FAQ) 的內容"
|
||||
- "Momentry Core 架構常見問題解答 (FAQ) 的主要目的是什麼?"
|
||||
- "如何操作或實施 Momentry Core 架構常見問題解答 (FAQ)?"
|
||||
---
|
||||
|
||||
# Momentry Core 架構常見問題解答 (FAQ)
|
||||
|
||||
## 目錄
|
||||
1. [設計與實現相關問題](#設計與實現相關問題)
|
||||
2. [開發與部署相關問題](#開發與部署相關問題)
|
||||
3. [分片與處理相關問題](#分片與處理相關問題)
|
||||
4. [數據庫與存儲相關問題](#數據庫與存儲相關問題)
|
||||
5. [性能與擴展相關問題](#性能與擴展相關問題)
|
||||
6. [安全與監控相關問題](#安全與監控相關問題)
|
||||
|
||||
---
|
||||
|
||||
## 設計與實現相關問題
|
||||
|
||||
### Q1.1: 為什麼設計文檔與實際代碼實現不一致?
|
||||
|
||||
**A**: 這是開發過程中的常見現象。主要原因包括:
|
||||
1. **設計先行**:架構設計通常在代碼實現之前完成
|
||||
2. **技術調整**:實際開發中根據技術可行性調整設計
|
||||
3. **資源限制**:某些功能因資源限制推遲實現
|
||||
4. **迭代開發**:敏捷開發中的持續改進
|
||||
|
||||
**解決方案**:
|
||||
- 以實際 Rust 代碼實現為最高權威
|
||||
- 定期更新設計文檔反映實際狀態
|
||||
- 建立設計與實現一致性檢查機制
|
||||
|
||||
### Q1.2: 如何理解分片類型的差異?
|
||||
|
||||
**A**: 設計文檔與實際代碼的分片類型對照:
|
||||
|
||||
| 設計概念 | 設計值 | 實現值 | 狀態 |
|
||||
|----------|--------|--------|------|
|
||||
| 句子級分片 | `sentence` | `Sentence` | ✅ 已實現 |
|
||||
| 視覺物件級分片 | `visual` | 無對應實現 | ❌ 未實現 |
|
||||
| 場景級分片 | `scene` | `Cut` | ⚠️ 部分實現 |
|
||||
| 摘要級分片 | `summary` | `Story` | ⚠️ 概念調整 |
|
||||
| 時間基準分片 | `time` | `TimeBased` | ✅ 已實現 |
|
||||
| 軌跡追蹤分片 | `trace` | `Trace` | ✅ 已實現 |
|
||||
|
||||
### Q1.3: 如何處理設計與實現的衝突?
|
||||
|
||||
**A**: 遵循以下原則:
|
||||
1. **優先級原則**:以實際代碼實現為準
|
||||
2. **文檔更新原則**:更新設計文檔反映實際實現
|
||||
3. **版本控制原則**:記錄設計變更歷史
|
||||
4. **團隊溝通原則**:確保團隊理解實際架構
|
||||
|
||||
---
|
||||
|
||||
## 開發與部署相關問題
|
||||
|
||||
### Q2.1: 如何快速開始開發?
|
||||
|
||||
**A**: 建議步驟:
|
||||
1. **環境設置**:
|
||||
```bash
|
||||
# 安裝 Rust
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
|
||||
# 安裝項目依賴
|
||||
cargo build
|
||||
```
|
||||
|
||||
2. **開發工作流**:
|
||||
```bash
|
||||
# 構建項目
|
||||
cargo build
|
||||
|
||||
# 運行測試
|
||||
cargo test
|
||||
|
||||
# 格式化代碼
|
||||
cargo fmt
|
||||
|
||||
# 代碼檢查
|
||||
cargo clippy
|
||||
```
|
||||
|
||||
3. **調試工具**:
|
||||
- 使用 `tracing` 日誌系統
|
||||
- 設置 `RUST_LOG=debug` 環境變數
|
||||
- 使用 `cargo test -- --nocapture` 查看測試輸出
|
||||
|
||||
### Q2.2: 開發環境和生產環境如何區分?
|
||||
|
||||
**A**: 系統支持完全環境隔離:
|
||||
|
||||
| 環境 | 二進制名稱 | Redis 網址 | 默認端口 |
|
||||
|------|------------|------------|----------|
|
||||
| 生產環境 | `momentry` | `momentry:` | 3002 |
|
||||
| 開發環境 | `momentry_playground` | `momentry_dev:` | 3003 |
|
||||
|
||||
**使用方法**:
|
||||
```bash
|
||||
# 生產環境
|
||||
cargo run -- server --host 0.0.0.0 --port 3002
|
||||
|
||||
# 開發環境
|
||||
cargo run --bin momentry_playground -- server
|
||||
```
|
||||
|
||||
### Q2.3: 如何添加新的處理器?
|
||||
|
||||
**A**: 標準步驟:
|
||||
1. **創建處理器模塊**:
|
||||
```rust
|
||||
// src/core/processor/new_processor.rs
|
||||
use crate::core::processor::Processor;
|
||||
|
||||
pub struct NewProcessor;
|
||||
|
||||
impl Processor for NewProcessor {
|
||||
// 實現處理器 trait
|
||||
}
|
||||
```
|
||||
|
||||
2. **註冊到處理器註冊表**:
|
||||
```rust
|
||||
// src/core/processor/mod.rs
|
||||
mod new_processor;
|
||||
pub use new_processor::NewProcessor;
|
||||
|
||||
// 註冊處理器
|
||||
registry.register("new_processor", Box::new(NewProcessor::new()));
|
||||
```
|
||||
|
||||
3. **集成到處理管道**:
|
||||
- 配置處理順序
|
||||
- 設置超時參數
|
||||
- 定義輸出格式
|
||||
|
||||
---
|
||||
|
||||
## 分片與處理相關問題
|
||||
|
||||
### Q3.1: 分片是如何生成的?
|
||||
|
||||
**A**: 分片生成流程:
|
||||
|
||||
```
|
||||
視訊輸入 → 多模態處理 → 分片規則應用 → 分片存儲
|
||||
↓ ↓ ↓ ↓
|
||||
ASR 文本提取 Rule1/2/3/4 數據庫存儲
|
||||
OCR 視覺特徵 → 分片類型 → 向量索引
|
||||
YOLO 場景檢測 → 檢索優化
|
||||
CUT
|
||||
```
|
||||
|
||||
**分片規則**:
|
||||
1. **Rule 1 (Sentence)**: 基於 ASR 結果的句子級分片
|
||||
2. **Rule 2 (Visual)**: 基於 YOLO 的視覺物件分片 (未實現)
|
||||
3. **Rule 3 (Cut)**: 基於 CUT 算法的場景分片
|
||||
4. **Rule 4 (Story)**: 基於分片聚合的故事級分片
|
||||
|
||||
### Q3.2: 處理管道如何工作?
|
||||
|
||||
**A**: 處理管道特點:
|
||||
|
||||
1. **統一執行框架**:
|
||||
- 所有 Python 腳本通過 `PythonExecutor` 執行
|
||||
- 統一的超時控制和錯誤處理
|
||||
- 標準化的輸出格式
|
||||
|
||||
2. **並行處理**:
|
||||
- 支持多個處理器並行執行
|
||||
- 資源分配和調度優化
|
||||
- 錯誤隔離和恢復
|
||||
|
||||
3. **結果整合**:
|
||||
- 多模態結果融合
|
||||
- 分片生成和關聯
|
||||
- 向量嵌入計算
|
||||
|
||||
### Q3.3: 如何擴展新的分片類型?
|
||||
|
||||
**A**: 擴展步驟:
|
||||
|
||||
1. **定義新的分片類型**:
|
||||
```rust
|
||||
// src/core/chunk/types.rs
|
||||
pub enum ChunkType {
|
||||
// 現有類型...
|
||||
NewType, // 新的分片類型
|
||||
}
|
||||
```
|
||||
|
||||
2. **創建專用內容結構**:
|
||||
```rust
|
||||
pub struct NewTypeContent {
|
||||
pub field1: String,
|
||||
pub field2: Vec<String>,
|
||||
// ... 其他字段
|
||||
}
|
||||
```
|
||||
|
||||
3. **實現分片生成規則**:
|
||||
- 創建新的規則處理器
|
||||
- 集成到處理管道
|
||||
- 定義分片內容格式
|
||||
|
||||
---
|
||||
|
||||
## 數據庫與存儲相關問題
|
||||
|
||||
### Q4.1: 為什麼使用多個數據庫?
|
||||
|
||||
**A**: 多數據庫架構的優勢:
|
||||
|
||||
| 數據庫 | 用途 | 優勢 |
|
||||
|--------|------|------|
|
||||
| PostgreSQL | 結構化數據 | ACID 事務,關係型查詢 |
|
||||
| Redis | 緩存和隊列 | 高性能,低延遲 |
|
||||
| Qdrant | 向量數據 | 向量相似度搜索,ANN 算法 |
|
||||
| MongoDB | 文檔數據 | 靈活 schema,易於擴展 |
|
||||
|
||||
**使用場景**:
|
||||
- **PostgreSQL**: 視訊元數據、分片信息、任務管理
|
||||
- **Redis**: 會話緩存、隊列管理、實時統計
|
||||
- **Qdrant**: 語義搜索、視覺檢索、推薦系統
|
||||
- **MongoDB**: 處理結果、日誌數據、配置存儲
|
||||
|
||||
### Q4.2: 數據一致性如何保證?
|
||||
|
||||
**A**: 數據一致性策略:
|
||||
|
||||
1. **事務處理**:
|
||||
- 關鍵操作使用 PostgreSQL 事務
|
||||
- 確保數據原子性和一致性
|
||||
|
||||
2. **冪等性設計**:
|
||||
- 處理器結果冪等性
|
||||
- 任務執行冪等性
|
||||
|
||||
3. **補償機制**:
|
||||
- 失敗操作的補償處理
|
||||
- 數據一致性修復工具
|
||||
|
||||
4. **監控和告警**:
|
||||
- 數據一致性監控
|
||||
- 異常檢測和自動修復
|
||||
|
||||
### Q4.3: 如何優化數據庫性能?
|
||||
|
||||
**A**: 性能優化建議:
|
||||
|
||||
1. **PostgreSQL**:
|
||||
```sql
|
||||
-- 創建索引
|
||||
CREATE INDEX idx_chunks_video_record_id ON chunks(video_record_id);
|
||||
CREATE INDEX idx_chunks_chunk_type ON chunks(chunk_type);
|
||||
|
||||
-- 分區表
|
||||
CREATE TABLE chunks_2026_04 PARTITION OF chunks
|
||||
FOR VALUES FROM ('2026-04-01') TO ('2026-05-01');
|
||||
```
|
||||
|
||||
2. **Redis**:
|
||||
- 使用連接池減少連接開銷
|
||||
- 合理設置過期時間避免內存洩漏
|
||||
- 使用 pipeline 批量操作
|
||||
|
||||
3. **Qdrant**:
|
||||
- 優化向量索引參數
|
||||
- 定期重建索引
|
||||
- 使用量化減少存儲空間
|
||||
|
||||
---
|
||||
|
||||
## 性能與擴展相關問題
|
||||
|
||||
### Q5.1: 如何評估系統性能?
|
||||
|
||||
**A**: 關鍵性能指標:
|
||||
|
||||
1. **處理性能**:
|
||||
- 視訊處理吞吐量 (分鐘/小時)
|
||||
- 分片生成速度 (分片/秒)
|
||||
- 向量嵌入計算時間 (毫秒/分片)
|
||||
|
||||
2. **檢索性能**:
|
||||
- 查詢響應時間 (毫秒)
|
||||
- 檢索準確率 (召回率,精確率)
|
||||
- 並發處理能力 (QPS)
|
||||
|
||||
3. **資源利用率**:
|
||||
- CPU 使用率
|
||||
- 內存佔用
|
||||
- 磁盤 I/O
|
||||
|
||||
**監控工具**:
|
||||
- Prometheus + Grafana 監控面板
|
||||
- 自定義性能指標收集
|
||||
- 壓力測試和基準測試
|
||||
|
||||
### Q5.2: 如何擴展系統處理能力?
|
||||
|
||||
**A**: 擴展策略:
|
||||
|
||||
1. **垂直擴展**:
|
||||
- 升級服務器硬件
|
||||
- 增加 GPU 資源
|
||||
- 擴展內存和存儲
|
||||
|
||||
2. **水平擴展**:
|
||||
- 微服務架構重構
|
||||
- 負載均衡和集群
|
||||
- 分布式處理管道
|
||||
|
||||
3. **軟件優化**:
|
||||
- 算法優化和並行化
|
||||
- 緩存策略優化
|
||||
- 數據庫查詢優化
|
||||
|
||||
### Q5.3: 如何處理大規模數據?
|
||||
|
||||
**A**: 大規模數據處理策略:
|
||||
|
||||
1. **分布式處理**:
|
||||
- 分片級別並行處理
|
||||
- 任務隊列和工作者模式
|
||||
- 結果聚合和歸一化
|
||||
|
||||
2. **增量處理**:
|
||||
- 流式處理支持
|
||||
- 增量更新和索引
|
||||
- 實時數據同步
|
||||
|
||||
3. **存儲優化**:
|
||||
- 數據分區和分片
|
||||
- 壓縮和編碼優化
|
||||
- 冷熱數據分離
|
||||
|
||||
---
|
||||
|
||||
## 安全與監控相關問題
|
||||
|
||||
### Q6.1: 系統安全如何保證?
|
||||
|
||||
**A**: 安全架構:
|
||||
|
||||
1. **訪問控制**:
|
||||
- API 密鑰認證
|
||||
- 角色基於權限控制 (RBAC)
|
||||
- 請求限流和防刷
|
||||
|
||||
2. **數據安全**:
|
||||
- 傳輸加密 (HTTPS)
|
||||
- 數據存儲加密
|
||||
- 敏感信息脫敏
|
||||
|
||||
3. **審計日誌**:
|
||||
- 操作日誌記錄
|
||||
- 安全事件監控
|
||||
- 異常行為檢測
|
||||
|
||||
### Q6.2: 如何監控系統狀態?
|
||||
|
||||
**A**: 監控體系:
|
||||
|
||||
1. **基礎設施監控**:
|
||||
- 服務器資源監控
|
||||
- 網絡連接狀態
|
||||
- 存儲空間使用
|
||||
|
||||
2. **應用監控**:
|
||||
- 服務健康檢查
|
||||
- 性能指標收集
|
||||
- 錯誤日誌分析
|
||||
|
||||
3. **業務監控**:
|
||||
- 用戶行為分析
|
||||
- 業務指標統計
|
||||
- 系統可用性監控
|
||||
|
||||
### Q6.3: 如何進行故障恢復?
|
||||
|
||||
**A**: 故障恢復策略:
|
||||
|
||||
1. **預防措施**:
|
||||
- 定期備份和快照
|
||||
- 系統健康檢查
|
||||
- 容量規劃和預警
|
||||
|
||||
2. **故障檢測**:
|
||||
- 自動化監控告警
|
||||
- 異常檢測算法
|
||||
- 性能閾值告警
|
||||
|
||||
3. **恢復機制**:
|
||||
- 自動化故障轉移
|
||||
- 數據恢復工具
|
||||
- 服務重啟策略
|
||||
|
||||
---
|
||||
|
||||
## 更多資源
|
||||
|
||||
### 官方文檔
|
||||
- [架構概覽](./ARCHITECTURE_OVERVIEW.md) - 系統架構全面介紹
|
||||
- [設計實現差異](./DESIGN_IMPLEMENTATION_GAP.md) - 設計與實現不一致分析
|
||||
- [執行計畫](./ARCHITECTURE_DECISION_EXECUTION_PLAN.md) - 架構改進執行方案
|
||||
|
||||
### 開發指南
|
||||
- [快速入門指南](./QUICK_START_GUIDE.md) - 5分鐘快速上手
|
||||
- [決策卡片](./ARCHITECTURE_DECISION_CARDS.md) - 架構決策記錄
|
||||
- [技術決策記錄](./TECHNICAL_DECISION_RECORDS.md) - 詳細技術決策
|
||||
|
||||
### 參考資料
|
||||
- [性能與擴展](./PERFORMANCE_AND_SCALABILITY.md) - 性能優化指南
|
||||
- [安全架構](./SECURITY_ARCHITECTURE.md) - 安全設計詳解
|
||||
- [監控架構](./MONITORING_ARCHITECTURE.md) - 監控系統設計
|
||||
|
||||
---
|
||||
|
||||
**最後更新**: 2026-04-22
|
||||
**文檔版本**: V1.0
|
||||
**更新頻率**: 每月審查更新
|
||||
**維護者**: OpenCode
|
||||
@@ -0,0 +1,700 @@
|
||||
---
|
||||
document_type: "architecture_design"
|
||||
service: "MOMENTRY_CORE"
|
||||
title: "Job Worker 實作計畫"
|
||||
date: "2026-03-24"
|
||||
version: "V1.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
tags:
|
||||
- "實作計畫"
|
||||
- "worker"
|
||||
ai_query_hints:
|
||||
- "查詢 Job Worker 實作計畫 的內容"
|
||||
- "Job Worker 實作計畫 的主要目的是什麼?"
|
||||
- "如何操作或實施 Job Worker 實作計畫?"
|
||||
---
|
||||
|
||||
# Job Worker 實作計畫
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | Warren / OpenCode |
|
||||
| 建立時間 | 2026-03-24 |
|
||||
| 文件版本 | V1.1 |
|
||||
| 狀態 | ✅ 已實作 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 |
|
||||
|------|------|------|--------|
|
||||
| V1.0 | 2026-03-24 | 建立實作計畫 | OpenCode |
|
||||
| V1.1 | 2026-03-25 | 實作完成,更新狀態 | OpenCode |
|
||||
|
||||
---
|
||||
|
||||
## 實作狀態
|
||||
|
||||
### ✅ 已完成
|
||||
|
||||
| 元件 | 檔案 | 狀態 |
|
||||
|------|------|------|
|
||||
| MonitorJob 結構 | `src/core/db/postgres_db.rs` | ✅ |
|
||||
| ProcessorResult 結構 | `src/core/db/postgres_db.rs` | ✅ |
|
||||
| Worker 配置 | `src/worker/config.rs` | ✅ |
|
||||
| Job Worker | `src/worker/job_worker.rs` | ✅ |
|
||||
| Processor Pool | `src/worker/processor.rs` | ✅ |
|
||||
| Worker 模組 | `src/worker/mod.rs` | ✅ |
|
||||
| PostgreSQL 表格 | `monitor_jobs`, `processor_results` | ✅ |
|
||||
| 類型修復 | `i32`, `NaiveDateTime` | ✅ |
|
||||
|
||||
### 待整合
|
||||
|
||||
| 項目 | 說明 |
|
||||
|------|------|
|
||||
| Worker 服務啟動 | 需要加入 launchd plist |
|
||||
| 監控整合 | 需要加入 MOMENTRY_CORE_MONITORING.md |
|
||||
| 備份涵蓋 | 需要確認備份包含新表格 |
|
||||
|
||||
---
|
||||
|
||||
## 1. 設計決策
|
||||
|
||||
### 1.1 確認的設計決策
|
||||
|
||||
| 項目 | 決策 | 理由 |
|
||||
|------|------|------|
|
||||
| 觸發方式 | 輪詢(Job Worker) | 暫無可靠的 API 觸發機制 |
|
||||
| 並行處理 | 最多 2 個 | 可根據 CPU/GPU 能力調整 |
|
||||
| 失敗處理 | 獨立模組,部分完成可接續 | 任何模組失敗都產出狀態記錄 |
|
||||
| Worker 啟動 | 獨立進程 | 隔離、易管理 |
|
||||
| 並行上限調整 | 環境變數 + 預設值 | 靈活、可調整 |
|
||||
| 狀態同步 | PostgreSQL + Redis | 可靠 + 即時 |
|
||||
|
||||
### 1.2 環境變數
|
||||
|
||||
| 變數 | 預設值 | 說明 |
|
||||
|------|--------|------|
|
||||
| `MOMENTRY_MAX_CONCURRENT` | 2 | 最大並行 processor 數 |
|
||||
| `MOMENTRY_POLL_INTERVAL` | 5 | 輪詢間隔(秒) |
|
||||
| `MOMENTRY_WORKER_ENABLED` | true | 是否啟用 worker |
|
||||
|
||||
---
|
||||
|
||||
## 2. 系統架構
|
||||
|
||||
### 2.1 完整流程圖
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ 檔案註冊觸發處理流程 │
|
||||
├─────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ 1. SFTPGo 上傳 │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ 2. Hook 呼叫 Register API │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ 3. Register API │
|
||||
│ ├─► ffprobe 提取 metadata │
|
||||
│ ├─► 寫入 videos 表 │
|
||||
│ └─► 建立 monitor_jobs 記錄 (status=pending) │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ 4. Job Worker (獨立進程,輪詢機制) │
|
||||
│ ├─► 輪詢 pending jobs │
|
||||
│ ├─► 檢查 videos 表 fs_json 決定需要處理什麼 │
|
||||
│ ├─► 並行執行 processors (最多 2 個) │
|
||||
│ └─► 更新 videos, monitor_jobs, processor_results 表 │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ 5. 處理結果 │
|
||||
│ ├─► 更新 videos 表 (fs_json, psql_chunk, qvector_chunk) │
|
||||
│ ├─► 更新 monitor_jobs 表 (status, progress) │
|
||||
│ ├─► 更新 processor_results 表 (每個模組狀態) │
|
||||
│ └─► Redis Pub/Sub 即時進度 │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 2.2 Job Worker 架構
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ Job Worker 架構 │
|
||||
├─────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
||||
│ │ PostgreSQL │ ───▶ │ Worker │ ───▶ │ Processor │ │
|
||||
│ │ Job Queue │ │ Loop │ │ Pool │ │
|
||||
│ └─────────────┘ └──────┬──────┘ └──────┬──────┘ │
|
||||
│ │ │ │
|
||||
│ ▼ ▼ │
|
||||
│ ┌─────────────┐ ┌─────────────┐ │
|
||||
│ │ Video State │ │ Processor 1 │ │
|
||||
│ │ Check │ │ (ASR/YOLO) │ │
|
||||
│ └─────────────┘ ├─────────────┤ │
|
||||
│ │ Processor 2 │ │
|
||||
│ │ (CUT/OCR) │ │
|
||||
│ └─────────────┘ │
|
||||
│ │
|
||||
│ Redis ──── Pub/Sub ──── 即時進度 │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 資料庫結構
|
||||
|
||||
### 3.1 Migration 檔案
|
||||
|
||||
**檔案**: `migrations/003_job_worker.sql`
|
||||
|
||||
```sql
|
||||
-- ================================================================
|
||||
-- Migration 003: Job Worker System
|
||||
-- ================================================================
|
||||
|
||||
-- 3.1.1 更新 videos 表
|
||||
ALTER TABLE videos ADD COLUMN IF NOT EXISTS status VARCHAR(20) DEFAULT 'pending';
|
||||
ALTER TABLE videos ADD COLUMN IF NOT EXISTS user_id BIGINT;
|
||||
ALTER TABLE videos ADD COLUMN IF NOT EXISTS job_id INTEGER REFERENCES monitor_jobs(id);
|
||||
|
||||
COMMENT ON COLUMN videos.status IS 'pending, processing, completed, failed';
|
||||
COMMENT ON COLUMN videos.user_id IS 'WordPress user ID';
|
||||
COMMENT ON COLUMN videos.job_id IS 'Associated monitor_jobs ID';
|
||||
|
||||
-- 3.1.2 更新 monitor_jobs 表
|
||||
ALTER TABLE monitor_jobs ADD COLUMN IF NOT EXISTS video_id BIGINT REFERENCES videos(id);
|
||||
ALTER TABLE monitor_jobs ADD COLUMN IF NOT EXISTS user_id BIGINT;
|
||||
ALTER TABLE monitor_jobs ADD COLUMN IF NOT EXISTS processors VARCHAR(20)[];
|
||||
ALTER TABLE monitor_jobs ADD COLUMN IF NOT EXISTS completed_processors VARCHAR(20)[];
|
||||
ALTER TABLE monitor_jobs ADD COLUMN IF NOT EXISTS failed_processors VARCHAR(20)[];
|
||||
|
||||
COMMENT ON COLUMN monitor_jobs.processors IS 'Processors to run: asr, cut, yolo, ocr, face, pose, asrx';
|
||||
COMMENT ON COLUMN monitor_jobs.completed_processors IS 'Successfully completed processors';
|
||||
COMMENT ON COLUMN monitor_jobs.failed_processors IS 'Failed processors';
|
||||
|
||||
-- 3.1.3 新增 processor_results 表
|
||||
CREATE TABLE IF NOT EXISTS processor_results (
|
||||
id SERIAL PRIMARY KEY,
|
||||
job_id INTEGER REFERENCES monitor_jobs(id) ON DELETE CASCADE,
|
||||
video_id BIGINT REFERENCES videos(id) ON DELETE CASCADE,
|
||||
processor VARCHAR(20) NOT NULL,
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'pending',
|
||||
output_path TEXT,
|
||||
started_at TIMESTAMP,
|
||||
completed_at TIMESTAMP,
|
||||
error_message TEXT,
|
||||
progress_total INT DEFAULT 0,
|
||||
progress_current INT DEFAULT 0,
|
||||
last_checkpoint JSONB,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
UNIQUE(job_id, processor)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_processor_results_job ON processor_results(job_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_processor_results_video ON processor_results(video_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_processor_results_status ON processor_results(status);
|
||||
|
||||
COMMENT ON TABLE processor_results IS 'Tracks individual processor execution status';
|
||||
COMMENT ON COLUMN processor_results.status IS 'pending, running, completed, failed, skipped';
|
||||
|
||||
-- 3.1.4 更新 videos 表標記欄位用途
|
||||
COMMENT ON COLUMN videos.fs_video IS 'Video file exists on filesystem';
|
||||
COMMENT ON COLUMN videos.fs_json IS 'All processor JSON files generated';
|
||||
COMMENT ON COLUMN videos.fs_chunks IS 'Chunk files generated';
|
||||
COMMENT ON COLUMN videos.fs_vectors IS 'Vector files generated';
|
||||
COMMENT ON COLUMN videos.psql_chunk IS 'Chunks stored in PostgreSQL';
|
||||
COMMENT ON COLUMN videos.pvector_chunk IS 'Vectors stored in PostgreSQL';
|
||||
COMMENT ON COLUMN videos.qvector_chunk IS 'Vectors stored in Qdrant';
|
||||
```
|
||||
|
||||
### 3.2 表關係圖
|
||||
|
||||
```
|
||||
videos monitor_jobs
|
||||
┌──────────────────────┐ ┌──────────────────────┐
|
||||
│ id (PK) │◄────────│ video_id (FK) │
|
||||
│ uuid │ │ user_id │
|
||||
│ status │ │ processors[] │
|
||||
│ fs_video │ │ completed_processors[]│
|
||||
│ fs_json │ │ failed_processors[] │
|
||||
│ job_id (FK)─────────┼────────►│ status │
|
||||
│ user_id │ │ id (PK) │
|
||||
└──────────────────────┘ └──────────────────────┘
|
||||
│
|
||||
│
|
||||
processor_results
|
||||
┌──────────────────────┐
|
||||
│ job_id (FK) │
|
||||
│ video_id (FK) │
|
||||
│ processor │
|
||||
│ status │
|
||||
│ progress_current │
|
||||
│ last_checkpoint │
|
||||
│ id (PK) │
|
||||
└──────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 模組並行策略
|
||||
|
||||
### 4.1 模組分類
|
||||
|
||||
| 模組 | 資源需求 | 獨立性 | 建議並行 |
|
||||
|------|----------|--------|----------|
|
||||
| ASR | GPU/CPU | 高 | ✅ 可並行 |
|
||||
| CUT | CPU | 高 | ✅ 可並行 |
|
||||
| YOLO | GPU | 中 | ✅ 可並行 |
|
||||
| OCR | GPU/CPU | 高 | ✅ 可並行 |
|
||||
| Face | GPU | 中 | ✅ 可並行 |
|
||||
| Pose | GPU | 中 | ✅ 可並行 |
|
||||
| ASRX | GPU/CPU | 高 | ✅ 可並行 |
|
||||
|
||||
### 4.2 建議並行組合
|
||||
|
||||
| 組合 | 模組 1 | 模組 2 | 說明 |
|
||||
|------|---------|---------|------|
|
||||
| GPU+CPU | YOLO/Pose/Face | ASR/CUT/OCR | 平衡負載 |
|
||||
| 雙GPU | YOLO | Pose | 雙 GPU 卡片 |
|
||||
| 雙CPU | ASR | CUT/OCR | 無 GPU 時 |
|
||||
|
||||
### 4.3 Worker 配置
|
||||
|
||||
```rust
|
||||
// src/worker/config.rs
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct WorkerConfig {
|
||||
pub max_concurrent: usize, // 預設 2
|
||||
pub poll_interval_secs: u64, // 預設 5
|
||||
pub enabled: bool, // 預設 true
|
||||
}
|
||||
|
||||
impl Default for WorkerConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
max_concurrent: 2,
|
||||
poll_interval_secs: 5,
|
||||
enabled: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WorkerConfig {
|
||||
pub fn from_env() -> Self {
|
||||
Self {
|
||||
max_concurrent: std::env::var("MOMENTRY_MAX_CONCURRENT")
|
||||
.ok()
|
||||
.and_then(|v| v.parse().ok())
|
||||
.unwrap_or(2),
|
||||
poll_interval_secs: std::env::var("MOMENTRY_POLL_INTERVAL")
|
||||
.ok()
|
||||
.and_then(|v| v.parse().ok())
|
||||
.unwrap_or(5),
|
||||
enabled: std::env::var("MOMENTRY_WORKER_ENABLED")
|
||||
.ok()
|
||||
.map(|v| v != "false")
|
||||
.unwrap_or(true),
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 失敗處理機制
|
||||
|
||||
### 5.1 設計原則
|
||||
|
||||
```
|
||||
每個模組獨立處理:
|
||||
- 成功 → 產出完整 .json,status=completed
|
||||
- 失敗 → 產出 .json 包含 error 狀態,status=failed
|
||||
- 部分完成 → 可從 checkpoint 繼續,status=running
|
||||
```
|
||||
|
||||
### 5.2 Processor 輸出格式
|
||||
|
||||
```json
|
||||
{
|
||||
"processor": "asr",
|
||||
"status": "completed|failed|partial",
|
||||
"completed_at": "2026-03-24T12:00:00Z",
|
||||
"result": { ... },
|
||||
"error": null,
|
||||
"last_checkpoint": {
|
||||
"frame": 5000,
|
||||
"timestamp": 180.5
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5.3 失敗處理流程
|
||||
|
||||
```rust
|
||||
async fn run_processor(&self, module: &str, video: &Video) -> Result<()> {
|
||||
let output_path = self.get_output_path(video, module);
|
||||
|
||||
match self.execute_processor(module, video, &output_path).await {
|
||||
Ok(result) => {
|
||||
// 成功:更新狀態
|
||||
self.db.update_processor_status(job_id, module, "completed").await?;
|
||||
self.publish_progress(job_id, module, 100).await?;
|
||||
}
|
||||
Err(e) => {
|
||||
// 失敗:仍然保存部分結果
|
||||
let partial_result = self.get_partial_result(&output_path);
|
||||
self.db.update_processor_status(job_id, module, "failed").await?;
|
||||
self.db.save_error_message(job_id, module, &e.to_string()).await?;
|
||||
|
||||
// 記錄錯誤但不中斷其他模組
|
||||
tracing::warn!("Processor {} failed: {}", module, e);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 實作結構
|
||||
|
||||
### 6.1 目錄結構
|
||||
|
||||
```
|
||||
src/
|
||||
├── worker/
|
||||
│ ├── mod.rs # Worker 模組導出
|
||||
│ ├── config.rs # Worker 配置
|
||||
│ ├── worker.rs # Worker 主邏輯
|
||||
│ ├── processor.rs # Processor 執行器
|
||||
│ ├── queue.rs # Job 佇列管理
|
||||
│ └── progress.rs # 進度追蹤
|
||||
├── api/
|
||||
│ └── server.rs # 更新 Register API
|
||||
└── main.rs # 新增 worker 命令
|
||||
```
|
||||
|
||||
### 6.2 核心模組
|
||||
|
||||
#### 6.2.1 Worker Config (`src/worker/config.rs`)
|
||||
|
||||
```rust
|
||||
pub struct WorkerConfig {
|
||||
pub max_concurrent: usize,
|
||||
pub poll_interval_secs: u64,
|
||||
pub enabled: bool,
|
||||
}
|
||||
|
||||
impl WorkerConfig {
|
||||
pub fn from_env() -> Self { ... }
|
||||
}
|
||||
```
|
||||
|
||||
#### 6.2.2 Worker Loop (`src/worker/worker.rs`)
|
||||
|
||||
```rust
|
||||
pub struct JobWorker {
|
||||
db: PostgresDb,
|
||||
redis: RedisCache,
|
||||
config: WorkerConfig,
|
||||
semaphore: Arc<Semaphore>,
|
||||
}
|
||||
|
||||
impl JobWorker {
|
||||
pub async fn run(&self) -> Result<()> {
|
||||
loop {
|
||||
if self.config.enabled {
|
||||
self.process_pending_jobs().await?;
|
||||
}
|
||||
tokio::time::sleep(Duration::from_secs(self.config.poll_interval_secs)).await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn process_pending_jobs(&self) -> Result<()> {
|
||||
// 1. 檢查並發數
|
||||
// 2. 取得 pending jobs
|
||||
// 3. 分配給 worker pool
|
||||
// 4. 並行執行 processors
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 6.2.3 Processor Pool (`src/worker/processor.rs`)
|
||||
|
||||
```rust
|
||||
pub struct ProcessorPool {
|
||||
max_concurrent: usize,
|
||||
}
|
||||
|
||||
impl ProcessorPool {
|
||||
pub async fn execute(&self, job: &Job, video: &Video) -> Result<ProcessorResult> {
|
||||
// 根據 videos 表決定需要執行哪些 processor
|
||||
// 並行執行最多 2 個
|
||||
// 處理失敗但不中斷其他 processor
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. API 端點設計
|
||||
|
||||
### 7.1 新增端點
|
||||
|
||||
| 端點 | 方法 | 說明 |
|
||||
|------|------|------|
|
||||
| `/api/v1/jobs` | GET | 列出所有 jobs |
|
||||
| `/api/v1/jobs/:uuid` | GET | 取得特定 job 詳細 |
|
||||
| `/api/v1/jobs/:uuid/retry` | POST | 重試失敗的 processor |
|
||||
| `/api/v1/jobs/:uuid/cancel` | POST | 取消 job |
|
||||
|
||||
### 7.2 端點詳情
|
||||
|
||||
#### GET /api/v1/jobs
|
||||
|
||||
```json
|
||||
Response:
|
||||
{
|
||||
"jobs": [
|
||||
{
|
||||
"id": 1,
|
||||
"uuid": "abc123def456",
|
||||
"status": "running",
|
||||
"progress": 60,
|
||||
"processors": ["asr", "cut", "yolo", "ocr", "face", "pose"],
|
||||
"completed": ["asr", "cut", "yolo"],
|
||||
"failed": []
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### GET /api/v1/jobs/:uuid
|
||||
|
||||
```json
|
||||
Response:
|
||||
{
|
||||
"id": 1,
|
||||
"uuid": "abc123def456",
|
||||
"video_id": 10,
|
||||
"status": "running",
|
||||
"processors": {
|
||||
"asr": {"status": "completed", "progress": 100},
|
||||
"cut": {"status": "completed", "progress": 100},
|
||||
"yolo": {"status": "running", "progress": 45, "current": 5000, "total": 11000},
|
||||
"ocr": {"status": "pending"},
|
||||
"face": {"status": "pending"},
|
||||
"pose": {"status": "pending"}
|
||||
},
|
||||
"created_at": "2026-03-24T12:00:00Z",
|
||||
"started_at": "2026-03-24T12:01:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Redis Key 設計
|
||||
|
||||
### 8.1 現有 Key 保持
|
||||
|
||||
```bash
|
||||
momentry:job:{uuid} # Job Hash
|
||||
momentry:job:{uuid}:processor:{name} # Processor Hash
|
||||
momentry:progress:{uuid} # Pub/Sub Channel
|
||||
momentry:jobs:active # Set: 運行中 UUIDs
|
||||
momentry:jobs:completed # Set: 完成 UUIDs
|
||||
momentry:jobs:failed # Set: 失敗 UUIDs
|
||||
```
|
||||
|
||||
### 8.2 進度更新時序
|
||||
|
||||
```
|
||||
Processor 執行
|
||||
│
|
||||
├─► 每秒更新 Redis Hash (即時)
|
||||
│
|
||||
├─► 每 10% 或完成時更新 PostgreSQL (持久)
|
||||
│
|
||||
└─► 失敗時立即更新 PostgreSQL (錯誤記錄)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. 實作順序
|
||||
|
||||
### Phase 1: 資料庫遷移
|
||||
|
||||
| 任務 | 說明 |
|
||||
|------|------|
|
||||
| 1.1 | 建立 `migrations/003_job_worker.sql` |
|
||||
| 1.2 | 更新 `postgres_db.rs` 對應的 struct |
|
||||
| 1.3 | 執行 migration 驗證 |
|
||||
|
||||
### Phase 2: Worker 框架
|
||||
|
||||
| 任務 | 說明 |
|
||||
|------|------|
|
||||
| 2.1 | 建立 `src/worker/mod.rs` |
|
||||
| 2.2 | 建立 `src/worker/config.rs` |
|
||||
| 2.3 | 建立 `src/worker/worker.rs` |
|
||||
| 2.4 | 建立 `src/worker/processor.rs` |
|
||||
|
||||
### Phase 3: Register API 整合
|
||||
|
||||
| 任務 | 說明 |
|
||||
|------|------|
|
||||
| 3.1 | 修改 `src/api/server.rs` 的 register 函數 |
|
||||
| 3.2 | 加入建立 monitor_jobs 的邏輯 |
|
||||
| 3.3 | 更新 videos 表 status 欄位 |
|
||||
|
||||
### Phase 4: Processor 執行
|
||||
|
||||
| 任務 | 說明 |
|
||||
|------|------|
|
||||
| 4.1 | 實作 processor 並行執行(最多 2 個) |
|
||||
| 4.2 | 實作失敗處理(保存部分結果) |
|
||||
| 4.3 | 實作 checkpoint 恢復 |
|
||||
|
||||
### Phase 5: 進度追蹤
|
||||
|
||||
| 任務 | 說明 |
|
||||
|------|------|
|
||||
| 5.1 | Redis Pub/Sub 整合 |
|
||||
| 5.2 | PostgreSQL 定期同步 |
|
||||
| 5.3 | API 進度端點更新 |
|
||||
|
||||
### Phase 6: API 端點
|
||||
|
||||
| 任務 | 說明 |
|
||||
|------|------|
|
||||
| 6.1 | GET /api/v1/jobs |
|
||||
| 6.2 | GET /api/v1/jobs/:uuid |
|
||||
| 6.3 | POST /api/v1/jobs/:uuid/retry |
|
||||
| 6.4 | POST /api/v1/jobs/:uuid/cancel |
|
||||
|
||||
### Phase 7: CLI 命令
|
||||
|
||||
| 任務 | 說明 |
|
||||
|------|------|
|
||||
| 7.1 | `cargo run -- worker` 命令 |
|
||||
| 7.2 | Worker 啟動/停止/狀態顯示 |
|
||||
| 7.3 | launchd plist 設定 |
|
||||
|
||||
### Phase 8: 測試
|
||||
|
||||
| 任務 | 說明 |
|
||||
|------|------|
|
||||
| 8.1 | 單元測試 |
|
||||
| 8.2 | 端到端測試 |
|
||||
| 8.3 | 失敗處理測試 |
|
||||
| 8.4 | 並行執行測試 |
|
||||
|
||||
---
|
||||
|
||||
## 10. CLI 命令
|
||||
|
||||
### 10.1 Worker 命令
|
||||
|
||||
```bash
|
||||
# 啟動 worker
|
||||
cargo run -- worker
|
||||
|
||||
# 顯示 worker 幫助
|
||||
cargo run -- worker --help
|
||||
```
|
||||
|
||||
### 10.2 環境變數
|
||||
|
||||
```bash
|
||||
# Worker 配置
|
||||
export MOMENTRY_MAX_CONCURRENT=2
|
||||
export MOMENTRY_POLL_INTERVAL=5
|
||||
export MOMENTRY_WORKER_ENABLED=true
|
||||
|
||||
# 現有環境變數
|
||||
export DATABASE_URL=postgres://accusys@localhost:5432/momentry
|
||||
export REDIS_URL=redis://:accusys@localhost:6379
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 11. 預估工時
|
||||
|
||||
| Phase | 任務 | 預估工時 |
|
||||
|-------|------|----------|
|
||||
| 1 | 資料庫遷移 | 2h |
|
||||
| 2 | Worker 框架 | 4h |
|
||||
| 3 | Register API 整合 | 2h |
|
||||
| 4 | Processor 執行 | 4h |
|
||||
| 5 | 進度追蹤 | 2h |
|
||||
| 6 | API 端點 | 3h |
|
||||
| 7 | CLI 命令 | 2h |
|
||||
| 8 | 測試 | 4h |
|
||||
| **總計** | | **23h** |
|
||||
|
||||
---
|
||||
|
||||
## 12. 參考文件
|
||||
|
||||
| 文件 | 用途 |
|
||||
|------|------|
|
||||
| `docs_v1.0/OPERATIONS/MOMENTRY_CORE_MONITORING.md` | 監控系統規範 |
|
||||
| `docs_v1.0/REFERENCE/MOMENTRY_CORE_REDIS_KEYS.md` | Redis Key 設計 |
|
||||
| `docs_v1.0/ARCHITECTURE/PROCESSING_PIPELINE.md` | 處理流程 |
|
||||
| `docs_v1.0/ARCHITECTURE/CHUNK_DESIGN.md` | 資料庫設計 |
|
||||
| `docs_v1.0/REFERENCE/API_REFERENCE.md` | API 參考 |
|
||||
|
||||
---
|
||||
|
||||
## 13. 附錄
|
||||
|
||||
### A. 狀態機
|
||||
|
||||
```
|
||||
┌──────────────┐
|
||||
│ PENDING │
|
||||
└──────┬───────┘
|
||||
│ register 後
|
||||
▼
|
||||
┌──────────────┐
|
||||
┌─────▶│ PROCESSING │◀──────┐
|
||||
│ └──────┬───────┘ │
|
||||
│ │ │
|
||||
部分失敗 all completed 全部失敗
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌──────────┐ ┌──────────┐ ┌──────────┐
|
||||
│ PARTIAL │ │COMPLETED │ │ FAILED │
|
||||
└──────────┘ └──────────┘ └──────────┘
|
||||
```
|
||||
|
||||
### B. videos 表 status 欄位
|
||||
|
||||
| 值 | 說明 |
|
||||
|------|------|
|
||||
| `pending` | 已註冊,等待處理 |
|
||||
| `processing` | 處理中 |
|
||||
| `completed` | 所有處理完成 |
|
||||
| `failed` | 處理失敗 |
|
||||
|
||||
### C. processor_results 表 status 欄位
|
||||
|
||||
| 值 | 說明 |
|
||||
|------|------|
|
||||
| `pending` | 等待執行 |
|
||||
| `running` | 執行中 |
|
||||
| `completed` | 執行成功 |
|
||||
| `failed` | 執行失敗 |
|
||||
| `skipped` | 跳過(如檔案已存在) |
|
||||
@@ -0,0 +1,800 @@
|
||||
---
|
||||
document_type: "architecture_design"
|
||||
service: "MOMENTRY_CORE"
|
||||
title: "Momentry 系統自動化安裝計劃"
|
||||
date: "2026-03-23"
|
||||
version: "V1.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
tags:
|
||||
- "momentry"
|
||||
- "系統自動化安裝計劃"
|
||||
ai_query_hints:
|
||||
- "查詢 Momentry 系統自動化安裝計劃 的內容"
|
||||
- "Momentry 系統自動化安裝計劃 的主要目的是什麼?"
|
||||
- "如何操作或實施 Momentry 系統自動化安裝計劃?"
|
||||
---
|
||||
|
||||
# Momentry 系統自動化安裝計劃
|
||||
|
||||
> **計劃階段** - 僅供討論,尚未執行
|
||||
> **建立時間**: 2026-03-23
|
||||
> **目標**: Thunderbolt NVMe 外開機完整安裝
|
||||
|
||||
---
|
||||
|
||||
## 系統概述
|
||||
|
||||
### 當前環境
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| **主控機** | Mac mini (M4, 16GB RAM) |
|
||||
| **作業系統** | macOS 26.3.1 (Tahoe) |
|
||||
| **儲存** | Thunderbolt NVMe (2TB) |
|
||||
| **用途** | 開機碟 + 完整 Momentry 系統 |
|
||||
|
||||
### 目標環境
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| **目標主機** | 其他 Mac (Intel 或 Apple Silicon) |
|
||||
| **安裝方式** | Thunderbolt NVMe 外接開機 |
|
||||
| **連接方式** | Thunderbolt 3/4 |
|
||||
| **控制方式** | SSH 遠端管理 |
|
||||
|
||||
---
|
||||
|
||||
## 系統架構
|
||||
|
||||
### 服務列表
|
||||
|
||||
| 服務 | 版本 | 用途 | Port |
|
||||
|------|------|------|------|
|
||||
| **PostgreSQL** | 18.1 | 主資料庫、n8n 資料庫 | 5432 |
|
||||
| **MongoDB** | 8.0 | 文件資料庫 | 27017 |
|
||||
| **MariaDB** | 11.4 | WordPress 資料庫 | 3306 |
|
||||
| **Redis** | 7.x | 快取、佇列 | 6379 |
|
||||
| **Qdrant** | 1.7.x | 向量資料庫 | 6333 |
|
||||
| **Ollama** | 0.13.5 | 本地 LLM | 11434 |
|
||||
| **Caddy** | 2.x | 反向代理 | 80/443 |
|
||||
| **Gitea** | 1.21 | Git 服務 | 3000 |
|
||||
| **PHP-FPM** | 8.5 | WordPress | 9000 |
|
||||
| **n8n** | 2.3.5 | 工作流程自動化 | 5678 |
|
||||
| **RustDesk** | hbbs/hbbr | 遠端桌面 | 21115-21119 |
|
||||
| **SFTPGo** | 2.x | SFTP 服務 | 2022 |
|
||||
| **Momentry Core** | 0.1.0 | 影片處理核心 | 3002 |
|
||||
| **Prometheus** | 3.9.1 | 監控 | 9090 |
|
||||
|
||||
### 目錄結構
|
||||
|
||||
```
|
||||
/Volumes/Momentry/
|
||||
├── System/
|
||||
│ └── macOS/ # macOS 系統
|
||||
├── Applications/
|
||||
│ └── Homebrew/ # Homebrew 應用程式
|
||||
├── momentry/
|
||||
│ ├── var/ # 資料目錄
|
||||
│ │ ├── postgresql/ # PostgreSQL 資料
|
||||
│ │ ├── mongodb/ # MongoDB 資料
|
||||
│ │ ├── mariadb/ # MariaDB 資料
|
||||
│ │ ├── redis/ # Redis 資料
|
||||
│ │ ├── qdrant/ # Qdrant 資料
|
||||
│ │ ├── n8n/ # n8n 資料
|
||||
│ │ ├── ollama/ # Ollama 模型
|
||||
│ │ └── ...
|
||||
│ ├── etc/ # 配置檔案
|
||||
│ │ ├── Caddyfile
|
||||
│ │ ├── gitea/
|
||||
│ │ ├── php/
|
||||
│ │ └── ...
|
||||
│ ├── log/ # 日誌
|
||||
│ ├── scripts/ # 管理腳本
|
||||
│ └── backup/ # 備份
|
||||
├── momentry_core/ # Rust 原始碼
|
||||
└── momentry_dashboard/ # Web Dashboard
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 階段一:前置準備
|
||||
|
||||
### 1.1 收集目標主機資訊
|
||||
|
||||
```bash
|
||||
# 需要收集的資訊
|
||||
- Mac 型號 (Intel/Apple Silicon)
|
||||
- macOS 版本
|
||||
- Thunderbolt 版本 (3/4)
|
||||
- 可用記憶體
|
||||
- 目標磁碟代號 (diskX)
|
||||
- 網路配置 (DHCP/固定 IP)
|
||||
```
|
||||
|
||||
### 1.2 準備 Thunderbolt NVMe
|
||||
|
||||
```bash
|
||||
# 檢查 Thunderbolt NVMe
|
||||
diskutil list external
|
||||
|
||||
# 預期輸出:
|
||||
# /dev/diskX (external, physical):
|
||||
# NAME TYPE SIZE
|
||||
# Thunderbolt NVMe ...
|
||||
```
|
||||
|
||||
### 1.3 準備主控機腳本
|
||||
|
||||
```bash
|
||||
# 主控機需要準備的腳本
|
||||
~/momentry/setup/
|
||||
├── 01_prepare_disk.sh
|
||||
├── 02_install_macos.sh
|
||||
├── 03_install_homebrew.sh
|
||||
├── 04_install_dependencies.sh
|
||||
├── 05_install_services.sh
|
||||
├── 06_install_momentry.sh
|
||||
├── 07_configure_network.sh
|
||||
├── 08_start_services.sh
|
||||
└── utils/
|
||||
├── common.sh
|
||||
├── backup.sh
|
||||
└── monitor.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 階段二:Thunderbolt NVMe 準備
|
||||
|
||||
### 2.1 分割磁碟方案 A(推薦)
|
||||
|
||||
```bash
|
||||
# 磁碟分割配置
|
||||
diskutil partitionDisk /dev/diskX \
|
||||
GPT \
|
||||
"APFS System" APFS "Momentry System" 200G \
|
||||
"APFS Data" APFS "Momentry Data" 1.8T
|
||||
```
|
||||
|
||||
### 2.2 分割磁碟方案 B(最小化)
|
||||
|
||||
```bash
|
||||
# 統一 APFS 容器
|
||||
diskutil partitionDisk /dev/diskX \
|
||||
GPT \
|
||||
APFS "Momentry" 100%
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 階段三:安裝 macOS
|
||||
|
||||
### 3.1 建立 macOS 安裝碟
|
||||
|
||||
```bash
|
||||
# 下載 macOS Sonoma (或最新版本)
|
||||
softwareupdate --fetch-full-installer --full-installer-version 14.0
|
||||
|
||||
# 建立可開機安裝碟
|
||||
sudo /Applications/Install\ macOS\ Sonoma.app/Contents/Resources/createinstallinstmedi \
|
||||
--volume /Volumes/Momentry \
|
||||
--nointeraction
|
||||
```
|
||||
|
||||
### 3.2 安裝 macOS 到 Thunderbolt NVMe
|
||||
|
||||
**兩種方法:**
|
||||
|
||||
#### 方法 A: 復原模式安裝
|
||||
1. 連接 Thunderbolt NVMe
|
||||
2. 重啟目標主機,按住Option鍵
|
||||
3. 選擇 Thunderbolt NVMe 開機
|
||||
4. 進入 Recovery Mode (Command+R)
|
||||
5. 使用 Disk Utility 格式化目標磁碟
|
||||
6. 安裝 macOS
|
||||
|
||||
#### 方法 B: ASR 複製(建議)
|
||||
```bash
|
||||
# 從主控機執行
|
||||
# 將現有系統複製到目標磁碟
|
||||
sudo asr restore \
|
||||
--source /Volumes/Macintosh\ HD \
|
||||
--target /Volumes/Momentry \
|
||||
--erase --noprompt
|
||||
```
|
||||
|
||||
### 3.3 設定 macOS
|
||||
|
||||
```bash
|
||||
# 自動化設定腳本
|
||||
./setup/scripts/03_install_homebrew.sh
|
||||
```
|
||||
|
||||
**設定項目:**
|
||||
- 電腦名稱:`momentry-<serial>`
|
||||
- 使用者帳號:`momentry` (管理員)
|
||||
- SSH 遠端登入:啟用
|
||||
- 螢幕鎖定:關閉
|
||||
- 節能設定:永不休眠
|
||||
|
||||
---
|
||||
|
||||
## 階段四:安裝 Homebrew
|
||||
|
||||
### 4.1 安裝 Homebrew
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# 04_install_homebrew.sh
|
||||
|
||||
# 檢查架構
|
||||
ARCH=$(uname -m)
|
||||
|
||||
if [ "$ARCH" = "arm64" ]; then
|
||||
# Apple Silicon
|
||||
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||
echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zprofile
|
||||
eval "$(/opt/homebrew/bin/brew shellenv)"
|
||||
elif [ "$ARCH" = "x86_64" ]; then
|
||||
# Intel
|
||||
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||
echo 'eval "$(/usr/local/bin/brew shellenv)"' >> ~/.zprofile
|
||||
eval "$(/usr/local/bin/brew shellenv)"
|
||||
fi
|
||||
|
||||
# 驗證
|
||||
brew --version
|
||||
```
|
||||
|
||||
### 4.2 安裝基礎工具
|
||||
|
||||
```bash
|
||||
# 基礎開發工具
|
||||
brew install \
|
||||
git \
|
||||
curl \
|
||||
wget \
|
||||
jq \
|
||||
yq \
|
||||
tree \
|
||||
htop \
|
||||
tmux \
|
||||
zsh \
|
||||
zsh-completions
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 階段五:安裝服務
|
||||
|
||||
### 5.1 安裝資料庫服務
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# 05_install_services.sh
|
||||
|
||||
# PostgreSQL
|
||||
brew install postgresql@18
|
||||
brew services start postgresql@18
|
||||
|
||||
# MongoDB
|
||||
brew tap mongodb/brew
|
||||
brew install mongodb-community
|
||||
brew services start mongodb-community
|
||||
|
||||
# MariaDB
|
||||
brew install mariadb
|
||||
brew services start mariadb
|
||||
|
||||
# Redis
|
||||
brew install redis
|
||||
brew services start redis
|
||||
|
||||
# Qdrant (需要 Cargo)
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
cargo install qdrant
|
||||
```
|
||||
|
||||
### 5.2 安裝應用服務
|
||||
|
||||
```bash
|
||||
# Ollama
|
||||
brew install ollama
|
||||
brew services start ollama
|
||||
|
||||
# Caddy
|
||||
brew install caddy
|
||||
brew services start caddy
|
||||
|
||||
# Gitea
|
||||
brew install gitea
|
||||
brew services start gitea
|
||||
|
||||
# PHP
|
||||
brew install php
|
||||
brew services start php
|
||||
|
||||
# n8n
|
||||
brew install n8n
|
||||
brew services start n8n
|
||||
```
|
||||
|
||||
### 5.3 Launchd 服務配置
|
||||
|
||||
```xml
|
||||
<!-- /Library/LaunchDaemons/com.momentry.postgresql.plist -->
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>com.momentry.postgresql</string>
|
||||
<key>UserName</key>
|
||||
<string>momentry</string>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>/opt/homebrew/opt/postgresql@18/bin/postgres</string>
|
||||
<string>-D</string>
|
||||
<string>/Volumes/Momentry/momentry/var/postgresql</string>
|
||||
</array>
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
<key>KeepAlive</key>
|
||||
<true/>
|
||||
<key>StandardOutPath</key>
|
||||
<string>/Volumes/Momentry/momentry/log/postgresql.log</string>
|
||||
<key>StandardErrorPath</key>
|
||||
<string>/Volumes/Momentry/momentry/log/postgresql.error.log</string>
|
||||
</dict>
|
||||
</plist>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 階段六:安裝 Momentry Core
|
||||
|
||||
### 6.1 複製原始碼
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# 06_install_momentry.sh
|
||||
|
||||
# 建立 Momentry 目錄
|
||||
mkdir -p /Volumes/Momentry/momentry/{var,etc,log,scripts,backup}
|
||||
mkdir -p /Volumes/Momentry/momentry_core
|
||||
|
||||
# 複製原始碼
|
||||
rsync -av \
|
||||
--exclude 'target' \
|
||||
--exclude '.git' \
|
||||
--exclude 'node_modules' \
|
||||
/Users/accusys/momentry_core_0.1/ \
|
||||
/Volumes/Momentry/momentry_core/
|
||||
|
||||
# 編譯 Rust 專案
|
||||
cd /Volumes/Momentry/momentry_core
|
||||
cargo build --release
|
||||
```
|
||||
|
||||
### 6.2 初始化資料庫
|
||||
|
||||
```bash
|
||||
# 建立 PostgreSQL 資料庫
|
||||
psql -U postgres <<EOF
|
||||
CREATE DATABASE momentry;
|
||||
CREATE DATABASE n8n;
|
||||
CREATE DATABASE video_register;
|
||||
CREATE USER momentry WITH PASSWORD 'momentry_password';
|
||||
CREATE USER n8n WITH PASSWORD 'n8n_password';
|
||||
GRANT ALL PRIVILEGES ON DATABASE momentry TO momentry;
|
||||
GRANT ALL PRIVILEGES ON DATABASE n8n TO n8n;
|
||||
EOF
|
||||
|
||||
# 執行 migration
|
||||
cd /Volumes/Momentry/momentry_core
|
||||
sqlx migrate run
|
||||
```
|
||||
|
||||
### 6.3 配置環境變數
|
||||
|
||||
```bash
|
||||
# ~/.zshrc 或 ~/.bash_profile
|
||||
export DATABASE_URL="postgres://momentry:momentry_password@localhost:5432/momentry"
|
||||
export REDIS_URL="redis://:momentry_password@localhost:6379"
|
||||
export QDRANT_URL="http://localhost:6333"
|
||||
export MONGODB_URI="mongodb://localhost:27017/momentry"
|
||||
export MOMENTRY_OUTPUT_DIR="/Volumes/Momentry/momentry/var/output"
|
||||
export MOMENTRY_LOG_LEVEL="info"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 階段七:網路配置
|
||||
|
||||
### 7.1 設定固定 IP(可選)
|
||||
|
||||
```bash
|
||||
# 網路配置腳本
|
||||
#!/bin/bash
|
||||
# 07_configure_network.sh
|
||||
|
||||
# 取得網路介面
|
||||
INTERFACE=$(networksetup -listallnetworkservices | grep "Thunderbolt")
|
||||
|
||||
# 設定固定 IP
|
||||
networksetup -setmanual "$INTERFACE" \
|
||||
192.168.1.100 \
|
||||
255.255.255.0 \
|
||||
192.168.1.1
|
||||
|
||||
# 設定 DNS
|
||||
networksetup -setdnsservers "$INTERFACE" \
|
||||
8.8.8.8 \
|
||||
8.8.4.4
|
||||
```
|
||||
|
||||
### 7.2 配置防火牆
|
||||
|
||||
```bash
|
||||
# 開放服務端口
|
||||
# 使用 macOS Firewall 或 pfctl
|
||||
```
|
||||
|
||||
### 7.3 設定 SSH 金鑰
|
||||
|
||||
```bash
|
||||
# 產生 SSH 金鑰對
|
||||
ssh-keygen -t ed25519 -C "momentry@$(hostname)"
|
||||
|
||||
# 複製公鑰到目標主機
|
||||
ssh-copy-id momentry@target-host
|
||||
|
||||
# 主控機 SSH 配置
|
||||
# ~/.ssh/config
|
||||
Host momentry-target
|
||||
HostName 192.168.1.100
|
||||
User momentry
|
||||
IdentityFile ~/.ssh/id_ed25519
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 階段八:啟動服務
|
||||
|
||||
### 8.1 啟動順序
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# 08_start_services.sh
|
||||
|
||||
# 1. 基礎服務
|
||||
launchctl load /Library/LaunchDaemons/com.momentry.postgresql.plist
|
||||
launchctl load /Library/LaunchDaemons/com.momentry.mongodb.plist
|
||||
launchctl load /Library/LaunchDaemons/com.momentry.mariadb.plist
|
||||
launchctl load /Library/LaunchDaemons/com.momentry.redis.plist
|
||||
|
||||
sleep 10
|
||||
|
||||
# 2. 向量資料庫
|
||||
launchctl load /Library/LaunchDaemons/com.momentry.qdrant.plist
|
||||
|
||||
sleep 5
|
||||
|
||||
# 3. 應用服務
|
||||
launchctl load /Library/LaunchDaemons/com.momentry.ollama.plist
|
||||
launchctl load /Library/LaunchDaemons/com.momentry.caddy.plist
|
||||
|
||||
sleep 5
|
||||
|
||||
# 4. 其他服務
|
||||
launchctl load /Library/LaunchDaemons/com.momentry.gitea.plist
|
||||
launchctl load /Library/LaunchDaemons/com.momentry.php.plist
|
||||
launchctl load /Library/LaunchDaemons/com.momentry.n8n.main.plist
|
||||
launchctl load /Library/LaunchDaemons/com.momentry.n8n.worker.plist
|
||||
|
||||
# 5. Momentry Core
|
||||
launchctl load /Library/LaunchDaemons/com.momentry.sftpgo.plist
|
||||
launchctl load /Library/LaunchDaemons/com.momentry.rustdesk.hbbs.plist
|
||||
launchctl load /Library/LaunchDaemons/com.momentry.rustdesk.hbbr.plist
|
||||
```
|
||||
|
||||
### 8.2 驗證服務
|
||||
|
||||
```bash
|
||||
# 檢查所有服務狀態
|
||||
function check_services() {
|
||||
services=(
|
||||
"postgresql"
|
||||
"mongodb"
|
||||
"mariadb"
|
||||
"redis"
|
||||
"qdrant"
|
||||
"ollama"
|
||||
"caddy"
|
||||
"gitea"
|
||||
"php"
|
||||
"n8n"
|
||||
"sftpgo"
|
||||
)
|
||||
|
||||
for service in "${services[@]}"; do
|
||||
if launchctl list | grep "$service" | grep -q "running"; then
|
||||
echo "✅ $service: Running"
|
||||
else
|
||||
echo "❌ $service: Not running"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
check_services
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 階段九:備份與還原
|
||||
|
||||
### 9.1 備份策略
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# 備份腳本
|
||||
|
||||
BACKUP_DIR="/Volumes/Momentry/backup/$(date +%Y%m%d)"
|
||||
|
||||
# 1. PostgreSQL 備份
|
||||
pg_dump -U momentry momentry > "$BACKUP_DIR/momentry.sql"
|
||||
pg_dump -U n8n n8n > "$BACKUP_DIR/n8n.sql"
|
||||
|
||||
# 2. MongoDB 備份
|
||||
mongodump --out "$BACKUP_DIR/mongodb"
|
||||
|
||||
# 3. Redis 備份
|
||||
redis-cli BGSAVE
|
||||
cp /Volumes/Momentry/var/redis/dump.rdb "$BACKUP_DIR/redis.rdb"
|
||||
|
||||
# 4. Qdrant 備份
|
||||
curl -X POST http://localhost:6333/collections/accusysdb/snapshots
|
||||
|
||||
# 5. 配置檔案備份
|
||||
tar -czf "$BACKUP_DIR/config.tar.gz" \
|
||||
/Volumes/Momentry/momentry/etc/
|
||||
```
|
||||
|
||||
### 9.2 自動備份 Cron
|
||||
|
||||
```bash
|
||||
# crontab -e
|
||||
0 2 * * * /Volumes/Momentry/scripts/backup.sh
|
||||
0 3 * * 0 /Volumes/Momentry/scripts/backup_full.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 階段十:監控與維護
|
||||
|
||||
### 10.1 健康檢查腳本
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# health_check.sh
|
||||
|
||||
# 檢查所有服務
|
||||
check_postgresql() {
|
||||
pg_isready -q && echo "✅ PostgreSQL" || echo "❌ PostgreSQL"
|
||||
}
|
||||
|
||||
check_mongodb() {
|
||||
mongosh --eval "db.stats()" > /dev/null 2>&1 && echo "✅ MongoDB" || echo "❌ MongoDB"
|
||||
}
|
||||
|
||||
check_redis() {
|
||||
redis-cli ping > /dev/null 2>&1 && echo "✅ Redis" || echo "❌ Redis"
|
||||
}
|
||||
|
||||
check_qdrant() {
|
||||
curl -s http://localhost:6333/health && echo "✅ Qdrant" || echo "❌ Qdrant"
|
||||
}
|
||||
|
||||
check_n8n() {
|
||||
curl -s http://localhost:5678/api/v1/workflows > /dev/null 2>&1 && echo "✅ n8n" || echo "❌ n8n"
|
||||
}
|
||||
|
||||
check_momentry() {
|
||||
curl -s http://localhost:3002/api/v1/videos > /dev/null 2>&1 && echo "✅ Momentry" || echo "❌ Momentry"
|
||||
}
|
||||
```
|
||||
|
||||
### 10.2 日誌輪替
|
||||
|
||||
```bash
|
||||
# 新聞日誌配置
|
||||
/Volumes/Momentry/momentry/log/*.log {
|
||||
daily
|
||||
rotate 7
|
||||
compress
|
||||
missingok
|
||||
notifempty
|
||||
create 644 momentry staff
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 自動化腳本架構
|
||||
|
||||
### 主控腳本:部署控制器
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# deploy_controller.sh
|
||||
# 用於從主控機部署到目標主機
|
||||
|
||||
set -e
|
||||
|
||||
# 配置
|
||||
TARGET_HOST="momentry@192.168.1.100"
|
||||
TARGET_DISK="/dev/disk2"
|
||||
|
||||
# 顏色定義
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
function log_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
function log_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
function log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# 階段執行
|
||||
function run_stage() {
|
||||
local stage=$1
|
||||
local script=$2
|
||||
|
||||
log_info "執行階段: $stage..."
|
||||
ssh "$TARGET_HOST" "bash /Volumes/Momentry/scripts/$script"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
log_info "✅ 階段完成: $stage"
|
||||
else
|
||||
log_error "❌ 階段失敗: $stage"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 主程序
|
||||
log_info "開始 Momentry 系統部署..."
|
||||
|
||||
# 執行各階段
|
||||
run_stage "磁碟準備" "01_prepare_disk.sh"
|
||||
run_stage "macOS 安裝" "02_install_macos.sh"
|
||||
run_stage "Homebrew 安裝" "03_install_homebrew.sh"
|
||||
run_stage "依賴安裝" "04_install_dependencies.sh"
|
||||
run_stage "服務安裝" "05_install_services.sh"
|
||||
run_stage "Momentry 安裝" "06_install_momentry.sh"
|
||||
run_stage "網路配置" "07_configure_network.sh"
|
||||
run_stage "啟動服務" "08_start_services.sh"
|
||||
|
||||
log_info "✅ 部署完成!"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 待確認事項
|
||||
|
||||
### 需要與使用者確認
|
||||
|
||||
1. **目標主機型號**
|
||||
- Intel Mac 或 Apple Silicon?
|
||||
- Thunderbolt 版本 (3/4)?
|
||||
|
||||
2. **網路配置**
|
||||
- DHCP 或固定 IP?
|
||||
- 目標 IP 網段?
|
||||
|
||||
3. **磁碟配置**
|
||||
- 分割方案 A (200G 系統 + 1.8T 資料)?
|
||||
- 分割方案 B (統一磁碟區)?
|
||||
|
||||
4. **服務需求**
|
||||
- 需要安裝全部服務?
|
||||
- 還是選擇性安裝?
|
||||
|
||||
5. **備份策略**
|
||||
- 本地備份?
|
||||
- 遠端備份?
|
||||
- 備份頻率?
|
||||
|
||||
6. **監控需求**
|
||||
- Prometheus + Grafana?
|
||||
- 簡單腳本監控?
|
||||
|
||||
---
|
||||
|
||||
## 預估時間
|
||||
|
||||
| 階段 | 預估時間 | 備註 |
|
||||
|------|---------|------|
|
||||
| 前置準備 | 30 分鐘 | 收集資訊、準備腳本 |
|
||||
| 磁碟準備 | 10 分鐘 | 分割格式化 |
|
||||
| macOS 安裝 | 30-60 分鐘 | 視 USB 速度 |
|
||||
| Homebrew 安裝 | 15 分鐘 | 下載速度 |
|
||||
| 服務安裝 | 60-90 分鐘 | 多個服務 |
|
||||
| Momentry 安裝 | 20 分鐘 | 編譯 Rust |
|
||||
| 網路配置 | 10 分鐘 | 固定 IP |
|
||||
| 服務啟動 | 15 分鐘 | 依序啟動 |
|
||||
| 驗證測試 | 30 分鐘 | 完整測試 |
|
||||
| **總計** | **3-4 小時** | 自動化後可縮短 |
|
||||
|
||||
---
|
||||
|
||||
## 風險與應對
|
||||
|
||||
| 風險 | 機率 | 影響 | 應對措施 |
|
||||
|------|------|------|---------|
|
||||
| Thunderbolt 不相容 | 低 | 高 | 準備多種驅動 |
|
||||
| macOS 安裝失敗 | 低 | 高 | 準備還原方案 |
|
||||
| 服務啟動失敗 | 中 | 中 | 日誌診斷腳本 |
|
||||
| 網路連線問題 | 中 | 中 | 有線網路備援 |
|
||||
| 儲存空間不足 | 低 | 高 | 磁碟空間檢查 |
|
||||
|
||||
---
|
||||
|
||||
## 下一步行動
|
||||
|
||||
1. ✅ 確認目標主機規格
|
||||
2. ✅ 確認 Thunderbolt NVMe 容量
|
||||
3. ✅ 確認網路配置
|
||||
4. ✅ 選擇服務清單
|
||||
5. ✅ 準備安裝腳本
|
||||
6. ✅ 測試腳本執行
|
||||
7. ✅ 正式部署
|
||||
|
||||
---
|
||||
|
||||
## 附錄
|
||||
|
||||
### A. 服務端口對照表
|
||||
|
||||
| 服務 | Port | 協議 |
|
||||
|------|------|------|
|
||||
| PostgreSQL | 5432 | TCP |
|
||||
| MongoDB | 27017 | TCP |
|
||||
| MariaDB | 3306 | TCP |
|
||||
| Redis | 6379 | TCP |
|
||||
| Qdrant API | 6333 | HTTP |
|
||||
| Qdrant gRPC | 6334 | gRPC |
|
||||
| Ollama | 11434 | HTTP |
|
||||
| Caddy HTTP | 80 | HTTP |
|
||||
| Caddy HTTPS | 443 | HTTPS |
|
||||
| Gitea | 3000 | HTTP |
|
||||
| PHP-FPM | 9000 | FastCGI |
|
||||
| n8n | 5678 | HTTP |
|
||||
| SFTPGo | 2022 | SFTP |
|
||||
| RustDesk hbbs | 21115 | TCP |
|
||||
| RustDesk hbbr | 21117 | TCP |
|
||||
| Momentry | 3002 | HTTP |
|
||||
| Prometheus | 9090 | HTTP |
|
||||
|
||||
### B. 環境變數清單
|
||||
|
||||
見 `.env` 範例檔案或 `docs_v1.0/OPERATIONS/MOMENTRY_CORE_MONITORING.md`
|
||||
|
||||
### C. 疑難排解
|
||||
|
||||
見 `docs_v1.0/REFERENCE/PENDING_ISSUES.md`
|
||||
|
||||
---
|
||||
|
||||
**計劃狀態**: 📝 草稿 - 等待使用者確認後執行
|
||||
|
||||
**負責人**: OpenCode AI Assistant
|
||||
|
||||
**最後更新**: 2026-03-23
|
||||
@@ -0,0 +1,549 @@
|
||||
---
|
||||
document_type: "architecture_design"
|
||||
service: "MOMENTRY_CORE"
|
||||
title: "MCP 按需加載策略分析"
|
||||
date: "2026-04-01"
|
||||
version: "V1.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
tags:
|
||||
- "按需加載策略分析"
|
||||
ai_query_hints:
|
||||
- "查詢 MCP 按需加載策略分析 的內容"
|
||||
- "MCP 按需加載策略分析 的主要目的是什麼?"
|
||||
- "如何操作或實施 MCP 按需加載策略分析?"
|
||||
---
|
||||
|
||||
# MCP 按需加載策略分析
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 分析日期 | 2026-04-01 |
|
||||
| 目標 | 節省 token,按需掛載 MCP 服務器 |
|
||||
|
||||
---
|
||||
|
||||
## 問題分析
|
||||
|
||||
### 當前困境
|
||||
|
||||
```
|
||||
每次對話啟動時,所有 MCP 工具定義都會載入到 context:
|
||||
|
||||
例如,當前 session:
|
||||
├─ Gitea MCP: ~80 個工具 → ~15,000 tokens
|
||||
├─ N8N MCP: ~30 個工具 → ~6,000 tokens
|
||||
├─ Playwright MCP: ~25 個工具 → ~5,000 tokens
|
||||
├─ MongoDB MCP: ~25 個工具 → ~5,000 tokens
|
||||
├─ Redis MCP: ~5 個工具 → ~1,000 tokens
|
||||
├─ Postgres MCP: ~1 個工具 → ~200 tokens
|
||||
├─ Sentry MCP: ~20 個工具 → ~4,000 tokens
|
||||
├─ Qdrant MCP: ~2 個工具 → ~400 tokens
|
||||
├─ Filesystem MCP: ~15 個工具 → ~3,000 tokens
|
||||
└─ Context7 MCP: ~2 個工具 → ~400 tokens
|
||||
|
||||
總計: ~205 個工具 → ~40,000 tokens ❌
|
||||
```
|
||||
|
||||
**問題**:
|
||||
- ❌ 每次對話都消耗 ~40k tokens(工具定義)
|
||||
- ❌ 大部分工具用不到
|
||||
- ❌ 浪費 context window
|
||||
- ❌ 降低可用 token 數量
|
||||
|
||||
---
|
||||
|
||||
## 解決方案
|
||||
|
||||
### 方案 1:MCP 配置文件切換 ⭐(推薦)
|
||||
|
||||
**原理**:使用不同的配置文件,按需啟動
|
||||
|
||||
```bash
|
||||
目錄結構:
|
||||
~/.config/claude/
|
||||
├── claude_desktop_config.json # 預設(最小)
|
||||
├── claude_desktop_config.dev.json # 開發模式
|
||||
├── claude_desktop_config.full.json # 完整模式
|
||||
└── claude_desktop_config.minimal.json # 極簡模式
|
||||
```
|
||||
|
||||
#### 實現方式
|
||||
|
||||
**1. 最小配置(日常使用)**
|
||||
|
||||
```json
|
||||
// ~/.config/claude/claude_desktop_config.minimal.json
|
||||
{
|
||||
"mcpServers": {
|
||||
"filesystem": {
|
||||
"command": "mcp-filesystem",
|
||||
"args": ["/Users/accusys/momentry_core_0.1"]
|
||||
},
|
||||
"redis": {
|
||||
"command": "mcp-redis"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Token 消耗**: ~4,000 tokens ✅
|
||||
|
||||
**2. 開發配置(程式開發)**
|
||||
|
||||
```json
|
||||
// ~/.config/claude/claude_desktop_config.dev.json
|
||||
{
|
||||
"mcpServers": {
|
||||
"filesystem": {...},
|
||||
"redis": {...},
|
||||
"gitea": {
|
||||
"command": "gitea-mcp-server",
|
||||
"args": ["--config", "~/.gitea-mcp/config.json"]
|
||||
},
|
||||
"postgres": {...}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Token 消耗**: ~20,000 tokens ✅
|
||||
|
||||
**3. 完整配置(需要所有工具)**
|
||||
|
||||
```json
|
||||
// ~/.config/claude/claude_desktop_config.full.json
|
||||
{
|
||||
"mcpServers": {
|
||||
"filesystem": {...},
|
||||
"redis": {...},
|
||||
"postgres": {...},
|
||||
"mongodb": {...},
|
||||
"gitea": {...},
|
||||
"n8n": {...},
|
||||
"playwright": {...},
|
||||
"sentry": {...},
|
||||
"qdrant": {...},
|
||||
"context7": {...}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Token 消耗**: ~40,000 tokens ⚠️
|
||||
|
||||
#### 切換腳本
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# switch_mcp_config.sh
|
||||
|
||||
CONFIG_DIR="$HOME/.config/claude"
|
||||
CURRENT_CONFIG="$CONFIG_DIR/claude_desktop_config.json"
|
||||
|
||||
case "$1" in
|
||||
minimal)
|
||||
cp "$CONFIG_DIR/claude_desktop_config.minimal.json" "$CURRENT_CONFIG"
|
||||
echo "✅ Switched to minimal config (~4k tokens)"
|
||||
;;
|
||||
dev)
|
||||
cp "$CONFIG_DIR/claude_desktop_config.dev.json" "$CURRENT_CONFIG"
|
||||
echo "✅ Switched to dev config (~20k tokens)"
|
||||
;;
|
||||
full)
|
||||
cp "$CONFIG_DIR/claude_desktop_config.full.json" "$CURRENT_CONFIG"
|
||||
echo "✅ Switched to full config (~40k tokens)"
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {minimal|dev|full}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# 重啟 Claude Desktop
|
||||
osascript -e 'quit app "Claude"'
|
||||
sleep 2
|
||||
open -a "Claude"
|
||||
```
|
||||
|
||||
**使用**:
|
||||
|
||||
```bash
|
||||
# 日常使用(最小 token)
|
||||
./switch_mcp_config.sh minimal
|
||||
|
||||
# 開發模式
|
||||
./switch_mcp_config.sh dev
|
||||
|
||||
# 完整功能
|
||||
./switch_mcp_config.sh full
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 方案 2:環境變數控制
|
||||
|
||||
**原理**:使用環境變數動態啟用 MCP
|
||||
|
||||
```json
|
||||
// ~/.config/claude/claude_desktop_config.json
|
||||
{
|
||||
"mcpServers": {
|
||||
"filesystem": {
|
||||
"command": "mcp-filesystem",
|
||||
"args": ["/Users/accusys/momentry_core_0.1"],
|
||||
"disabled": false
|
||||
},
|
||||
"gitea": {
|
||||
"command": "gitea-mcp-server",
|
||||
"disabled": "${GITEA_MCP_ENABLED:-true}" == "false"
|
||||
},
|
||||
"mongodb": {
|
||||
"command": "mcp-mongodb",
|
||||
"disabled": "${MONGODB_MCP_ENABLED:-true}" == "false"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**⚠️ 限制**:Claude Desktop 可能不支援環境變數
|
||||
|
||||
---
|
||||
|
||||
### 方案 3:輕量級 MCP 代理
|
||||
|
||||
**原理**:使用代理服務器按需轉發
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
MCP Proxy Server - 按需載入 MCP 服務器
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
from typing import Dict, Any
|
||||
|
||||
class MCPProxy:
|
||||
"""MCP 代理服務器"""
|
||||
|
||||
def __init__(self):
|
||||
self.loaded_servers = {}
|
||||
self.available_servers = {
|
||||
"gitea": {"command": "gitea-mcp-server", "token_cost": 15000},
|
||||
"n8n": {"command": "mcp-n8n", "token_cost": 6000},
|
||||
"playwright": {"command": "mcp-playwright", "token_cost": 5000},
|
||||
"mongodb": {"command": "mcp-mongodb", "token_cost": 5000},
|
||||
"sentry": {"command": "mcp-sentry", "token_cost": 4000},
|
||||
}
|
||||
|
||||
async def list_tools(self, only_loaded: bool = True):
|
||||
"""列出可用工具"""
|
||||
if only_loaded:
|
||||
# 只返回已載入的工具(節省 token)
|
||||
tools = []
|
||||
for server_name, server in self.loaded_servers.items():
|
||||
tools.extend(await server.list_tools())
|
||||
return tools
|
||||
else:
|
||||
# 返回所有可用工具(包含未載入的)
|
||||
return [
|
||||
{
|
||||
"name": f"load_{name}",
|
||||
"description": f"Load {name} MCP server",
|
||||
"token_cost": info["token_cost"]
|
||||
}
|
||||
for name, info in self.available_servers.items()
|
||||
]
|
||||
|
||||
async def call_tool(self, tool_name: str, arguments: Dict):
|
||||
"""調用工具"""
|
||||
# 檢查是否需要先載入服務器
|
||||
server_name = self._get_server_name(tool_name)
|
||||
|
||||
if server_name not in self.loaded_servers:
|
||||
print(f"[MCP Proxy] Loading {server_name} on demand...")
|
||||
await self.load_server(server_name)
|
||||
|
||||
# 轉發調用
|
||||
server = self.loaded_servers[server_name]
|
||||
return await server.call_tool(tool_name, arguments)
|
||||
|
||||
async def load_server(self, name: str):
|
||||
"""按需載入 MCP 服務器"""
|
||||
if name in self.loaded_servers:
|
||||
return
|
||||
|
||||
if name not in self.available_servers:
|
||||
raise ValueError(f"Unknown server: {name}")
|
||||
|
||||
# 啟動服務器
|
||||
config = self.available_servers[name]
|
||||
# ... 啟動邏輯
|
||||
|
||||
self.loaded_servers[name] = server
|
||||
print(f"[MCP Proxy] Loaded {name} ({config['token_cost']} tokens)")
|
||||
|
||||
# 啟動代理
|
||||
if __name__ == "__main__":
|
||||
proxy = MCPProxy()
|
||||
# 啟動 MCP 服務器...
|
||||
```
|
||||
|
||||
**優點**:
|
||||
- ✅ 完全按需載入
|
||||
- ✅ 只在調用時才消耗 token
|
||||
- ✅ 透明代理
|
||||
|
||||
**缺點**:
|
||||
- ⚠️ 需要自行實現代理邏輯
|
||||
- ⚠️ 首次調用有延遲
|
||||
|
||||
---
|
||||
|
||||
### 方案 4:Claude Desktop 功能請求
|
||||
|
||||
**原理**:向 Anthropy 提交功能請求
|
||||
|
||||
```markdown
|
||||
Feature Request: Lazy Loading MCP Servers
|
||||
|
||||
Problem:
|
||||
- All MCP tools loaded at startup
|
||||
- Consumes ~40k tokens per session
|
||||
- Most tools unused in typical sessions
|
||||
|
||||
Proposed Solution:
|
||||
1. Add "lazy": true flag to MCP config
|
||||
2. Only load tool definitions when first called
|
||||
3. Show "Load {server_name}" placeholder in tool list
|
||||
|
||||
Example:
|
||||
{
|
||||
"mcpServers": {
|
||||
"gitea": {
|
||||
"command": "gitea-mcp-server",
|
||||
"lazy": true // Only load on demand
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Benefits:
|
||||
- Save ~30-35k tokens per session
|
||||
- Faster startup
|
||||
- Better UX
|
||||
```
|
||||
|
||||
**提交位置**:
|
||||
- GitHub Issues: https://github.com/anthropics/anthropic-cookbook/issues
|
||||
- Discord: Anthropic Community
|
||||
|
||||
---
|
||||
|
||||
## 實際測試
|
||||
|
||||
### Token 消耗對比
|
||||
|
||||
| 配置 | 工具數 | Token 消耗 | 適用場景 |
|
||||
|------|--------|-----------|---------|
|
||||
| **最小** | 20 | ~4,000 | 日常對話 ⭐ |
|
||||
| **開發** | 80 | ~20,000 | 程式開發 |
|
||||
| **完整** | 205 | ~40,000 | 特殊需求 |
|
||||
|
||||
### 節省效果
|
||||
|
||||
```
|
||||
預設(最小配置):
|
||||
每次對話節省: 40,000 - 4,000 = 36,000 tokens
|
||||
100 次對話節省: 3,600,000 tokens ≈ $36 USD
|
||||
|
||||
開發配置:
|
||||
每次對話節省: 40,000 - 20,000 = 20,000 tokens
|
||||
100 次對話節省: 2,000,000 tokens ≈ $20 USD
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 推薦策略
|
||||
|
||||
### 策略 1:配置文件分離(立即可用)⭐
|
||||
|
||||
```bash
|
||||
# 1. 創建配置文件
|
||||
~/.config/claude/
|
||||
├─ claude_desktop_config.minimal.json # 4k tokens
|
||||
├─ claude_desktop_config.dev.json # 20k tokens
|
||||
└─ claude_desktop_config.full.json # 40k tokens
|
||||
|
||||
# 2. 使用腳本切換
|
||||
./switch_mcp_config.sh minimal # 節省 36k tokens
|
||||
./switch_mcp_config.sh dev # 節省 20k tokens
|
||||
./switch_mcp_config.sh full # 完整功能
|
||||
|
||||
# 3. 重啟 Claude Desktop
|
||||
```
|
||||
|
||||
### 策略 2:預設最小配置
|
||||
|
||||
```json
|
||||
// 預設只載入最常用的 MCP
|
||||
{
|
||||
"mcpServers": {
|
||||
"filesystem": {...}, // 文件操作(必需)
|
||||
"redis": {...} // 快取(常用)
|
||||
}
|
||||
}
|
||||
|
||||
// 需要其他功能時,切換配置
|
||||
```
|
||||
|
||||
### 策略 3:按項目配置
|
||||
|
||||
```bash
|
||||
# 不同項目使用不同配置
|
||||
momentry_core_0.1/
|
||||
└─ .claude_config.json # 項目專用配置
|
||||
|
||||
# 啟動時自動載入項目配置
|
||||
if [ -f ".claude_config.json" ]; then
|
||||
cp .claude_config.json ~/.config/claude/claude_desktop_config.json
|
||||
fi
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 實施步驟
|
||||
|
||||
### Step 1:創建配置文件(立即)
|
||||
|
||||
```bash
|
||||
# 創建配置目錄
|
||||
mkdir -p ~/.config/claude
|
||||
|
||||
# 創建最小配置(推薦預設)
|
||||
cat > ~/.config/claude/claude_desktop_config.minimal.json << 'EOF'
|
||||
{
|
||||
"mcpServers": {
|
||||
"filesystem": {
|
||||
"command": "mcp-filesystem",
|
||||
"args": ["/Users/accusys"]
|
||||
},
|
||||
"redis": {
|
||||
"command": "mcp-redis"
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
# 創建開發配置
|
||||
cat > ~/.config/claude/claude_desktop_config.dev.json << 'EOF'
|
||||
{
|
||||
"mcpServers": {
|
||||
"filesystem": {...},
|
||||
"redis": {...},
|
||||
"gitea": {...},
|
||||
"postgres": {...}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
# 設定預設為最小配置
|
||||
cp ~/.config/claude/claude_desktop_config.minimal.json \
|
||||
~/.config/claude/claude_desktop_config.json
|
||||
```
|
||||
|
||||
### Step 2:創建切換腳本
|
||||
|
||||
```bash
|
||||
# 創建腳本
|
||||
cat > ~/bin/switch_mcp << 'EOF'
|
||||
#!/bin/bash
|
||||
# MCP 配置切換器
|
||||
|
||||
CONFIG_DIR="$HOME/.config/claude"
|
||||
CURRENT="$CONFIG_DIR/claude_desktop_config.json"
|
||||
|
||||
case "$1" in
|
||||
minimal|dev|full)
|
||||
cp "$CONFIG_DIR/claude_desktop_config.$1.json" "$CURRENT"
|
||||
echo "✅ Switched to $1 config"
|
||||
echo "🔄 Restarting Claude Desktop..."
|
||||
osascript -e 'quit app "Claude"'
|
||||
sleep 2
|
||||
open -a "Claude"
|
||||
;;
|
||||
status)
|
||||
if [ -L "$CURRENT" ]; then
|
||||
echo "Current: $(readlink $CURRENT)"
|
||||
else
|
||||
echo "Current: standalone config"
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echo "Usage: switch_mcp {minimal|dev|full|status}"
|
||||
;;
|
||||
esac
|
||||
EOF
|
||||
|
||||
chmod +x ~/bin/switch_mcp
|
||||
```
|
||||
|
||||
### Step 3:使用
|
||||
|
||||
```bash
|
||||
# 日常使用(最小 token)
|
||||
switch_mcp minimal
|
||||
|
||||
# 開發模式
|
||||
switch_mcp dev
|
||||
|
||||
# 完整功能
|
||||
switch_mcp full
|
||||
|
||||
# 查看當前配置
|
||||
switch_mcp status
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 總結
|
||||
|
||||
### ✅ 推薦方案
|
||||
|
||||
**配置文件切換**(立即可用):
|
||||
- ✅ 節省 20-36k tokens per session
|
||||
- ✅ 無需等待 Anthropy 實現
|
||||
- ✅ 靈活可控
|
||||
- ✅ 快速切換
|
||||
|
||||
### 📋 配置建議
|
||||
|
||||
```
|
||||
預設(90% 場景):
|
||||
├─ filesystem
|
||||
└─ redis
|
||||
Token: ~4,000 ✅
|
||||
|
||||
開發(8% 場景):
|
||||
├─ filesystem
|
||||
├─ redis
|
||||
├─ gitea
|
||||
└─ postgres
|
||||
Token: ~20,000 ✅
|
||||
|
||||
完整(2% 場景):
|
||||
└─ 所有 MCP
|
||||
Token: ~40,000 ⚠️
|
||||
```
|
||||
|
||||
### 🎯 預期效果
|
||||
|
||||
```
|
||||
每次對話節省:
|
||||
預設使用最小配置: 節省 36,000 tokens ≈ $0.036
|
||||
|
||||
每月節省(假設 500 次對話):
|
||||
500 × 36,000 = 18,000,000 tokens ≈ $18 USD
|
||||
|
||||
年度節省:
|
||||
$216 USD ✅
|
||||
```
|
||||
@@ -0,0 +1,445 @@
|
||||
---
|
||||
document_type: "architecture_design"
|
||||
service: "MOMENTRY_CORE"
|
||||
title: "模組標準化實施計劃"
|
||||
date: "2026-04-25"
|
||||
version: "V1.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
tags:
|
||||
- "模組標準化實施計劃"
|
||||
ai_query_hints:
|
||||
- "查詢 模組標準化實施計劃 的內容"
|
||||
- "模組標準化實施計劃 的主要目的是什麼?"
|
||||
- "如何操作或實施 模組標準化實施計劃?"
|
||||
---
|
||||
|
||||
# 模組標準化實施計劃
|
||||
|
||||
## 概述
|
||||
|
||||
本計劃詳細說明如何將現有的處理器模組按照《處理器模組標準化規範》進行標準化改造。計劃從 ASR 模組開始,逐步擴展到所有處理器模組。
|
||||
|
||||
## 實施策略
|
||||
|
||||
### 階段式實施
|
||||
1. **階段 1**: ASR 模組標準化(示範項目)
|
||||
2. **階段 2**: OCR、YOLO 模組標準化
|
||||
3. **階段 3**: Face、Pose、CUT 模組標準化
|
||||
4. **階段 4**: ASRX、Caption、Story 模組標準化
|
||||
5. **階段 5**: 系統整合與優化
|
||||
|
||||
### 並行工作流
|
||||
```
|
||||
分析現有代碼 → 創建標準模板 → 重構模組 → 測試驗證 → 文檔更新
|
||||
```
|
||||
|
||||
## 階段 1: ASR 模組標準化
|
||||
|
||||
### 目標
|
||||
將 ASR 模組作為示範項目,完整實施標準化規範,建立可重用的模板和流程。
|
||||
|
||||
### 當前狀態分析
|
||||
|
||||
#### Rust 模組 (`src/core/processor/asr.rs`)
|
||||
**優點**:
|
||||
- 結構相對清晰
|
||||
- 已有完整的結果結構定義
|
||||
- 使用標準的 PythonExecutor
|
||||
|
||||
**需要改進**:
|
||||
1. 配置管理不統一(硬編碼超時 vs 環境變量)
|
||||
2. 缺少性能監控指標
|
||||
3. 測試覆蓋不完整
|
||||
4. 文檔不完整
|
||||
|
||||
#### Python 腳本 (`scripts/asr_processor.py`)
|
||||
**問題**:
|
||||
1. 過於複雜(953 行)
|
||||
2. 包含不必要的監控邏輯
|
||||
3. Redis 發布依賴
|
||||
4. 錯誤處理不規範
|
||||
5. 缺少模塊化設計
|
||||
|
||||
### 實施步驟
|
||||
|
||||
#### 步驟 1: 創建標準化模板
|
||||
1. 創建 Rust 模組模板
|
||||
2. 創建 Python 腳本模板
|
||||
3. 創建配置模板
|
||||
4. 創建測試模板
|
||||
|
||||
#### 步驟 2: 備份現有代碼
|
||||
```bash
|
||||
# 備份原始文件
|
||||
cp src/core/processor/asr.rs src/core/processor/asr_legacy.rs
|
||||
cp scripts/asr_processor.py scripts/asr_processor_legacy.py
|
||||
```
|
||||
|
||||
#### 步驟 3: 重構 Rust 模組
|
||||
1. 更新配置管理
|
||||
2. 添加性能監控
|
||||
3. 完善錯誤處理
|
||||
4. 補充文檔註釋
|
||||
|
||||
#### 步驟 4: 重構 Python 腳本
|
||||
1. 簡化架構(目標: <300 行)
|
||||
2. 移除不必要的監控邏輯
|
||||
3. 規範錯誤處理
|
||||
4. 添加模塊化設計
|
||||
|
||||
#### 步驟 5: 更新配置
|
||||
1. 統一環境變量
|
||||
2. 添加性能相關配置
|
||||
3. 文檔化配置選項
|
||||
|
||||
#### 步驟 6: 添加測試
|
||||
1. 單元測試
|
||||
2. 集成測試
|
||||
3. 性能測試
|
||||
4. 回歸測試
|
||||
|
||||
#### 步驟 7: 驗證功能
|
||||
1. 功能測試
|
||||
2. 性能對比
|
||||
3. 兼容性驗證
|
||||
|
||||
### 詳細任務分解
|
||||
|
||||
#### 任務 1.1: 分析 ASR 模組依賴
|
||||
```bash
|
||||
# 檢查 Python 腳本依賴
|
||||
grep -n "import" scripts/asr_processor.py
|
||||
grep -n "from" scripts/asr_processor.py
|
||||
|
||||
# 檢查 Rust 依賴
|
||||
grep -n "use" src/core/processor/asr.rs
|
||||
```
|
||||
|
||||
#### 任務 1.2: 創建標準化模板
|
||||
```bash
|
||||
# 創建模板目錄
|
||||
mkdir -p docs/templates/module_standardization
|
||||
|
||||
# 創建 Rust 模板
|
||||
cat > docs/templates/module_standardization/rust_module_template.rs << 'EOF'
|
||||
// Rust 模組標準模板
|
||||
EOF
|
||||
|
||||
# 創建 Python 模板
|
||||
cat > docs/templates/module_standardization/python_processor_template.py << 'EOF'
|
||||
# Python 處理器標準模板
|
||||
EOF
|
||||
```
|
||||
|
||||
#### 任務 1.3: 重構 ASR Rust 模組
|
||||
**改進點**:
|
||||
1. 統一配置管理
|
||||
2. 添加 `ProcessingMetrics` 結構
|
||||
3. 完善錯誤處理鏈
|
||||
4. 添加詳細日誌
|
||||
5. 補充文檔註釋
|
||||
|
||||
#### 任務 1.4: 重構 ASR Python 腳本
|
||||
**簡化策略**:
|
||||
1. 移除 `ResourceMonitor` 類
|
||||
2. 移除 Redis 發布邏輯
|
||||
3. 簡化 chunking 邏輯
|
||||
4. 規範錯誤處理
|
||||
5. 添加模塊化設計
|
||||
|
||||
#### 任務 1.5: 更新配置系統
|
||||
```rust
|
||||
// 在 src/core/config.rs 中添加
|
||||
pub static ASR_MODEL: Lazy<String> = Lazy::new(|| {
|
||||
env::var("MOMENTRY_ASR_MODEL").unwrap_or_else(|_| "base".to_string())
|
||||
});
|
||||
|
||||
pub static ASR_CHUNK_SIZE: Lazy<u64> = Lazy::new(|| {
|
||||
env::var("MOMENTRY_ASR_CHUNK_SIZE")
|
||||
.unwrap_or_else(|_| "300".to_string())
|
||||
.parse()
|
||||
.unwrap_or(300)
|
||||
});
|
||||
|
||||
pub static ASR_CACHE_ENABLED: Lazy<bool> = Lazy::new(|| {
|
||||
env::var("MOMENTRY_ASR_CACHE_ENABLED")
|
||||
.unwrap_or_else(|_| "true".to_string())
|
||||
.parse()
|
||||
.unwrap_or(true)
|
||||
});
|
||||
```
|
||||
|
||||
#### 任務 1.6: 創建測試套件
|
||||
```rust
|
||||
// 測試文件結構
|
||||
tests/
|
||||
├── unit/
|
||||
│ ├── asr_result_test.rs
|
||||
│ └── asr_serialization_test.rs
|
||||
├── integration/
|
||||
│ └── asr_integration_test.rs
|
||||
└── performance/
|
||||
└── asr_benchmark.rs
|
||||
```
|
||||
|
||||
#### 任務 1.7: 創建遷移文檔
|
||||
```markdown
|
||||
# ASR 模組標準化遷移指南
|
||||
|
||||
## 變更摘要
|
||||
1. 簡化 Python 腳本架構
|
||||
2. 統一配置管理
|
||||
3. 添加性能監控
|
||||
4. 完善錯誤處理
|
||||
|
||||
## 兼容性說明
|
||||
- API 保持不變
|
||||
- 輸出格式保持兼容
|
||||
- 配置方式向後兼容
|
||||
|
||||
## 遷移步驟
|
||||
1. 備份現有文件
|
||||
2. 更新 Rust 模組
|
||||
3. 更新 Python 腳本
|
||||
4. 更新環境變量
|
||||
5. 運行測試驗證
|
||||
```
|
||||
|
||||
### 時間安排
|
||||
|
||||
| 任務 | 預計工時 | 負責人 | 狀態 |
|
||||
|------|----------|--------|------|
|
||||
| 分析現有代碼 | 2 小時 | Warren | 待開始 |
|
||||
| 創建標準模板 | 4 小時 | Warren | 待開始 |
|
||||
| 重構 Rust 模組 | 6 小時 | Warren | 待開始 |
|
||||
| 重構 Python 腳本 | 8 小時 | Warren | 待開始 |
|
||||
| 更新配置系統 | 3 小時 | Warren | 待開始 |
|
||||
| 創建測試套件 | 6 小時 | Warren | 待開始 |
|
||||
| 功能驗證測試 | 4 小時 | Warren | 待開始 |
|
||||
| 文檔更新 | 3 小時 | Warren | 待開始 |
|
||||
| **總計** | **36 小時** | | |
|
||||
|
||||
### 成功標準
|
||||
|
||||
#### 功能標準
|
||||
1. ✅ 保持現有 API 兼容性
|
||||
2. ✅ 輸出格式保持不變
|
||||
3. ✅ 處理準確率不降低
|
||||
4. ✅ 錯誤處理更完善
|
||||
|
||||
#### 性能標準
|
||||
1. ⬆️ 處理時間減少 20%
|
||||
2. ⬇️ 內存使用減少 30%
|
||||
3. ⬆️ 代碼可讀性提高
|
||||
4. ⬆️ 維護性提高
|
||||
|
||||
#### 質量標準
|
||||
1. ✅ 單元測試覆蓋率 >80%
|
||||
2. ✅ 集成測試通過率 100%
|
||||
3. ✅ 文檔完整度 100%
|
||||
4. ✅ 代碼審查通過
|
||||
|
||||
## 階段 2: OCR 和 YOLO 模組標準化
|
||||
|
||||
### 目標
|
||||
基於 ASR 模組的經驗,標準化 OCR 和 YOLO 模組。
|
||||
|
||||
### 實施步驟
|
||||
1. 應用 ASR 標準化模板
|
||||
2. 處理模組特定邏輯
|
||||
3. 優化性能配置
|
||||
4. 創建模組特定測試
|
||||
|
||||
### 時間安排
|
||||
- OCR 模組: 20 小時
|
||||
- YOLO 模組: 24 小時
|
||||
- 總計: 44 小時
|
||||
|
||||
## 階段 3: Face、Pose、CUT 模組標準化
|
||||
|
||||
### 目標
|
||||
完成較簡單的處理器模組標準化。
|
||||
|
||||
### 實施步驟
|
||||
1. 批量應用模板
|
||||
2. 重點處理配置統一
|
||||
3. 創建共享工具函數
|
||||
|
||||
### 時間安排
|
||||
- 每個模組: 12-16 小時
|
||||
- 總計: 40-48 小時
|
||||
|
||||
## 階段 4: ASRX、Caption、Story 模組標準化
|
||||
|
||||
### 目標
|
||||
完成所有處理器模組標準化。
|
||||
|
||||
### 實施步驟
|
||||
1. 處理複雜模組邏輯
|
||||
2. 優化資源使用
|
||||
3. 創建高級功能測試
|
||||
|
||||
### 時間安排
|
||||
- 每個模組: 16-20 小時
|
||||
- 總計: 48-60 小時
|
||||
|
||||
## 階段 5: 系統整合與優化
|
||||
|
||||
### 目標
|
||||
1. 統一配置管理系統
|
||||
2. 創建模組管理器
|
||||
3. 實現動態加載
|
||||
4. 優化資源共享
|
||||
|
||||
### 實施步驟
|
||||
1. 創建 `ModuleRegistry` 管理所有模組
|
||||
2. 實現配置熱重載
|
||||
3. 添加模組健康檢查
|
||||
4. 創建性能監控面板
|
||||
|
||||
### 時間安排
|
||||
- 系統整合: 40 小時
|
||||
- 性能優化: 32 小時
|
||||
- 文檔完善: 16 小時
|
||||
- 總計: 88 小時
|
||||
|
||||
## 總體時間規劃
|
||||
|
||||
| 階段 | 預計工時 | 累計工時 | 時間窗口 |
|
||||
|------|----------|----------|----------|
|
||||
| 階段 1: ASR 示範 | 36 小時 | 36 小時 | 第 1 周 |
|
||||
| 階段 2: OCR/YOLO | 44 小時 | 80 小時 | 第 2 周 |
|
||||
| 階段 3: Face/Pose/CUT | 44 小時 | 124 小時 | 第 3 周 |
|
||||
| 階段 4: ASRX/Caption/Story | 54 小時 | 178 小時 | 第 4 周 |
|
||||
| 階段 5: 系統整合 | 88 小時 | 266 小時 | 第 5-6 周 |
|
||||
| **總計** | **266 小時** | | **6 周** |
|
||||
|
||||
## 風險管理
|
||||
|
||||
### 技術風險
|
||||
1. **兼容性問題**: 現有代碼依賴複雜
|
||||
- 緩解: 逐步遷移,保持 API 兼容
|
||||
- 監控: 回歸測試套件
|
||||
|
||||
2. **性能回歸**: 標準化可能引入開銷
|
||||
- 緩解: 性能基準測試
|
||||
- 監控: 持續性能監控
|
||||
|
||||
3. **依賴問題**: Python 庫版本衝突
|
||||
- 緩解: 虛擬環境隔離
|
||||
- 監控: 依賴版本鎖定
|
||||
|
||||
### 項目風險
|
||||
1. **時間超支**: 複雜度估計不足
|
||||
- 緩解: 分階段實施,定期評估
|
||||
- 監控: 每周進度報告
|
||||
|
||||
2. **資源不足**: 開發人員時間有限
|
||||
- 緩解: 優先級排序,外包簡單任務
|
||||
- 監控: 資源分配跟蹤
|
||||
|
||||
3. **質量問題**: 測試覆蓋不足
|
||||
- 緩解: 測試驅動開發
|
||||
- 監控: 代碼覆蓋率報告
|
||||
|
||||
## 資源需求
|
||||
|
||||
### 人力資源
|
||||
- **技術負責人**: 1 人(Warren)
|
||||
- **開發人員**: 1-2 人(可選)
|
||||
- **測試人員**: 1 人(可選)
|
||||
- **文檔專員**: 1 人(可選)
|
||||
|
||||
### 技術資源
|
||||
- **測試服務器**: 用於性能測試
|
||||
- **CI/CD 管道**: 自動化測試部署
|
||||
- **監控工具**: 性能監控和告警
|
||||
- **文檔平台**: 文檔管理和發布
|
||||
|
||||
### 軟件資源
|
||||
- **開發工具**: Rust, Python, 編輯器
|
||||
- **測試框架**: cargo test, pytest
|
||||
- **性能工具**: perf, valgrind, py-spy
|
||||
- **文檔工具**: mdBook, Sphinx
|
||||
|
||||
## 溝通計劃
|
||||
|
||||
### 定期會議
|
||||
- **每日站會**: 15 分鐘,進度同步
|
||||
- **每周評審**: 1 小時,進度評估和調整
|
||||
- **階段總結**: 每階段結束,經驗總結
|
||||
|
||||
### 報告機制
|
||||
- **進度報告**: 每周書面報告
|
||||
- **問題報告**: 即時問題上報
|
||||
- **變更請求**: 規範變更流程
|
||||
|
||||
### 文檔更新
|
||||
- **技術文檔**: 實時更新
|
||||
- **用戶文檔**: 階段性更新
|
||||
- **API 文檔**: 自動生成
|
||||
|
||||
## 質量保證
|
||||
|
||||
### 代碼質量
|
||||
1. **代碼審查**: 所有變更必須經過審查
|
||||
2. **靜態分析**: Rust clippy, Python pylint
|
||||
3. **格式化檢查**: rustfmt, black
|
||||
4. **依賴檢查**: cargo audit, safety
|
||||
|
||||
### 測試質量
|
||||
1. **測試覆蓋率**: >80% 行覆蓋率
|
||||
2. **集成測試**: 端到端功能測試
|
||||
3. **性能測試**: 基準測試和比較
|
||||
4. **壓力測試**: 高負載場景測試
|
||||
|
||||
### 文檔質量
|
||||
1. **完整性**: 所有功能都有文檔
|
||||
2. **準確性**: 文檔與代碼同步
|
||||
3. **可讀性**: 清晰易懂的說明
|
||||
4. **示例**: 豐富的使用示例
|
||||
|
||||
## 驗收標準
|
||||
|
||||
### 階段驗收
|
||||
每個階段完成後需要驗收:
|
||||
1. ✅ 功能測試通過
|
||||
2. ✅ 性能測試達標
|
||||
3. ✅ 文檔更新完成
|
||||
4. ✅ 代碼審查通過
|
||||
|
||||
### 最終驗收
|
||||
項目完成後需要驗收:
|
||||
1. ✅ 所有模組標準化完成
|
||||
2. ✅ 系統整合測試通過
|
||||
3. ✅ 性能基準達標
|
||||
4. ✅ 文檔完整發布
|
||||
5. ✅ 團隊培訓完成
|
||||
|
||||
## 後續維護
|
||||
|
||||
### 維護計劃
|
||||
1. **錯誤修復**: 24 小時內響應
|
||||
2. **性能優化**: 定期性能審查
|
||||
3. **安全更新**: 及時更新依賴
|
||||
4. **功能增強**: 根據需求迭代
|
||||
|
||||
### 監控指標
|
||||
1. **運行時指標**: 成功率、延遲、資源使用
|
||||
2. **代碼指標**: 覆蓋率、複雜度、債務
|
||||
3. **用戶指標**: 使用頻率、滿意度、問題反饋
|
||||
|
||||
### 改進機制
|
||||
1. **定期回顧**: 每季度技術回顧
|
||||
2. **用戶反饋**: 收集和分析反饋
|
||||
3. **技術調研**: 跟蹤新技術發展
|
||||
4. **重構計劃**: 持續技術債務管理
|
||||
|
||||
---
|
||||
|
||||
*版本: 1.0.0*
|
||||
*創建日期: 2026-03-27*
|
||||
*負責人: Warren (Technical Lead)*
|
||||
*狀態: 審核中*
|
||||
@@ -0,0 +1,541 @@
|
||||
# Momentry Core 全新系統架構設計
|
||||
|
||||
> 更新日期: 2026-04-25
|
||||
> 版本: V1.0 (全新設計)
|
||||
> 狀態: 設計中
|
||||
|
||||
---
|
||||
|
||||
## 1. 核心設計理念
|
||||
|
||||
### 1.1 兩大核心實體
|
||||
|
||||
系統僅有兩種核心概念:
|
||||
|
||||
| 實體 | 說明 | 範例 |
|
||||
|------|------|------|
|
||||
| **File** | 任何檔案 | video, pdf, ppt, png, doc, audio... |
|
||||
| **Identity** | 任何可識別列管的 object | 人、物件、品牌、概念、場景... |
|
||||
|
||||
### 1.2 關係模型
|
||||
|
||||
```
|
||||
File ──[包含/出現]──→ Identity
|
||||
Identity ──[出現在]──→ File
|
||||
```
|
||||
|
||||
- 一個 File 可包含多個 Identity
|
||||
- 一個 Identity 可出現在多個 File
|
||||
- Identity 可歸屬於分類系統
|
||||
|
||||
---
|
||||
|
||||
## 2. Identity 設計
|
||||
|
||||
### 2.1 Identity 類型
|
||||
|
||||
任何可命名的事物都是 Identity:
|
||||
|
||||
| 類型 | 說明 | 範例 |
|
||||
|------|------|------|
|
||||
| people | 人 | 演員、公眾人物、虛構角色 |
|
||||
| object | 物件 | 車輛、建築、道具 |
|
||||
| brand | 品牌 | LV、Hello Kitty、Nike |
|
||||
| logo | 商標 | LV logo、Nike 勾勾 |
|
||||
| concept | 概念 | 愛、自由、科技 |
|
||||
| scene | 場景 | 室內、室外、街道 |
|
||||
|
||||
### 2.2 People Identity 特殊設計
|
||||
|
||||
**核心需求**: 同一個人(演員)在不同電影中有不同的角色名和定妝造型。
|
||||
|
||||
#### 階層結構
|
||||
```
|
||||
Identity (真實人物): 張曼玉
|
||||
├── File A (花樣年華): 角色 "蘇麗珍" → 定妝: 旗袍造型、老妝+白髮頭套
|
||||
├── File B (東邪西毒): 角色 "歐陽鋒妻子" → 定妝: 武俠造型
|
||||
├── File C (甜蜜蜜): 角色 "李翹" → 定妝: 現代造型
|
||||
└── File D: 角色 "XXX" → 定妝: 醜妝+傷妝
|
||||
```
|
||||
|
||||
#### 在 File 中的呈現方式
|
||||
| 呈現方式 | 說明 | 數據來源 |
|
||||
|----------|------|----------|
|
||||
| face | 臉孔出現 | Face Detection |
|
||||
| speaker | 聲音出現 | ASR/Speaker Diarization |
|
||||
| pose | 姿態/身體出現 | Pose Estimation |
|
||||
| name_mention | 名字被提到 | ASR 文本/OCR |
|
||||
|
||||
### 2.3 Identity 屬性
|
||||
|
||||
```sql
|
||||
CREATE TABLE identities (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
uuid VARCHAR(36) UNIQUE NOT NULL,
|
||||
name TEXT NOT NULL, -- 可識別名稱
|
||||
identity_type VARCHAR(30), -- people, object, brand, concept...
|
||||
description TEXT,
|
||||
|
||||
-- People 特有
|
||||
real_name TEXT, -- 真實姓名
|
||||
|
||||
-- TMDB 整合
|
||||
tmdb_id INTEGER, -- TMDB 人物 ID
|
||||
tmdb_profile TEXT, -- TMDB 人臉照 URL
|
||||
source VARCHAR(20), -- 'tmdb', 'manual', 'ai_detection'
|
||||
|
||||
-- 參考向量 (用於自動比對)
|
||||
face_embedding VECTOR(512), -- 參考臉向量 (ArcFace)
|
||||
voice_embedding VECTOR(192), -- 參考聲紋向量 (ECAPA-TDNN)
|
||||
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. File 設計
|
||||
|
||||
### 3.1 File 屬性
|
||||
|
||||
```sql
|
||||
CREATE TABLE files (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
uuid VARCHAR(36) UNIQUE NOT NULL,
|
||||
file_path TEXT NOT NULL,
|
||||
file_name TEXT NOT NULL,
|
||||
file_type VARCHAR(20), -- video, pdf, ppt, png, audio...
|
||||
file_size BIGINT,
|
||||
mime_type VARCHAR(100),
|
||||
metadata JSONB, -- 類型特定元數據
|
||||
tmdb_movie_id INTEGER, -- TMDB 電影 ID (可選)
|
||||
imdb_id VARCHAR(20), -- IMDb ID (可選)
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
```
|
||||
|
||||
### 3.2 File 類型特定元數據
|
||||
|
||||
| 檔案類型 | 元數據內容 |
|
||||
|----------|-----------|
|
||||
| video | duration, width, height, fps, codec |
|
||||
| audio | duration, sample_rate, channels |
|
||||
| image | width, height, format |
|
||||
| document | page_count, language |
|
||||
|
||||
---
|
||||
|
||||
## 4. File-Identity 關聯設計
|
||||
|
||||
### 4.1 關聯表
|
||||
|
||||
```sql
|
||||
CREATE TABLE file_identities (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
file_uuid VARCHAR(36) REFERENCES files(uuid),
|
||||
identity_uuid VARCHAR(36) REFERENCES identities(uuid),
|
||||
|
||||
-- People 特有
|
||||
role_name TEXT, -- 劇中角色名
|
||||
costume_design TEXT, -- 定妝造型描述
|
||||
presentation TEXT[], -- ['face', 'speaker', 'pose', 'name_mention']
|
||||
|
||||
-- 時間位置
|
||||
timestamp_start FLOAT, -- 開始時間 (秒)
|
||||
timestamp_end FLOAT, -- 結束時間
|
||||
frame_start BIGINT, -- 開始幀
|
||||
frame_end BIGINT, -- 結束幀
|
||||
|
||||
-- 檢測數據
|
||||
face_data JSONB, -- {face_id, confidence, bbox}
|
||||
speaker_data JSONB, -- {speaker_id, audio_segment}
|
||||
pose_data JSONB, -- {keypoints, action}
|
||||
|
||||
-- 匹配資訊
|
||||
match_confidence FLOAT, -- AI 匹配置信度
|
||||
is_confirmed BOOLEAN DEFAULT FALSE, -- 人工確認
|
||||
|
||||
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
```
|
||||
|
||||
### 4.2 呈現方式說明
|
||||
|
||||
| 呈現方式 | 說明 | 適用 Identity 類型 |
|
||||
|----------|------|-------------------|
|
||||
| face | 臉孔出現在畫面中 | people |
|
||||
| speaker | 聲音出現在音軌中 | people |
|
||||
| pose | 身體姿態出現在畫面中 | people |
|
||||
| name_mention | 名字在文本中被提到 | people, brand, concept |
|
||||
| object_detection | 物件被檢測到 | object, brand, logo |
|
||||
| text_mention | 文字提到 | 所有類型 |
|
||||
| logo_detection | 商標被檢測到 | brand, logo |
|
||||
|
||||
---
|
||||
|
||||
## 5. 分類系統設計
|
||||
|
||||
### 5.1 階層式編號格式
|
||||
|
||||
參考 IPC 但更靈活:
|
||||
|
||||
```
|
||||
X-NNN-NNN/NNN
|
||||
│ │ │ └─ 細分類 (Subgroup)
|
||||
│ │ └───── 主分類 (Main Group)
|
||||
│ └───────── 子分類 (Subclass)
|
||||
└──────────── 大分類 (Section)
|
||||
```
|
||||
|
||||
### 5.2 範例
|
||||
|
||||
```
|
||||
P-001-000/000 人物 (People)
|
||||
├── P-001-010/000 演員
|
||||
│ ├── P-001-010/010 電影演員
|
||||
│ └── P-001-010/020 電視演員
|
||||
├── P-001-020/000 公眾人物
|
||||
└── P-001-030/000 虛構角色
|
||||
|
||||
B-002-000/000 品牌 (Brand)
|
||||
├── B-002-010/000 時尚品牌
|
||||
│ ├── B-002-010/010 LV
|
||||
│ └── B-002-010/020 Gucci
|
||||
└── B-002-020/000 科技品牌
|
||||
|
||||
O-003-000/000 物件 (Object)
|
||||
├── O-003-010/000 車輛
|
||||
├── O-003-020/000 建築
|
||||
└── O-003-030/000 道具
|
||||
|
||||
C-004-000/000 概念 (Concept)
|
||||
├── C-004-010/000 情感
|
||||
│ ├── C-004-010/010 愛
|
||||
│ └── C-004-010/020 自由
|
||||
└── C-004-020/000 思想
|
||||
```
|
||||
|
||||
### 5.3 分類表結構
|
||||
|
||||
```sql
|
||||
CREATE TABLE categories (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
code VARCHAR(20) UNIQUE NOT NULL, -- P-001-010/010
|
||||
name TEXT NOT NULL,
|
||||
parent_code VARCHAR(20) REFERENCES categories(code),
|
||||
description TEXT,
|
||||
category_type VARCHAR(20), -- 'file', 'identity', 'both'
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- File-Category 關聯
|
||||
CREATE TABLE file_categories (
|
||||
file_uuid VARCHAR(36) REFERENCES files(uuid),
|
||||
category_code VARCHAR(20) REFERENCES categories(code),
|
||||
PRIMARY KEY (file_uuid, category_code)
|
||||
);
|
||||
|
||||
-- Identity-Category 關聯
|
||||
CREATE TABLE identity_categories (
|
||||
identity_uuid VARCHAR(36) REFERENCES identities(uuid),
|
||||
category_code VARCHAR(20) REFERENCES categories(code),
|
||||
PRIMARY KEY (identity_uuid, category_code)
|
||||
);
|
||||
```
|
||||
|
||||
### 5.4 特點
|
||||
|
||||
1. **可擴充**:任何層級都可新增,不需重新編號
|
||||
2. **有意義**:首字母代表大分類 (P=People, B=Brand, O=Object, C=Concept...)
|
||||
3. **層級清晰**:通過編號即可知道所属分类深度
|
||||
4. **靈活套用**:可套用在 File、Identity 或兩者
|
||||
|
||||
---
|
||||
|
||||
## 6. TMDB 整合設計
|
||||
|
||||
### 6.1 資料流
|
||||
|
||||
```
|
||||
TMDB API → 電影資訊 + 演員名單 → 自動建立 Identity → 關聯到 File
|
||||
```
|
||||
|
||||
### 6.2 整合流程
|
||||
|
||||
1. **匯入電影檔案時**:
|
||||
- 用戶提供 TMDB 電影 ID 或 IMDb ID
|
||||
- 系統自動從 TMDB API 獲取:
|
||||
- 演員名單 + 角色名
|
||||
- 演員人臉照 (profile_path)
|
||||
- 電影元數據
|
||||
|
||||
2. **建立 Identity**:
|
||||
- 自動建立或更新 Identity(演員)
|
||||
- 儲存 TMDB ID + 人臉照 URL
|
||||
- 關聯到 File(這部電影)
|
||||
|
||||
3. **提取參考向量**:
|
||||
- 下載 TMDB 人臉照
|
||||
- 提取 face_embedding (512-dim)
|
||||
- 儲存到 identities 表
|
||||
|
||||
4. **後續 AI 識別**:
|
||||
- 系統檢測 File 中的 Face
|
||||
- 自動匹配到已有的 Identity
|
||||
- 更新 file_identities 表
|
||||
|
||||
### 6.3 TMDB API 端點
|
||||
|
||||
| 端點 | 說明 |
|
||||
|------|------|
|
||||
| `/api/v1/tmdb/search/movie?q=關鍵字` | 搜尋電影 |
|
||||
| `/api/v1/tmdb/movie/:id` | 獲取電影詳情 |
|
||||
| `/api/v1/tmdb/movie/:id/cast` | 獲取演員列表 |
|
||||
| `/api/v1/tmdb/person/:id` | 獲取人物詳情 |
|
||||
| `/api/v1/tmdb/person/:id/images` | 獲取人物照片 |
|
||||
|
||||
---
|
||||
|
||||
## 7. 自動辨識比對設計
|
||||
|
||||
### 7.1 核心目標
|
||||
|
||||
**從 Identity (People) 的一張參考臉,自動辨識比對,找到所有出現的 File 和片段。**
|
||||
|
||||
### 7.2 比對流程
|
||||
|
||||
```
|
||||
1. 建立 Identity
|
||||
└── 取得參考臉 (TMDB 照片或手動上傳)
|
||||
└── 提取 face_embedding (512-dim)
|
||||
|
||||
2. 處理 File (Video)
|
||||
└── AI 檢測所有 Face → 提取每張臉的向量
|
||||
└── AI 檢測所有 Speaker → 提取聲紋向量
|
||||
└── AI 檢測所有 Object → 提取特徵向量
|
||||
|
||||
3. 自動比對匹配
|
||||
└── Face 比對: 檢測臉 vs Identity face_embedding
|
||||
└── Voice 比對: 檢測聲紋 vs Identity voice_embedding
|
||||
└── 超過閾值 → 自動建立 file_identities 關聯
|
||||
|
||||
4. 人工確認 (可選)
|
||||
└── 低置信度匹配標記為待確認
|
||||
└── 用戶確認/修正
|
||||
```
|
||||
|
||||
### 7.3 比對閾值
|
||||
|
||||
| 比對類型 | 預設閾值 | 說明 |
|
||||
|----------|----------|------|
|
||||
| Face | 0.85 | ArcFace 512-dim |
|
||||
| Voice | 0.75 | ECAPA-TDNN 192-dim |
|
||||
| Object | 0.80 | YOLO + 特徵 |
|
||||
|
||||
---
|
||||
|
||||
## 8. API 架構
|
||||
|
||||
### 8.1 File API
|
||||
|
||||
```
|
||||
GET /api/v1/files # 列表
|
||||
參數: ?page=1&page_size=20&type=video&category=P-001&sort=created_at
|
||||
|
||||
GET /api/v1/files/search?q=關鍵字 # 搜尋
|
||||
|
||||
GET /api/v1/files/:uuid # 詳情
|
||||
|
||||
GET /api/v1/files/:uuid/identities # File 有哪些 Identity
|
||||
參數: ?presentation=face&role_name=蘇麗珍
|
||||
|
||||
POST /api/v1/files/:uuid/import-tmdb # 從 TMDB 匯入演員
|
||||
Body: {"tmdb_movie_id": 12345}
|
||||
```
|
||||
|
||||
### 8.2 Identity API
|
||||
|
||||
```
|
||||
GET /api/v1/identities # 列表
|
||||
參數: ?page=1&page_size=20&type=people&category=B-002&sort=name
|
||||
|
||||
GET /api/v1/identities/search?q=名字 # 搜尋
|
||||
|
||||
GET /api/v1/identities/:id # 詳情
|
||||
|
||||
GET /api/v1/identities/:id/files # Identity 在哪些 File
|
||||
參數: ?presentation=face&role_name=蘇麗珍
|
||||
|
||||
POST /api/v1/identities # 手動建立 Identity
|
||||
|
||||
PUT /api/v1/identities/:id # 更新 Identity
|
||||
|
||||
POST /api/v1/identities/:id/reference-face # 上傳參考臉
|
||||
|
||||
POST /api/v1/identities/:id/scan # 掃描所有 File 比對
|
||||
```
|
||||
|
||||
### 8.3 Category API
|
||||
|
||||
```
|
||||
GET /api/v1/categories # 分類樹
|
||||
|
||||
GET /api/v1/categories/:code/children # 子分類
|
||||
|
||||
POST /api/v1/categories # 建立分類
|
||||
|
||||
PUT /api/v1/categories/:code # 更新分類
|
||||
|
||||
DELETE /api/v1/categories/:code # 刪除分類
|
||||
```
|
||||
|
||||
### 8.4 TMDB API
|
||||
|
||||
```
|
||||
GET /api/v1/tmdb/search/movie?q=關鍵字 # 搜尋電影
|
||||
|
||||
GET /api/v1/tmdb/movie/:id # 獲取電影詳情
|
||||
|
||||
GET /api/v1/tmdb/movie/:id/cast # 獲取演員列表
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. 搜尋範例
|
||||
|
||||
### 9.1 場景:找到張曼玉出現在哪些 File
|
||||
|
||||
```bash
|
||||
# Step 1: 搜尋 Identity
|
||||
GET /api/v1/identities/search?q=張曼玉
|
||||
|
||||
# Step 2: 獲取相關 File
|
||||
GET /api/v1/identities/{identity_uuid}/files
|
||||
|
||||
# 返回:
|
||||
{
|
||||
"files": [
|
||||
{
|
||||
"file_uuid": "xxx",
|
||||
"file_name": "花樣年華.mp4",
|
||||
"role_name": "蘇麗珍",
|
||||
"costume_design": "老妝+白髮頭套",
|
||||
"presentation": ["face", "speaker"],
|
||||
"timestamp_start": 120.5,
|
||||
"timestamp_end": 135.2
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 9.2 場景:找到某 File 中所有演員
|
||||
|
||||
```bash
|
||||
GET /api/v1/files/{file_uuid}/identities?presentation=face
|
||||
|
||||
# 返回:
|
||||
{
|
||||
"identities": [
|
||||
{
|
||||
"identity_uuid": "abc",
|
||||
"name": "張曼玉",
|
||||
"role_name": "蘇麗珍",
|
||||
"presentation": ["face", "speaker"]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 9.3 場景:通過分類瀏覽
|
||||
|
||||
```bash
|
||||
# 所有演員相關 Identity
|
||||
GET /api/v1/identities?category=P-001-010
|
||||
|
||||
# 所有電影相關 File
|
||||
GET /api/v1/files?category=M-001-010
|
||||
```
|
||||
|
||||
### 9.4 場景:組合搜尋
|
||||
|
||||
```bash
|
||||
GET /api/v1/identities/search?q=張&type=people&category=P-001
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. 執行計畫
|
||||
|
||||
### Phase 1: 資料庫重構
|
||||
- [ ] 建立新表 (files, identities, file_identities, categories, file_categories, identity_categories)
|
||||
- [ ] 資料遷移腳本 (從現有 videos/person_identities 遷移)
|
||||
- [ ] 向量索引配置 (face_embedding, voice_embedding)
|
||||
- [ ] 測試資料建立
|
||||
|
||||
### Phase 2: 核心 API
|
||||
- [ ] File CRUD + 列表/搜尋/過濾
|
||||
- [ ] Identity CRUD + 列表/搜尋/過濾
|
||||
- [ ] Category 樹狀結構 CRUD
|
||||
- [ ] File-Identity 關聯 API
|
||||
- [ ] Category 關聯 API
|
||||
|
||||
### Phase 3: TMDB 整合
|
||||
- [ ] TMDB API 串接 (搜尋電影、獲取演員、獲取照片)
|
||||
- [ ] 自動建立 Identity 流程
|
||||
- [ ] 人臉照下載與向量提取
|
||||
- [ ] 角色名自動關聯
|
||||
|
||||
### Phase 4: AI 自動辨識
|
||||
- [ ] Face 檢測整合 (現有)
|
||||
- [ ] 向量比對匹配邏輯
|
||||
- [ ] file_identities 自動建立
|
||||
- [ ] 低置信度標記與人工確認流程
|
||||
|
||||
### Phase 5: Portal 前端
|
||||
- [ ] File 列表 + 搜尋 + 過濾
|
||||
- [ ] Identity 列表 + 搜尋
|
||||
- [ ] 分類瀏覽
|
||||
- [ ] Identity 詳情 (顯示所有相關 File)
|
||||
- [ ] File 詳情 (顯示所有 Identity)
|
||||
- [ ] TMDB 匯入介面
|
||||
- [ ] 參考臉上傳介面
|
||||
|
||||
---
|
||||
|
||||
## 11. 待確認問題
|
||||
|
||||
| 編號 | 問題 | 選項 | 決策 |
|
||||
|------|------|------|------|
|
||||
| Q1 | 參考臉來源 | TMDB / 手動上傳 / 兩者都有 | |
|
||||
| Q2 | 比對閾值 | Face: 0.85, Voice: 0.75 | |
|
||||
| Q3 | 非電影檔案 | 手動建立 Identity | |
|
||||
| Q4 | 分類編號格式 | P-001-010/010 | |
|
||||
| Q5 | 現有系統遷移 | 需要相容層 | |
|
||||
| Q6 | People 階層 | Identity → File (含角色名+造型) | |
|
||||
| Q7 | 非人物件階層 | 是否需要類似造型層級? | |
|
||||
| Q8 | AI 識別觸發 | 自動 / 手動 / 兩者都有 | |
|
||||
|
||||
---
|
||||
|
||||
## 12. 技術棧
|
||||
|
||||
| 層級 | 技術 |
|
||||
|------|------|
|
||||
| 後端 | Rust (momentry_core) |
|
||||
| 前端 | Vue 3 + TypeScript (Portal) |
|
||||
| 資料庫 | PostgreSQL + pgvector |
|
||||
| 向量庫 | Qdrant |
|
||||
| 快取 | Redis |
|
||||
| AI 處理 | Python (Whisper, ArcFace, YOLO...) |
|
||||
| TMDB | TMDB API v3 |
|
||||
|
||||
---
|
||||
|
||||
## 13. 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 |
|
||||
|------|------|------|--------|
|
||||
| V1.0 | 2026-04-25 | 全新設計 (File + Identity + Category) | OpenCode |
|
||||
@@ -0,0 +1,392 @@
|
||||
---
|
||||
document_type: "architecture_design"
|
||||
service: "MOMENTRY_CORE"
|
||||
title: "Momentry Core 監控架構設計"
|
||||
date: "2026-04-22"
|
||||
version: "V1.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
tags:
|
||||
- "momentry"
|
||||
- "core"
|
||||
- "監控架構設計"
|
||||
ai_query_hints:
|
||||
- "查詢 Momentry Core 監控架構設計 的內容"
|
||||
- "Momentry Core 監控架構設計 的主要目的是什麼?"
|
||||
- "如何操作或實施 Momentry Core 監控架構設計?"
|
||||
---
|
||||
|
||||
# Momentry Core 監控架構設計
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | OpenCode |
|
||||
| 建立時間 | 2026-04-22 |
|
||||
| 文件版本 | V1.0 |
|
||||
| 相關文件 | [ARCHITECTURE_OVERVIEW.md](./ARCHITECTURE_OVERVIEW.md)<br>[PERFORMANCE_AND_SCALABILITY.md](./PERFORMANCE_AND_SCALABILITY.md)<br>[SECURITY_ARCHITECTURE.md](./SECURITY_ARCHITECTURE.md) |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-04-22 | 創建監控架構設計文件 | OpenCode | OpenCode / deepseek-v3.2 |
|
||||
|
||||
---
|
||||
|
||||
## 1. 監控架構概述
|
||||
|
||||
### 1.1 監控目標
|
||||
1. **系統健康**:確保所有服務正常運行
|
||||
2. **效能監控**:追蹤系統效能指標與瓶頸
|
||||
3. **業務指標**:監控關鍵業務流程與用戶行為
|
||||
4. **安全監控**:偵測安全威脅與異常行為
|
||||
5. **成本監控**:追蹤資源使用與成本優化
|
||||
|
||||
### 1.2 監控層次
|
||||
```
|
||||
應用層監控
|
||||
├── 業務指標 (用戶行為、轉化率)
|
||||
├── 應用效能 (API 響應、錯誤率)
|
||||
└── 用戶體驗 (頁面載入、互動延遲)
|
||||
↓
|
||||
系統層監控
|
||||
├── 服務健康 (進程狀態、端口監聽)
|
||||
├── 資源使用 (CPU、記憶體、磁碟)
|
||||
└── 網絡流量 (帶寬、連接數)
|
||||
↓
|
||||
基礎設施監控
|
||||
├── 硬件狀態 (服務器、儲存)
|
||||
├── 網絡設備 (路由器、交換機)
|
||||
└── 電源環境 (UPS、溫度)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 監控指標體系
|
||||
|
||||
### 2.1 系統資源監控
|
||||
|
||||
#### 2.1.1 CPU 監控
|
||||
| 指標 | 描述 | 告警閾值 | 測量頻率 |
|
||||
|------|------|----------|----------|
|
||||
| **CPU 使用率** | 總體 CPU 使用百分比 | > 80% 持續5分鐘 | 10秒 |
|
||||
| **CPU 負載** | 平均負載 (1, 5, 15分鐘) | > 核心數×2 | 1分鐘 |
|
||||
| **CPU 核心數** | 可用 CPU 核心數量 | 變化時告警 | 5分鐘 |
|
||||
| **CPU 等待時間** | I/O 等待時間百分比 | > 20% 持續2分鐘 | 30秒 |
|
||||
|
||||
#### 2.1.2 記憶體監控
|
||||
| 指標 | 描述 | 告警閾值 | 測量頻率 |
|
||||
|------|------|----------|----------|
|
||||
| **記憶體使用率** | 已用記憶體百分比 | > 85% 持續5分鐘 | 10秒 |
|
||||
| **Swap 使用率** | Swap 空間使用百分比 | > 50% | 30秒 |
|
||||
| **緩存使用量** | 文件緩存大小 | 監控趨勢 | 1分鐘 |
|
||||
| **OOM 事件** | Out of Memory 事件 | 發生即告警 | 實時 |
|
||||
|
||||
#### 2.1.3 儲存監控
|
||||
| 指標 | 描述 | 告警閾值 | 測量頻率 |
|
||||
|------|------|----------|----------|
|
||||
| **磁碟使用率** | 磁碟空間使用百分比 | > 90% | 5分鐘 |
|
||||
| **磁碟 I/O** | 讀寫速度與延遲 | > 100ms 延遲 | 30秒 |
|
||||
| **Inode 使用率** | Inode 使用百分比 | > 80% | 5分鐘 |
|
||||
| **文件系統錯誤** | 文件系統錯誤數 | > 0 | 5分鐘 |
|
||||
|
||||
### 2.2 網絡監控
|
||||
|
||||
#### 2.2.1 網絡流量監控
|
||||
| 指標 | 描述 | 告警閾值 | 測量頻率 |
|
||||
|------|------|----------|----------|
|
||||
| **帶寬使用率** | 網絡帶寬使用百分比 | > 80% 持續5分鐘 | 30秒 |
|
||||
| **網絡錯誤率** | 錯誤包與丟包率 | > 1% | 1分鐘 |
|
||||
| **TCP 連接數** | 活躍 TCP 連接數量 | > 10000 | 30秒 |
|
||||
| **網絡延遲** | 網絡往返延遲 | > 100ms | 10秒 |
|
||||
|
||||
#### 2.2.2 端口監控
|
||||
| 端口 | 服務 | 監控內容 | 告警條件 |
|
||||
|------|------|----------|----------|
|
||||
| **3002** | Momentry API | 端口監聽、響應時間 | 端口關閉、響應>1秒 |
|
||||
| **3003** | Playground | 端口監聽、可用性 | 端口關閉、無法訪問 |
|
||||
| **5432** | PostgreSQL | 連接數、查詢延遲 | 連接失敗、查詢>500ms |
|
||||
| **6379** | Redis | 內存使用、命中率 | 內存>90%、命中率<80% |
|
||||
| **6333** | Qdrant | 向量搜索延遲 | 搜索>100ms |
|
||||
|
||||
### 2.3 應用監控
|
||||
|
||||
#### 2.3.1 API 監控
|
||||
| 端點 | 監控指標 | 告警閾值 | SLO 目標 |
|
||||
|------|----------|----------|----------|
|
||||
| `GET /api/health` | 響應時間、狀態碼 | 響應>200ms、非200狀態 | 99.9% 可用性 |
|
||||
| `POST /api/videos/register` | 處理時間、成功率 | 處理>5分鐘、成功率<95% | 95% 成功率 |
|
||||
| `GET /api/search` | 響應時間、召回率 | 響應>2秒、召回率<85% | P95 < 1.5秒 |
|
||||
| `GET /api/chunks/{id}` | 緩存命中率、延遲 | 命中率<70%、延遲>500ms | 80% 緩存命中 |
|
||||
|
||||
#### 2.3.2 處理器監控
|
||||
| 處理器 | 監控指標 | 告警閾值 | 恢復策略 |
|
||||
|--------|----------|----------|----------|
|
||||
| **ASR** | 處理時間、錯誤率 | 超時(3600s)、錯誤>10% | 重試、降級處理 |
|
||||
| **OCR** | GPU 使用率、準確率 | GPU>90%、準確率<80% | 調整批量大小 |
|
||||
| **CUT** | 算法複雜度、內存使用 | 內存泄漏、O(n²)增長 | 優化算法 |
|
||||
| **YOLO** | 推理時間、檢測準確率 | 推理>100ms/幀、準確率下降 | 模型優化 |
|
||||
|
||||
---
|
||||
|
||||
## 3. 監控工具棧
|
||||
|
||||
### 3.1 監控系統架構
|
||||
```
|
||||
數據收集層
|
||||
├── Prometheus (指標收集)
|
||||
├── Fluentd (日誌收集)
|
||||
├── OpenTelemetry (追蹤數據)
|
||||
└── Filebeat (文件日誌)
|
||||
↓
|
||||
數據存儲層
|
||||
├── Prometheus TSDB (指標存儲)
|
||||
├── Elasticsearch (日誌存儲)
|
||||
├── Jaeger (追蹤存儲)
|
||||
└── InfluxDB (時序數據)
|
||||
↓
|
||||
可視化層
|
||||
├── Grafana (儀表板)
|
||||
├── Kibana (日誌分析)
|
||||
└── Jaeger UI (追蹤可視化)
|
||||
↓
|
||||
告警層
|
||||
├── Alertmanager (告警管理)
|
||||
├── PagerDuty (值班管理)
|
||||
└── Slack/Email (通知渠道)
|
||||
```
|
||||
|
||||
### 3.2 監控工具配置
|
||||
|
||||
#### 3.2.1 Prometheus 配置
|
||||
```yaml
|
||||
# prometheus.yml
|
||||
global:
|
||||
scrape_interval: 15s
|
||||
evaluation_interval: 15s
|
||||
|
||||
scrape_configs:
|
||||
- job_name: 'momentry-api'
|
||||
static_configs:
|
||||
- targets: ['localhost:3002']
|
||||
labels:
|
||||
service: 'momentry-api'
|
||||
environment: 'production'
|
||||
|
||||
- job_name: 'postgresql'
|
||||
static_configs:
|
||||
- targets: ['localhost:9187'] # postgres_exporter
|
||||
labels:
|
||||
service: 'postgresql'
|
||||
|
||||
- job_name: 'redis'
|
||||
static_configs:
|
||||
- targets: ['localhost:9121'] # redis_exporter
|
||||
labels:
|
||||
service: 'redis'
|
||||
|
||||
- job_name: 'node'
|
||||
static_configs:
|
||||
- targets: ['localhost:9100'] # node_exporter
|
||||
labels:
|
||||
service: 'node'
|
||||
```
|
||||
|
||||
#### 3.2.2 Grafana 儀表板
|
||||
| 儀表板 | 用途 | 關鍵面板 |
|
||||
|--------|------|----------|
|
||||
| **系統概覽** | 整體系統健康 | CPU/記憶體/磁碟使用率 |
|
||||
| **API 監控** | API 效能監控 | 響應時間、錯誤率、QPS |
|
||||
| **數據庫監控** | 數據庫效能 | 查詢延遲、連接數、鎖等待 |
|
||||
| **處理器監控** | 視頻處理監控 | 處理時間、隊列長度、錯誤率 |
|
||||
| **業務監控** | 業務指標 | 註冊數、搜索數、用戶活躍度 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 日誌管理
|
||||
|
||||
### 4.1 日誌分級與格式
|
||||
|
||||
#### 4.1.1 日誌級別
|
||||
| 級別 | 描述 | 使用場景 |
|
||||
|------|------|----------|
|
||||
| **ERROR** | 錯誤,需要立即處理 | 系統崩潰、數據丟失 |
|
||||
| **WARN** | 警告,需要注意 | 效能下降、配置問題 |
|
||||
| **INFO** | 信息,正常操作 | 用戶操作、系統狀態 |
|
||||
| **DEBUG** | 調試,開發使用 | 詳細調試信息 |
|
||||
| **TRACE** | 追蹤,詳細追蹤 | 性能分析、調試 |
|
||||
|
||||
#### 4.1.2 日誌格式規範
|
||||
```json
|
||||
{
|
||||
"timestamp": "2026-04-22T10:30:00Z",
|
||||
"level": "INFO",
|
||||
"service": "momentry-api",
|
||||
"module": "video_processor",
|
||||
"message": "Video processing completed",
|
||||
"video_id": "video_123",
|
||||
"duration_ms": 12345,
|
||||
"user_id": "user_456",
|
||||
"request_id": "req_789",
|
||||
"correlation_id": "corr_abc"
|
||||
}
|
||||
```
|
||||
|
||||
### 4.2 日誌收集與分析
|
||||
|
||||
#### 4.2.1 日誌收集配置
|
||||
```yaml
|
||||
# fluentd 配置
|
||||
<source>
|
||||
@type tail
|
||||
path /var/log/momentry/*.log
|
||||
tag momentry.*
|
||||
format json
|
||||
</source>
|
||||
|
||||
<filter momentry.**>
|
||||
@type record_transformer
|
||||
<record>
|
||||
hostname ${hostname}
|
||||
environment production
|
||||
</record>
|
||||
</filter>
|
||||
|
||||
<match momentry.**>
|
||||
@type elasticsearch
|
||||
host elasticsearch.local
|
||||
port 9200
|
||||
logstash_format true
|
||||
</match>
|
||||
```
|
||||
|
||||
#### 4.2.2 日誌分析用例
|
||||
| 分析場景 | 查詢語句 | 告警條件 |
|
||||
|----------|----------|----------|
|
||||
| **錯誤率分析** | `level:ERROR | stats count by service` | 錯誤數 > 10/分鐘 |
|
||||
| **效能分析** | `message: /processing.*duration_ms/ | stats avg(duration_ms)` | 平均處理時間 > 警告閾值 |
|
||||
| **用戶行為** | `message: /user.*action/ | stats count by user_id,action` | 異常行為模式 |
|
||||
| **安全審計** | `message: /(login|auth|access)/ | search suspicious_pattern` | 登錄失敗 > 5次 |
|
||||
|
||||
---
|
||||
|
||||
## 5. 告警管理
|
||||
|
||||
### 5.1 告警策略
|
||||
|
||||
#### 5.1.1 告警級別
|
||||
| 級別 | 響應時間 | 通知方式 | 處理流程 |
|
||||
|------|----------|----------|----------|
|
||||
| **P0 - 緊急** | 立即 | 電話、短信、推送 | 立即處理,全員通知 |
|
||||
| **P1 - 高** | 15分鐘 | Slack、Email | 值班工程師處理 |
|
||||
| **P2 - 中** | 1小時 | Email、儀表板 | 工作日處理 |
|
||||
| **P3 - 低** | 24小時 | 儀表板 | 計劃性處理 |
|
||||
|
||||
#### 5.1.2 告警規則示例
|
||||
```yaml
|
||||
# alertmanager 配置
|
||||
groups:
|
||||
- name: momentry-critical
|
||||
rules:
|
||||
- alert: APIDown
|
||||
expr: up{job="momentry-api"} == 0
|
||||
for: 1m
|
||||
labels:
|
||||
severity: critical
|
||||
annotations:
|
||||
summary: "API service is down"
|
||||
description: "{{ $labels.instance }} has been down for more than 1 minute"
|
||||
|
||||
- alert: HighCPUUsage
|
||||
expr: rate(process_cpu_seconds_total[5m]) * 100 > 80
|
||||
for: 5m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "High CPU usage detected"
|
||||
description: "CPU usage is above 80% for 5 minutes"
|
||||
```
|
||||
|
||||
### 5.2 值班管理
|
||||
|
||||
#### 5.2.1 值班排班
|
||||
| 時段 | 值班人員 | 聯繫方式 | 覆蓋範圍 |
|
||||
|------|----------|----------|----------|
|
||||
| **工作日 9:00-18:00** | 開發團隊 | Slack、內部電話 | P0-P2 告警 |
|
||||
| **工作日 18:00-9:00** | 值班工程師 | 手機、短信 | P0-P1 告警 |
|
||||
| **週末/節假日** | 輪值工程師 | 手機、緊急電話 | P0 告警 |
|
||||
|
||||
#### 5.2.2 告警升級流程
|
||||
```
|
||||
檢測到告警 → 初始響應
|
||||
↓
|
||||
評估嚴重程度
|
||||
↓
|
||||
P0/P1: 立即通知值班人員
|
||||
P2/P3: 記錄到工單系統
|
||||
↓
|
||||
開始處理
|
||||
↓
|
||||
問題解決
|
||||
↓
|
||||
撰寫事後報告
|
||||
↓
|
||||
改進預防措施
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 監控最佳實踐
|
||||
|
||||
### 6.1 監控設計原則
|
||||
1. **關鍵指標優先**:監控最重要的業務指標
|
||||
2. **分層監控**:從基礎設施到應用層全面監控
|
||||
3. **自動化監控**:自動發現、配置、告警
|
||||
4. **可視化優先**:儀表板清晰展示關鍵信息
|
||||
5. **告警有效性**:避免告警疲勞,確保告警有意義
|
||||
|
||||
### 6.2 效能優化建議
|
||||
1. **指標採樣**:合理設置採樣頻率,平衡精度與成本
|
||||
2. **日誌輪轉**:自動清理舊日誌,控制儲存成本
|
||||
3. **查詢優化**:使用索引、聚合優化監控告警
|
||||
4. **儲存分層**:熱數據快速訪問,冷數據歸檔存儲
|
||||
|
||||
### 6.3 成本控制策略
|
||||
1. **監控成本分析**:定期分析監控系統成本
|
||||
2. **資源優化**:根據使用模式調整資源配置
|
||||
3. **數據保留策略**:設置合理的數據保留期限
|
||||
4. **雲服務優化**:選擇合適的雲監控服務方案
|
||||
|
||||
---
|
||||
|
||||
## 7. 未來發展方向
|
||||
|
||||
### 7.1 近期改進(1-3個月)
|
||||
1. **AI 異常檢測**:使用機器學習檢測異常模式
|
||||
2. **預測性監控**:基於歷史數據預測潛在問題
|
||||
3. **自動化修復**:簡單問題自動修復機制
|
||||
|
||||
### 7.2 中期規劃(3-6個月)
|
||||
1. **跨區域監控**:支持多區域部署監控
|
||||
2. **多租戶監控**:為不同客戶提供隔離監控
|
||||
3. **移動監控**:移動端監控應用
|
||||
|
||||
### 7.3 長期願景(6-12個月)
|
||||
1. **智能運維**:AI 驅動的智能運維平台
|
||||
2. **業務影響分析**:監控事件對業務影響分析
|
||||
3. **自動擴展預測**:基於監控數據的自動擴展
|
||||
|
||||
---
|
||||
|
||||
## 8. 總結
|
||||
|
||||
Momentry Core 的監控架構設計提供:
|
||||
1. **全面覆蓋**:從基礎設施到應用層的完整監控
|
||||
2. **實時響應**:快速檢測問題並通知相關人員
|
||||
3. **數據驅動**:基於數據的決策與優化
|
||||
4. **持續改進**:不斷優化監控策略與工具
|
||||
|
||||
通過完善的監控體系,確保系統穩定運行,快速發現並解決問題,為用戶提供高質量的服務。
|
||||
@@ -0,0 +1,192 @@
|
||||
---
|
||||
document_type: "architecture_design"
|
||||
service: "MOMENTRY_CORE"
|
||||
title: "監控系統實戰部署指南"
|
||||
date: "2026-04-25"
|
||||
version: "V1.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
tags:
|
||||
- "監控系統實戰部署指南"
|
||||
ai_query_hints:
|
||||
- "查詢 監控系統實戰部署指南 的內容"
|
||||
- "監控系統實戰部署指南 的主要目的是什麼?"
|
||||
- "如何操作或實施 監控系統實戰部署指南?"
|
||||
---
|
||||
|
||||
# 監控系統實戰部署指南
|
||||
|
||||
## 1. 快速部署方案
|
||||
|
||||
### 1.1 Docker Compose 部署
|
||||
|
||||
創建 `docker-compose.monitoring.yml`:
|
||||
|
||||
```yaml
|
||||
version: '3.8'
|
||||
services:
|
||||
prometheus:
|
||||
image: prom/prometheus:latest
|
||||
container_name: momentry_prometheus
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ./monitoring/prometheus:/etc/prometheus
|
||||
ports:
|
||||
- "9090:9090"
|
||||
networks:
|
||||
- monitoring
|
||||
|
||||
grafana:
|
||||
image: grafana/grafana:latest
|
||||
container_name: momentry_grafana
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- GF_SECURITY_ADMIN_PASSWORD=admin
|
||||
volumes:
|
||||
- ./monitoring/grafana:/var/lib/grafana
|
||||
ports:
|
||||
- "3000:3000"
|
||||
networks:
|
||||
- monitoring
|
||||
depends_on:
|
||||
- prometheus
|
||||
|
||||
networks:
|
||||
monitoring:
|
||||
driver: bridge
|
||||
```
|
||||
|
||||
### 1.2 創建配置文件
|
||||
|
||||
```bash
|
||||
mkdir -p monitoring/prometheus
|
||||
|
||||
cat > monitoring/prometheus/prometheus.yml << 'EOF'
|
||||
global:
|
||||
scrape_interval: 15s
|
||||
evaluation_interval: 15s
|
||||
|
||||
scrape_configs:
|
||||
- job_name: 'prometheus'
|
||||
static_configs:
|
||||
- targets: ['localhost:9090']
|
||||
|
||||
- job_name: 'momentry-api'
|
||||
static_configs:
|
||||
- targets: ['host.docker.internal:3002']
|
||||
metrics_path: '/metrics'
|
||||
scrape_interval: 30s
|
||||
EOF
|
||||
```
|
||||
|
||||
### 1.3 啟動監控系統
|
||||
|
||||
```bash
|
||||
docker-compose -f docker-compose.monitoring.yml up -d
|
||||
```
|
||||
|
||||
**訪問地址**:
|
||||
- **Grafana**: http://localhost:3000 (帳號: admin, 密碼: admin)
|
||||
- **Prometheus**: http://localhost:9090
|
||||
|
||||
---
|
||||
|
||||
## 2. Momentry Core 指標集成
|
||||
|
||||
### 2.1 添加 Prometheus 依賴
|
||||
|
||||
在 `Cargo.toml` 中添加:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
prometheus = "0.13"
|
||||
```
|
||||
|
||||
### 2.2 創建指標模塊
|
||||
|
||||
創建 `src/core/metrics/mod.rs`:
|
||||
|
||||
```rust
|
||||
use prometheus::{self, Encoder, TextEncoder, Gauge, Counter, Registry};
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
pub static ref API_REQUESTS_TOTAL: Counter = register_counter!(
|
||||
"momentry_api_requests_total",
|
||||
"API 請求總數"
|
||||
).unwrap();
|
||||
|
||||
pub static ref ACTIVE_CONNECTIONS: Gauge = register_gauge!(
|
||||
"momentry_active_connections",
|
||||
"活躍連接數"
|
||||
).unwrap();
|
||||
}
|
||||
|
||||
pub static REGISTRY: Lazy<Registry> = Lazy::new(|| {
|
||||
let registry = Registry::new();
|
||||
registry.register(Box::new(API_REQUESTS_TOTAL.clone())).unwrap();
|
||||
registry.register(Box::new(ACTIVE_CONNECTIONS.clone())).unwrap();
|
||||
registry
|
||||
});
|
||||
|
||||
pub fn gather_metrics() -> String {
|
||||
let metric_families = REGISTRY.gather();
|
||||
let encoder = TextEncoder::new();
|
||||
let mut buffer = vec![];
|
||||
encoder.encode(&metric_families, &mut buffer).unwrap();
|
||||
String::from_utf8(buffer).unwrap()
|
||||
}
|
||||
```
|
||||
|
||||
### 2.3 添加 API 指標端點
|
||||
|
||||
在 API 路由中添加:
|
||||
|
||||
```rust
|
||||
use axum::{Router, routing::get, response::IntoResponse};
|
||||
use crate::core::metrics;
|
||||
|
||||
pub fn metrics_routes() -> Router {
|
||||
Router::new().route("/metrics", get(metrics_handler))
|
||||
}
|
||||
|
||||
async fn metrics_handler() -> impl IntoResponse {
|
||||
(
|
||||
[(axum::http::header::CONTENT_TYPE, "text/plain; version=0.0.4")],
|
||||
metrics::gather_metrics(),
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 生產部署建議
|
||||
|
||||
### 3.1 安全配置
|
||||
|
||||
1. **更改默認密碼**
|
||||
```bash
|
||||
# 更改 Grafana 管理員密碼
|
||||
docker exec momentry_grafana grafana-cli admin reset-admin-password newpassword
|
||||
```
|
||||
|
||||
2. **啟用 HTTPS**
|
||||
```yaml
|
||||
grafana:
|
||||
environment:
|
||||
- GF_SERVER_PROTOCOL=https
|
||||
```
|
||||
|
||||
### 3.2 監控項目
|
||||
|
||||
| 監控項目 | 指標名稱 | 告警閾值 |
|
||||
|----------|----------|----------|
|
||||
| API 請求數 | `momentry_api_requests_total` | N/A |
|
||||
| 活躍連接數 | `momentry_active_connections` | >100 |
|
||||
| 錯誤率 | `momentry_api_errors_total` | >10% |
|
||||
| 響應時間 | `momentry_api_response_time` | >1s |
|
||||
|
||||
---
|
||||
|
||||
**最後更新**: 2026-04-22
|
||||
**部署時間**: 10-30 分鐘
|
||||
@@ -0,0 +1,381 @@
|
||||
# Momentry Core 多模態語義搜尋設計文檔 V5.0 (全集)
|
||||
|
||||
**更新日期**: 2026-04-10
|
||||
**版本**: V5.0 (Final Integration)
|
||||
**狀態**: 設計完成,準備實作
|
||||
|
||||
---
|
||||
|
||||
## 1. 系統架構總覽
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
%% 樣式定義
|
||||
classDef storage fill:#e1f5fe,stroke:#01579b,stroke-width:2px;
|
||||
classDef processor fill:#fff3e0,stroke:#e65100,stroke-width:2px;
|
||||
classDef identity fill:#e8f5e9,stroke:#1b5e20,stroke-width:2px;
|
||||
classDef search fill:#f3e5f5,stroke:#4a148c,stroke-width:2px;
|
||||
|
||||
subgraph "1. Input Sources (輸入源)"
|
||||
Video[Video/Audio File]
|
||||
end
|
||||
|
||||
subgraph "2. Analysis Processors (分析模組 - 模組化)"
|
||||
ASR[ASR Processor\n(Whisper)]:::processor
|
||||
ASRX[ASRX Processor\n(SpeechBrain)]:::processor
|
||||
YOLO[YOLO Processor\n(Object Detection)]:::processor
|
||||
OCR[OCR Processor\n(Text Recognition)]:::processor
|
||||
FACE[Face Processor\n(Face ID/Cluster)]:::processor
|
||||
POSE[Pose Processor\n(KeyPoints 33)]:::processor
|
||||
SCENE[Scene Processor\n(Places365)]:::processor
|
||||
AUDIO[AUDIO EVENT Processor\n(PANNs/YAMNet)]:::processor
|
||||
POSE_ANALYZER[Pose Analyzer Processor\n(Action/Gesture/Sports)]:::processor
|
||||
CONTEXT[Context Inference Processor\n(Rule Engine + LLM)]:::processor
|
||||
SPORTS[Sports Classifier Processor\n(Rule Engine)]:::processor
|
||||
end
|
||||
|
||||
subgraph "3. Identity & Binding (身份與綁定)"
|
||||
VoiceBind[Voice Binding\n(Speaker -> Talent)]:::identity
|
||||
FaceBind[Face Binding\n(Face -> Talent)]:::identity
|
||||
RoleBind[Role Casting\n(Talent -> Character)]:::identity
|
||||
TalentDB[(Talents DB\nVoice/Face Embeddings)]:::storage
|
||||
CharDB[(Characters DB\nRoles/Multi-lang)]:::storage
|
||||
end
|
||||
|
||||
subgraph "4. Data Storage (數據存儲)"
|
||||
PG[(PostgreSQL\nChunks/Relations/Metadata)]:::storage
|
||||
Qdrant[(Qdrant\nVector Search Engine)]:::storage
|
||||
end
|
||||
|
||||
subgraph "5. Search & Query (搜尋與查詢)"
|
||||
SearchProc[Search Processor\n(LLM Parser -> Hybrid Query)]:::search
|
||||
User[User / API]
|
||||
end
|
||||
|
||||
%% 數據流向
|
||||
Video --> ASR
|
||||
Video --> ASRX
|
||||
Video --> YOLO
|
||||
Video --> OCR
|
||||
Video --> FACE
|
||||
Video --> POSE
|
||||
Video --> SCENE
|
||||
Video --> AUDIO
|
||||
|
||||
%% 處理結果 -> 特徵提取/分析
|
||||
POSE --> POSE_ANALYZER
|
||||
POSE --> SPORTS
|
||||
AUDIO --> AUDIO
|
||||
SCENE --> CONTEXT
|
||||
YOLO --> CONTEXT
|
||||
YOLO --> SPORTS
|
||||
ASRX --> CONTEXT
|
||||
ASR --> CONTEXT
|
||||
POSE_ANALYZER --> SPORTS
|
||||
|
||||
%% 結果寫入
|
||||
ASR --> PG
|
||||
ASRX --> PG
|
||||
YOLO --> PG
|
||||
OCR --> PG
|
||||
FACE --> PG
|
||||
SCENE --> PG
|
||||
AUDIO --> PG
|
||||
POSE_ANALYZER --> PG
|
||||
CONTEXT --> PG
|
||||
SPORTS --> PG
|
||||
|
||||
%% 綁定邏輯
|
||||
ASRX -.->|Speaker ID| VoiceBind
|
||||
FACE -.->|Face ID| FaceBind
|
||||
VoiceBind --> TalentDB
|
||||
FaceBind --> TalentDB
|
||||
TalentDB --> RoleBind
|
||||
CharDB --> RoleBind
|
||||
RoleBind -.->|Resolve Names| PG
|
||||
|
||||
%% 向量同步
|
||||
PG -.->|Sync Payload & Vectors| Qdrant
|
||||
|
||||
%% 搜尋邏輯
|
||||
User --> SearchProc
|
||||
SearchProc --> PG
|
||||
SearchProc --> Qdrant
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 核心處理模組清單 (Processor Modules)
|
||||
|
||||
每個模組遵循單一職責原則 (Single Responsibility Principle)。
|
||||
|
||||
| 模組名稱 | 職責 (Responsibility) | 核心技術/模型 | 輸出維度 (Dimension) |
|
||||
|:---|:---|:---|:---|
|
||||
| **ASR** | 語音轉文字 | Whisper (small/int8 + VAD) | **Text Content** (語音內容) |
|
||||
| **ASRX** | 說話人分離/聲紋提取 | SpeechBrain (ECAPA-TDNN) | **Voice ID**, **Speaker Embedding** (192-dim) |
|
||||
| **YOLO** | 物體檢測 | YOLOv8 (COCO 80 classes) | **Object** (車輛、武器、物品、運動裝備) |
|
||||
| **OCR** | 畫面文字識別 | EasyOCR / PaddleOCR | **Text** (字幕、招牌、文件) |
|
||||
| **FACE** | 人臉檢測與聚類 | RetinaFace / ArcFace | **Face ID**, **Face Embedding** |
|
||||
| **POSE** | 骨架關鍵點提取 | MediaPipe / YOLO-Pose | **Keypoints** (33 點坐標) |
|
||||
| **POSE ANALYZER** | 動作/手勢解碼 | Heuristics (規則引擎) | **Action** (站/坐/臥/揮手/打鬥/泳姿/旋轉) |
|
||||
| **SCENE** | 場景分類 | Places365 (ResNet18) | **Location** (Macro/Semantic/Raw 三層級) |
|
||||
| **AUDIO EVENT** | 環境/特效音識別 | PANNs / YAMNet | **Audio Event** (槍聲/雨聲/狗叫/樂器/哨音) |
|
||||
| **CONTEXT INFERENCE** | 環境/氛圍推論 | Rule Engine + LLM | **Context** (季節/溫度/節慶/天氣) |
|
||||
| **SPORTS CLASSIFIER** | 運動項目識別 | Multi-Modal Rule Engine | **Sport Type** (棒球/足球/游泳/跳水/滑冰...) |
|
||||
|
||||
---
|
||||
|
||||
## 3. 數據架構設計 (Data Architecture)
|
||||
|
||||
### 3.1 Chunk 定義 (Video Chunk)
|
||||
**定義**: 特定視頻文件 (`uuid`) 內,從 `start_frame` 到 `end_frame` 之間的**連續畫面**。
|
||||
**存儲**:
|
||||
* **PostgreSQL**: 權威主數據 (Metadata, Relations, Complex Queries).
|
||||
* **Qdrant**: 向量檢索與 Payload 過濾 (Fast Retrieval).
|
||||
|
||||
### 3.2 數據庫 Schema (PostgreSQL)
|
||||
|
||||
```sql
|
||||
-- ==========================================
|
||||
-- 1. 核心 Chunk 表
|
||||
-- ==========================================
|
||||
CREATE TABLE chunks (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
uuid VARCHAR(32) NOT NULL, -- 視頻 ID
|
||||
chunk_id VARCHAR(64) NOT NULL,
|
||||
|
||||
-- 物理邊界定義 (核心)
|
||||
start_frame BIGINT NOT NULL,
|
||||
end_frame BIGINT NOT NULL,
|
||||
fps FLOAT8 NOT NULL,
|
||||
duration_sec FLOAT8 GENERATED ALWAYS AS ((end_frame - start_frame) / fps) STORED,
|
||||
|
||||
-- 2. 人 (Who)
|
||||
speaker_ids TEXT[] DEFAULT '{}', -- 觀察到的說話人 ID (Speaker X)
|
||||
face_ids TEXT[] DEFAULT '{}', -- 觀察到的人臉 ID (Face Y)
|
||||
|
||||
-- 3. 事 (What) - 語音與行為
|
||||
text_content TEXT, -- ASR 文本
|
||||
action_tags TEXT[] DEFAULT '{}', -- Pose 動作 (e.g. ['running', 'fighting', 'swimming'])
|
||||
audio_events TEXT[] DEFAULT '{}', -- 音頻事件 (e.g. ['gunshot', 'scream', 'whistle'])
|
||||
event_tags JSONB DEFAULT '[]', -- 融合事件 (e.g. [{"tag":"gunfight", "score":0.8}])
|
||||
sport_type VARCHAR(32), -- 運動項目 (e.g. 'baseball', 'diving')
|
||||
sport_actions TEXT[] DEFAULT '{}', -- 運動細分動作 (e.g. ['pitching', 'smash'])
|
||||
sport_sequence JSONB DEFAULT '[]', -- 動作序列 (e.g. ["takeoff", "twist", "entry"])
|
||||
|
||||
-- 4. 地 (Where) & 物 (Object)
|
||||
scene_raw TEXT[] DEFAULT '{}', -- Places365 原始標籤
|
||||
scene_semantic TEXT[] DEFAULT '{}', -- 高層語義 (e.g. ['office', 'indoor'])
|
||||
object_tags TEXT[] DEFAULT '{}', -- YOLO 物件 (e.g. ['car', 'gun', 'baseball_bat'])
|
||||
|
||||
-- 5. 上下文 (Context)
|
||||
context_season VARCHAR(16), -- 'winter', 'summer'
|
||||
context_temp VARCHAR(16), -- 'hot', 'cold'
|
||||
context_weather VARCHAR(16), -- 'rainy', 'snowy'
|
||||
context_festivals TEXT[] DEFAULT '{}', -- ['christmas', 'halloween']
|
||||
|
||||
-- 向量與索引
|
||||
vector_ids JSONB, -- 指向 Qdrant Point ID
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
|
||||
UNIQUE(chunk_id),
|
||||
UNIQUE(uuid, chunk_id)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_chunks_uuid ON chunks(uuid);
|
||||
CREATE INDEX idx_chunks_frame_range ON chunks(uuid, start_frame, end_frame);
|
||||
CREATE INDEX idx_chunks_attrs ON chunks USING GIN (scene_semantic, object_tags, audio_events);
|
||||
|
||||
-- ==========================================
|
||||
-- 6. 身份綁定表 (Identity Binding)
|
||||
-- ==========================================
|
||||
|
||||
-- 真實人才庫 (Talent)
|
||||
CREATE TABLE talents (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
real_name TEXT,
|
||||
voice_embedding VECTOR(192), -- 聲紋參考向量 (ECAPA-TDNN)
|
||||
face_embedding VECTOR(512) -- 人臉參考向量 (ArcFace)
|
||||
);
|
||||
|
||||
-- 劇中角色庫 (Character)
|
||||
CREATE TABLE characters (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
video_uuid TEXT NOT NULL,
|
||||
name TEXT NOT NULL, -- 角色名
|
||||
language_track TEXT DEFAULT 'original', -- 語言軌道 (dub_zh_tw, dub_en)
|
||||
is_voice_only BOOLEAN DEFAULT FALSE, -- 無臉角色 (動畫/旁白/AI)
|
||||
metadata JSONB DEFAULT '{}'
|
||||
);
|
||||
|
||||
-- 飾演關係 (Casting)
|
||||
CREATE TABLE castings (
|
||||
talent_id BIGINT REFERENCES talents(id),
|
||||
character_id BIGINT REFERENCES characters(id),
|
||||
track_type TEXT DEFAULT 'original',
|
||||
PRIMARY KEY (talent_id, character_id, track_type)
|
||||
);
|
||||
|
||||
-- 綁定映射 (Signal -> Talent)
|
||||
CREATE TABLE identity_bindings (
|
||||
binding_type VARCHAR(32), -- 'face', 'speaker'
|
||||
binding_value VARCHAR(64), -- 機器 ID (e.g. 'face_1', 'speaker_3')
|
||||
talent_id BIGINT REFERENCES talents(id),
|
||||
UNIQUE(binding_type, binding_value)
|
||||
);
|
||||
```
|
||||
|
||||
### 3.3 Qdrant Payload 結構 (扁平化過濾)
|
||||
|
||||
```json
|
||||
{
|
||||
"uuid": "384b0ff44aaaa1f1",
|
||||
"chunk_id": "chunk_001",
|
||||
"start_frame": 100,
|
||||
"end_frame": 200,
|
||||
|
||||
"who_is_present": ["Alice", "Bob"],
|
||||
"who_is_speaking": ["Alice"],
|
||||
|
||||
"what_happening": ["arguing", "shouting"],
|
||||
"what_objects": ["person", "table"],
|
||||
"what_audio": ["raised_voice"],
|
||||
"sport_type": null,
|
||||
|
||||
"where_semantic": ["office", "indoor"],
|
||||
"where_weather": null,
|
||||
|
||||
"context_season": null,
|
||||
"context_time": "day"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 搜尋維度 (5W1H + Context + Sports)
|
||||
|
||||
### 4.1 人 (Person / Who)
|
||||
* **身份解析**: `speaker_X` / `face_Y` -> `talent` -> `character`.
|
||||
* **屬性過濾**: 性別、年齡、體型、五官、服裝 (VLM/Heuristics).
|
||||
* **聲紋檢索**: 上傳音頻片段 -> Cosine Similarity (ECAPA-TDNN 192-dim).
|
||||
|
||||
### 4.2 事 (Event / What)
|
||||
* **語音語義**: ASR 文本向量檢索.
|
||||
* **視覺行為**: Pose Analyzer 標籤 (打架、擁抱、揮手).
|
||||
* **融合事件**: `gunfight`, `romantic_scene`, `interview` (多信號規則融合).
|
||||
|
||||
### 4.3 時 (Time / When)
|
||||
* **精確幀**: `start_frame`, `end_frame`.
|
||||
* **相對時間**: "最後 5 分鐘".
|
||||
|
||||
### 4.4 地 (Location / Where)
|
||||
* **場景語義**: Places365 -> 宏觀/語義/原始三層映射 (e.g., `beach` -> `outdoor`).
|
||||
* **天氣/環境**: `rainy`, `sunny`, `night` (Context Inference).
|
||||
|
||||
### 4.5 物 (Object / Which)
|
||||
* **YOLO 物件**: `car`, `gun`, `dog`.
|
||||
* **音頻物件**: `siren`, `barking`.
|
||||
|
||||
### 4.6 上下文 (Context)
|
||||
* **季節**: `winter` (雪/圍巾), `summer` (泳衣/太陽眼鏡).
|
||||
* **節慶**: `christmas` (聖誕樹/鈴鐺聲), `cny` (鞭炮/紅燈籠).
|
||||
|
||||
### 4.7 運動 (Sports)
|
||||
* **球類**: 棒球 (球棒/打擊聲/揮棒), 籃球 (運球聲/投籃), 足球 (哨音/踢球).
|
||||
* **水上/冰上運動 (詳細特徵)**:
|
||||
* **🏊 游泳 (Swimming)**:
|
||||
* *場景*: `swimming_pool`, `water`.
|
||||
* *物件*: `goggles`, `swim_cap`, `lane_rope`.
|
||||
* *動作*: `freestyle_stroke` (自由式), `breaststroke` (蛙式), `butterfly` (蝶式), `backstroke` (仰式).
|
||||
* *音頻*: `water_splash` (水花聲), `rhythmic_breathing` (規律換氣聲).
|
||||
* **🤿 跳水 (Diving)**:
|
||||
* *場景*: `diving_board`, `platform_10m`.
|
||||
* *動作序列*: `takeoff` (起跳) → `aerial_twist` (空中翻轉) → `entry` (入水).
|
||||
* *音頻*: `high_pitch_whistle` (哨音) → `massive_splash` (巨大入水聲).
|
||||
* **⛸️ 滑冰 (Ice Skating)**:
|
||||
* *場景*: `ice_rink`, `winter`.
|
||||
* *物件*: `ice_skates`, `barrier`.
|
||||
* *動作*: `gliding` (滑行), `spinning` (旋轉), `jumping` (跳躍).
|
||||
* *音頻*: `blade_on_ice` (冰刀摩擦聲), `classical_music` (花滑配樂).
|
||||
|
||||
---
|
||||
|
||||
## 5. 搜尋執行流程 (Search Workflow)
|
||||
|
||||
### 5.1 用戶輸入
|
||||
> *"找一下昨天在辦公室,那個穿西裝的男人在生氣地罵人,旁邊還有狗叫的片段。"*
|
||||
|
||||
### 5.2 LLM 解析 (`Search Processor`)
|
||||
```json
|
||||
{
|
||||
"who": {
|
||||
"clothing": ["suit"],
|
||||
"expression": ["angry"],
|
||||
"gender": "male"
|
||||
},
|
||||
"where": {
|
||||
"semantic": ["office"]
|
||||
},
|
||||
"what": {
|
||||
"action": ["arguing", "shouting"],
|
||||
"audio_event": ["dog_bark"]
|
||||
},
|
||||
"when": {
|
||||
"relative": "yesterday"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5.3 混合查詢 (Hybrid Query)
|
||||
|
||||
1. **解析身份 (Who)**:
|
||||
* 查詢 `identity_bindings`,找到符合 "穿西裝男人" 的機器 ID (`face_5`).
|
||||
2. **構建 SQL (PostgreSQL)**:
|
||||
```sql
|
||||
SELECT chunk_id, start_frame, end_frame FROM chunks
|
||||
WHERE uuid = '384b0ff44aaaa1f1'
|
||||
AND 'face_5' = ANY(face_ids)
|
||||
AND scene_semantic @> ARRAY['office']
|
||||
AND action_tags @> ARRAY['arguing', 'shouting']
|
||||
AND audio_events @> ARRAY['dog_bark'];
|
||||
```
|
||||
3. **構建 Vector Search (Qdrant)**:
|
||||
* 如果 SQL 結果為空或用戶語意模糊,切換至 Qdrant Payload Filter + Vector Similarity.
|
||||
4. **返回結果**:
|
||||
* Chunk 列表,包含精確的 `start_frame`, `end_frame`.
|
||||
|
||||
---
|
||||
|
||||
## 6. 實施路線圖 (Implementation Roadmap)
|
||||
|
||||
### Phase 1: 基礎設施與 Schema (第 1 週)
|
||||
- [ ] 執行 PostgreSQL Schema V5 更新 (Chunks, Talents, Castings, Bindings, Sports).
|
||||
- [ ] 建立 Qdrant Collection (`momentry_chunks`),配置 Multi-Vector 和 Payload 索引.
|
||||
- [ ] 編寫 `scene_hierarchy_processor.py` (場景映射層).
|
||||
- [ ] 編寫 `scene_mapping.json`.
|
||||
|
||||
### Phase 2: 信號提取模組 (第 2-3 週)
|
||||
- [ ] 部署 `audio_event_processor.py` (PANNs/YAMNet).
|
||||
- [ ] 部署 `pose_analyzer_processor.py` (基礎規則:站/坐/揮手/打鬥/泳姿).
|
||||
- [ ] 部署 `context_inference_processor.py` (季節/節慶/天氣推斷).
|
||||
- [ ] 部署 `sports_classifier_processor.py` (運動分類規則引擎).
|
||||
- [ ] 確保所有處理器的輸出能正確映射並寫入 `chunks` 表.
|
||||
|
||||
### Phase 3: 身份綁定系統 (第 4 週)
|
||||
- [ ] 部署 `voice_embedding_extractor.py` (聲紋提取與比對).
|
||||
- [ ] 實現 `identity_resolver.py`:將機器 ID 綁定到 `talents` 和 `characters`.
|
||||
- [ ] 提供 API: `POST /api/v1/person/bind`.
|
||||
|
||||
### Phase 4: 搜尋引擎整合 (第 5 週)
|
||||
- [ ] 開發 `search_processor.py` (LLM Parser + SQL Builder).
|
||||
- [ ] 實現 `POST /api/v1/search/smart` 端點.
|
||||
- [ ] 測試複雜查詢 (人+事+時+地+物+上下文+運動).
|
||||
|
||||
### Phase 5: 優化與前端對接 (第 6 週)
|
||||
- [ ] 性能優化 (索引調整、查詢緩存).
|
||||
- [ ] 前端搜尋介面展示多維度過濾條件.
|
||||
- [ ] 前端視頻播放器跳轉至精確 `start_frame`.
|
||||
|
||||
---
|
||||
|
||||
此設計文檔已涵蓋所有需求,確立了 Momentry Core 作為一個**高度模組化、多模態、支持深度語義搜尋**的系統架構。所有討論過的維度 (包括運動、配音、動畫、聲紋) 均已整合。
|
||||
@@ -0,0 +1,709 @@
|
||||
---
|
||||
document_type: "architecture_design"
|
||||
service: "N8N"
|
||||
title: "n8n Video RAG Workflow - Node 設計"
|
||||
date: "2026-03-22"
|
||||
version: "V1.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
tags:
|
||||
- "workflow"
|
||||
- "video"
|
||||
- "node"
|
||||
ai_query_hints:
|
||||
- "查詢 n8n Video RAG Workflow - Node 設計 的內容"
|
||||
- "n8n Video RAG Workflow - Node 設計 的主要目的是什麼?"
|
||||
- "如何操作或實施 n8n Video RAG Workflow - Node 設計?"
|
||||
---
|
||||
|
||||
# n8n Video RAG Workflow - Node 設計
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | Warren |
|
||||
| 建立時間 | 2026-03-22 |
|
||||
| 文件版本 | V1.1 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-03-22 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
|
||||
| V1.1 | 2026-03-25 | 更新API回應格式 (media_url→file_path) 與認證標頭 | OpenCode | deepseek-reasoner |
|
||||
|
||||
---
|
||||
|
||||
## 完整 Workflow 架構
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ n8n Workflow: Video RAG Demo │
|
||||
├─────────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ Phase 1: SFTPGo 準備 (全部在 n8n Node 內執行) │ │
|
||||
│ │ │ │
|
||||
│ │ ① Webhook Trigger │ │
|
||||
│ │ ↓ │ │
|
||||
│ │ ② Set Variables (解析 file_name, query) │ │
|
||||
│ │ ↓ │ │
|
||||
│ │ ③ Get SFTPGo Token │ │
|
||||
│ │ ↓ │ │
|
||||
│ │ ④ Upload to SFTPGo │ │
|
||||
│ │ ↓ │ │
|
||||
│ │ ⑤ Create Share Link │ │
|
||||
│ │ ↓ │ │
|
||||
│ │ ⑥ Verify Upload (List Files + List Shares) │ │
|
||||
│ └─────────────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ Phase 2: Momentry 註冊 (只處理 ASR, ASRX, STORY) │ │
|
||||
│ │ │ │
|
||||
│ │ ⑦ Register Video (modules=asr,asrx,story) │ │
|
||||
│ └─────────────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ Phase 3: Progress Loop (n8n Logs 記錄) │ │
|
||||
│ │ │ │
|
||||
│ │ ⑧ Wait 10s ─────────────────────────────────────────────────┐ │ │
|
||||
│ │ ↓ │ │
|
||||
│ │ ⑨ Check Progress (API) │ │
|
||||
│ │ ↓ │ │
|
||||
│ │ ⑩ Log Progress (Code Node → n8n Logs) │ │
|
||||
│ │ ↓ │ │
|
||||
│ │ ⑪ Is Complete? (IF) │ │
|
||||
│ │ │ │ │
|
||||
│ │ ├── NO ──────────────────────────────── Loop Back ─────────┘ │ │
|
||||
│ │ └── YES ────────────────────────────────────────────── Exit ──┘ │ │
|
||||
│ └─────────────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ Phase 4: 搜尋與回應 │ │
|
||||
│ │ │ │
|
||||
│ │ ⑫ Hybrid Search (Vector + BM25) │ │
|
||||
│ │ ↓ │ │
|
||||
│ │ ⑬ Build Response │ │
|
||||
│ │ ↓ │ │
|
||||
│ │ ⑭ Respond to Webhook │ │
|
||||
│ └─────────────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 模組說明
|
||||
|
||||
| 模組 | 用途 | 輸出 |
|
||||
|------|------|------|
|
||||
| `asr` | 語音轉文字 (Whisper) | 字幕/文字稿 |
|
||||
| `asrx` | 說話者分離 (WhisperX) | 誰在什麼時候說什麼 |
|
||||
| `story` | 故事線生成 (Parent-Child Chunks) | 敘事結構 + 父子區塊關聯 |
|
||||
|
||||
**注意**: 只處理語音和故事相關模組,跳過 YOLO、OCR、Face、Pose 等視覺分析。
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ n8n Workflow: Video RAG Demo │
|
||||
├─────────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ Phase 1: SFTPGo 準備 (全部在 n8n Node 內執行) │ │
|
||||
│ │ │ │
|
||||
│ │ ① Webhook Trigger │ │
|
||||
│ │ ↓ │ │
|
||||
│ │ ② Set Variables (解析 file_name, query) │ │
|
||||
│ │ ↓ │ │
|
||||
│ │ ③ Get SFTPGo Token │ │
|
||||
│ │ ↓ │ │
|
||||
│ │ ④ Upload to SFTPGo │ │
|
||||
│ │ ↓ │ │
|
||||
│ │ ⑤ Create Share Link │ │
|
||||
│ │ ↓ │ │
|
||||
│ │ ⑥ Verify Upload (List Files + List Shares) │ │
|
||||
│ └─────────────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ Phase 2: Momentry 註冊 │ │
|
||||
│ │ │ │
|
||||
│ │ ⑦ Register Video │ │
|
||||
│ └─────────────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ Phase 3: Progress Loop (n8n Logs 記錄) │ │
|
||||
│ │ │ │
|
||||
│ │ ⑧ Wait 10s ─────────────────────────────────────────────────┐ │ │
|
||||
│ │ ↓ │ │ │
|
||||
│ │ ⑨ Check Progress (API) │ │ │
|
||||
│ │ ↓ │ │ │
|
||||
│ │ ⑩ Log Progress (Code Node → n8n Logs) │ │ │
|
||||
│ │ ↓ │ │ │
|
||||
│ │ ⑪ Is Complete? (IF) │ │ │
|
||||
│ │ │ │ │ │
|
||||
│ │ ├── NO ──────────────────────────────── Loop Back ─────────┘ │ │
|
||||
│ │ └── YES ────────────────────────────────────────────── Exit ──┘ │ │
|
||||
│ └─────────────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ Phase 4: 搜尋與回應 │ │
|
||||
│ │ │ │
|
||||
│ │ ⑫ Natural Language Search │ │
|
||||
│ │ ↓ │ │
|
||||
│ │ ⑬ Get File Path (含 file_path) │ │
|
||||
│ │ ↓ │ │
|
||||
│ │ ⑭ Build Response │ │
|
||||
│ │ ↓ │ │
|
||||
│ │ ⑮ Respond to Webhook │ │
|
||||
│ └─────────────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Node 詳細配置
|
||||
|
||||
### Node ①: Webhook Trigger (觸發器)
|
||||
|
||||
```yaml
|
||||
Node Name: "Webhook Trigger"
|
||||
Node Type: "Webhook"
|
||||
|
||||
Configuration:
|
||||
HTTP Method: POST
|
||||
Path: "video-rag"
|
||||
Response Mode: "Response Node"
|
||||
Response Node: "Respond to Webhook"
|
||||
|
||||
Input JSON Example:
|
||||
{
|
||||
"file_name": "Old_Time_Movie_Show_-_Charade_1963.HD.mov",
|
||||
"query": "What is the movie about?"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Node ②: Set Variables (變數設定)
|
||||
|
||||
```yaml
|
||||
Node Name: "Set Variables"
|
||||
Node Type: "Set"
|
||||
|
||||
Configuration:
|
||||
Keep Only Set: true
|
||||
|
||||
Variables:
|
||||
- Name: "file_name"
|
||||
Value: "{{ $json.body.file_name }}"
|
||||
|
||||
- Name: "query"
|
||||
Value: "{{ $json.body.query }}"
|
||||
|
||||
- Name: "sftpgo_path"
|
||||
Value: "/{{ $json.body.file_name }}"
|
||||
|
||||
- Name: "register_path"
|
||||
Value: "/Users/accusys/sftpgo_test/demo/{{ $json.body.file_name }}"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Node ③: Get SFTPGo Token (取得權杖)
|
||||
|
||||
```yaml
|
||||
Node Name: "Get SFTPGo Token"
|
||||
Node Type: "HTTP Request"
|
||||
|
||||
Configuration:
|
||||
Method: GET
|
||||
URL: "http://localhost:8080/api/v2/user/token"
|
||||
Authentication: "Basic Auth"
|
||||
User: "demo"
|
||||
Password: "demopassword123"
|
||||
|
||||
Output:
|
||||
{
|
||||
"access_token": "eyJhbGci...",
|
||||
"expires_at": "2026-03-22T07:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Node ④: Upload to SFTPGo (上傳檔案)
|
||||
|
||||
```yaml
|
||||
Node Name: "Upload to SFTPGo"
|
||||
Node Type: "HTTP Request"
|
||||
|
||||
Configuration:
|
||||
Method: POST
|
||||
URL: "http://localhost:8080/api/v2/user/files"
|
||||
Authentication: "Bearer Token"
|
||||
Bearer Token: "{{ $json.access_token }}"
|
||||
|
||||
Body Content Type: "Form-Data Multipart"
|
||||
|
||||
Body:
|
||||
path: /demo
|
||||
mkdir_parents: true
|
||||
filenames: @{{ $json.file_name }}
|
||||
|
||||
Output:
|
||||
{"message":"Upload completed"}
|
||||
```
|
||||
|
||||
**檔案來源選項:**
|
||||
1. **Webhook 接收**: 從 Webhook 的 binary data 取得
|
||||
2. **固定路徑**: 指定本地檔案路徑
|
||||
3. **URL 下載**: 先下載遠端檔案再上傳
|
||||
|
||||
---
|
||||
|
||||
### Node ⑤: Create Share Link (建立分享連結)
|
||||
|
||||
```yaml
|
||||
Node Name: "Create Share Link"
|
||||
Node Type: "HTTP Request"
|
||||
|
||||
Configuration:
|
||||
Method: POST
|
||||
URL: "http://localhost:8080/api/v2/user/shares"
|
||||
Authentication: "Bearer Token"
|
||||
Bearer Token: "{{ $json.access_token }}"
|
||||
|
||||
Body Content Type: "JSON"
|
||||
|
||||
Body:
|
||||
{
|
||||
"name": "{{ $json.file_name }}_share",
|
||||
"paths": ["/{{ $json.file_name }}"],
|
||||
"scope": 1,
|
||||
"expires_at": 0
|
||||
}
|
||||
|
||||
Output:
|
||||
{
|
||||
"id": "CjmQfrkXY5qDtC46WVZY2S",
|
||||
"name": "Charade_share"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Node ⑥: Verify Upload (驗證上傳)
|
||||
|
||||
```yaml
|
||||
Node Name: "Verify Upload - List Shares"
|
||||
Node Type: "HTTP Request"
|
||||
|
||||
Configuration:
|
||||
Method: GET
|
||||
URL: "http://localhost:8080/api/v2/user/shares"
|
||||
Authentication: "Bearer Token"
|
||||
Bearer Token: "{{ $json.access_token }}"
|
||||
|
||||
Output:
|
||||
[
|
||||
{
|
||||
"id": "CjmQfrkXY5qDtC46WVZY2S",
|
||||
"name": "Charade_share",
|
||||
"paths": ["/Old_Time_Movie_Show_-_Charade_1963.HD.mov"]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Node ⑦: Register Video (註冊影片)
|
||||
|
||||
**說明**: 只註冊 ASR、ASRX、STORY 模組處理
|
||||
|
||||
```yaml
|
||||
Node Name: "Register Video"
|
||||
Node Type: "HTTP Request"
|
||||
|
||||
Configuration:
|
||||
Method: POST
|
||||
URL: "http://localhost:3002/api/v1/register"
|
||||
|
||||
Body Content Type: "JSON"
|
||||
|
||||
Body:
|
||||
{
|
||||
"path": "{{ $json.register_path }}",
|
||||
"modules": "asr,asrx,story"
|
||||
}
|
||||
|
||||
Output:
|
||||
{
|
||||
"uuid": "a1b10138a6bbb0cd",
|
||||
"video_id": 7,
|
||||
"file_name": "Old_Time_Movie_Show_-_Charade_1963.HD.mov",
|
||||
"duration": 6879.33,
|
||||
"width": 1920,
|
||||
"height": 1080
|
||||
}
|
||||
```
|
||||
|
||||
**可用模組**:
|
||||
| 模組 | 說明 |
|
||||
|------|------|
|
||||
| `asr` | 語音轉文字 (Whisper) |
|
||||
| `asrx` | 說話者分離 (WhisperX) |
|
||||
| `story` | 故事線生成 (Parent-Child) |
|
||||
| `yolo` | 物體偵測 (可選) |
|
||||
| `cut` | 場景偵測 (可選) |
|
||||
| `ocr` | 文字辨識 (可選) |
|
||||
| `face` | 人臉偵測 (可選) |
|
||||
| `pose` | 姿態估計 (可選) |
|
||||
|
||||
---
|
||||
|
||||
### Node ⑧: Wait 10 Seconds (輪詢間隔)
|
||||
|
||||
```yaml
|
||||
Node Name: "Wait 10 Seconds"
|
||||
Node Type: "Wait"
|
||||
|
||||
Configuration:
|
||||
Amount: 10
|
||||
Unit: "Seconds"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Node ⑨: Check Progress (檢查進度)
|
||||
|
||||
```yaml
|
||||
Node Name: "Check Progress"
|
||||
Node Type: "HTTP Request"
|
||||
|
||||
Configuration:
|
||||
Method: GET
|
||||
URL: "http://localhost:3002/api/v1/progress/{{ $('Register Video').item.json.uuid }}"
|
||||
|
||||
Output:
|
||||
{
|
||||
"uuid": "a1b10138a6bbb0cd",
|
||||
"processors": [
|
||||
{"name": "asr", "status": "complete", "message": "1867 segments"},
|
||||
{"name": "asrx", "status": "progress", "message": "ASRX_TRANSCRIBING"},
|
||||
{"name": "story", "status": "pending", "message": ""}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
> **注意**: API 現在返回 `file_path`(檔案系統路徑)而非 `media_url`(網頁 URL)。如需在網頁中播放影片,請將檔案路徑轉換為可訪問的 URL(例如透過 SFTPGo 分享連結)。
|
||||
|
||||
---
|
||||
|
||||
### Node ⑩: Log Progress (記錄進度)
|
||||
|
||||
```yaml
|
||||
Node Name: "Log Progress"
|
||||
Node Type: "Code"
|
||||
|
||||
Configuration:
|
||||
Language: "JavaScript"
|
||||
|
||||
Code:
|
||||
```javascript
|
||||
const progress = $input.first().json;
|
||||
const processors = progress.processors;
|
||||
|
||||
const totalProcessors = processors.length;
|
||||
const completedProcessors = processors.filter(p => p.status === 'complete').length;
|
||||
const overallProgress = Math.round((completedProcessors / totalProcessors) * 100);
|
||||
|
||||
const currentProcessor = processors.find(p =>
|
||||
p.status === 'progress' || p.status === 'info'
|
||||
);
|
||||
|
||||
const progressMessage = `
|
||||
═══════════════════════════════════════════════
|
||||
📹 Video RAG Processing: ${overallProgress}%
|
||||
UUID: ${progress.uuid}
|
||||
|
||||
${processors.map(p => {
|
||||
const icon = p.status === 'complete' ? '✅' :
|
||||
p.status === 'progress' || p.status === 'info' ? '🔄' : '⏳';
|
||||
return ` ${icon} ${p.name.padEnd(6)} ${p.message || p.status}`;
|
||||
}).join('\n')}
|
||||
|
||||
${currentProcessor ? `Current: ${currentProcessor.name}` : 'All complete!'}
|
||||
═══════════════════════════════════════════════
|
||||
`.trim();
|
||||
|
||||
console.log(progressMessage);
|
||||
|
||||
return {
|
||||
json: {
|
||||
uuid: progress.uuid,
|
||||
overall_progress: overallProgress,
|
||||
completed_processors: completedProcessors,
|
||||
total_processors: totalProcessors,
|
||||
current_processor: currentProcessor?.name || 'idle',
|
||||
processors: processors,
|
||||
log_message: progressMessage
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
Output:
|
||||
{
|
||||
"uuid": "a1b10138a6bbb0cd",
|
||||
"overall_progress": 33,
|
||||
"log_message": "📹 Video RAG Processing: 33%..."
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Node ⑪: Is Complete? (判斷分支)
|
||||
|
||||
```yaml
|
||||
Node Name: "Is Complete?"
|
||||
Node Type: "IF"
|
||||
|
||||
Configuration:
|
||||
Condition:
|
||||
$json.processors.every(p => p.status === 'complete')
|
||||
|
||||
Connections:
|
||||
TRUE (完成): → Node ⑫ Natural Language Search
|
||||
FALSE (未完成): → Node ⑧ Wait 10 Seconds (Loop)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Node ⑫: Natural Language Search (RAG 搜尋)
|
||||
|
||||
```yaml
|
||||
Node Name: "Natural Language Search"
|
||||
Node Type: "HTTP Request"
|
||||
|
||||
Configuration:
|
||||
Method: POST
|
||||
URL: "http://localhost:3002/api/v1/search"
|
||||
|
||||
Body Content Type: "JSON"
|
||||
|
||||
Body:
|
||||
{
|
||||
"query": "{{ $('Set Variables').item.json.query }}",
|
||||
"limit": 10,
|
||||
"uuid": "{{ $('Register Video').item.json.uuid }}"
|
||||
}
|
||||
|
||||
Output:
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"uuid": "a1b10138a6bbb0cd",
|
||||
"chunk_id": "c_001",
|
||||
"text": "Hello and welcome to the old-time movie show...",
|
||||
"score": 0.92
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Node ⑫B: Hybrid Search (Vector + BM25)
|
||||
|
||||
**說明**: 使用混合搜尋,結合向量相似度和全文檢索
|
||||
|
||||
```yaml
|
||||
Node Name: "Hybrid Search"
|
||||
Node Type: "HTTP Request"
|
||||
|
||||
Configuration:
|
||||
Method: POST
|
||||
URL: "http://localhost:3002/api/v1/search/hybrid"
|
||||
|
||||
Body Content Type: "JSON"
|
||||
|
||||
Body:
|
||||
{
|
||||
"query": "{{ $('Set Variables').item.json.query }}",
|
||||
"limit": 10,
|
||||
"uuid": "{{ $('Register Video').item.json.uuid }}",
|
||||
"vector_weight": 0.7,
|
||||
"bm25_weight": 0.3
|
||||
}
|
||||
|
||||
Output:
|
||||
{
|
||||
"query": "What is the movie about?",
|
||||
"results": [
|
||||
{
|
||||
"uuid": "a1b10138a6bbb0cd",
|
||||
"chunk_id": "c_001",
|
||||
"chunk_type": "sentence",
|
||||
"start_time": 0.0,
|
||||
"end_time": 5.0,
|
||||
"text": "Hello and welcome to the old-time movie show...",
|
||||
"vector_score": 0.85,
|
||||
"bm25_score": 0.75,
|
||||
"combined_score": 0.80
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**權重建議**:
|
||||
| 查詢類型 | vector_weight | bm25_weight |
|
||||
|----------|---------------|-------------|
|
||||
| 主題查詢 | 0.8 | 0.2 |
|
||||
| 事實查找 | 0.5 | 0.5 |
|
||||
| 平衡查詢 | 0.7 | 0.3 |
|
||||
|
||||
---
|
||||
|
||||
### Node ⑬: Get Media URL (取得媒體連結)
|
||||
|
||||
```yaml
|
||||
Node Name: "Get Media URL"
|
||||
Node Type: "HTTP Request"
|
||||
|
||||
Configuration:
|
||||
Method: POST
|
||||
URL: "http://localhost:3002/api/v1/n8n/search"
|
||||
|
||||
Body Content Type: "JSON"
|
||||
|
||||
Body:
|
||||
{
|
||||
"query": "{{ $('Set Variables').item.json.query }}",
|
||||
"limit": 10,
|
||||
"uuid": "{{ $('Register Video').item.json.uuid }}"
|
||||
}
|
||||
|
||||
Output:
|
||||
{
|
||||
"count": 10,
|
||||
"hits": [
|
||||
{
|
||||
"id": "c_001",
|
||||
"vid": "a1b10138a6bbb0cd",
|
||||
"text": "Hello and welcome to the old-time movie show...",
|
||||
"score": 0.92,
|
||||
"file_path": "/Users/accusys/momentry/var/sftpgo/data/demo/video.mp4"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Node ⑭: Build Response (組合結果)
|
||||
|
||||
```yaml
|
||||
Node Name: "Build Response"
|
||||
Node Type: "Set"
|
||||
|
||||
Configuration:
|
||||
Keep Only Set: true
|
||||
|
||||
Variables:
|
||||
- Name: "ok"
|
||||
Value: true
|
||||
|
||||
- Name: "uuid"
|
||||
Value: "{{ $('Register Video').item.json.uuid }}"
|
||||
|
||||
- Name: "file_name"
|
||||
Value: "{{ $('Set Variables').item.json.file_name }}"
|
||||
|
||||
- Name: "query"
|
||||
Value: "{{ $('Set Variables').item.json.query }}"
|
||||
|
||||
- Name: "count"
|
||||
Value: "{{ $('Get Media URL').item.json.count }}"
|
||||
|
||||
- Name: "results"
|
||||
Value: "{{ $('Get Media URL').item.json.hits }}"
|
||||
|
||||
- Name: "overall_progress"
|
||||
Value: "{{ $('Log Progress').item.json.overall_progress }}"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Node ⑮: Respond to Webhook (回傳結果)
|
||||
|
||||
```yaml
|
||||
Node Name: "Respond to Webhook"
|
||||
Node Type: "Respond to Webhook"
|
||||
|
||||
Configuration:
|
||||
Respond With: "JSON"
|
||||
|
||||
Response Body:
|
||||
{
|
||||
"ok": true,
|
||||
"uuid": "{{ $json.uuid }}",
|
||||
"file_name": "{{ $json.file_name }}",
|
||||
"query": "{{ $json.query }}",
|
||||
"count": {{ $json.count }},
|
||||
"results": {{ $json.results }},
|
||||
"overall_progress": {{ $json.overall_progress }},
|
||||
"message": "Video RAG completed successfully"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 快速複製所需資訊
|
||||
|
||||
### SFTPGo 設定
|
||||
| 項目 | 值 |
|
||||
|------|-----|
|
||||
| API Base | `http://localhost:8080/api/v2` |
|
||||
| Demo User | `demo` |
|
||||
| Demo Password | `demopassword123` |
|
||||
| Demo Home | `/Users/accusys/sftpgo_test/demo` |
|
||||
| Token Endpoint | `/api/v2/user/token` |
|
||||
| Upload Endpoint | `/api/v2/user/files` |
|
||||
| Share Endpoint | `/api/v2/user/shares` |
|
||||
|
||||
### Momentry 設定
|
||||
| 項目 | 值 |
|
||||
|------|-----|
|
||||
| API Base | `http://localhost:3002` |
|
||||
| Authentication | `X-API-Key` header (所有 `/api/v1/*` 端點) |
|
||||
| Register | `POST /api/v1/register` |
|
||||
| Progress | `GET /api/v1/progress/{uuid}` |
|
||||
| Search | `POST /api/v1/search` |
|
||||
| n8n Search | `POST /api/v1/n8n/search` |
|
||||
| Hybrid Search | `POST /api/v1/search/hybrid` |
|
||||
| Media Base | `https://wp.momentry.ddns.net` (僅供參考,API 返回 `file_path` 而非 URL) |
|
||||
|
||||
### Demo 測試資料
|
||||
|
||||
**Charade (1963) Demo Video**
|
||||
- UUID: `a1b10138a6bbb0cd`
|
||||
- 位置: `/Users/accusys/test_video/Old_Time_Movie_Show_-_Charade_1963.HD.mov`
|
||||
- 時長: 6872 秒 (~1.9 小時)
|
||||
|
||||
**已處理檔案**:
|
||||
| 檔案 | 大小 | 內容 |
|
||||
|------|------|------|
|
||||
| `asr.json` | 210KB | 1867 語音區段 |
|
||||
| `cut.json` | 220KB | 1331 場景 |
|
||||
| `story.json` | 1.8MB | 641 父子區塊 |
|
||||
| `transcript.txt` | 40KB | 可讀文字稿 |
|
||||
|
||||
**Output 目錄**: `/Users/accusys/momentry_core_0.1/output`
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 日期 | 版本 | 變更 |
|
||||
|------|------|------|
|
||||
| 2026-03-22 | v1.0 | 初始建立 |
|
||||
| 2026-03-22 | v1.1 | 新增 Hybrid Search (Vector + BM25) 節點 |
|
||||
| 2026-03-22 | v1.2 | 簡化為只處理 ASR、ASRX、STORY 模組 |
|
||||
@@ -0,0 +1,190 @@
|
||||
---
|
||||
document_type: "architecture_design"
|
||||
service: "N8N"
|
||||
title: "Momentry Video RAG MCP Workflow"
|
||||
date: "2026-03-22"
|
||||
version: "V1.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
tags:
|
||||
- "n8n"
|
||||
- "workflow"
|
||||
- "rag"
|
||||
- "mcp"
|
||||
- "video-search"
|
||||
ai_query_hints:
|
||||
- "N8N Video RAG MCP 工作流程是什麼?"
|
||||
- "如何配置 Momentry Video RAG Webhook?"
|
||||
- "Video RAG MCP 的搜尋流程如何運作?"
|
||||
---
|
||||
|
||||
# Momentry Video RAG MCP Workflow
|
||||
|
||||
## 工作流程資訊
|
||||
|
||||
- **名稱**: Momentry Video RAG MCP
|
||||
- **ID**: WlVvpX2OeKK83QOK
|
||||
- **Webhook Path**: `video-rag-mcp`
|
||||
- **狀態**: ✅ Active (已啟動)
|
||||
- **建立時間**: 2026-03-22
|
||||
|
||||
## 工作流程架構
|
||||
|
||||
```
|
||||
┌─────────────────┐ ┌──────────────────────┐ ┌───────────────────┐ ┌─────────────────┐
|
||||
│ Webhook │────▶│ Search Momentry │────▶│ Process RAG │────▶│ Respond to │
|
||||
│ Trigger │ │ Core │ │ Results │ │ Webhook │
|
||||
└─────────────────┘ └──────────────────────┘ └───────────────────┘ └─────────────────┘
|
||||
│
|
||||
│ POST http://localhost:5678/webhook/video-rag-mcp
|
||||
│
|
||||
▼
|
||||
{
|
||||
"query": "搜尋關鍵字",
|
||||
"limit": 5,
|
||||
"uuid": "可選的影片UUID"
|
||||
}
|
||||
```
|
||||
|
||||
## Node 說明
|
||||
|
||||
### 1. Webhook Trigger
|
||||
- **類型**: Webhook
|
||||
- **Method**: POST
|
||||
- **Path**: `video-rag-mcp`
|
||||
- **Response Mode**: Last Node (等待最後一個節點完成後回應)
|
||||
|
||||
### 2. Search Momentry Core
|
||||
- **類型**: HTTP Request
|
||||
- **URL**: `http://localhost:3002/api/v1/n8n/search`
|
||||
- **Method**: POST
|
||||
- **Body**:
|
||||
```json
|
||||
{
|
||||
"query": "搜尋關鍵字",
|
||||
"limit": 5,
|
||||
"uuid": "可選的影片UUID"
|
||||
}
|
||||
```
|
||||
- **Timeout**: 30秒
|
||||
|
||||
### 3. Process RAG Results
|
||||
- **類型**: Code (JavaScript)
|
||||
- **功能**:
|
||||
- 處理 Momentry Core 搜尋結果
|
||||
- 格式化 hits 為結構化資料
|
||||
- 建立 RAG context(用於 LLM 問答)
|
||||
- 計算相關度百分比
|
||||
|
||||
**輸出格式**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"query": "搜尋關鍵字",
|
||||
"totalFound": 5,
|
||||
"context": "[1] 文本內容... (Video: 影片標題, Time: 10s-20s)\n\n[2] ...",
|
||||
"results": [
|
||||
{
|
||||
"index": 1,
|
||||
"id": "chunk_id",
|
||||
"title": "影片標題",
|
||||
"text": "文本內容",
|
||||
"startTime": 10,
|
||||
"endTime": 20,
|
||||
"relevance": "85%",
|
||||
"videoUuid": "uuid",
|
||||
"mediaUrl": "影片URL",
|
||||
"deepLink": "影片URL#t=10,20"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Respond to Webhook
|
||||
- **類型**: Respond to Webhook
|
||||
- **Response**: JSON 格式結果
|
||||
- **Status Code**: 200
|
||||
|
||||
## 使用方式
|
||||
|
||||
### 直接呼叫 Webhook
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:5678/webhook/video-rag-mcp \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"query": "charade",
|
||||
"limit": 5
|
||||
}'
|
||||
```
|
||||
|
||||
### 指定特定影片搜尋
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:5678/webhook/video-rag-mcp \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"query": "audrey hepburn",
|
||||
"limit": 3,
|
||||
"uuid": "a1b10138a6bbb0cd"
|
||||
}'
|
||||
```
|
||||
|
||||
### 在 n8n 工作流程中使用
|
||||
|
||||
可以將此 Webhook 作為子工作流程觸發器,或使用 HTTP Request Node 呼叫:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Call Video RAG",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"url": "http://localhost:5678/webhook/video-rag-mcp",
|
||||
"method": "POST",
|
||||
"body": {
|
||||
"query": "={{ $json.searchTerm }}",
|
||||
"limit": 5
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## RAG Context 用途
|
||||
|
||||
工作流程產生的 `context` 欄位可直接用於 LLM 提示:
|
||||
|
||||
```javascript
|
||||
// Example: 使用 context 進行問答
|
||||
const prompt = `
|
||||
基於以下影片片段資訊回答問題:
|
||||
|
||||
${context}
|
||||
|
||||
問題:${userQuestion}
|
||||
|
||||
請根據上述內容提供準確的答案。
|
||||
`;
|
||||
```
|
||||
|
||||
## 相關文件
|
||||
|
||||
- [Momentry Core API 文件](./API_ACCESS.md)
|
||||
- [n8n MCP 測試報告](./maintenance_records/changes/CHANGE_N8N_MCP_INTEGRATION_TEST_2026_03_23.md)
|
||||
- [N8N_DEMO_WORKFLOW.md](./N8N_DEMO_WORKFLOW.md) - 完整工作流程設計
|
||||
|
||||
## MCP 建立指令
|
||||
|
||||
此工作流程是透過 MCP 工具建立的:
|
||||
|
||||
```bash
|
||||
# 使用 MCP 建立工作流程
|
||||
node create_workflow.js | mcp-n8n
|
||||
|
||||
# 使用 MCP 啟動工作流程
|
||||
echo '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"n8n_activate_workflow","arguments":{"workflowId":"WlVvpX2OeKK83QOK"}}}' | mcp-n8n
|
||||
```
|
||||
|
||||
## 工作流程檔案
|
||||
|
||||
- 原始檔案: `docs/n8n_workflow_video_rag_mcp.json`
|
||||
@@ -0,0 +1,709 @@
|
||||
---
|
||||
document_type: "architecture_design"
|
||||
service: "MOMENTRY_CORE"
|
||||
title: "影片 On-the-Fly 實時處理架構設計"
|
||||
date: "2026-04-01"
|
||||
version: "V1.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
tags:
|
||||
- "實時處理架構設計"
|
||||
ai_query_hints:
|
||||
- "查詢 影片 On-the-Fly 實時處理架構設計 的內容"
|
||||
- "影片 On-the-Fly 實時處理架構設計 的主要目的是什麼?"
|
||||
- "如何操作或實施 影片 On-the-Fly 實時處理架構設計?"
|
||||
---
|
||||
|
||||
# 影片 On-the-Fly 實時處理架構設計
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 目標 | **影片上傳時即時處理完成**(On-the-Fly Processing) |
|
||||
| 分析日期 | 2026-04-01 |
|
||||
| 硬體 | M4 Mac Mini 16GB → Mac Studio 64GB |
|
||||
| 部署模式 | 邊緣 AI(本地運行) |
|
||||
|
||||
---
|
||||
|
||||
## 執行摘要
|
||||
|
||||
### 目標定義
|
||||
|
||||
```
|
||||
傳統流程:
|
||||
上傳 (5分鐘) → 等待 → 處理 (10分鐘) → 完成
|
||||
總時間: 15分鐘 ❌
|
||||
|
||||
On-the-Fly 目標:
|
||||
上傳 (5分鐘) + 處理 (同步進行) → 完成
|
||||
總時間: 5分鐘 ✅
|
||||
```
|
||||
|
||||
### 關鍵挑戰
|
||||
|
||||
1. **處理速度必須快於上傳速度**
|
||||
2. **邊上傳邊處理(串流處理)**
|
||||
3. **資源調度優化**
|
||||
4. **用戶體驗即時反饋**
|
||||
|
||||
---
|
||||
|
||||
## 上傳速度分析
|
||||
|
||||
### 網路環境假設
|
||||
|
||||
| 網路類型 | 上傳速度 | 10分鐘影片 | 1小時影片 |
|
||||
|---------|---------|-----------|----------|
|
||||
| **光纖 100Mbps** | 12.5 MB/s | ~1.5分鐘 | ~9分鐘 |
|
||||
| **光纖 500Mbps** | 62.5 MB/s | ~18秒 | ~1.8分鐘 |
|
||||
| **企業級 1Gbps** | 125 MB/s | ~9秒 | ~54秒 |
|
||||
| **SFTP (區網)** | 500+ MB/s | ~2秒 | ~13秒 |
|
||||
|
||||
### 影片大小估算
|
||||
|
||||
```
|
||||
1080p 30fps:
|
||||
- 檔案大小: ~100MB/分鐘
|
||||
- H.264 壓縮: ~50MB/分鐘
|
||||
- H.265 壓縮: ~25MB/分鐘
|
||||
|
||||
4K 60fps:
|
||||
- 檔案大小: ~400MB/分鐘
|
||||
- H.264 壓縮: ~200MB/分鐘
|
||||
- H.265 壓縮: ~100MB/分鐘
|
||||
```
|
||||
|
||||
### On-the-Fly 處理時間限制
|
||||
|
||||
```
|
||||
假設: 10分鐘影片 (1080p H.264, ~500MB)
|
||||
|
||||
上傳時間:
|
||||
- 100Mbps: 40秒
|
||||
- 500Mbps: 8秒
|
||||
- 1Gbps: 4秒
|
||||
|
||||
處理必須在此時間內完成!
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 處理器效能 vs 上傳速度
|
||||
|
||||
### M4 Mac Mini 16GB(現有)
|
||||
|
||||
| 處理器 | 10分鐘影片 | 是否能 On-the-Fly |
|
||||
|--------|-----------|------------------|
|
||||
| **ASR** | 50s | ⚠️ 勉強(100Mbps) |
|
||||
| **ASRX** | 180s | ❌ 無法 |
|
||||
| **OCR** | 150s | ❌ 無法 |
|
||||
| **YOLO** | 300s | ❌ 無法 |
|
||||
| **Face** | 5s | ✅ 可以 |
|
||||
| **Pose** | 300s | ❌ 無法 |
|
||||
| **Scene** | 15s | ✅ 可以 |
|
||||
| **CUT** | 0.5s | ✅ 可以 |
|
||||
|
||||
**結論**:M4 Mini 無法實現完整 On-the-Fly
|
||||
|
||||
### Mac Studio 64GB(推薦)
|
||||
|
||||
| 處理器 | 10分鐘影片 | 是否能 On-the-Fly |
|
||||
|--------|-----------|------------------|
|
||||
| **ASR** | 15s | ✅ 可以 |
|
||||
| **ASRX** | 60s | ✅ 可以(100Mbps) |
|
||||
| **OCR** | 50s | ✅ 可以(100Mbps) |
|
||||
| **YOLO** | 100s | ⚠️ 勉強(500Mbps) |
|
||||
| **Face** | 2s | ✅ 可以 |
|
||||
| **Pose** | 100s | ⚠️ 勉強(500Mbps) |
|
||||
| **Scene** | 5s | ✅ 可以 |
|
||||
| **CUT** | 0.2s | ✅ 可以 |
|
||||
|
||||
**結論**:Mac Studio 可實現大部分 On-the-Fly
|
||||
|
||||
---
|
||||
|
||||
## On-the-Fly 架構設計
|
||||
|
||||
### 方案 A:串流處理(Streaming Processing)⭐
|
||||
|
||||
```
|
||||
上傳流程:
|
||||
|
||||
[SFTP 上傳] ──→ [分塊接收] ──→ [即時處理]
|
||||
│ │ │
|
||||
│ ├─ ASR (音頻流)
|
||||
│ ├─ Scene (關鍵幀)
|
||||
│ └─ Face (關鍵幀)
|
||||
│
|
||||
└─ 上傳完成 → [完整處理]
|
||||
├─ OCR
|
||||
├─ YOLO
|
||||
└─ Pose
|
||||
```
|
||||
|
||||
**實現**:
|
||||
|
||||
```python
|
||||
class StreamingProcessor:
|
||||
"""串流處理器 - 邊上傳邊處理"""
|
||||
|
||||
def __init__(self):
|
||||
self.buffer = VideoBuffer()
|
||||
self.processors = {
|
||||
"fast": [SceneProcessor(), FaceProcessor()],
|
||||
"delayed": [OCRProcessor(), YOLOProcessor(), PoseProcessor()]
|
||||
}
|
||||
|
||||
async def process_stream(self, video_stream):
|
||||
"""處理串流"""
|
||||
async for chunk in video_stream:
|
||||
# 1. 寫入緩衝區
|
||||
self.buffer.write(chunk)
|
||||
|
||||
# 2. 快速處理器(立即執行)
|
||||
for processor in self.processors["fast"]:
|
||||
await processor.process_chunk(chunk)
|
||||
|
||||
# 3. 更新進度
|
||||
await self.update_progress()
|
||||
|
||||
# 4. 上傳完成,執行延遲處理器
|
||||
for processor in self.processors["delayed"]:
|
||||
await processor.process_full(self.buffer)
|
||||
```
|
||||
|
||||
### 方案 B:並行管線處理(Parallel Pipeline)
|
||||
|
||||
```
|
||||
並行管線:
|
||||
|
||||
[上傳] ──┬─ [ASR] ──→ 結果 1 (15s)
|
||||
├─ [Face] ──→ 結果 2 (2s)
|
||||
├─ [Scene] ──→ 結果 3 (5s)
|
||||
├─ [CUT] ──→ 結果 4 (0.2s)
|
||||
│
|
||||
└─ 上傳完成後:
|
||||
├─ [OCR] ──→ 結果 5 (50s)
|
||||
├─ [YOLO] ──→ 結果 6 (100s)
|
||||
└─ [Pose] ──→ 結果 7 (100s)
|
||||
|
||||
總時間: max(上傳, ASR, Face, Scene, CUT) + max(OCR, YOLO, Pose)
|
||||
= max(40s, 15s, 2s, 5s, 0.2s) + max(50s, 100s, 100s)
|
||||
= 40s + 100s = 140s
|
||||
```
|
||||
|
||||
**Mac Studio 優勢**:
|
||||
- 可同時運行 4-6 個處理器
|
||||
- 大幅縮短總處理時間
|
||||
|
||||
### 方案 C:智能降級處理(Adaptive Quality)
|
||||
|
||||
```python
|
||||
class AdaptiveProcessor:
|
||||
"""自適應處理器 - 根據上傳速度調整"""
|
||||
|
||||
def __init__(self):
|
||||
self.upload_speed = self._detect_upload_speed()
|
||||
self.video_duration = None
|
||||
|
||||
def select_processing_profile(self):
|
||||
"""根據上傳速度選擇處理配置"""
|
||||
estimated_upload_time = self._estimate_upload_time()
|
||||
|
||||
if estimated_upload_time < 30:
|
||||
# 快速上傳(>500Mbps)→ 完整處理
|
||||
return "professional"
|
||||
elif estimated_upload_time < 120:
|
||||
# 中速上傳(100-500Mbps)→ 標準處理
|
||||
return "standard"
|
||||
else:
|
||||
# 慢速上傳(<100Mbps)→ 快速處理
|
||||
return "fast"
|
||||
|
||||
def get_processing_config(self, profile):
|
||||
"""取得處理配置"""
|
||||
configs = {
|
||||
"professional": {
|
||||
"audio": {"model": "large-v3", "diarization": True},
|
||||
"ocr": {"sample_interval": 1},
|
||||
"yolo": {"sample_interval": 1},
|
||||
"face": {"sample_interval": 1},
|
||||
"scene": {"sample_interval": 2}
|
||||
},
|
||||
"standard": {
|
||||
"audio": {"model": "base", "diarization": True},
|
||||
"ocr": {"sample_interval": 2},
|
||||
"yolo": {"sample_interval": 2},
|
||||
"face": {"sample_interval": 2},
|
||||
"scene": {"sample_interval": 3}
|
||||
},
|
||||
"fast": {
|
||||
"audio": {"model": "tiny", "diarization": False},
|
||||
"ocr": {"sample_interval": 5},
|
||||
"yolo": {"sample_interval": 5},
|
||||
"face": {"sample_interval": 3},
|
||||
"scene": {"sample_interval": 5}
|
||||
}
|
||||
}
|
||||
return configs[profile]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 串流處理實現
|
||||
|
||||
### 1. 影片分塊接收
|
||||
|
||||
```python
|
||||
class ChunkedVideoReceiver:
|
||||
"""分塊影片接收器"""
|
||||
|
||||
def __init__(self, chunk_size_mb=10):
|
||||
self.chunk_size = chunk_size_mb * 1024 * 1024
|
||||
self.buffer = io.BytesIO()
|
||||
self.chunk_count = 0
|
||||
self.processors = []
|
||||
|
||||
async def receive_chunk(self, chunk_data):
|
||||
"""接收影片塊"""
|
||||
# 寫入緩衝區
|
||||
self.buffer.write(chunk_data)
|
||||
self.chunk_count += 1
|
||||
|
||||
# 達到塊大小時,觸發處理
|
||||
if self.buffer.tell() >= self.chunk_size:
|
||||
await self._process_chunk()
|
||||
|
||||
async def _process_chunk(self):
|
||||
"""處理當前塊"""
|
||||
# 提取關鍵幀
|
||||
frames = await self._extract_key_frames()
|
||||
|
||||
# 快速處理器
|
||||
for processor in self.processors:
|
||||
if processor.is_fast():
|
||||
await processor.process_frames(frames)
|
||||
|
||||
# 清空緩衝區
|
||||
self.buffer = io.BytesIO()
|
||||
|
||||
async def finalize(self):
|
||||
"""上傳完成,處理完整影片"""
|
||||
# 執行完整處理
|
||||
for processor in self.processors:
|
||||
if not processor.is_fast():
|
||||
await processor.process_full(self.temp_file)
|
||||
```
|
||||
|
||||
### 2. 音頻串流處理
|
||||
|
||||
```python
|
||||
class AudioStreamProcessor:
|
||||
"""音頻串流處理器"""
|
||||
|
||||
def __init__(self):
|
||||
self.audio_buffer = []
|
||||
self.sample_rate = 16000
|
||||
self.chunk_duration = 10 # 10秒音頻塊
|
||||
|
||||
async def process_audio_stream(self, audio_stream):
|
||||
"""處理音頻串流"""
|
||||
import whisperx
|
||||
|
||||
# 載入模型(預載入)
|
||||
model = ModelCache.get_model("large-v3")
|
||||
|
||||
async for audio_chunk in audio_stream:
|
||||
# 累積音頻
|
||||
self.audio_buffer.append(audio_chunk)
|
||||
|
||||
# 達到處理長度
|
||||
if self._get_buffer_duration() >= self.chunk_duration:
|
||||
# 即時轉錄
|
||||
result = model.transcribe(self._merge_buffer())
|
||||
|
||||
# 發送即時結果
|
||||
await self._send_partial_result(result)
|
||||
|
||||
# 清空緩衝區
|
||||
self.audio_buffer = []
|
||||
|
||||
def _get_buffer_duration(self):
|
||||
"""計算緩衝區時長"""
|
||||
total_samples = sum(len(chunk) for chunk in self.audio_buffer)
|
||||
return total_samples / self.sample_rate
|
||||
```
|
||||
|
||||
### 3. 關鍵幀提取與處理
|
||||
|
||||
```python
|
||||
class KeyFrameProcessor:
|
||||
"""關鍵幀處理器"""
|
||||
|
||||
def __init__(self, extraction_interval=2.0):
|
||||
self.extraction_interval = extraction_interval
|
||||
self.last_extraction_time = 0
|
||||
|
||||
async def process_video_stream(self, video_stream):
|
||||
"""處理影片串流"""
|
||||
import cv2
|
||||
|
||||
cap = cv2.VideoCapture(video_stream)
|
||||
fps = cap.get(cv2.CAP_PROP_FPS)
|
||||
|
||||
frame_count = 0
|
||||
key_frames = []
|
||||
|
||||
while True:
|
||||
ret, frame = cap.read()
|
||||
if not ret:
|
||||
break
|
||||
|
||||
frame_count += 1
|
||||
current_time = frame_count / fps
|
||||
|
||||
# 提取關鍵幀(每 N 秒)
|
||||
if current_time - self.last_extraction_time >= self.extraction_interval:
|
||||
key_frames.append({
|
||||
"frame": frame,
|
||||
"timestamp": current_time
|
||||
})
|
||||
self.last_extraction_time = current_time
|
||||
|
||||
# 達到批次大小,立即處理
|
||||
if len(key_frames) >= 10:
|
||||
await self._process_batch(key_frames)
|
||||
key_frames = []
|
||||
|
||||
# 處理剩餘幀
|
||||
if key_frames:
|
||||
await self._process_batch(key_frames)
|
||||
|
||||
async def _process_batch(self, frames):
|
||||
"""批次處理關鍵幀"""
|
||||
# 並行運行快速處理器
|
||||
tasks = [
|
||||
self._run_scene(frames),
|
||||
self._run_face(frames),
|
||||
self._run_cut(frames)
|
||||
]
|
||||
|
||||
await asyncio.gather(*tasks)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Mac Studio 優化配置
|
||||
|
||||
### 記憶體分配策略
|
||||
|
||||
```python
|
||||
class MemoryAllocator:
|
||||
"""Mac Studio 記憶體分配"""
|
||||
|
||||
# 64GB Mac Studio 配置
|
||||
ALLOCATION = {
|
||||
"system_reserved": 4000, # 4GB 系統保留
|
||||
"database": 2000, # 2GB 資料庫
|
||||
"api_server": 500, # 0.5GB API
|
||||
"video_buffer": 8000, # 8GB 影片緩衝
|
||||
"audio_buffer": 4000, # 4GB 音頻緩衝
|
||||
"model_cache": 16000, # 16GB 模型快取
|
||||
"processing": 28000 # 28GB 處理器運行
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self.total_memory = 64 * 1024 # MB
|
||||
self.verify_allocation()
|
||||
|
||||
def verify_allocation(self):
|
||||
"""驗證記憶體分配"""
|
||||
total_allocated = sum(self.ALLOCATION.values())
|
||||
assert total_allocated <= self.total_memory, \
|
||||
f"Memory over-allocated: {total_allocated}MB > {self.total_memory}MB"
|
||||
```
|
||||
|
||||
### 並行處理調度
|
||||
|
||||
```python
|
||||
class ParallelScheduler:
|
||||
"""並行處理調度器"""
|
||||
|
||||
def __init__(self, max_workers=6):
|
||||
self.max_workers = max_workers
|
||||
self.executor = concurrent.futures.ThreadPoolExecutor(max_workers)
|
||||
|
||||
async def schedule_processing(self, video_uuid):
|
||||
"""調度處理任務"""
|
||||
# Phase 1: 上傳時即時處理
|
||||
fast_tasks = [
|
||||
self.executor.submit(self.run_scene, video_uuid),
|
||||
self.executor.submit(self.run_face, video_uuid),
|
||||
self.executor.submit(self.run_cut, video_uuid)
|
||||
]
|
||||
|
||||
# 等待上傳完成
|
||||
await self.wait_for_upload_complete(video_uuid)
|
||||
|
||||
# Phase 2: 上傳完成後處理
|
||||
slow_tasks = [
|
||||
self.executor.submit(self.run_asr, video_uuid),
|
||||
self.executor.submit(self.run_ocr, video_uuid),
|
||||
self.executor.submit(self.run_yolo, video_uuid),
|
||||
self.executor.submit(self.run_pose, video_uuid)
|
||||
]
|
||||
|
||||
# 收集結果
|
||||
results = await self.collect_results(fast_tasks + slow_tasks)
|
||||
return results
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 用戶體驗設計
|
||||
|
||||
### 即時反饋 UI
|
||||
|
||||
```
|
||||
上傳進度:
|
||||
████████░░░░░░░░░░░░ 40%
|
||||
|
||||
即時處理結果:
|
||||
✅ 場景識別: 辦公室、會議室
|
||||
✅ 人臉檢測: 3 人
|
||||
✅ 鏡頭切換: 5 次
|
||||
⏳ 語音轉錄: 處理中...
|
||||
⏳ OCR: 等待上傳完成
|
||||
⏳ YOLO: 等待上傳完成
|
||||
|
||||
預計剩餘時間: 2分30秒
|
||||
```
|
||||
|
||||
### WebSocket 即時更新
|
||||
|
||||
```python
|
||||
from fastapi import WebSocket
|
||||
|
||||
class ProgressWebSocket:
|
||||
"""即時進度推送"""
|
||||
|
||||
async def broadcast_progress(self, video_uuid, processor, progress):
|
||||
"""廣播處理進度"""
|
||||
message = {
|
||||
"type": "progress",
|
||||
"video_uuid": video_uuid,
|
||||
"processor": processor,
|
||||
"progress": progress,
|
||||
"timestamp": time.time()
|
||||
}
|
||||
|
||||
await self.websocket.send_json(message)
|
||||
|
||||
async def broadcast_result(self, video_uuid, processor, result):
|
||||
"""廣播處理結果"""
|
||||
message = {
|
||||
"type": "result",
|
||||
"video_uuid": video_uuid,
|
||||
"processor": processor,
|
||||
"result": result,
|
||||
"timestamp": time.time()
|
||||
}
|
||||
|
||||
await self.websocket.send_json(message)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 效能基準
|
||||
|
||||
### Mac Studio 64GB On-the-Fly 測試
|
||||
|
||||
#### 測試案例 1:10分鐘影片(1080p)
|
||||
|
||||
```
|
||||
上傳時間(100Mbps): 40秒
|
||||
|
||||
即時處理(上傳期間):
|
||||
├─ Scene: 5秒 ✅
|
||||
├─ Face: 2秒 ✅
|
||||
└─ CUT: 0.2秒 ✅
|
||||
|
||||
延遲處理(上傳完成後):
|
||||
├─ ASR: 15秒 ✅
|
||||
├─ OCR: 50秒 ✅
|
||||
├─ YOLO: 100秒 ⚠️
|
||||
└─ Pose: 100秒 ⚠️
|
||||
|
||||
總時間: 40秒(上傳)+ 100秒(處理)= 140秒
|
||||
結果: 上傳後 100 秒完成所有處理
|
||||
```
|
||||
|
||||
#### 測試案例 2:1小時影片(1080p)
|
||||
|
||||
```
|
||||
上傳時間(100Mbps): 240秒
|
||||
|
||||
即時處理(上傳期間):
|
||||
├─ Scene: 30秒 ✅
|
||||
├─ Face: 12秒 ✅
|
||||
└─ CUT: 1秒 ✅
|
||||
|
||||
延遲處理(上傳完成後):
|
||||
├─ ASR: 90秒 ✅
|
||||
├─ OCR: 300秒 ⚠️
|
||||
├─ YOLO: 600秒 ⚠️
|
||||
└─ Pose: 600秒 ⚠️
|
||||
|
||||
總時間: 240秒(上傳)+ 600秒(處理)= 840秒
|
||||
結果: 上傳後 10 分鐘完成所有處理
|
||||
```
|
||||
|
||||
#### 測試案例 3:10分鐘影片(企業級網路 1Gbps)
|
||||
|
||||
```
|
||||
上傳時間: 4秒 ✅
|
||||
|
||||
處理時間(Mac Studio 64GB):
|
||||
├─ 快速處理器: 5秒 ✅
|
||||
└─ 慢速處理器: 100秒 ⚠️
|
||||
|
||||
總時間: 4秒(上傳)+ 100秒(處理)= 104秒
|
||||
結果: 上傳後 1.7 分鐘完成所有處理
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 優化建議
|
||||
|
||||
### 1. 採樣策略優化
|
||||
|
||||
```python
|
||||
# 根據網速自動調整採樣間隔
|
||||
def get_adaptive_sample_interval(upload_speed, video_duration):
|
||||
"""
|
||||
upload_speed: MB/s
|
||||
video_duration: 秒
|
||||
"""
|
||||
if upload_speed > 100: # > 800Mbps
|
||||
return 1.0 # 精細處理
|
||||
elif upload_speed > 50: # 400-800Mbps
|
||||
return 2.0 # 標準處理
|
||||
elif upload_speed > 10: # 80-400Mbps
|
||||
return 3.0 # 快速處理
|
||||
else:
|
||||
return 5.0 # 極速處理
|
||||
```
|
||||
|
||||
### 2. 優先級處理
|
||||
|
||||
```python
|
||||
class PriorityProcessor:
|
||||
"""優先級處理器"""
|
||||
|
||||
PRIORITY = {
|
||||
"high": ["scene", "face", "cut", "asr"], # 用戶最關心
|
||||
"medium": ["ocr", "yolo"], # 次要
|
||||
"low": ["pose"] # 可選
|
||||
}
|
||||
|
||||
async def process_by_priority(self, video_uuid):
|
||||
# 高優先級:立即處理
|
||||
for processor in self.PRIORITY["high"]:
|
||||
await self.run(processor, video_uuid)
|
||||
|
||||
# 中優先級:並行處理
|
||||
await asyncio.gather(*[
|
||||
self.run(p, video_uuid)
|
||||
for p in self.PRIORITY["medium"]
|
||||
])
|
||||
|
||||
# 低優先級:背景處理
|
||||
for processor in self.PRIORITY["low"]:
|
||||
asyncio.create_task(self.run(processor, video_uuid))
|
||||
```
|
||||
|
||||
### 3. 快取預載入
|
||||
|
||||
```python
|
||||
# Mac Studio 啟動時預載入所有模型
|
||||
class PreloadManager:
|
||||
"""模型預載入管理器"""
|
||||
|
||||
@staticmethod
|
||||
def preload_all():
|
||||
"""預載入所有模型到記憶體"""
|
||||
models = [
|
||||
("asr", "whisperx_large_v3"),
|
||||
("scene", "resnet18_places365"),
|
||||
("face", "face_model"),
|
||||
("yolo", "yolov8x"),
|
||||
("ocr", "ocr_model"),
|
||||
("pose", "pose_model")
|
||||
]
|
||||
|
||||
for name, model_path in models:
|
||||
ModelCache.load(name, model_path)
|
||||
|
||||
print(f"[Preload] All models loaded into memory")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 最終建議
|
||||
|
||||
### ✅ Mac Studio 64GB 可實現 On-the-Fly
|
||||
|
||||
**配置**:
|
||||
|
||||
```
|
||||
硬體:
|
||||
├─ Mac Studio M4 Max 64GB
|
||||
├─ 14核心 CPU
|
||||
├─ 30核心 GPU
|
||||
└─ 1TB SSD
|
||||
|
||||
軟體:
|
||||
├─ 預載入所有模型(16GB)
|
||||
├─ 並行處理(4-6 workers)
|
||||
├─ 串流處理(音頻/關鍵幀)
|
||||
└─ 智能降級(根據網速)
|
||||
```
|
||||
|
||||
**預期效果**:
|
||||
|
||||
| 影片時長 | 網速 | 上傳時間 | 處理時間 | 總時間 | On-the-Fly |
|
||||
|---------|------|---------|---------|--------|-----------|
|
||||
| 10分鐘 | 100Mbps | 40s | 100s | **140s** | ⚠️ 部分實現 |
|
||||
| 10分鐘 | 1Gbps | 4s | 100s | **104s** | ✅ 基本實現 |
|
||||
| 30分鐘 | 100Mbps | 120s | 300s | **420s** | ⚠️ 部分實現 |
|
||||
| 30分鐘 | 1Gbps | 12s | 300s | **312s** | ⚠️ 部分實現 |
|
||||
|
||||
**結論**:
|
||||
- ✅ 10分鐘影片 + 企業級網路:**接近 On-the-Fly**
|
||||
- ⚠️ 長影片:處理時間仍較長
|
||||
- ✅ 快速處理器:**完全 On-the-Fly**
|
||||
- ⚠️ 慢速處理器(YOLO/Pose):需優化
|
||||
|
||||
### 📋 實施步驟
|
||||
|
||||
1. **立即**:實現串流處理架構
|
||||
2. **Mac Studio 到達**:部署並行處理
|
||||
3. **第一週**:優化 YOLO/Pose 採樣
|
||||
4. **第二週**:實現智能降級
|
||||
5. **第三週**:用戶體驗優化(WebSocket)
|
||||
|
||||
### 🎯 達成目標
|
||||
|
||||
```
|
||||
目標: 上傳完成時,處理也完成
|
||||
|
||||
現實:
|
||||
- 快速處理器: ✅ 可達成
|
||||
- 慢速處理器: ⚠️ 需 1-3 分鐘額外時間
|
||||
|
||||
妥協方案:
|
||||
- 上傳期間: 快速結果即時顯示
|
||||
- 上傳完成: 1-3 分鐘後完整結果
|
||||
- 用戶體驗: 良好(有即時反饋)
|
||||
```
|
||||
@@ -0,0 +1,120 @@
|
||||
# Parent Chunk 覆蓋率分析
|
||||
|
||||
> **日期**: 2026-04-14 | **影片 UUID**: 384b0ff44aaaa1f1
|
||||
|
||||
---
|
||||
|
||||
## 1. 總覽
|
||||
|
||||
| 項目 | 數量 |
|
||||
|------|------|
|
||||
| ASR chunks (sentence) | 1,961 |
|
||||
| parent_chunks (scene) | 17 |
|
||||
| 有 parent 的 ASR chunks | 1,864 (95.1%) |
|
||||
| 無 parent 的 ASR chunks | 97 (4.9%) |
|
||||
|
||||
---
|
||||
|
||||
## 2. 結論:不是每個 ASR chunk 都有 parent chunk
|
||||
|
||||
**95.1% 的 ASR chunks 有 parent**,但仍有 **97 個 orphan chunks** 未關聯。
|
||||
|
||||
---
|
||||
|
||||
## 3. Orphan Chunks 分佈
|
||||
|
||||
| 類型 | 數量 | 說明 |
|
||||
|------|------|------|
|
||||
| 在 parent 之間的間隙 | 93 | parent_chunks 未完全覆蓋全片 |
|
||||
| 在第一個 parent 之前 | 2 | 0-1.66s (片頭) |
|
||||
| 在最後一個 parent 之後 | 2 | 6849-6865s (片尾) |
|
||||
|
||||
### 時間覆蓋
|
||||
|
||||
```
|
||||
0s 1.66s 6849s 6865s
|
||||
|── 2 chunks ─┤────────── 17 parent_chunks ─────────┤── 2 chunks ──┤
|
||||
↑ ↑
|
||||
第一個 parent 最後一個 parent
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 每個 Parent 涵蓋的 ASR Chunks
|
||||
|
||||
| Parent ID | Scene | 時間範圍 | 時長 | ASR chunks |
|
||||
|-----------|-------|---------|------|:---:|
|
||||
| 1 | 0 | 1.66s - 474.62s | 7.9 min | 83 |
|
||||
| 3 | 1 | 474.62s - 942.86s | 7.8 min | 111 |
|
||||
| 4 | 2 | 942.86s - 1395.69s | 7.5 min | 104 |
|
||||
| 2 | 3 | 1395.69s - 1656.84s | 4.4 min | 97 |
|
||||
| 5 | 4 | 1656.88s - 2080.90s | 7.1 min | 109 |
|
||||
| 6 | 5 | 2080.90s - 2538.22s | 7.6 min | 125 |
|
||||
| 7 | 6 | 2538.22s - 2889.09s | 5.9 min | 85 |
|
||||
| 8 | 7 | 2889.09s - 3532.62s | 10.7 min | 136 |
|
||||
| 9 | 8 | 3532.62s - 3820.90s | 4.8 min | 141 |
|
||||
| 10 | 9 | 3820.90s - 4166.84s | 5.8 min | 103 |
|
||||
| 11 | 10 | 4166.84s - 4430.15s | 4.4 min | 105 |
|
||||
| 12 | 11 | 4430.15s - 4717.13s | 4.8 min | 103 |
|
||||
| 13 | 12 | 4717.13s - 5102.38s | 6.4 min | 103 |
|
||||
| 14 | 13 | 5102.38s - 5352.86s | 4.2 min | 114 |
|
||||
| 15 | 14 | 5352.86s - 5851.60s | 8.3 min | 161 |
|
||||
| 16 | 15 | 5851.60s - 6639.13s | 13.1 min | 114 |
|
||||
| 17 | 16 | 6639.13s - 6849.01s | 3.5 min | 70 |
|
||||
|
||||
---
|
||||
|
||||
## 5. Parent Chunks 結構
|
||||
|
||||
```sql
|
||||
CREATE TABLE parent_chunks (
|
||||
id SERIAL PRIMARY KEY,
|
||||
uuid TEXT NOT NULL, -- 影片 UUID
|
||||
scene_order INTEGER, -- 場景順序
|
||||
start_time DOUBLE PRECISION NOT NULL,
|
||||
end_time DOUBLE PRECISION NOT NULL,
|
||||
summary_text TEXT, -- AI 摘要
|
||||
summary_vector VECTOR(768), -- 摘要嵌入
|
||||
start_frame BIGINT, -- 起始幀 (精確)
|
||||
end_frame BIGINT, -- 結束幀 (精確)
|
||||
fps DOUBLE PRECISION,
|
||||
metadata JSONB,
|
||||
rule_3_markers JSONB,
|
||||
created_at TIMESTAMPTZ
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 關聯問題
|
||||
|
||||
### 目前狀態
|
||||
```
|
||||
parent_chunks: 17 筆 (scene-level)
|
||||
chunks: 4,018 筆 (sentence/cut/time-level)
|
||||
|
||||
❌ chunks.parent_chunk_id 全部為 NULL
|
||||
❌ chunks.child_chunk_ids 全部為 []
|
||||
❌ 兩者未建立外鍵關聯
|
||||
```
|
||||
|
||||
### 應建立但尚未建立的關聯
|
||||
```sql
|
||||
-- 應為每個 sentence chunk 設定 parent_chunk_id
|
||||
UPDATE chunks c
|
||||
SET parent_chunk_id = pc.id::varchar
|
||||
FROM parent_chunks pc
|
||||
WHERE c.uuid = pc.uuid
|
||||
AND c.chunk_type = 'sentence'
|
||||
AND c.start_time >= pc.start_time
|
||||
AND c.end_time <= pc.end_time
|
||||
AND c.parent_chunk_id IS NULL;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 建議
|
||||
|
||||
1. **補齊 orphan chunks 的 parent**: 為 93 個間隙 chunks 建立新的 parent_chunks
|
||||
2. **建立 parent-child 關聯**: 執行上述 UPDATE 將 `parent_chunk_id` 填入
|
||||
3. **dev schema 同步**: dev.parent_chunks 目前為 0 筆,需同步資料
|
||||
@@ -0,0 +1,303 @@
|
||||
---
|
||||
document_type: "architecture_design"
|
||||
service: "MOMENTRY_CORE"
|
||||
title: "Momentry Core 效能與可擴展性架構"
|
||||
date: "2026-04-22"
|
||||
version: "V1.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
tags:
|
||||
- "momentry"
|
||||
- "core"
|
||||
- "效能與可擴展性架構"
|
||||
ai_query_hints:
|
||||
- "查詢 Momentry Core 效能與可擴展性架構 的內容"
|
||||
- "Momentry Core 效能與可擴展性架構 的主要目的是什麼?"
|
||||
- "如何操作或實施 Momentry Core 效能與可擴展性架構?"
|
||||
---
|
||||
|
||||
# Momentry Core 效能與可擴展性架構
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | OpenCode |
|
||||
| 建立時間 | 2026-04-22 |
|
||||
| 文件版本 | V1.0 |
|
||||
| 相關文件 | [ARCHITECTURE_OVERVIEW.md](./ARCHITECTURE_OVERVIEW.md)<br>[ARCHITECTURE_ROADMAP.md](./ARCHITECTURE_ROADMAP.md)<br>[TECHNICAL_DECISION_RECORDS.md](./TECHNICAL_DECISION_RECORDS.md) |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-04-22 | 創建效能與可擴展性架構文件 | OpenCode | OpenCode / deepseek-v3.2 |
|
||||
|
||||
---
|
||||
|
||||
## 1. 效能基準指標
|
||||
|
||||
### 1.1 關鍵效能指標 (KPIs)
|
||||
|
||||
| 指標類別 | 指標 | 目標值 | 測量方法 |
|
||||
|----------|------|--------|----------|
|
||||
| **響應時間** | API 響應時間 (P95) | < 500ms | 請求端到端時間 |
|
||||
| | 視頻註冊處理時間 | < 5分鐘 (10分鐘影片) | 從上傳到完成 |
|
||||
| | 查詢響應時間 | < 2秒 | RAG 搜索完成 |
|
||||
| **吞吐量** | 併發註冊任務 | 5+ 併發 | 同時處理視頻數量 |
|
||||
| | 同時查詢用戶 | 50+ 併發 | 同時 RAG 搜索 |
|
||||
| | 資料庫 QPS | 1000+ QPS | 讀寫操作 |
|
||||
| **資源使用** | CPU 使用率 | < 70% 平均 | 系統監控 |
|
||||
| | 記憶體使用率 | < 80% 平均 | 系統監控 |
|
||||
| | 儲存 I/O | < 50MB/s 讀寫 | 磁碟監控 |
|
||||
| **質量指標** | 分片準確率 | > 95% | 人工抽樣驗證 |
|
||||
| | 嵌入向量品質 | > 0.8 相似度 | 人工測試集 |
|
||||
| | 搜索召回率 | > 90% | 標準測試集 |
|
||||
|
||||
### 1.2 當前效能現狀
|
||||
|
||||
根據現有系統分析:
|
||||
|
||||
1. **視頻處理管道**:
|
||||
- ASR: ~1-2分鐘/10分鐘影片(CPU 密集型)
|
||||
- OCR: ~30秒/10分鐘影片(GPU 加速)
|
||||
- CUT: ~1分鐘/10分鐘影片(算法複雜度 O(n²))
|
||||
- YOLO: ~45秒/10分鐘影片(GPU 推理)
|
||||
|
||||
2. **記憶體消耗**:
|
||||
- 嵌入引擎: 500MB-1GB(取決於模型)
|
||||
- 處理器: 100-300MB/任務
|
||||
- 向量資料庫: 2GB+(隨資料增長)
|
||||
|
||||
3. **儲存需求**:
|
||||
- 原始視頻: 100-500MB/小時影片
|
||||
- 處理結果: 50-100MB/10分鐘影片
|
||||
- 向量資料: 1-2GB/100小時影片
|
||||
|
||||
---
|
||||
|
||||
## 2. 可擴展性策略
|
||||
|
||||
### 2.1 水平擴展 (Horizontal Scaling)
|
||||
|
||||
#### 2.1.1 無狀態服務擴展
|
||||
|
||||
| 服務類型 | 擴展策略 | 瓶頸點 |
|
||||
|----------|----------|--------|
|
||||
| **API Server** | 多實例 + 負載均衡 | Redis 連線數限制 |
|
||||
| **處理器 Worker** | 任務隊列 + 多 Worker | 外部依賴(Python 腳本) |
|
||||
| **嵌入引擎** | 模型分片 + 請求路由 | GPU 記憶體限制 |
|
||||
|
||||
#### 2.1.2 有狀態服務擴展
|
||||
|
||||
| 服務類型 | 擴展策略 | 瓶頸點 |
|
||||
|----------|----------|--------|
|
||||
| **PostgreSQL** | 讀寫分離 + 連接池 | 單主節點寫入 |
|
||||
| **Redis** | 集群模式 + 分片 | 網絡延遲 |
|
||||
| **Qdrant** | 分片 + 副本 | 向量搜索計算量 |
|
||||
|
||||
### 2.2 垂直擴展 (Vertical Scaling)
|
||||
|
||||
| 資源類型 | 升級策略 | 預期效益 |
|
||||
|----------|----------|----------|
|
||||
| **CPU** | 更多核心 + 更高時脈 | 提高並行處理能力 |
|
||||
| **GPU** | 更高記憶體 + 更多核心 | 加速深度學習推理 |
|
||||
| **記憶體** | 更大容量 + 更高頻率 | 減少磁碟交換 |
|
||||
| **儲存** | NVMe SSD + RAID | 提高 I/O 吞吐量 |
|
||||
|
||||
---
|
||||
|
||||
## 3. 效能優化措施
|
||||
|
||||
### 3.1 計算優化
|
||||
|
||||
| 優化點 | 技術方案 | 預期改進 |
|
||||
|--------|----------|----------|
|
||||
| **向量相似度計算** | SIMD 指令集優化 | 10-100 倍加速 |
|
||||
| **CUT 算法優化** | 啟發式剪枝 + 並行化 | 從 O(n²) 到 O(n log n) |
|
||||
| **Python 執行器** | 進程池 + 結果緩存 | 減少啟動開銷 |
|
||||
| **FFmpeg 處理** | 硬體加速 (VideoToolbox) | 2-5 倍加速 |
|
||||
|
||||
### 3.2 記憶體優化
|
||||
|
||||
| 優化點 | 技術方案 | 預期改進 |
|
||||
|--------|----------|----------|
|
||||
| **嵌入向量緩存** | LRU 緩存 + 分級存儲 | 減少重複計算 |
|
||||
| **視頻幀緩衝** | 滑動窗口 + 智能預載 | 控制峰值記憶體 |
|
||||
| **資料庫連接池** | 連接復用 + 超時釋放 | 減少連接開銷 |
|
||||
| **模型量化** | INT8/FP16 量化 | 50-75% 記憶體節省 |
|
||||
|
||||
### 3.3 儲存優化
|
||||
|
||||
| 優化點 | 技術方案 | 預期改進 |
|
||||
|--------|----------|----------|
|
||||
| **向量索引** | HNSW 索引 + 壓縮 | 更快搜索 + 更少空間 |
|
||||
| **文件存儲** | 分層存儲 + 去重 | 節省儲存空間 |
|
||||
| **日誌輪轉** | 自動清理 + 壓縮 | 控制日誌增長 |
|
||||
| **快照備份** | 增量備份 + 壓縮 | 減少備份窗口 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 負載測試策略
|
||||
|
||||
### 4.1 測試場景設計
|
||||
|
||||
| 場景 | 目標 | 測試指標 |
|
||||
|------|------|----------|
|
||||
| **正常負載** | 系統日常使用 | 響應時間、成功率 |
|
||||
| **峰值負載** | 節假日/活動 | 吞吐量、錯誤率 |
|
||||
| **壓力測試** | 極限條件 | 崩潰點、恢復能力 |
|
||||
| **耐久測試** | 長時間運行 | 記憶體泄漏、穩定性 |
|
||||
|
||||
### 4.2 測試工具與方法
|
||||
|
||||
```bash
|
||||
# 使用 Apache Bench 進行 API 測試
|
||||
ab -n 1000 -c 50 http://localhost:3002/api/health
|
||||
|
||||
# 使用 k6 進行複雜場景測試
|
||||
k6 run --vus 50 --duration 30s script.js
|
||||
|
||||
# 自定義負載生成器
|
||||
python scripts/load_test.py --scenario video_registration
|
||||
```
|
||||
|
||||
### 4.3 性能基準測試套件
|
||||
|
||||
```
|
||||
benchmarks/
|
||||
├── api_benchmarks/ # API 效能測試
|
||||
├── video_processing/ # 視頻處理測試
|
||||
├── search_benchmarks/ # 搜索效能測試
|
||||
├── memory_profiling/ # 記憶體分析
|
||||
└── reports/ # 測試報告
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 監控與告警
|
||||
|
||||
### 5.1 效能監控儀表板
|
||||
|
||||
| 監控維度 | 指標 | 告警閾值 |
|
||||
|----------|------|----------|
|
||||
| **系統資源** | CPU 使用率 | > 80% 持續 5分鐘 |
|
||||
| | 記憶體使用率 | > 85% 持續 5分鐘 |
|
||||
| | 磁碟使用率 | > 90% |
|
||||
| **應用效能** | API 響應時間 | P95 > 1秒 |
|
||||
| | 錯誤率 | > 1% |
|
||||
| | 任務佇列長度 | > 100 |
|
||||
| **業務指標** | 視頻處理成功率 | < 95% |
|
||||
| | 搜索召回率 | < 85% |
|
||||
| | 用戶滿意度 | < 4.0/5.0 |
|
||||
|
||||
### 5.2 效能分析工具
|
||||
|
||||
| 工具 | 用途 | 集成方式 |
|
||||
|------|------|----------|
|
||||
| **Prometheus** | 指標收集 | Rust 客戶端 + 暴露端點 |
|
||||
| **Grafana** | 視覺化儀表板 | 預設儀表板 |
|
||||
| **Jaeger** | 分佈式追蹤 | OpenTelemetry |
|
||||
| **pprof** | CPU/記憶體分析 | 性能剖析端點 |
|
||||
| **Valgrind** | 記憶體泄漏檢測 | 開發環境測試 |
|
||||
|
||||
---
|
||||
|
||||
## 6. 未來優化方向
|
||||
|
||||
### 6.1 短期優化(1-3個月)
|
||||
|
||||
1. **CUT 算法重構**:
|
||||
- 實現增量計算
|
||||
- 添加啟發式剪枝
|
||||
- 預期效能提升:5-10 倍
|
||||
|
||||
2. **Python 執行器優化**:
|
||||
- 進程池預熱
|
||||
- 結果序列化優化
|
||||
- 預期效能提升:2-3 倍
|
||||
|
||||
3. **向量搜索優化**:
|
||||
- HNSW 參數調優
|
||||
- 查詢預處理
|
||||
- 預期效能提升:30-50%
|
||||
|
||||
### 6.2 中期優化(3-6個月)
|
||||
|
||||
1. **異步處理管道**:
|
||||
- 完全異步任務調度
|
||||
- 實時進度回報
|
||||
- 預期吞吐量提升:2-3 倍
|
||||
|
||||
2. **模型壓縮與量化**:
|
||||
- INT8 量化支持
|
||||
- 模型分片部署
|
||||
- 預期記憶體節省:50-75%
|
||||
|
||||
3. **分散式計算**:
|
||||
- 多機部署支持
|
||||
- 負載均衡策略
|
||||
- 預期橫向擴展:線性增長
|
||||
|
||||
### 6.3 長期願景(6-12個月)
|
||||
|
||||
1. **邊緣計算集成**:
|
||||
- 輕量級處理器
|
||||
- 離線模式支持
|
||||
- 應用場景:移動端、IoT
|
||||
|
||||
2. **硬體加速**:
|
||||
- GPU 推理優化
|
||||
- FPGA 加速支持
|
||||
- 預期效能提升:10-100 倍
|
||||
|
||||
3. **智能調度**:
|
||||
- AI 驅動的資源分配
|
||||
- 預測性擴展
|
||||
- 預期成本節省:30-50%
|
||||
|
||||
---
|
||||
|
||||
## 7. 相關資源
|
||||
|
||||
### 7.1 效能測試數據
|
||||
|
||||
- [效能基準報告](./benchmarks/reports/latest.md)
|
||||
- [壓力測試結果](./benchmarks/reports/stress_test.md)
|
||||
- [監控儀表板](http://localhost:3000/d/momentry-performance)
|
||||
|
||||
### 7.2 配置參數調優
|
||||
|
||||
```toml
|
||||
# 效能相關配置
|
||||
[performance]
|
||||
max_concurrent_tasks = 5
|
||||
vector_cache_size = "1GB"
|
||||
database_pool_size = 20
|
||||
|
||||
# 擴展配置
|
||||
[scaling]
|
||||
auto_scaling_enabled = false
|
||||
min_instances = 1
|
||||
max_instances = 10
|
||||
```
|
||||
|
||||
### 7.3 參考文檔
|
||||
|
||||
- [Redis 效能調優指南](https://redis.io/topics/latency)
|
||||
- [PostgreSQL 效能優化](https://www.postgresql.org/docs/current/performance.html)
|
||||
- [向量資料庫效能最佳實踐](https://qdrant.tech/documentation/performance/)
|
||||
|
||||
---
|
||||
|
||||
## 8. 結論
|
||||
|
||||
Momentry Core 的效能與可擴展性設計遵循以下原則:
|
||||
|
||||
1. **分層優化**:從計算、記憶體、儲存多個維度進行系統性優化
|
||||
2. **漸進式改進**:短期解決現有瓶頸,中期建立完善架構,長期實現智能調度
|
||||
3. **數據驅動**:建立完整的監控體系,基於實際數據進行決策
|
||||
4. **平衡策略**:在效能、成本、複雜度之間找到最佳平衡點
|
||||
|
||||
通過實施上述策略,Momentry Core 能夠支持從小型部署到大型企業級應用的各種場景,提供穩定、高效、可擴展的視頻內容分析服務。
|
||||
@@ -0,0 +1,619 @@
|
||||
# 人物身份整合架构设计
|
||||
|
||||
## 概述
|
||||
|
||||
将人脸识别(Face Recognition)和声纹识别(ASRX Speaker Diarization)整合,在视频块(Chunk)中标注人物身份。
|
||||
|
||||
## 架构设计
|
||||
|
||||
### 数据流
|
||||
|
||||
```
|
||||
视频文件
|
||||
↓
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ 并行处理 │
|
||||
├─────────────────────────────────────────────┤
|
||||
│ 1. Face Detection → face_detections │
|
||||
│ 2. ASRX → asrx_segments (speaker_id) │
|
||||
│ 3. Chunk Generation → chunks │
|
||||
└─────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ 时间重叠分析 │
|
||||
├─────────────────────────────────────────────┤
|
||||
│ 匹配规则: │
|
||||
│ - face_detections.timestamp ∈ [asrx.start, asrx.end]
|
||||
│ - 提取时间重叠最大的配对 │
|
||||
└─────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ 创建人物身份关联 │
|
||||
├─────────────────────────────────────────────┤
|
||||
│ person_identities (person_id) │
|
||||
│ ├─ face_id (外键) │
|
||||
│ ├─ speaker_id (字符串) │
|
||||
│ ├─ confidence (关联置信度) │
|
||||
│ └─ video_uuid (来源视频) │
|
||||
└─────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ 更新 Chunk 元数据 │
|
||||
├─────────────────────────────────────────────┤
|
||||
│ chunks.metadata: { │
|
||||
│ "person_identities": [ │
|
||||
│ { │
|
||||
│ "person_id": "person_xxx", │
|
||||
│ "face_id": "face_123", │
|
||||
│ "speaker_id": "SPEAKER_00", │
|
||||
│ "confidence": 0.85 │
|
||||
│ } │
|
||||
│ ] │
|
||||
│ } │
|
||||
└─────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## 数据库表设计
|
||||
|
||||
### 1. person_identities(人物身份表)
|
||||
|
||||
```sql
|
||||
CREATE TABLE person_identities (
|
||||
id SERIAL PRIMARY KEY,
|
||||
person_id VARCHAR(255) NOT NULL UNIQUE,
|
||||
|
||||
-- 身份关联
|
||||
face_identity_id INTEGER REFERENCES face_identities(id) ON DELETE SET NULL,
|
||||
speaker_id VARCHAR(64), -- SPEAKER_00, SPEAKER_01, etc.
|
||||
|
||||
-- 关联信息
|
||||
video_uuid VARCHAR(255) NOT NULL,
|
||||
confidence DOUBLE PRECISION DEFAULT 0.0,
|
||||
|
||||
-- 元数据
|
||||
name VARCHAR(255), -- 人物姓名(手动标注)
|
||||
metadata JSONB DEFAULT '{}'::jsonb,
|
||||
|
||||
-- 时间戳
|
||||
first_appearance_time DOUBLE PRECISION,
|
||||
last_appearance_time DOUBLE PRECISION,
|
||||
total_appearance_duration DOUBLE PRECISION DEFAULT 0.0,
|
||||
appearance_count INTEGER DEFAULT 0,
|
||||
|
||||
-- 审计字段
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
is_confirmed BOOLEAN DEFAULT FALSE, -- 用户确认的身份
|
||||
|
||||
-- 约束
|
||||
CONSTRAINT unique_person_identity UNIQUE (video_uuid, face_identity_id, speaker_id)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_person_identities_video_uuid ON person_identities(video_uuid);
|
||||
CREATE INDEX idx_person_identities_face ON person_identities(face_identity_id);
|
||||
CREATE INDEX idx_person_identities_speaker ON person_identities(speaker_id);
|
||||
CREATE INDEX idx_person_identities_name ON person_identities(name);
|
||||
```
|
||||
|
||||
### 2. person_appearances(人物出场记录表)
|
||||
|
||||
```sql
|
||||
CREATE TABLE person_appearances (
|
||||
id SERIAL PRIMARY KEY,
|
||||
person_id VARCHAR(255) NOT NULL REFERENCES person_identities(person_id) ON DELETE CASCADE,
|
||||
|
||||
-- 出场信息
|
||||
video_uuid VARCHAR(255) NOT NULL,
|
||||
start_time DOUBLE PRECISION NOT NULL,
|
||||
end_time DOUBLE PRECISION NOT NULL,
|
||||
duration DOUBLE PRECISION NOT NULL,
|
||||
|
||||
-- 来源信息
|
||||
face_detection_id INTEGER REFERENCES face_detections(id) ON DELETE SET NULL,
|
||||
asrx_segment_id INTEGER, -- 暂不设外键,ASRX 结果存储在 JSON 中
|
||||
|
||||
-- 元数据
|
||||
confidence DOUBLE PRECISION DEFAULT 0.0,
|
||||
metadata JSONB DEFAULT '{}'::jsonb,
|
||||
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX idx_person_appearances_person ON person_appearances(person_id);
|
||||
CREATE INDEX idx_person_appearances_video ON person_appearances(video_uuid);
|
||||
CREATE INDEX idx_person_appearances_time ON person_appearances(video_uuid, start_time, end_time);
|
||||
```
|
||||
|
||||
### 3. 增强 chunks 表
|
||||
|
||||
```sql
|
||||
-- 在 chunks.metadata 中添加人物身份信息
|
||||
-- 示例结构:
|
||||
{
|
||||
"person_identities": [
|
||||
{
|
||||
"person_id": "person_abc123",
|
||||
"face_id": "face_456",
|
||||
"speaker_id": "SPEAKER_00",
|
||||
"confidence": 0.85,
|
||||
"name": "张三"
|
||||
}
|
||||
],
|
||||
"speaker_id": "SPEAKER_00", -- 主要说话人
|
||||
"face_count": 2 // 检测到的人脸数量
|
||||
}
|
||||
```
|
||||
|
||||
## 核心算法
|
||||
|
||||
### 算法 1:时间重叠匹配
|
||||
|
||||
```python
|
||||
def match_face_with_speaker(face_detections, asrx_segments, threshold=0.5):
|
||||
"""
|
||||
根据时间重叠匹配人脸和说话人
|
||||
|
||||
参数:
|
||||
- face_detections: 人脸检测列表 [{timestamp, face_id, ...}]
|
||||
- asrx_segments: ASRX 片段列表 [{start, end, speaker_id, ...}]
|
||||
- threshold: 最小重叠比例阈值
|
||||
|
||||
返回:
|
||||
- 匹配列表 [{face_id, speaker_id, confidence}]
|
||||
"""
|
||||
matches = []
|
||||
|
||||
for face in face_detections:
|
||||
face_time = face['timestamp']
|
||||
|
||||
# 找到时间重叠的 ASRX 片段
|
||||
for segment in asrx_segments:
|
||||
if segment['start'] <= face_time <= segment['end']:
|
||||
# 计算重叠比例
|
||||
overlap_duration = min(face_time - segment['start'],
|
||||
segment['end'] - face_time)
|
||||
total_duration = segment['end'] - segment['start']
|
||||
overlap_ratio = overlap_duration / total_duration
|
||||
|
||||
if overlap_ratio >= threshold:
|
||||
matches.append({
|
||||
'face_id': face['face_id'],
|
||||
'speaker_id': segment['speaker_id'],
|
||||
'confidence': overlap_ratio,
|
||||
'timestamp': face_time
|
||||
})
|
||||
|
||||
return matches
|
||||
```
|
||||
|
||||
### 算法 2:人物身份聚类
|
||||
|
||||
```python
|
||||
def cluster_person_identities(matches, face_embeddings, similarity_threshold=0.7):
|
||||
"""
|
||||
将匹配结果聚类为人物身份
|
||||
|
||||
参数:
|
||||
- matches: 匹配列表
|
||||
- face_embeddings: 人脸嵌入向量 {face_id: embedding}
|
||||
- similarity_threshold: 相似度阈值
|
||||
|
||||
返回:
|
||||
- 人物身份列表 [{person_id, face_ids, speaker_ids}]
|
||||
"""
|
||||
from sklearn.cluster import DBSCAN
|
||||
import numpy as np
|
||||
|
||||
# 收集所有 face_id 和对应的嵌入向量
|
||||
face_ids = list(set(m['face_id'] for m in matches))
|
||||
embeddings = [face_embeddings[face_id] for face_id in face_ids]
|
||||
|
||||
# 聚类
|
||||
clustering = DBSCAN(eps=1-similarity_threshold, min_samples=2, metric='cosine')
|
||||
labels = clustering.fit_predict(embeddings)
|
||||
|
||||
# 按聚类分组
|
||||
person_identities = {}
|
||||
for face_id, label in zip(face_ids, labels):
|
||||
if label == -1:
|
||||
continue # 噪声
|
||||
|
||||
person_id = f"person_{label}"
|
||||
if person_id not in person_identities:
|
||||
person_identities[person_id] = {
|
||||
'person_id': person_id,
|
||||
'face_ids': [],
|
||||
'speaker_ids': set()
|
||||
}
|
||||
|
||||
person_identities[person_id]['face_ids'].append(face_id)
|
||||
|
||||
# 添加对应的 speaker_id
|
||||
for match in matches:
|
||||
if match['face_id'] == face_id:
|
||||
person_identities[person_id]['speaker_ids'].add(match['speaker_id'])
|
||||
|
||||
# 转换 set 为 list
|
||||
for person in person_identities.values():
|
||||
person['speaker_ids'] = list(person['speaker_ids'])
|
||||
|
||||
return list(person_identities.values())
|
||||
```
|
||||
|
||||
### 算法 3:更新 Chunk 人物信息
|
||||
|
||||
```python
|
||||
def update_chunk_person_identities(chunk, person_appearances):
|
||||
"""
|
||||
更新 Chunk 的人物身份信息
|
||||
|
||||
参数:
|
||||
- chunk: Chunk 对象
|
||||
- person_appearances: 人物出场记录列表
|
||||
|
||||
返回:
|
||||
- 更新后的 Chunk
|
||||
"""
|
||||
chunk_start = chunk['start_time']
|
||||
chunk_end = chunk['end_time']
|
||||
|
||||
# 找到与 Chunk 时间重叠的人物出场
|
||||
overlapping_persons = []
|
||||
for appearance in person_appearances:
|
||||
if (appearance['start_time'] <= chunk_end and
|
||||
appearance['end_time'] >= chunk_start):
|
||||
|
||||
# 计算重叠时长
|
||||
overlap_start = max(chunk_start, appearance['start_time'])
|
||||
overlap_end = min(chunk_end, appearance['end_time'])
|
||||
overlap_duration = overlap_end - overlap_start
|
||||
|
||||
overlapping_persons.append({
|
||||
'person_id': appearance['person_id'],
|
||||
'name': appearance.get('name'),
|
||||
'overlap_duration': overlap_duration,
|
||||
'confidence': appearance['confidence']
|
||||
})
|
||||
|
||||
# 按重叠时长排序
|
||||
overlapping_persons.sort(key=lambda x: x['overlap_duration'], reverse=True)
|
||||
|
||||
# 更新 Chunk 元数据
|
||||
metadata = chunk.get('metadata', {})
|
||||
metadata['person_identities'] = overlapping_persons
|
||||
|
||||
# 设置主要人物(重叠时长最长)
|
||||
if overlapping_persons:
|
||||
metadata['primary_person'] = overlapping_persons[0]['person_id']
|
||||
|
||||
chunk['metadata'] = metadata
|
||||
|
||||
return chunk
|
||||
```
|
||||
|
||||
## API 设计
|
||||
|
||||
### 1. 创建人物身份关联
|
||||
|
||||
```http
|
||||
POST /api/v1/person/identify
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"video_uuid": "abc123",
|
||||
"auto_match": true,
|
||||
"match_threshold": 0.5
|
||||
}
|
||||
|
||||
Response:
|
||||
{
|
||||
"success": true,
|
||||
"message": "Identified 3 persons",
|
||||
"persons": [
|
||||
{
|
||||
"person_id": "person_0",
|
||||
"face_ids": ["face_123", "face_456"],
|
||||
"speaker_ids": ["SPEAKER_00"],
|
||||
"confidence": 0.85,
|
||||
"appearance_count": 15,
|
||||
"total_duration": 120.5
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 查询人物出场时间轴
|
||||
|
||||
```http
|
||||
GET /api/v1/person/:person_id/timeline?video_uuid=abc123
|
||||
|
||||
Response:
|
||||
{
|
||||
"success": true,
|
||||
"person_id": "person_0",
|
||||
"name": "张三",
|
||||
"timeline": [
|
||||
{
|
||||
"start_time": 10.5,
|
||||
"end_time": 25.3,
|
||||
"duration": 14.8,
|
||||
"confidence": 0.92
|
||||
},
|
||||
{
|
||||
"start_time": 45.0,
|
||||
"end_time": 60.2,
|
||||
"duration": 15.2,
|
||||
"confidence": 0.88
|
||||
}
|
||||
],
|
||||
"statistics": {
|
||||
"total_appearances": 15,
|
||||
"total_duration": 120.5,
|
||||
"first_appearance": 10.5,
|
||||
"last_appearance": 350.2
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 手动标注人物姓名
|
||||
|
||||
```http
|
||||
PATCH /api/v1/person/:person_id
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"name": "张三",
|
||||
"metadata": {
|
||||
"role": "主持人",
|
||||
"department": "新闻部"
|
||||
}
|
||||
}
|
||||
|
||||
Response:
|
||||
{
|
||||
"success": true,
|
||||
"message": "Person identity updated",
|
||||
"person_id": "person_0"
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 查询 Chunk 中的人物
|
||||
|
||||
```http
|
||||
GET /api/v1/chunks/:chunk_id/persons
|
||||
|
||||
Response:
|
||||
{
|
||||
"success": true,
|
||||
"chunk_id": "sentence_0012",
|
||||
"persons": [
|
||||
{
|
||||
"person_id": "person_0",
|
||||
"name": "张三",
|
||||
"confidence": 0.85,
|
||||
"overlap_duration": 3.5
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 实现步骤
|
||||
|
||||
### Phase 1: 数据库表创建 (Day 1)
|
||||
|
||||
1. ✅ 创建迁移文件 `007_person_identity_tables.sql`
|
||||
2. ✅ 创建 `person_identities` 表
|
||||
3. ✅ 创建 `person_appearances` 表
|
||||
4. ✅ 创建索引和约束
|
||||
5. ✅ 运行迁移测试
|
||||
|
||||
### Phase 2: 核心算法实现 (Day 2-3)
|
||||
|
||||
1. ⏳ 实现 Rust 结构体
|
||||
- `PersonIdentity`
|
||||
- `PersonAppearance`
|
||||
- `PersonMatch`
|
||||
|
||||
2. ⏳ 实现匹配算法
|
||||
- `match_face_with_speaker()`
|
||||
- `cluster_person_identities()`
|
||||
- `update_chunk_person_identities()`
|
||||
|
||||
3. ⏳ 实现数据库操作
|
||||
- `store_person_identity()`
|
||||
- `store_person_appearance()`
|
||||
- `update_chunks_with_persons()`
|
||||
|
||||
### Phase 3: API 实现 (Day 4)
|
||||
|
||||
1. ⏳ 创建 `src/api/person_identity.rs`
|
||||
2. ⏳ 实现 API 端点
|
||||
- `POST /api/v1/person/identify`
|
||||
- `GET /api/v1/person/:person_id/timeline`
|
||||
- `PATCH /api/v1/person/:person_id`
|
||||
- `GET /api/v1/chunks/:chunk_id/persons`
|
||||
|
||||
3. ⏳ 添加路由到 `server.rs`
|
||||
|
||||
### Phase 4: 集成测试 (Day 5)
|
||||
|
||||
1. ⏳ 准备测试视频
|
||||
2. ⏳ 运行完整处理流程
|
||||
- Face Detection
|
||||
- ASRX
|
||||
- Chunk Generation
|
||||
- Person Identity Creation
|
||||
|
||||
3. ⏳ 验证结果
|
||||
- 数据库记录正确性
|
||||
- API 响应正确性
|
||||
- 时间轴查询正确性
|
||||
|
||||
### Phase 5: 文档和优化 (Day 6)
|
||||
|
||||
1. ⏳ 编写 API 文档
|
||||
2. ⏳ 编写使用指南
|
||||
3. ⏳ 性能优化
|
||||
4. ⏳ 错误处理增强
|
||||
|
||||
## 性能优化
|
||||
|
||||
### 1. 批量插入
|
||||
|
||||
```rust
|
||||
// 使用事务批量插入人物出场记录
|
||||
pub async fn batch_insert_person_appearances(
|
||||
db: &PostgresDb,
|
||||
appearances: &[PersonAppearance],
|
||||
) -> Result<()> {
|
||||
let mut tx = db.pool().begin().await?;
|
||||
|
||||
for appearance in appearances {
|
||||
sqlx::query(r#"
|
||||
INSERT INTO person_appearances (
|
||||
person_id, video_uuid, start_time, end_time,
|
||||
duration, confidence, metadata
|
||||
) VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||
"#)
|
||||
.bind(&appearance.person_id)
|
||||
.bind(&appearance.video_uuid)
|
||||
.bind(appearance.start_time)
|
||||
.bind(appearance.end_time)
|
||||
.bind(appearance.duration)
|
||||
.bind(appearance.confidence)
|
||||
.bind(&appearance.metadata)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
}
|
||||
|
||||
tx.commit().await?;
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 索引优化
|
||||
|
||||
```sql
|
||||
-- 为常用查询添加复合索引
|
||||
CREATE INDEX idx_person_appearances_video_time
|
||||
ON person_appearances(video_uuid, start_time, end_time);
|
||||
|
||||
CREATE INDEX idx_person_identities_video_face
|
||||
ON person_identities(video_uuid, face_identity_id);
|
||||
|
||||
CREATE INDEX idx_person_identities_video_speaker
|
||||
ON person_identities(video_uuid, speaker_id);
|
||||
```
|
||||
|
||||
### 3. 缓存策略
|
||||
|
||||
```rust
|
||||
// 使用 Redis 缓存人物时间轴查询
|
||||
pub async fn get_person_timeline_cached(
|
||||
redis: &RedisClient,
|
||||
person_id: &str,
|
||||
video_uuid: &str,
|
||||
) -> Result<Vec<PersonAppearance>> {
|
||||
let cache_key = format!("person_timeline:{}:{}", video_uuid, person_id);
|
||||
|
||||
// 尝试从缓存获取
|
||||
if let Some(cached) = redis.get(&cache_key).await? {
|
||||
return Ok(serde_json::from_str(&cached)?);
|
||||
}
|
||||
|
||||
// 从数据库查询
|
||||
let timeline = query_person_timeline_from_db(person_id, video_uuid).await?;
|
||||
|
||||
// 缓存结果(5分钟)
|
||||
redis.set_ex(&cache_key, &serde_json::to_string(&timeline)?, 300).await?;
|
||||
|
||||
Ok(timeline)
|
||||
}
|
||||
```
|
||||
|
||||
## 错误处理
|
||||
|
||||
### 1. 匹配置信度过低
|
||||
|
||||
```rust
|
||||
if confidence < MIN_MATCH_CONFIDENCE {
|
||||
tracing::warn!(
|
||||
"[PERSON] Low confidence match: face={}, speaker={}, confidence={}",
|
||||
face_id, speaker_id, confidence
|
||||
);
|
||||
// 记录但不创建关联
|
||||
return Ok(None);
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 重复匹配
|
||||
|
||||
```rust
|
||||
// 检查是否已存在相同关联
|
||||
let existing = sqlx::query!(
|
||||
"SELECT id FROM person_identities
|
||||
WHERE video_uuid = $1 AND face_identity_id = $2 AND speaker_id = $3",
|
||||
video_uuid, face_id, speaker_id
|
||||
)
|
||||
.fetch_optional(db.pool())
|
||||
.await?;
|
||||
|
||||
if existing.is_some() {
|
||||
tracing::info!("[PERSON] Identity already exists, skipping");
|
||||
return Ok(());
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 时间范围无效
|
||||
|
||||
```rust
|
||||
if start_time >= end_time {
|
||||
anyhow::bail!(
|
||||
"Invalid time range: start={} >= end={}",
|
||||
start_time, end_time
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## 监控指标
|
||||
|
||||
```rust
|
||||
// Prometheus 指标
|
||||
lazy_static! {
|
||||
static ref PERSON_IDENTITIES_CREATED: Counter =
|
||||
register_counter!("person_identities_created_total").unwrap();
|
||||
|
||||
static ref PERSON_MATCHES_TOTAL: Counter =
|
||||
register_counter!("person_matches_total").unwrap();
|
||||
|
||||
static ref PERSON_MATCH_CONFIDENCE: Histogram =
|
||||
register_histogram!("person_match_confidence").unwrap();
|
||||
}
|
||||
```
|
||||
|
||||
## 未来扩展
|
||||
|
||||
### 1. 多模态融合
|
||||
|
||||
- 结合 OCR 文字识别(字幕、名牌)
|
||||
- 结合场景分类(新闻演播室、会议室)
|
||||
- 结合姿态识别(站立、坐着)
|
||||
|
||||
### 2. 跨视频人物追踪
|
||||
|
||||
- 全局人物身份库
|
||||
- 人脸嵌入向量相似度匹配
|
||||
- 服装、配饰特征
|
||||
|
||||
### 3. 实时处理
|
||||
|
||||
- 流式视频处理
|
||||
- 实时人物识别
|
||||
- WebSocket 推送更新
|
||||
|
||||
## 参考资料
|
||||
|
||||
- [InsightFace Documentation](https://github.com/deepinsight/insightface)
|
||||
- [WhisperX Speaker Diarization](https://github.com/m-bain/whisperX)
|
||||
- [PostgreSQL pgvector](https://github.com/pgvector/pgvector)
|
||||
- [DBSCAN Clustering Algorithm](https://scikit-learn.org/stable/modules/clustering.html#dbscan)
|
||||
@@ -0,0 +1,395 @@
|
||||
# 人物身份整合功能使用指南
|
||||
|
||||
## 概述
|
||||
|
||||
该功能通过整合人脸识别(Face Recognition)和声纹识别(ASRX Speaker Diarization),在视频块(Chunk)中自动标注人物身份。
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 1. 处理视频
|
||||
|
||||
首先需要处理视频以提取人脸和声纹信息:
|
||||
|
||||
```bash
|
||||
# 处理视频,提取所有特征
|
||||
cargo run -- process /path/to/video.mp4 --modules face,asrx
|
||||
|
||||
# 或者使用 playground 进行测试
|
||||
cargo run --bin momentry_playground -- process /path/to/video.mp4 --modules face,asrx
|
||||
```
|
||||
|
||||
这将生成:
|
||||
- `face.json` - 人脸检测结果
|
||||
- `asrx.json` - 说话人分离结果
|
||||
|
||||
### 2. 自动识别人物身份
|
||||
|
||||
使用 API 自动匹配人脸和声纹:
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:3002/api/v1/person/identify \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: your_api_key" \
|
||||
-d '{
|
||||
"video_uuid": "your_video_uuid",
|
||||
"auto_match": true,
|
||||
"match_threshold": 0.5
|
||||
}'
|
||||
```
|
||||
|
||||
响应示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Identified 3 persons",
|
||||
"persons": [
|
||||
{
|
||||
"person_id": "person_abc123",
|
||||
"speaker_id": "SPEAKER_00",
|
||||
"confidence": 0.85,
|
||||
"appearance_count": 15,
|
||||
"total_appearance_duration": 120.5
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 查询人物时间轴
|
||||
|
||||
查询某个人物在视频中的出场时间:
|
||||
|
||||
```bash
|
||||
curl -X GET "http://localhost:3002/api/v1/person/person_abc123/timeline?video_uuid=your_video_uuid" \
|
||||
-H "X-API-Key: your_api_key"
|
||||
```
|
||||
|
||||
响应示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"person_id": "person_abc123",
|
||||
"name": "张三",
|
||||
"timeline": [
|
||||
{
|
||||
"start_time": 10.5,
|
||||
"end_time": 25.3,
|
||||
"duration": 14.8,
|
||||
"confidence": 0.92
|
||||
}
|
||||
],
|
||||
"statistics": {
|
||||
"total_appearances": 15,
|
||||
"total_duration": 120.5,
|
||||
"first_appearance": 10.5,
|
||||
"last_appearance": 350.2,
|
||||
"average_confidence": 0.88
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 手动标注人物姓名
|
||||
|
||||
为识别的人物添加姓名:
|
||||
|
||||
```bash
|
||||
curl -X PATCH http://localhost:3002/api/v1/person/person_abc123 \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: your_api_key" \
|
||||
-d '{
|
||||
"name": "张三",
|
||||
"metadata": {
|
||||
"role": "主持人",
|
||||
"department": "新闻部"
|
||||
},
|
||||
"is_confirmed": true
|
||||
}'
|
||||
```
|
||||
|
||||
### 5. 查询 Chunk 中的人物
|
||||
|
||||
查看某个视频块中出现的人物:
|
||||
|
||||
```bash
|
||||
curl -X GET http://localhost:3002/api/v1/chunks/sentence_0012/persons \
|
||||
-H "X-API-Key: your_api_key"
|
||||
```
|
||||
|
||||
响应示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"chunk_id": "sentence_0012",
|
||||
"persons": [
|
||||
{
|
||||
"person_id": "person_abc123",
|
||||
"name": "张三",
|
||||
"confidence": 0.85,
|
||||
"overlap_duration": 3.5
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## API 端点总结
|
||||
|
||||
| 端点 | 方法 | 描述 |
|
||||
|------|------|------|
|
||||
| `/api/v1/person/identify` | POST | 自动识别人物身份 |
|
||||
| `/api/v1/person/:person_id` | GET | 获取人物详情 |
|
||||
| `/api/v1/person/:person_id` | PATCH | 更新人物信息 |
|
||||
| `/api/v1/person/:person_id/timeline` | GET | 查询人物时间轴 |
|
||||
| `/api/v1/person/:person_id/appearances` | GET | 查询人物出场记录 |
|
||||
| `/api/v1/chunks/:chunk_id/persons` | GET | 查询 Chunk 中的人物 |
|
||||
|
||||
## 数据库表结构
|
||||
|
||||
### person_identities(人物身份表)
|
||||
|
||||
| 字段 | 类型 | 描述 |
|
||||
|------|------|------|
|
||||
| person_id | VARCHAR(255) | 人物唯一标识 |
|
||||
| face_identity_id | INTEGER | 关联的人脸身份 ID |
|
||||
| speaker_id | VARCHAR(64) | 说话人 ID(SPEAKER_00, SPEAKER_01...) |
|
||||
| video_uuid | VARCHAR(255) | 来源视频 UUID |
|
||||
| name | VARCHAR(255) | 人物姓名(手动标注) |
|
||||
| confidence | DOUBLE PRECISION | 关联置信度 |
|
||||
| appearance_count | INTEGER | 出场次数 |
|
||||
| total_appearance_duration | DOUBLE PRECISION | 总出场时长(秒) |
|
||||
| is_confirmed | BOOLEAN | 是否已确认 |
|
||||
|
||||
### person_appearances(人物出场记录表)
|
||||
|
||||
| 字段 | 类型 | 描述 |
|
||||
|------|------|------|
|
||||
| person_id | VARCHAR(255) | 关联的人物身份 ID |
|
||||
| video_uuid | VARCHAR(255) | 视频 UUID |
|
||||
| start_time | DOUBLE PRECISION | 开始时间(秒) |
|
||||
| end_time | DOUBLE PRECISION | 结束时间(秒) |
|
||||
| duration | DOUBLE PRECISION | 持续时间(秒) |
|
||||
| face_detection_id | INTEGER | 关联的人脸检测 ID |
|
||||
| confidence | DOUBLE PRECISION | 置信度 |
|
||||
|
||||
## 工作流程
|
||||
|
||||
### 完整处理流程
|
||||
|
||||
```
|
||||
1. 视频上传
|
||||
↓
|
||||
2. 并行处理
|
||||
├─ Face Detection → face_detections
|
||||
├─ ASRX Processing → speaker_id
|
||||
└─ Chunk Generation → chunks
|
||||
↓
|
||||
3. 自动匹配
|
||||
├─ 时间重叠分析
|
||||
├─ Face ID + Speaker ID → Person Identity
|
||||
└─ 创建 person_identities 和 person_appearances
|
||||
↓
|
||||
4. 更新 Chunks
|
||||
└─ 在 metadata 中添加人物信息
|
||||
↓
|
||||
5. 查询和使用
|
||||
├─ 时间轴查询
|
||||
├─ 人物搜索
|
||||
└─ Chunk 标注
|
||||
```
|
||||
|
||||
### 匹配算法
|
||||
|
||||
核心匹配算法基于**时间重叠**:
|
||||
|
||||
1. 对于每个人脸检测,找到时间重叠的 ASRX 片段
|
||||
2. 计算重叠比例 = overlap_duration / segment_duration
|
||||
3. 如果 overlap_ratio >= threshold,则创建匹配
|
||||
4. 按匹配数量和置信度聚类,形成人物身份
|
||||
|
||||
## 配置参数
|
||||
|
||||
### 匹配阈值
|
||||
|
||||
```rust
|
||||
// 默认匹配阈值
|
||||
const DEFAULT_MATCH_THRESHOLD: f64 = 0.5;
|
||||
|
||||
// 最小置信度
|
||||
const MIN_CONFIDENCE: f64 = 0.6;
|
||||
```
|
||||
|
||||
### 数据库索引
|
||||
|
||||
系统自动创建以下索引以优化查询性能:
|
||||
|
||||
```sql
|
||||
-- 时间范围查询
|
||||
CREATE INDEX idx_person_appearances_time
|
||||
ON person_appearances(video_uuid, start_time, end_time);
|
||||
|
||||
-- 人物查询
|
||||
CREATE INDEX idx_person_identities_video_uuid
|
||||
ON person_identities(video_uuid);
|
||||
|
||||
-- 说话人查询
|
||||
CREATE INDEX idx_person_identities_speaker
|
||||
ON person_identities(speaker_id);
|
||||
```
|
||||
|
||||
## 最佳实践
|
||||
|
||||
### 1. 视频处理顺序
|
||||
|
||||
```bash
|
||||
# 推荐:先处理基础特征,再识别人物
|
||||
cargo run -- process video.mp4 --modules asr,asrx,face
|
||||
```
|
||||
|
||||
### 2. 批量处理
|
||||
|
||||
```bash
|
||||
# 批量处理多个视频
|
||||
for video in /path/to/videos/*.mp4; do
|
||||
cargo run -- process "$video" --modules asr,asrx,face
|
||||
|
||||
# 获取 UUID
|
||||
uuid=$(basename "$video" .mp4)
|
||||
|
||||
# 自动识别人物
|
||||
curl -X POST http://localhost:3002/api/v1/person/identify \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: your_api_key" \
|
||||
-d "{\"video_uuid\": \"$uuid\", \"auto_match\": true}"
|
||||
done
|
||||
```
|
||||
|
||||
### 3. 人物标注工作流
|
||||
|
||||
```bash
|
||||
# 1. 列出未确认的人物
|
||||
curl -X GET "http://localhost:3002/api/v1/person/list?is_confirmed=false"
|
||||
|
||||
# 2. 查看人物出场片段
|
||||
curl -X GET "http://localhost:3002/api/v1/person/person_xxx/timeline"
|
||||
|
||||
# 3. 确认并标注姓名
|
||||
curl -X PATCH http://localhost:3002/api/v1/person/person_xxx \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"name": "张三", "is_confirmed": true}'
|
||||
```
|
||||
|
||||
## 故障排查
|
||||
|
||||
### 问题 1:匹配数量过低
|
||||
|
||||
**原因**:匹配阈值过高
|
||||
|
||||
**解决**:降低阈值到 0.3-0.5
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:3002/api/v1/person/identify \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"video_uuid": "xxx", "match_threshold": 0.3}'
|
||||
```
|
||||
|
||||
### 问题 2:人物身份重复
|
||||
|
||||
**原因**:同一人物被识别为多个身份
|
||||
|
||||
**解决**:使用 merge API 合并
|
||||
|
||||
```sql
|
||||
-- 直接在数据库中合并
|
||||
SELECT merge_person_identities(
|
||||
'person_target',
|
||||
ARRAY['person_source1', 'person_source2']
|
||||
);
|
||||
```
|
||||
|
||||
### 问题 3:时间轴查询慢
|
||||
|
||||
**原因**:缺少索引或数据量大
|
||||
|
||||
**解决**:
|
||||
1. 确认索引已创建:`\d person_appearances`
|
||||
2. 使用 EXPLAIN 分析查询
|
||||
3. 考虑分区表(按 video_uuid)
|
||||
|
||||
## 性能优化
|
||||
|
||||
### 1. 批量插入
|
||||
|
||||
```rust
|
||||
// 使用事务批量插入出场记录
|
||||
pub async fn batch_insert_appearances(
|
||||
db: &PostgresDb,
|
||||
appearances: &[PersonAppearance],
|
||||
) -> Result<()> {
|
||||
let mut tx = db.pool().begin().await?;
|
||||
|
||||
for appearance in appearances {
|
||||
sqlx::query("INSERT INTO ...")
|
||||
.bind(...)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
}
|
||||
|
||||
tx.commit().await?;
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 缓存策略
|
||||
|
||||
```rust
|
||||
// 使用 Redis 缓存时间轴查询
|
||||
let cache_key = format!("person_timeline:{}:{}", video_uuid, person_id);
|
||||
|
||||
if let Some(cached) = redis.get(&cache_key).await? {
|
||||
return Ok(serde_json::from_str(&cached)?);
|
||||
}
|
||||
|
||||
// 查询数据库并缓存
|
||||
let timeline = query_from_db().await?;
|
||||
redis.set_ex(&cache_key, &serde_json::to_string(&timeline)?, 300).await?;
|
||||
```
|
||||
|
||||
## 监控指标
|
||||
|
||||
```rust
|
||||
// Prometheus 指标
|
||||
lazy_static! {
|
||||
static ref PERSON_IDENTITIES_CREATED: Counter =
|
||||
register_counter!("person_identities_created_total").unwrap();
|
||||
|
||||
static ref PERSON_MATCH_CONFIDENCE: Histogram =
|
||||
register_histogram!("person_match_confidence").unwrap();
|
||||
}
|
||||
```
|
||||
|
||||
## 未来扩展
|
||||
|
||||
### 1. 多模态融合
|
||||
|
||||
- 结合 OCR(字幕、名牌)
|
||||
- 结合场景分类
|
||||
- 结合姿态识别
|
||||
|
||||
### 2. 跨视频追踪
|
||||
|
||||
- 全局人物身份库
|
||||
- 人脸嵌入相似度匹配
|
||||
- 服装特征识别
|
||||
|
||||
### 3. 实时处理
|
||||
|
||||
- 流式视频处理
|
||||
- 实时人物识别
|
||||
- WebSocket 推送更新
|
||||
|
||||
## 参考资料
|
||||
|
||||
- [InsightFace Documentation](https://github.com/deepinsight/insightface)
|
||||
- [WhisperX Speaker Diarization](https://github.com/m-bain/whisperX)
|
||||
- [PostgreSQL pgvector](https://github.com/pgvector/pgvector)
|
||||
- [完整架构设计文档](./PERSON_IDENTITY_INTEGRATION.md)
|
||||
@@ -0,0 +1,237 @@
|
||||
---
|
||||
document_type: "architecture_design"
|
||||
service: "MOMENTRY_CORE"
|
||||
title: "Momentry Core 全域資源與處理管線架構 (v1.0)"
|
||||
date: "2026-04-21"
|
||||
version: "V1.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
tags:
|
||||
- "momentry"
|
||||
- "core"
|
||||
- "全域資源與處理管線架構"
|
||||
ai_query_hints:
|
||||
- "查詢 Momentry Core 全域資源與處理管線架構 (v1.0) 的內容"
|
||||
- "Momentry Core 全域資源與處理管線架構 (v1.0) 的主要目的是什麼?"
|
||||
- "如何操作或實施 Momentry Core 全域資源與處理管線架構 (v1.0)?"
|
||||
---
|
||||
|
||||
# Momentry Core 全域資源與處理管線架構 (v1.0)
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | OpenCode |
|
||||
| 建立時間 | 2026-04-21 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-04-21 | 創建從檔案到知識的端到端處理管線架構 | OpenCode | OpenCode / Qwen3.6-Plus |
|
||||
|
||||
---
|
||||
|
||||
## 0. 設計目標
|
||||
|
||||
建立一套**標準化、可追溯、可擴展**的媒體處理管線,將原始媒體檔案自動轉化為結構化知識與可檢索內容。
|
||||
|
||||
核心原則:
|
||||
1. **一切皆資源**: 檔案、處理器、服務、產出文件皆受資料庫納管。
|
||||
2. **異步與容錯**: 註冊、處理、索引全階段解耦,支援斷點續傳與失敗重試。
|
||||
3. **版本精確追溯**: 從模型 GGUF Hash 到處理器 Build Time,確保結果可重現。
|
||||
4. **第一階段即時可用**: ASR/文本處理完成後立即提供 BM25/向量搜尋。
|
||||
|
||||
---
|
||||
|
||||
## 1. 大框架總覽:從檔案到知識
|
||||
|
||||
```
|
||||
[原始檔案] (SFTP/API)
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ 階段一:檔案註冊納管作業 (Onboarding Pipeline) │
|
||||
│ • Hash 計算 & UUID 分配 │
|
||||
│ • ffprobe 探針分析 & 分類 │
|
||||
│ • Smart Thumbnail (跳過黑屏截圖) │
|
||||
│ • 狀態更新: CREATED → PENDING │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ 階段二:處理器調度與執行作業 (Orchestration) │
|
||||
│ • 排程器取出 PENDING 任務 │
|
||||
│ • 查詢 Services Registry (確認 Ollama/GPU/Qdrant 在線) │
|
||||
│ • 分配 Processors (Python/Shell/CLI/Docker) │
|
||||
│ • 執行 ASR / OCR / Face / Yolo / 向量嵌入 │
|
||||
│ • 狀態更新: PENDING → PROCESSING → COMPLETED/FAILED │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ 階段三:產出解析與索引建立 (Output & Indexing) │
|
||||
│ • 解析標準化 JSON 產出 (Pre-Chunks, Frames) │
|
||||
│ - Pre-Chunk: 以 frame 為基準的區間 (start_frame, end_frame) │
|
||||
│ - Frame: 單幀偵測數據 (frame_number) │
|
||||
│ • 參考時間換算: timestamp_sec = frame / probe_fps │
|
||||
│ • 存入 Raw Data Tables (segments, detections) │
|
||||
│ • Chunk 聚合: 依據 Rule 1/2/3 將 Pre-Chunk 組裝為 Chunk │
|
||||
│ • 向量嵌入: 呼叫 Embedding Service (nomic-v2-moe) │
|
||||
│ • 寫入 Qdrant 建立索引 │
|
||||
│ • 狀態更新: INDEXING → READY (可搜尋) │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
[搜尋 API / Portal / N8N Webhooks]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 階段一:檔案註冊納管作業 (Onboarding)
|
||||
|
||||
將陌生媒體轉化為系統可識別的標準資產。
|
||||
|
||||
### 2.1 `assets` 表設計
|
||||
|
||||
```sql
|
||||
CREATE TABLE assets (
|
||||
id UUID PRIMARY KEY,
|
||||
file_path TEXT NOT NULL,
|
||||
file_hash VARCHAR(64) UNIQUE NOT NULL, -- SHA-256 防重複
|
||||
asset_type VARCHAR(20), -- video, audio, image
|
||||
media_info JSONB, -- ffprobe 原始輸出
|
||||
status VARCHAR(20) DEFAULT 'CREATED', -- 狀態機核心欄位
|
||||
metadata JSONB, -- 標題、語言、來源標籤
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
```
|
||||
|
||||
### 2.2 核心流程
|
||||
1. **上傳/偵測**: SFTPGo 觸發 Webhook 或用戶透過 API 上傳。
|
||||
2. **探針分析**: `ffprobe` 提取解析度、幀率、音軌、編碼、時長。
|
||||
3. **智能預處理**: 呼叫 `Smart Thumbnail` 處理器,跳過片頭黑屏,提取正片首幀。
|
||||
4. **分類標記**: 根據探針結果自動標記類型(如 `duration > 300s` 標記為 `long_form`)。
|
||||
5. **入隊**: 狀態轉為 `PENDING`,寫入 Redis 任務隊列 `queue:processing`。
|
||||
|
||||
---
|
||||
|
||||
## 3. 階段二:處理器調度與執行作業 (Orchestration)
|
||||
|
||||
排程器根據資源可用性與任務優先級,動態分配處理器。
|
||||
|
||||
### 3.1 排程邏輯 (Scheduler)
|
||||
```sql
|
||||
-- 取出可執行的任務
|
||||
SELECT * FROM tasks
|
||||
WHERE status = 'queued'
|
||||
AND required_services <@ (SELECT id FROM services WHERE status = 'online')
|
||||
ORDER BY priority DESC, created_at ASC
|
||||
LIMIT 1;
|
||||
```
|
||||
|
||||
### 3.2 執行標準化介面
|
||||
所有處理器接收統一參數,確保多態兼容:
|
||||
| 參數 | 說明 | 範例 |
|
||||
|:---|:---|:---|
|
||||
| `--uuid` | 任務唯一標識 | `--uuid 384b0ff4...` |
|
||||
| `--input` | 輸入媒體路徑 | `--input /data/raw/charade.mov` |
|
||||
| `--output` | 產出目錄 | `--output /data/output/384b...` |
|
||||
| `--config` | (選填) 執行配置 | `--config model_config.json` |
|
||||
|
||||
### 3.3 資源依賴檢查
|
||||
執行前,排程器驗證 `services` 表:
|
||||
- ASR 需要 `llm_engine` 或本地 GPU。
|
||||
- 向量嵌入需要 `embedding_engine` (Ollama nomic-v2-moe) 在線。
|
||||
- 若依賴服務離線,任務自動降級或進入 `retry_queue`。
|
||||
|
||||
*(詳細處理器註冊與多態設計請見 `PROCESSOR_REGISTRY_ARCHITECTURE.md`)*
|
||||
|
||||
---
|
||||
|
||||
## 4. 階段三:產出管理與第一階段搜尋
|
||||
|
||||
處理完成後,系統自動將非結構化 JSON 轉化為可檢索的結構化數據。
|
||||
|
||||
### 4.1 產出文件規範:Pre-Chunk 與 Frame
|
||||
所有處理器產出之 JSON 皆基於 **Frame (幀)** 為時間權威單位。
|
||||
- **時間計算**: `timestamp = frame_number / fps` (fps 來自 ffprobe)。
|
||||
- **Pre-Chunk**: 具持續時間的片段 (如 ASR 語句),記錄 `start_frame`, `end_frame`。
|
||||
- **Frame**: 單幀偵測數據 (如 Face, OCR),記錄 `frame_number`。
|
||||
- **命名**: `{asset_uuid}_{processor_type}_{timestamp}.json`
|
||||
|
||||
### 4.2 數據解析與落庫
|
||||
| 處理器產出 | 數據類型 | 對應 DB 表 | 搜尋能力 |
|
||||
|------------|----------|------------|----------|
|
||||
| `asr.json` | Pre-Chunk | `segments` | 語音關鍵字 BM25、說話者過濾 |
|
||||
| `ocr.json` | Frame | `visual_texts` | 畫面文字搜尋、浮水印過濾 |
|
||||
| `face.json` | Frame | `face_detections` | 人物出現時間軸、身份匹配 |
|
||||
| `chunks.json` | Pre-Chunk | `chunks` + `parent_chunks` | 語意搜尋、父子關聯檢索 |
|
||||
|
||||
### 4.3 向量索引建立
|
||||
1. 提取文本內容 (ASR + OCR + Chunk Summary)。
|
||||
2. 呼叫 `embedding_engine` 服務 (`nomic-embed-text-v2-moe`) 生成 768-dim 向量。
|
||||
3. 寫入 Qdrant Collection (`momentry_rule1`, `rule2`, `rule3`)。
|
||||
4. 狀態更新至 `READY`,觸發 Webhook 通知使用者。
|
||||
|
||||
---
|
||||
|
||||
## 5. 底層支撐:服務與處理器註冊中心
|
||||
|
||||
管線的高效運行依賴於兩個註冊中心的動態協調:
|
||||
|
||||
### 5.1 服務註冊中心 (`services`)
|
||||
管理底層基礎設施 (Ollama, Qdrant, Redis, SFTPGo)。
|
||||
- **健康監控**: 定期探活 `/health`,自動標記 `offline`。
|
||||
- **配置動態注入**: 處理器不需寫死 IP/Key,啟動時從註冊中心讀取。
|
||||
- **備份與路徑**: 統一管理 `storage_paths` 與 `backup_policy`。
|
||||
|
||||
*(詳細服務註冊設計請見 `SERVICE_REGISTRY_ARCHITECTURE.md`)*
|
||||
|
||||
### 5.2 處理器註冊中心 (`processors`)
|
||||
管理執行邏輯與腳本 (ASR, OCR, Face, Thumbnail)。
|
||||
- **多態執行**: 支援 Python, Shell, CLI, Docker, HTTP。
|
||||
- **產出驗證**: 定義 `output_spec` JSON Schema,確保下游解析不崩潰。
|
||||
- **版本追溯**: 記錄 `version` 與 `build_time`,支持結果重現與比對。
|
||||
|
||||
---
|
||||
|
||||
## 6. 狀態機與異常處理 (State Machine)
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> CREATED: 檔案上傳
|
||||
CREATED --> PREPARING: 開始探針分析
|
||||
PREPARING --> PENDING: 預處理完成
|
||||
PENDING --> PROCESSING: 排程器分配處理器
|
||||
PROCESSING --> INDEXING: 處理器產出 JSON
|
||||
INDEXING --> READY: 向量/全文索引完成
|
||||
PROCESSING --> FAILED: 超時/依賴服務離線
|
||||
FAILED --> PENDING: 自動重試 (Max 3次)
|
||||
READY --> [*]: 可對外提供 API
|
||||
```
|
||||
|
||||
### 容錯機制
|
||||
- **心跳超時**: 處理器每 30s 寫入 Redis `progress:{uuid}`,超時則判定為假死並 Kill。
|
||||
- **依賴降級**: 若 Ollama 離線,可跳過 Vector 索引,僅保留 BM25 搜尋 (功能降級但不中斷)。
|
||||
- **產出校驗**: JSON 寫入前驗證 `output_spec`,損壞檔案觸發重新處理。
|
||||
|
||||
---
|
||||
|
||||
## 7. 總結
|
||||
|
||||
本架構確立了 Momentry Core 的端到端資料流:
|
||||
|
||||
| 階段 | 核心動作 | 產出物 | 關鍵技術 |
|
||||
|------|----------|--------|----------|
|
||||
| **納管** | Hash / Probe / Thumbnail | `assets` 記錄 | `ffprobe`, `blackdetect` |
|
||||
| **調度** | 依賴檢查 / 多態分發 | 執行進程 | Redis Queue, Service Registry |
|
||||
| **處理** | AI 推論 / 特徵提取 | 標準化 JSON | WhisperX, EasyOCR, InsightFace |
|
||||
| **索引** | 解析 / Embedding / 寫入 | BM25 + Vector | `nomic-v2-moe`, Qdrant, PGVector |
|
||||
| **服務** | 健康檢查 / 配置注入 | 高可用叢集 | Health Check Worker, Backup Policy |
|
||||
|
||||
此設計將「檔案」、「處理器」、「服務」三大維度統一納管,實現了從原始媒體到智能搜尋的完全自動化與可追溯性。
|
||||
@@ -0,0 +1,521 @@
|
||||
---
|
||||
document_type: "architecture_design"
|
||||
service: "MOMENTRY_CORE"
|
||||
title: "Playground 開發架構隔離規劃"
|
||||
date: "2026-03-31"
|
||||
version: "V1.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
tags:
|
||||
- "開發架構隔離規劃"
|
||||
- "playground"
|
||||
ai_query_hints:
|
||||
- "查詢 Playground 開發架構隔離規劃 的內容"
|
||||
- "Playground 開發架構隔離規劃 的主要目的是什麼?"
|
||||
- "如何操作或實施 Playground 開發架構隔離規劃?"
|
||||
---
|
||||
|
||||
# Playground 開發架構隔離規劃
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | Warren |
|
||||
| 建立時間 | 2026-03-31 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-03-31 | 創建 Playground 隔離架構規劃 | Warren | OpenCode |
|
||||
|
||||
---
|
||||
|
||||
## 概述
|
||||
|
||||
本文檔說明 Momentry Core Playground(開發環境)的隔離架構規劃,確保開發測試環境與正式生產環境的數據能夠完整隔離,避免測試數據污染生產數據。
|
||||
|
||||
Playground 是 `momentry` 專案的開發專用二進制文件(binary),設計用於本地開發和功能測試,與生產環境(Production)使用不同的配置和資源池。
|
||||
|
||||
---
|
||||
|
||||
## 當前狀態
|
||||
|
||||
| 項目 | 狀態 |
|
||||
|------|------|
|
||||
| Redis 隔離 | ✅ 已隔離 |
|
||||
| File System 隔離 | ✅ 已隔離 |
|
||||
| PostgreSQL Schema 隔離 | 🔄 待實現 |
|
||||
| MongoDB Database 隔離 | 🔄 待實現 |
|
||||
| Qdrant Collection 隔離 | 🔄 待實現 |
|
||||
|
||||
---
|
||||
|
||||
## 隔離架構總覽
|
||||
|
||||
### 當前架構(部分隔離)
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ Playground (Development) 現況 │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─────────────┐ ┌─────────────┐ │
|
||||
│ │ Playground │ │ Production │ │
|
||||
│ │ Server │ │ Server │ │
|
||||
│ │ Port:3003 │ │ Port:3002 │ │
|
||||
│ │ │ │ │ │
|
||||
│ │ Redis: │ │ Redis: │ │
|
||||
│ │ momentry_dev│ │ momentry: │◀── 隔離 ✅ │
|
||||
│ └──────┬──────┘ └──────┬──────┘ │
|
||||
│ │ │ │
|
||||
│ │ ┌───────────────┴───────────────┐ │
|
||||
│ │ │ 共享資源 │ │
|
||||
│ ▼ ▼ ▼ │
|
||||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │
|
||||
│ │ PostgreSQL │ │ MongoDB │ │ Qdrant │ │
|
||||
│ │ momentry │ │ momentry │ │ momentry_rule1 │ │
|
||||
│ │ (同一DB) │ │ (同一DB) │ │ (同一collection) │ │
|
||||
│ └─────────────┘ └─────────────┘ └─────────────────────────┘ │
|
||||
│ │ │ │ │
|
||||
│ └────────────────────┴────────────────────┘ │
|
||||
│ │ ❌ 未隔離 │
|
||||
│ ▼ │
|
||||
│ 數據混合污染風險 │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 規劃中的隔離架構
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Playground 完整隔離架構 │
|
||||
├─────────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─────────────────────────────┐ ┌─────────────────────────────────┐ │
|
||||
│ │ Development (Playground) │ │ Production │ │
|
||||
│ │ Port: 3003 │ │ Port: 3002 │ │
|
||||
│ │ Binary: debug │ │ Binary: release │ │
|
||||
│ │ Worker: disabled │ │ Worker: enabled │ │
|
||||
│ └──────────────┬──────────────┘ └──────────────┬────────────────┘ │
|
||||
│ │ │ │
|
||||
│ │ ┌──────────────────────────────────┴──────────────┐ │
|
||||
│ │ │ 共享基礎設施 │ │
|
||||
│ │ └──────────────────────────────────┬──────────────┘ │
|
||||
│ │ │ │
|
||||
│ ▼ ▼ │
|
||||
│ ┌────────────────────┐ ┌───────────────────────────────┐ │
|
||||
│ │ PostgreSQL Schema │ │ PostgreSQL Schema │ │
|
||||
│ │ (dev schema) │ │ (public schema) │ │
|
||||
│ ├────────────────────┤ ├───────────────────────────────┤ │
|
||||
│ │ videos_dev │ │ videos │ │
|
||||
│ │ chunks_dev │ │ chunks │ │
|
||||
│ │ pre_chunks_dev │ │ pre_chunks │ │
|
||||
│ │ frames_dev │ │ frames │ │
|
||||
│ │ processor_results │ (隔離 ✅) │ processor_results │ │
|
||||
│ │ file_registry_dev │ │ file_registry │ │
|
||||
│ │ face_*_dev │ │ face_* │ │
|
||||
│ ├────────────────────┤ ├───────────────────────────────┤ │
|
||||
│ │ 可共享: │ │ 可共享: │ │
|
||||
│ │ api_keys (獨立的) │ │ api_keys │ │
|
||||
│ │ monitor_* │ │ monitor_* │ │
|
||||
│ │ backup_* │ │ backup_* │ │
|
||||
│ └────────────────────┘ └───────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌────────────────────┐ ┌───────────────────────────────┐ │
|
||||
│ │ MongoDB │ │ MongoDB │ │
|
||||
│ │ Database: │ │ Database: │ │
|
||||
│ │ momentry_dev │ (隔離 ✅) │ momentry │ │
|
||||
│ ├────────────────────┤ ├───────────────────────────────┤ │
|
||||
│ │ - chunks │ │ - chunks │ │
|
||||
│ │ - cache │ │ - cache │ │
|
||||
│ └────────────────────┘ └───────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌────────────────────┐ ┌───────────────────────────────┐ │
|
||||
│ │ Qdrant │ │ Qdrant │ │
|
||||
│ │ Collection: │ │ Collection: │ │
|
||||
│ │ momentry_dev_ │ (隔離 ✅) │ momentry_rule1 │ │
|
||||
│ │ rule1 │ │ │ │
|
||||
│ └────────────────────┘ └───────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌────────────────────┐ ┌───────────────────────────────┐ │
|
||||
│ │ Redis │ │ Redis │ │
|
||||
│ │ Prefix: │ (已有 ✅) │ Prefix: │ │
|
||||
│ │ momentry_dev: │ │ momentry: │ │
|
||||
│ └────────────────────┘ └───────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌────────────────────┐ ┌───────────────────────────────┐ │
|
||||
│ │ File System │ (已有 ✅) │ File System │ │
|
||||
│ │ /output_dev │ │ /output │ │
|
||||
│ │ /backup_dev │ │ /backup/momentry │ │
|
||||
│ └────────────────────┘ └───────────────────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 隔離矩陣
|
||||
|
||||
| 資源 | Production | Development | 隔離狀態 | 隔離方式 |
|
||||
|------|-----------|-------------|---------|---------|
|
||||
| **API Server** | Port 3002 | Port 3003 | ✅ | 環境變數配置 |
|
||||
| **Redis Prefix** | `momentry:` | `momentry_dev:` | ✅ | 環境變數配置 |
|
||||
| **File Output** | `/Users/accusys/momentry/output` | `/Users/accusys/momentry/output_dev` | ✅ | 環境變數配置 |
|
||||
| **File Backup** | `/Users/accusys/momentry/backup/momentry` | `/Users/accusys/momentry/backup/momentry_dev` | ✅ | 環境變數配置 |
|
||||
| **PostgreSQL** | `public` schema | `dev` schema | ❌ | Schema 隔離 |
|
||||
| **MongoDB** | `momentry` database | `momentry_dev` database | ❌ | Database 隔離 |
|
||||
| **Qdrant** | `momentry_rule1` collection | `momentry_dev_rule1` collection | ❌ | Collection 隔離 |
|
||||
|
||||
---
|
||||
|
||||
## 需要隔離的數據表
|
||||
|
||||
### PostgreSQL(使用 Schema 隔離)
|
||||
|
||||
#### 需要隔離的表(放入 dev schema)
|
||||
|
||||
| 表名 | 說明 |
|
||||
|------|------|
|
||||
| `videos` | 視頻記錄 |
|
||||
| `chunks` | 區塊數據 |
|
||||
| `pre_chunks` | 預處理區塊 |
|
||||
| `chunk_vectors` | 向量數據 |
|
||||
| `frames` | 幀數據 |
|
||||
| `processor_results` | 處理器結果 |
|
||||
| `file_registry` | 文件註冊 |
|
||||
| `file_lifecycle` | 文件生命周期 |
|
||||
| `face_clusters` | 人臉聚類 |
|
||||
| `face_detections` | 人臉檢測 |
|
||||
| `face_identities` | 人臉身份 |
|
||||
| `face_recognition_results` | 人臉識別結果 |
|
||||
|
||||
#### 可共享的表(留在 public schema)
|
||||
|
||||
| 表名 | 說明 |
|
||||
|------|------|
|
||||
| `api_keys` | API 金鑰(使用獨立的 Development API Key) |
|
||||
| `api_key_audit_log` | API 金鑰審計日誌 |
|
||||
| `api_key_anomalies` | API 金鑰異常 |
|
||||
| `monitor_*` | 所有監控相關表 |
|
||||
| `backup_*` | 備份記錄表 |
|
||||
| `gitea_tokens` | Gitea API Token |
|
||||
| `n8n_api_keys` | n8n API 金鑰 |
|
||||
| `node_*` | 節點相關表 |
|
||||
| `python_*` | Python 版本基線 |
|
||||
| `storage_*` | 存儲統計表 |
|
||||
| `v_idle_workflows` | 空閒工作流視圖 |
|
||||
| `v_recent_anomalies` | 最近異常視圖 |
|
||||
| `v_service_health` | 服務健康視圖 |
|
||||
| `v_storage_overview` | 存儲概覽視圖 |
|
||||
|
||||
---
|
||||
|
||||
## 配置對比
|
||||
|
||||
### 環境變數對比
|
||||
|
||||
| 變數 | Production (.env) | Development (.env.development) |
|
||||
|------|------------------|------------------------------|
|
||||
| `MOMENTRY_SERVER_PORT` | 3002 | 3003 |
|
||||
| `MOMENTRY_REDIS_PREFIX` | `momentry:` | `momentry_dev:` |
|
||||
| `MOMENTRY_OUTPUT_DIR` | `/Users/accusys/momentry/output` | `/Users/accusys/momentry/output_dev` |
|
||||
| `MOMENTRY_BACKUP_DIR` | `/Users/accusys/momentry/backup/momentry` | `/Users/accusys/momentry/backup/momentry_dev` |
|
||||
| `DATABASE_URL` | `postgres://accusys@localhost:5432/momentr` | `postgres://accusys@localhost:5432/momentry` |
|
||||
| `MONGODB_URL` | `mongodb://localhost:27017` | `mongodb://localhost:27017` |
|
||||
| `QDRANT_URL` | `http://localhost:6333` | `http://localhost:6333` |
|
||||
| `QDRANT_COLLECTION` | `momentry_rule1` | `momentry_dev_rule1` |
|
||||
| `RUST_LOG` | info | debug |
|
||||
| `MOMENTRY_WORKER_ENABLED` | true | false |
|
||||
|
||||
### 二進制對比
|
||||
|
||||
| 屬性 | Production | Playground |
|
||||
|------|-----------|------------|
|
||||
| Binary Name | `momentry` | `momentry_playground` |
|
||||
| Build Mode | release | debug |
|
||||
| Port | 3002 | 3003 |
|
||||
| Config File | `.env` | `.env.development` |
|
||||
|
||||
---
|
||||
|
||||
## 實施步驟
|
||||
|
||||
### Step 1: 修改配置檔案
|
||||
|
||||
#### 更新 `.env.development`
|
||||
|
||||
在現有配置中添加數據庫隔離相關變數:
|
||||
|
||||
```bash
|
||||
# Database Schema (PostgreSQL)
|
||||
DATABASE_SCHEMA=dev
|
||||
|
||||
# MongoDB Database
|
||||
MONGODB_DATABASE=momentry_dev
|
||||
|
||||
# Qdrant Collection
|
||||
QDRANT_COLLECTION=momentry_dev_rule1
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 2: 修改代碼支持 Schema 切換
|
||||
|
||||
#### 2.1 更新 `src/core/config.rs`
|
||||
|
||||
添加新的配置項:
|
||||
|
||||
```rust
|
||||
pub static DEV_SCHEMA: Lazy<String> = Lazy::new(|| {
|
||||
env::var("DATABASE_SCHEMA").unwrap_or_else(|_| "dev".to_string())
|
||||
});
|
||||
|
||||
pub static DEV_DATABASE: Lazy<String> = Lazy::new(|| {
|
||||
env::var("MONGODB_DATABASE").unwrap_or_else(|_| "momentry_dev".to_string())
|
||||
});
|
||||
|
||||
pub static DEV_QDRANT_COLLECTION: Lazy<String> = Lazy::new(|| {
|
||||
env::var("QDRANT_COLLECTION").unwrap_or_else(|_| "momentry_dev_rule1".to_string())
|
||||
});
|
||||
```
|
||||
|
||||
#### 2.2 更新 `src/core/db/postgres_db.rs`
|
||||
|
||||
在查詢方法中添加 schema 參數支持:
|
||||
|
||||
```rust
|
||||
// 在連接配置中使用 schema
|
||||
let schema = DEV_SCHEMA.as_str();
|
||||
let query = format!("SET search_path TO {}", schema);
|
||||
sqlx::query(&query).execute(&pool).await?;
|
||||
```
|
||||
|
||||
#### 2.3 更新 `src/core/db/mongodb.rs`
|
||||
|
||||
支持數據庫切換:
|
||||
|
||||
```rust
|
||||
let database_name = DEV_DATABASE.as_str();
|
||||
let database = client.database(database_name);
|
||||
```
|
||||
|
||||
#### 2.4 更新 `src/core/db/qdrant_db.rs`
|
||||
|
||||
支持 collection 切換:
|
||||
|
||||
```rust
|
||||
let collection_name = DEV_QDRANT_COLLECTION.as_str();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 3: 創建開發環境數據庫
|
||||
|
||||
#### 3.1 PostgreSQL - 創建 Schema
|
||||
|
||||
```bash
|
||||
# 連接 PostgreSQL
|
||||
psql -U accusys -d momentry
|
||||
|
||||
# 創建 dev schema
|
||||
CREATE SCHEMA IF NOT EXISTS dev;
|
||||
|
||||
# 將現有表的結構復製到 dev schema
|
||||
CREATE TABLE dev.videos AS SELECT * FROM public.videos WHERE 1=0;
|
||||
CREATE TABLE dev.chunks AS SELECT * FROM public.chunks WHERE 1=0;
|
||||
-- ... 其他表
|
||||
```
|
||||
|
||||
#### 3.2 MongoDB - 創建 Database
|
||||
|
||||
```bash
|
||||
# 複製數據到開發數據庫
|
||||
use admin
|
||||
db.copyDatabase('momentry', 'momentry_dev')
|
||||
```
|
||||
|
||||
#### 3.3 Qdrant - 創建 Collection
|
||||
|
||||
```bash
|
||||
# 使用 Qdrant API 創建新的 collection
|
||||
curl -X PUT 'http://localhost:6333/collections/momentry_dev_rule1' \
|
||||
-H 'api-key: Test3200Test3200Test3200' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"vectors": {
|
||||
"size": 1024,
|
||||
"distance": "Cosine"
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 4: 驗證隔離效果
|
||||
|
||||
#### 4.1 啟動服務驗證
|
||||
|
||||
```bash
|
||||
# Terminal 1: 啟動 Production Server
|
||||
cargo run --bin momentry -- server --port 3002
|
||||
|
||||
# Terminal 2: 啟動 Playground Server
|
||||
cargo run --bin momentry_playground -- server --port 3003
|
||||
```
|
||||
|
||||
#### 4.2 數據隔離驗證
|
||||
|
||||
```bash
|
||||
# 驗證 Redis 隔離
|
||||
redis-cli KEYS "momentry:job:*"
|
||||
redis-cli KEYS "momentry_dev:job:*"
|
||||
|
||||
# 驗證 PostgreSQL Schema
|
||||
psql -U accusys -d momentry -c "\dt dev.*"
|
||||
psql -U accusys -d momentry -c "\dt public.*"
|
||||
|
||||
# 驗證 MongoDB
|
||||
mongosh --eval "db.adminCommand('listDatabases')" | grep momentry
|
||||
|
||||
# 驗證 Qdrant
|
||||
curl -s -H "api-key: Test3200Test3200Test3200" \
|
||||
'http://localhost:6333/collections' | jq '.result[].name'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 檔案位置
|
||||
|
||||
| 類型 | 路徑 | 說明 |
|
||||
|------|------|------|
|
||||
| Production Config | `/Users/accusys/momentry_core_0.1/.env` | 生產環境配置 |
|
||||
| Development Config | `/Users/accusys/momentry_core_0.1/.env.development` | 開發環境配置 |
|
||||
| Binary | `/Users/accusys/momentry_core_0.1/src/playground.rs` | Playground 二進制源碼 |
|
||||
| Config Module | `/Users/accusys/momentry_core_0.1/src/core/config.rs` | 配置模組 |
|
||||
| PostgreSQL Module | `/Users/accusys/momentry_core_0.1/src/core/db/postgres_db.rs` | PostgreSQL 模組 |
|
||||
| MongoDB Module | `/Users/accusys/momentry_core_0.1/src/core/db/mongodb.rs` | MongoDB 模組 |
|
||||
| Qdrant Module | `/Users/accusys/momentry_core_0.1/src/core/db/qdrant_db.rs` | Qdrant 模組 |
|
||||
|
||||
---
|
||||
|
||||
## 常用指令
|
||||
|
||||
### 啟動服務
|
||||
|
||||
```bash
|
||||
# 啟動 Production Server
|
||||
cargo run --bin momentry -- server
|
||||
|
||||
# 啟動 Playground Server
|
||||
cargo run --bin momentry_playground -- server
|
||||
|
||||
# 指定 Port
|
||||
cargo run --bin momentry_playground -- server --port 3003
|
||||
|
||||
# 啟動 Worker (Production)
|
||||
cargo run --bin momentry -- worker --max-concurrent 2
|
||||
```
|
||||
|
||||
### 驗證隔離
|
||||
|
||||
```bash
|
||||
# 驗證 Redis 隔離
|
||||
redis-cli KEYS "momentry:*"
|
||||
redis-cli KEYS "momentry_dev:*"
|
||||
|
||||
# 驗證 PostgreSQL Schema
|
||||
psql -U accusys -d momentry -c "\dt dev.*"
|
||||
psql -U accusys -d momentry -c "\dt public.*"
|
||||
|
||||
# 驗證文件系統隔離
|
||||
ls -la /Users/accusys/momentry/output/
|
||||
ls -la /Users/accusys/momentry/output_dev/
|
||||
```
|
||||
|
||||
### 數據庫操作
|
||||
|
||||
```bash
|
||||
# 連接 PostgreSQL
|
||||
psql -U accusys -d momentry
|
||||
|
||||
# 切換 Schema
|
||||
SET search_path TO dev;
|
||||
|
||||
# 列出 Schema 表
|
||||
\dt
|
||||
|
||||
# MongoDB 數據庫列表
|
||||
mongosh --eval "db.adminCommand('listDatabases')"
|
||||
|
||||
# 切換 MongoDB 數據庫
|
||||
use momentry_dev
|
||||
db.chunks.countDocuments()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 版本資訊
|
||||
|
||||
- 版本: V1.0
|
||||
- 建立日期: 2026-03-31
|
||||
- 文件更新: 2026-03-31
|
||||
|
||||
---
|
||||
|
||||
## 相關文件
|
||||
|
||||
| 文件 | 說明 |
|
||||
|------|------|
|
||||
| `PLAYGROUND_BINARY_IMPLEMENTATION.md` | Playground 二進制實現計劃 |
|
||||
| `SERVICES.md` | 服務端口分配 |
|
||||
| `MOMENTRY_CORE_REDIS_KEYS.md` | Redis Key 設計規範 |
|
||||
| `AGENTS.md` | AI 代理執行指令 |
|
||||
| `DOCS_STANDARD.md` | 文件創建規範 |
|
||||
|
||||
---
|
||||
|
||||
## 附錄:AI Agent 友好資訊
|
||||
|
||||
### 可用 Tools
|
||||
|
||||
| Tool | 用途 |
|
||||
|------|------|
|
||||
| `postgres_query` | 執行 PostgreSQL 查詢 |
|
||||
| `mongodb_*` | MongoDB 操作 |
|
||||
| `redis_*` | Redis 操作 |
|
||||
| `qdrant_qdrant-*` | Qdrant 向量數據庫操作 |
|
||||
|
||||
### 數據庫 Schema
|
||||
|
||||
#### PostgreSQL Tables (dev schema)
|
||||
|
||||
```
|
||||
videos, chunks, pre_chunks, chunk_vectors, frames,
|
||||
processor_results, file_registry, file_lifecycle,
|
||||
face_clusters, face_detections, face_identities,
|
||||
face_recognition_results
|
||||
```
|
||||
|
||||
#### MongoDB Collections
|
||||
|
||||
```
|
||||
momentry_dev: chunks, cache
|
||||
```
|
||||
|
||||
#### Qdrant Collections
|
||||
|
||||
```
|
||||
momentry_dev_rule1
|
||||
```
|
||||
|
||||
### 環境變數
|
||||
|
||||
```
|
||||
MOMENTRY_SERVER_PORT, MOMENTRY_REDIS_PREFIX,
|
||||
DATABASE_SCHEMA, MONGODB_DATABASE, QDRANT_COLLECTION,
|
||||
MOMENTRY_OUTPUT_DIR, MOMENTRY_BACKUP_DIR
|
||||
```
|
||||
@@ -0,0 +1,392 @@
|
||||
# Playground Binary Implementation Plan
|
||||
|
||||
| Item | Content |
|
||||
|------|---------|
|
||||
| Author | Warren |
|
||||
| Created | 2026-03-23 |
|
||||
| Document Version | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## Version History
|
||||
|
||||
| Version | Date | Purpose | Operator | Tool/Model |
|
||||
|---------|------|---------|----------|------------|
|
||||
| V1.0 | 2026-03-23 | Create implementation plan | Warren | OpenCode |
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Create separate `momentry_playground` binary with distinct configuration from `momentry` (production).
|
||||
|
||||
| Aspect | Production (`momentry`) | Development (`momentry_playground`) |
|
||||
|--------|------------------------|-------------------------------------|
|
||||
| **Port** | 3002 | 3003 |
|
||||
| **Redis Prefix** | `momentry:` | `momentry_dev:` |
|
||||
| **Worker** | Enabled | Disabled |
|
||||
| **Purpose** | Production deployment | Testing/Development |
|
||||
|
||||
---
|
||||
|
||||
## Files to Modify
|
||||
|
||||
```
|
||||
Files Changed: 6 files (+1 new)
|
||||
├── src/core/config.rs ← Add server_port(), redis_key_prefix()
|
||||
├── src/core/db/redis_client.rs ← Replace hardcoded prefixes
|
||||
├── src/core/cache/redis_cache.rs ← Use configurable prefix
|
||||
├── src/main.rs ← Update CLI defaults
|
||||
├── src/playground.rs ← NEW: Development binary
|
||||
├── Cargo.toml ← Add new binary
|
||||
└── .env.development ← NEW: Dev environment config
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Implementation Steps
|
||||
|
||||
### Step 1: Update `src/core/config.rs`
|
||||
|
||||
Add after line 51 (after `MEDIA_BASE_URL`):
|
||||
|
||||
```rust
|
||||
pub static SERVER_PORT: Lazy<u16> = Lazy::new(|| {
|
||||
env::var("MOMENTRY_SERVER_PORT")
|
||||
.unwrap_or_else(|_| "3002".to_string())
|
||||
.parse()
|
||||
.unwrap_or(3002)
|
||||
});
|
||||
|
||||
pub static REDIS_KEY_PREFIX: Lazy<String> = Lazy::new(|| {
|
||||
env::var("MOMENTRY_REDIS_PREFIX")
|
||||
.unwrap_or_else(|_| "momentry:".to_string())
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 2: Update `src/core/db/redis_client.rs`
|
||||
|
||||
Replace all hardcoded `momentry:` prefixes with configurable prefix.
|
||||
|
||||
**Import at top:**
|
||||
```rust
|
||||
use crate::core::config::REDIS_KEY_PREFIX;
|
||||
```
|
||||
|
||||
**Pattern for each method:**
|
||||
```rust
|
||||
let prefix = REDIS_KEY_PREFIX.as_str();
|
||||
let key = format!("{}job:{}", prefix, uuid);
|
||||
```
|
||||
|
||||
**Affected lines:**
|
||||
|
||||
| Line | Key Pattern |
|
||||
|------|-------------|
|
||||
| 47 | `job:{uuid}` |
|
||||
| 81, 109 | `job:{uuid}:processor:{processor}` |
|
||||
| 136, 146 | `progress:{uuid}` |
|
||||
| 172 | `jobs:active` |
|
||||
| 179 | `jobs:active` → `jobs:completed` |
|
||||
| 187 | `jobs:active` → `jobs:failed` |
|
||||
| 194 | `jobs:active` |
|
||||
| 201, 208 | `health:momentry_core` |
|
||||
| 214 | `monitor:job:{uuid}` |
|
||||
| 242, 300 | `errors:{uuid}` |
|
||||
| 258, 281 | `anomaly:alerts`, `anomaly:key:{key_id}` |
|
||||
| 317, 346, 364, 392, 397 | `worker:job:{uuid}...` |
|
||||
| 406, 410 | `worker:job:*` |
|
||||
|
||||
---
|
||||
|
||||
### Step 3: Update `src/core/cache/redis_cache.rs`
|
||||
|
||||
**Import:**
|
||||
```rust
|
||||
use crate::core::config::REDIS_KEY_PREFIX;
|
||||
```
|
||||
|
||||
**Replace line 10:**
|
||||
```rust
|
||||
// Remove: const KEY_PREFIX: &str = "momentry:cache:";
|
||||
```
|
||||
|
||||
**Update `prefixed_key` method (line 24):**
|
||||
```rust
|
||||
fn prefixed_key(&self, key: &str) -> String {
|
||||
format!("{}cache:{}", REDIS_KEY_PREFIX.as_str(), key)
|
||||
}
|
||||
```
|
||||
|
||||
**Update tests (lines 161-162):**
|
||||
```rust
|
||||
#[test]
|
||||
fn test_prefixed_key() {
|
||||
// Note: This test will use the configured prefix
|
||||
let cache = RedisCache::new().unwrap();
|
||||
// With default prefix "momentry:"
|
||||
assert_eq!(cache.prefixed_key("test"), "momentry:cache:test");
|
||||
assert_eq!(cache.prefixed_key("video:abc"), "momentry:cache:video:abc");
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 4: Update `src/main.rs`
|
||||
|
||||
**Change CLI defaults (Lines 691-695):**
|
||||
|
||||
```rust
|
||||
// Before:
|
||||
#[arg(long, default_value = "3000")]
|
||||
port: u16,
|
||||
|
||||
// After:
|
||||
#[arg(long)]
|
||||
port: Option<u16>,
|
||||
```
|
||||
|
||||
**Update Server match arm (around line 2398):**
|
||||
|
||||
```rust
|
||||
Commands::Server { host, port } => {
|
||||
let port = port.unwrap_or_else(|| *crate::core::config::SERVER_PORT);
|
||||
momentry_core::api::start_server(&host, port).await?;
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
**Update Redis key usage (Line 1098):**
|
||||
|
||||
```rust
|
||||
// Before:
|
||||
let key = format!("momentry:job:{}:processor:{}", uuid, processor);
|
||||
|
||||
// After:
|
||||
let key = format!(
|
||||
"{}job:{}:processor:{}",
|
||||
crate::core::config::REDIS_KEY_PREFIX.as_str(),
|
||||
uuid,
|
||||
processor
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 5: Create `src/playground.rs`
|
||||
|
||||
```rust
|
||||
use anyhow::{Context, Result};
|
||||
use clap::{Parser, Subcommand};
|
||||
// ... same imports as main.rs ...
|
||||
|
||||
fn main() -> Result<()> {
|
||||
// Load development environment first
|
||||
dotenv::from_filename(".env.development").ok();
|
||||
|
||||
tracing_subscriber::fmt::init();
|
||||
tracing::info!("Starting momentry_playground (development binary)");
|
||||
tracing::info!("Port: {}", *momentry_core::core::config::SERVER_PORT);
|
||||
tracing::info!("Redis prefix: {}", *momentry_core::core::config::REDIS_KEY_PREFIX);
|
||||
|
||||
let cli = Cli::parse();
|
||||
// ... rest identical to main.rs ...
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 6: Update `Cargo.toml`
|
||||
|
||||
**Add after line 90:**
|
||||
|
||||
```toml
|
||||
[[bin]]
|
||||
name = "momentry_playground"
|
||||
path = "src/playground.rs"
|
||||
```
|
||||
|
||||
**Add dependency (if not present):**
|
||||
|
||||
```toml
|
||||
dotenv = "0.15"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 7: Create `.env.development`
|
||||
|
||||
```bash
|
||||
# Development Environment Configuration
|
||||
# Used by: momentry_playground binary
|
||||
|
||||
# Server Configuration
|
||||
MOMENTRY_SERVER_PORT=3003
|
||||
MOMENTRY_REDIS_PREFIX=momentry_dev:
|
||||
|
||||
# Worker Configuration (disabled for development)
|
||||
MOMENTRY_WORKER_ENABLED=false
|
||||
MOMENTRY_MAX_CONCURRENT=1
|
||||
MOMENTRY_POLL_INTERVAL=10
|
||||
|
||||
# Database (can use separate dev database)
|
||||
DATABASE_URL=postgres://accusys@localhost:5432/momentry
|
||||
MONGODB_URL=mongodb://accusys:Test3200Test3200@localhost:27017/admin
|
||||
|
||||
# Redis
|
||||
REDIS_URL=redis://:accusys@localhost:6379
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 8: Update `.env` (Production)
|
||||
|
||||
Add these lines:
|
||||
|
||||
```bash
|
||||
# Production Environment Configuration
|
||||
# Used by: momentry binary
|
||||
|
||||
# Server Configuration
|
||||
MOMENTRY_SERVER_PORT=3002
|
||||
MOMENTRY_REDIS_PREFIX=momentry:
|
||||
|
||||
# Worker Configuration
|
||||
MOMENTRY_WORKER_ENABLED=true
|
||||
MOMENTRY_MAX_CONCURRENT=2
|
||||
MOMENTRY_POLL_INTERVAL=5
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
### 1. Build and Run Production Binary
|
||||
|
||||
```bash
|
||||
cargo build --release --bin momentry
|
||||
cargo run --bin momentry -- server
|
||||
# Expected: Listening on http://127.0.0.1:3002
|
||||
|
||||
cargo run --bin momentry -- worker
|
||||
# Expected: Worker started with momentry: prefix
|
||||
```
|
||||
|
||||
### 2. Build and Run Development Binary
|
||||
|
||||
```bash
|
||||
cargo build --bin momentry_playground
|
||||
cargo run --bin momentry_playground -- server
|
||||
# Expected: Listening on http://127.0.0.1:3003
|
||||
```
|
||||
|
||||
### 3. Verify Redis Key Isolation
|
||||
|
||||
```bash
|
||||
# Production data
|
||||
redis-cli KEYS "momentry:*"
|
||||
# Development data
|
||||
redis-cli KEYS "momentry_dev:*"
|
||||
# Should be separate
|
||||
```
|
||||
|
||||
### 4. Run Both Simultaneously
|
||||
|
||||
```bash
|
||||
# Terminal 1: Production
|
||||
cargo run --bin momentry -- server
|
||||
|
||||
# Terminal 2: Development
|
||||
cargo run --bin momentry_playground -- server
|
||||
|
||||
# Both should run without port conflicts
|
||||
```
|
||||
|
||||
### 5. Unit Tests
|
||||
|
||||
```bash
|
||||
cargo test --lib
|
||||
# All tests should pass
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Redis Key Structure
|
||||
|
||||
### Production (`momentry:`)
|
||||
|
||||
```
|
||||
momentry:job:{uuid} # Job status
|
||||
momentry:job:{uuid}:processor:{name} # Processor progress
|
||||
momentry:progress:{uuid} # Progress pub/sub
|
||||
momentry:jobs:active # Active job set
|
||||
momentry:jobs:completed # Completed job set
|
||||
momentry:jobs:failed # Failed job set
|
||||
momentry:health:momentry_core # Health status
|
||||
momentry:cache:{key} # Cache entries
|
||||
momentry:worker:job:{uuid} # Worker job
|
||||
momentry:worker:job:{uuid}:processor:{name}
|
||||
```
|
||||
|
||||
### Development (`momentry_dev:`)
|
||||
|
||||
```
|
||||
momentry_dev:job:{uuid}
|
||||
momentry_dev:job:{uuid}:processor:{name}
|
||||
momentry_dev:progress:{uuid}
|
||||
momentry_dev:jobs:active
|
||||
momentry_dev:jobs:completed
|
||||
momentry_dev:jobs:failed
|
||||
momentry_dev:health:momentry_core
|
||||
momentry_dev:cache:{key}
|
||||
momentry_dev:worker:job:{uuid}
|
||||
momentry_dev:worker:job:{uuid}:processor:{name}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Potential Issues & Solutions
|
||||
|
||||
| Issue | Solution |
|
||||
|-------|----------|
|
||||
| `dotenv` crate not in dependencies | Add to Cargo.toml |
|
||||
| Tests use hardcoded prefix | Update tests to use config, or use `#[cfg(test)]` defaults |
|
||||
| Worker starts in playground | Check `MOMENTRY_WORKER_ENABLED=false` in `.env.development` |
|
||||
| Port already in use | Graceful error message with suggestion to use `--port` flag |
|
||||
| Mixed data in Redis | Ensure prefix is loaded before any Redis operations |
|
||||
|
||||
---
|
||||
|
||||
## Files Summary
|
||||
|
||||
| File | Lines Changed | Purpose |
|
||||
|------|---------------|---------|
|
||||
| `src/core/config.rs` | +15 | Add SERVER_PORT and REDIS_KEY_PREFIX |
|
||||
| `src/core/db/redis_client.rs` | ~50 | Replace hardcoded prefixes |
|
||||
| `src/core/cache/redis_cache.rs` | ~10 | Use configurable prefix |
|
||||
| `src/main.rs` | ~15 | Update CLI defaults, Redis key usage |
|
||||
| `src/playground.rs` | NEW (~2800) | Development binary |
|
||||
| `Cargo.toml` | +4 | Add binary definition |
|
||||
| `.env.development` | NEW (~20) | Development environment |
|
||||
|
||||
**Total**: ~60 lines modified + ~2800 lines new file
|
||||
|
||||
---
|
||||
|
||||
## Reference Documents
|
||||
|
||||
| Document | Purpose |
|
||||
|----------|---------|
|
||||
| `docs/SERVICES.md` | Port allocations |
|
||||
| `docs/MOMENTRY_CORE_REDIS_KEYS.md` | Redis key design |
|
||||
| `AGENTS.md` | Code style and conventions |
|
||||
|
||||
---
|
||||
|
||||
## Version History
|
||||
|
||||
| Version | Date | Author | Changes |
|
||||
|---------|------|--------|---------|
|
||||
| 1.0 | 2025-03-25 | OpenCode | Initial implementation plan |
|
||||
@@ -0,0 +1,318 @@
|
||||
---
|
||||
document_type: "architecture_design"
|
||||
service: "MOMENTRY_CORE"
|
||||
title: "Video Processing Pipeline - 處理流程"
|
||||
date: "2026-03-22"
|
||||
version: "V1.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
tags:
|
||||
- "processing"
|
||||
- "video"
|
||||
- "pipeline"
|
||||
- "處理流程"
|
||||
ai_query_hints:
|
||||
- "查詢 Video Processing Pipeline - 處理流程 的內容"
|
||||
- "Video Processing Pipeline - 處理流程 的主要目的是什麼?"
|
||||
- "如何操作或實施 Video Processing Pipeline - 處理流程?"
|
||||
---
|
||||
|
||||
# Video Processing Pipeline - 處理流程
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | Warren |
|
||||
| 建立時間 | 2026-03-22 |
|
||||
| 文件版本 | V1.1 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-03-22 | 創建文件 | Warren | OpenCode |
|
||||
| V1.1 | 2026-03-26 | 更新流程圖文字 (media_url→file_path) | OpenCode | deepseek-reasoner |
|
||||
|
||||
---
|
||||
|
||||
## 處理流程架構
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Video Processing Pipeline │
|
||||
├─────────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ Stage 1: JSON 生成 (Process) │ │
|
||||
│ │ │ │
|
||||
│ │ video.mp4 ──→ [ASR] ──→ asr.json (語音辨識) │ │
|
||||
│ │ ──→ [CUT] ──→ cut.json (場景偵測) │ │
|
||||
│ │ ──→ [ASRX] ──→ asrx.json (說話者分離) │ │
|
||||
│ │ ──→ [YOLO] ──→ yolo.json (物體偵測) │ │
|
||||
│ │ ──→ [OCR] ──→ ocr.json (文字辨識) │ │
|
||||
│ │ ──→ [Face] ──→ face.json (人臉偵測) │ │
|
||||
│ │ ──→ [Pose] ──→ pose.json (姿態估計) │ │
|
||||
│ └─────────────────────────────────────────────────────────────────────┘ │
|
||||
│ ↓ │
|
||||
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ Stage 2: 入庫 (Import) │ │
|
||||
│ │ │ │
|
||||
│ │ .json files ──→ PostgreSQL (fs_json = true) │ │
|
||||
│ │ ↓ │ │
|
||||
│ │ pre_chunks 表 (from ASR, CUT) │ │
|
||||
│ │ frames 表 (from YOLO, OCR, Face, Pose) │ │
|
||||
│ └─────────────────────────────────────────────────────────────────────┘ │
|
||||
│ ↓ │
|
||||
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ Stage 3: Chunk 生成 (Chunk) │ │
|
||||
│ │ │ │
|
||||
│ │ pre_chunks ──→ [Chunk Rule] ──→ chunks 表 │ │
|
||||
│ │ ↓ │ │
|
||||
│ │ 清洗 → 純文字 │ │
|
||||
│ └─────────────────────────────────────────────────────────────────────┘ │
|
||||
│ ↓ │
|
||||
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ Stage 4: 向量化 (Vectorize) │ │
|
||||
│ │ │ │
|
||||
│ │ chunks ──→ [Embedding Model] ──→ vectors │ │
|
||||
│ │ ↓ │ │
|
||||
│ │ Qdrant (主要向量庫) │ │
|
||||
│ │ PGVector (備份向量庫) │ │
|
||||
│ └─────────────────────────────────────────────────────────────────────┘ │
|
||||
│ ↓ │
|
||||
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ Stage 5: 搜尋 (Search) │ │
|
||||
│ │ │ │
|
||||
│ │ Natural Language Query ──→ [Embedding] ──→ [Qdrant Search] │ │
|
||||
│ │ ↓ │ │
|
||||
│ │ 返回結果含 file_path │ │
|
||||
│ └─────────────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## CLI 命令
|
||||
|
||||
### Stage 1: JSON 生成 (Process)
|
||||
|
||||
```bash
|
||||
# 基本用法
|
||||
cargo run --bin momentry -- process <uuid_or_path>
|
||||
|
||||
# 只處理特定模組
|
||||
cargo run --bin momentry -- process <uuid> --modules asr,cut
|
||||
|
||||
# 強制重新處理(忽略完整性檢查)
|
||||
cargo run --bin momentry -- process <uuid> --force
|
||||
|
||||
# 從中斷點續傳
|
||||
cargo run --bin momentry -- process <uuid> --resume
|
||||
|
||||
# 模組使用雲端處理
|
||||
cargo run --bin momentry -- process <uuid> --modules yolo,face --cloud yolo
|
||||
|
||||
# 完整範例
|
||||
cargo run --bin momentry -- process /path/to/video.mp4 \
|
||||
--modules asr,cut,yolo,ocr \
|
||||
--cloud yolo
|
||||
```
|
||||
|
||||
### Stage 2: 入庫 (Import)
|
||||
|
||||
```bash
|
||||
# 目前入庫在 process 完成後自動執行
|
||||
# 計劃新增獨立的 import 命令
|
||||
# cargo run --bin momentry -- import <uuid>
|
||||
```
|
||||
|
||||
### Stage 3: Chunk 生成
|
||||
|
||||
```bash
|
||||
# 生成 chunks
|
||||
cargo run --bin momentry -- chunk <uuid>
|
||||
```
|
||||
|
||||
### Stage 4: 向量化
|
||||
|
||||
```bash
|
||||
# 向量化 chunks(使用預設模型 nomic-embed-text-v2-moe:latest)
|
||||
cargo run --bin momentry -- vectorize <uuid>
|
||||
|
||||
# 明確指定模型
|
||||
cargo run --bin momentry -- vectorize <uuid> --model nomic-embed-text-v2-moe:latest
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 處理模式選項
|
||||
|
||||
### --force (強制重新處理)
|
||||
|
||||
- 刪除現有的 JSON 檔案
|
||||
- 從頭開始處理
|
||||
- 適用於:處理失敗、模型更新、需要重新處理
|
||||
|
||||
```bash
|
||||
# 強制重新處理 YOLO
|
||||
cargo run --bin momentry -- process <uuid> --modules yolo --force
|
||||
```
|
||||
|
||||
### --resume (續傳)
|
||||
|
||||
- 檢查現有 JSON 的進度
|
||||
- 從中斷點繼續處理
|
||||
- 適用於:處理中斷、系統崩潰後恢復
|
||||
|
||||
```bash
|
||||
# 從上次中斷點繼續
|
||||
cargo run --bin momentry -- process <uuid> --resume
|
||||
```
|
||||
|
||||
### 預設行為 (Smart Mode)
|
||||
|
||||
- 如果 JSON 完全:跳過
|
||||
- 如果 JSON 不完整:警告 + 跳過(需要 --resume 或 --force)
|
||||
- 如果 JSON 不存在:處理
|
||||
|
||||
```
|
||||
Output:
|
||||
ASR: ✓ Already complete, skipping
|
||||
|
||||
⚠️ Found incomplete JSON file: /path/to/yolo.json
|
||||
Progress: 73800/412343 (17.9%)
|
||||
Use --resume to continue from checkpoint
|
||||
Use --force to reprocess from scratch
|
||||
YOLO: ✓ Already complete, skipping
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 可用模組
|
||||
|
||||
| 模組 | 功能 | 輸出 | 用途 |
|
||||
|------|------|------|------|
|
||||
| asr | 自動語音辨識 | asr.json | 語音轉文字 |
|
||||
| cut | 場景偵測 | cut.json | 影片分段 |
|
||||
| asrx | 說話者分離 | asrx.json | 多人對話分析 |
|
||||
| yolo | 物體偵測 | yolo.json | 物體辨識 |
|
||||
| ocr | 文字辨識 | ocr.json | 畫面文字 |
|
||||
| face | 人臉偵測 | face.json | 人臉辨識 |
|
||||
| pose | 姿態估計 | pose.json | 人體姿態 |
|
||||
|
||||
---
|
||||
|
||||
## 向量化模型選擇
|
||||
|
||||
### 專用嵌入模型
|
||||
Momentry Core 統一使用 **`nomic-embed-text-v2-moe:latest`** 作為所有規則的嵌入模型:
|
||||
|
||||
```bash
|
||||
# 統一模型(所有 Rule 1/2/3 使用)
|
||||
--model nomic-embed-text-v2-moe:latest
|
||||
```
|
||||
|
||||
### 模型特性
|
||||
| 特性 | 說明 |
|
||||
|------|------|
|
||||
| **模型名稱** | `nomic-embed-text-v2-moe:latest` |
|
||||
| **向量維度** | 768 維 |
|
||||
| **多語言支持** | ✅ 完整支持(英語、中文、日語、韓語等) |
|
||||
| **模型架構** | Mixture of Experts (MoE) |
|
||||
| **推理速度** | 快速,適合實時應用 |
|
||||
|
||||
### 使用方式
|
||||
```rust
|
||||
// Rust 代碼中使用
|
||||
let embedder = Embedder::new("nomic-embed-text-v2-moe:latest".to_string());
|
||||
|
||||
// 文檔嵌入(用於儲存)
|
||||
let document_vector = embedder.embed_document("文本內容").await?;
|
||||
|
||||
// 查詢嵌入(用於搜索)
|
||||
let query_vector = embedder.embed_query("搜索查詢").await?;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 資料庫儲存
|
||||
|
||||
### PostgreSQL (主要關聯式資料庫)
|
||||
|
||||
- 影片資訊
|
||||
- Chunks 資料
|
||||
- Pre-chunks 資料
|
||||
- Frames 資料
|
||||
- 使用者資料
|
||||
|
||||
### Qdrant (主要向量資料庫)
|
||||
|
||||
- Chunk 向量
|
||||
- 相似度搜尋
|
||||
|
||||
### PGVector (備份向量資料庫)
|
||||
|
||||
- Chunk 向量副本
|
||||
- 備援機制
|
||||
|
||||
---
|
||||
|
||||
## Pipeline 狀態追蹤
|
||||
|
||||
### PostgreSQL 狀態欄位
|
||||
|
||||
```sql
|
||||
-- 影片處理狀態
|
||||
videos.status: 'pending' | 'processing' | 'completed' | 'failed'
|
||||
|
||||
-- 檔案處理狀態
|
||||
videos.fs_json: true/false
|
||||
videos.fs_chunks: true/false
|
||||
videos.fs_vectors: true/false
|
||||
|
||||
-- pre_chunks 狀態
|
||||
pre_chunks.imported: true/false
|
||||
|
||||
-- frames 狀態
|
||||
frames.imported: true/false
|
||||
|
||||
-- chunks 狀態
|
||||
chunks.cleaned: true/false
|
||||
chunks.vectorized: true/false
|
||||
```
|
||||
|
||||
### 進度查詢 API
|
||||
|
||||
```bash
|
||||
# 查詢處理進度
|
||||
curl http://localhost:3002/api/v1/progress/{uuid}
|
||||
|
||||
# 回應範例
|
||||
{
|
||||
"uuid": "a1b10138a6bbb0cd",
|
||||
"file_name": "video.mp4",
|
||||
"overall_progress": 65,
|
||||
"cpu_percent": 45.2,
|
||||
"gpu_percent": 98.5,
|
||||
"memory_mb": 8500,
|
||||
"processors": [
|
||||
{"name": "asr", "status": "complete", "progress": 100},
|
||||
{"name": "cut", "status": "complete", "progress": 100},
|
||||
{"name": "yolo", "status": "progress", "progress": 45},
|
||||
{"name": "ocr", "status": "pending", "progress": 0}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 下一步
|
||||
|
||||
1. **API 端點** - 支援 --modules 和 --cloud 參數
|
||||
2. **獨立 Import 命令** - 分離入庫流程
|
||||
3. **獨立 Chunk 命令** - 分離 chunk 生成
|
||||
4. **獨立 Vectorize 命令** - 分離向量化流程
|
||||
5. **模型管理** - 新增、選擇、預覽模型
|
||||
@@ -0,0 +1,165 @@
|
||||
---
|
||||
document_type: "architecture_design"
|
||||
service: "MOMENTRY_CORE"
|
||||
title: "Momentry Core 架構 5 分鐘快速入門指南"
|
||||
date: "2026-04-25"
|
||||
version: "V1.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
tags:
|
||||
- "分鐘快速入門指南"
|
||||
- "momentry"
|
||||
- "core"
|
||||
ai_query_hints:
|
||||
- "查詢 Momentry Core 架構 5 分鐘快速入門指南 的內容"
|
||||
- "Momentry Core 架構 5 分鐘快速入門指南 的主要目的是什麼?"
|
||||
- "如何操作或實施 Momentry Core 架構 5 分鐘快速入門指南?"
|
||||
---
|
||||
|
||||
# Momentry Core 架構 5 分鐘快速入門指南
|
||||
|
||||
## 1. 系統核心概念 (60秒)
|
||||
|
||||
**Momentry Core** 是一個 Rust 基礎的數位資產管理系統,專注於:
|
||||
|
||||
1. **視訊分析**:ASR、OCR、YOLO、場景檢測等多模態處理
|
||||
2. **智慧分片**:將視訊分解為不同粒度級別的內容片段
|
||||
3. **向量檢索**:基於語義和視覺特徵的相似度搜索
|
||||
4. **RAG 功能**:檢索增強生成,提供情境化回答
|
||||
|
||||
**核心設計原則**:當設計文檔與實際代碼衝突時,**以 Rust 代碼實現為準**。
|
||||
|
||||
## 2. 系統架構圖 (30秒)
|
||||
|
||||
```
|
||||
輸入 → 處理管道 → 分片生成 → 向量存儲 → 檢索服務
|
||||
↓ ↓ ↓ ↓
|
||||
ASR Sentence Qdrant API
|
||||
OCR Cut PostgreSQL Player
|
||||
YOLO Story Redis CLI
|
||||
CUT Trace
|
||||
```
|
||||
|
||||
## 3. 關鍵數據結構 (60秒)
|
||||
|
||||
### 分片類型 (ChunkType)
|
||||
```rust
|
||||
pub enum ChunkType {
|
||||
TimeBased, // 時間基準分片
|
||||
Sentence, // 句子級分片 (基於 ASR)
|
||||
Cut, // 場景分片 (基於 CUT 算法)
|
||||
Trace, // 軌跡追蹤分片
|
||||
Story, // 故事級分片 (基於分片聚合)
|
||||
}
|
||||
```
|
||||
|
||||
### 分片數據結構
|
||||
```rust
|
||||
pub struct Chunk {
|
||||
pub file_id: i32,
|
||||
pub uuid: String,
|
||||
pub chunk_id: String,
|
||||
pub chunk_type: ChunkType,
|
||||
pub start_frame: i64,
|
||||
pub end_frame: i64,
|
||||
pub content: serde_json::Value, // 動態 JSON 內容
|
||||
pub vector_id: Option<String>,
|
||||
// ... 其他字段
|
||||
}
|
||||
```
|
||||
|
||||
## 4. 處理管道 (60秒)
|
||||
|
||||
### 標準處理流程
|
||||
1. **ASR 轉錄**:語音轉文字,生成句子級分片
|
||||
2. **OCR 識別**:文字區域檢測和識別
|
||||
3. **YOLO 檢測**:視覺物件檢測和分類
|
||||
4. **CUT 場景檢測**:基於視覺相似度的場景分割
|
||||
5. **分片生成**:基於處理結果生成不同類型的分片
|
||||
|
||||
### 處理器特點
|
||||
- 統一使用 `PythonExecutor` 執行外部腳本
|
||||
- 支持超時控制和錯誤恢復
|
||||
- 處理結果存儲為結構化 JSON
|
||||
|
||||
## 5. 數據庫架構 (60秒)
|
||||
|
||||
### 多數據庫系統
|
||||
1. **PostgreSQL**:結構化數據存儲
|
||||
- `video_records`:視訊基礎資訊
|
||||
- `chunks`:分片數據
|
||||
- `jobs`:處理任務
|
||||
2. **Redis**:緩存和隊列
|
||||
- `momentry:` 網址:生產環境
|
||||
- `momentry_dev:` 網址:開發環境
|
||||
3. **Qdrant**:向量數據庫
|
||||
- 存儲分片嵌入向量
|
||||
- 支持語義和視覺相似度搜索
|
||||
4. **MongoDB**:文檔存儲
|
||||
- 存儲非結構化處理結果
|
||||
|
||||
## 6. 開發與部署 (30秒)
|
||||
|
||||
### 開發環境
|
||||
```bash
|
||||
# 構建項目
|
||||
cargo build
|
||||
cargo build --release
|
||||
|
||||
# 運行 CLI
|
||||
cargo run -- register /path/to/video.mp4
|
||||
cargo run -- server --host 0.0.0.0 --port 3002
|
||||
|
||||
# 運行開發版
|
||||
cargo run --bin momentry_playground -- server
|
||||
```
|
||||
|
||||
### 測試
|
||||
```bash
|
||||
# 運行所有測試
|
||||
cargo test
|
||||
|
||||
# 運行單個測試
|
||||
cargo test test_name
|
||||
|
||||
# 帶輸出的測試
|
||||
cargo test -- --nocapture
|
||||
```
|
||||
|
||||
## 7. 下一步學習路徑
|
||||
|
||||
### 初學者 (新團隊成員)
|
||||
1. 閱讀 [ARCHITECTURE_OVERVIEW.md](./ARCHITECTURE_OVERVIEW.md) - 系統概覽
|
||||
2. 查看 [DESIGN_IMPLEMENTATION_GAP.md](./DESIGN_IMPLEMENTATION_GAP.md) - 設計與實現差異
|
||||
3. 運行 `cargo run -- --help` 熟悉 CLI 命令
|
||||
|
||||
### 開發者 (功能開發)
|
||||
1. 查看 [TECHNICAL_DECISION_RECORDS.md](./TECHNICAL_DECISION_RECORDS.md) - 技術決策記錄
|
||||
2. 研究 [PROCESSING_PIPELINE.md](./PROCESSING_PIPELINE.md) - 處理管道詳情
|
||||
3. 查看 [ARCHITECTURE_DECISION_EXECUTION_PLAN.md](./ARCHITECTURE_DECISION_EXECUTION_PLAN.md) - 執行計劃
|
||||
|
||||
### 架構師 (系統設計)
|
||||
1. 查看 [PERFORMANCE_AND_SCALABILITY.md](./PERFORMANCE_AND_SCALABILITY.md) - 效能與擴展
|
||||
2. 研究 [SECURITY_ARCHITECTURE.md](./SECURITY_ARCHITECTURE.md) - 安全架構
|
||||
3. 查看 [MONITORING_ARCHITECTURE.md](./MONITORING_ARCHITECTURE.md) - 監控架構
|
||||
|
||||
## 8. 常見問題 (FAQ)
|
||||
|
||||
### Q1: 如何開始添加新的處理器?
|
||||
A: 參考 `src/core/processor/` 目錄下的現有處理器,實現 `Processor` trait。
|
||||
|
||||
### Q2: 如何擴展分片類型?
|
||||
A: 在 `src/core/chunk/types.rs` 中擴展 `ChunkType` 枚舉。
|
||||
|
||||
### Q3: 如何集成新的 AI 模型?
|
||||
A: 通過 `PythonExecutor` 執行 Python 腳本,或直接集成到 Rust 代碼中。
|
||||
|
||||
### Q4: 如何優化檢索性能?
|
||||
A: 調整 Qdrant 向量索引參數,優化嵌入模型,添加緩存層。
|
||||
|
||||
---
|
||||
|
||||
**更新時間**: 2026-04-22
|
||||
**適用對象**: 新團隊成員、開發者、架構師
|
||||
**建議閱讀時間**: 5 分鐘
|
||||
@@ -0,0 +1,364 @@
|
||||
---
|
||||
document_type: "architecture_design"
|
||||
service: "MOMENTRY_CORE"
|
||||
title: "處理器生命週期管理"
|
||||
date: "2026-04-22"
|
||||
version: "V1.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
tags:
|
||||
- "處理器生命週期管理"
|
||||
ai_query_hints:
|
||||
- "查詢 處理器生命週期管理 的內容"
|
||||
- "處理器生命週期管理 的主要目的是什麼?"
|
||||
- "如何操作或實施 處理器生命週期管理?"
|
||||
---
|
||||
|
||||
# 處理器生命週期管理
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | OpenCode |
|
||||
| 建立時間 | 2026-04-22 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 |
|
||||
|------|------|------|--------|
|
||||
| V1.0 | 2026-04-22 | 創建處理器生命週期管理文檔 | OpenCode |
|
||||
|
||||
---
|
||||
|
||||
## 1. 處理器生命週期概覽
|
||||
|
||||
處理器(Processor)是 Momentry Core 中執行視頻分析任務的核心組件。完整的生命週期包括以下階段:
|
||||
|
||||
```
|
||||
開發階段 → 測試階段 → 部署階段 → 運行階段 → 維護階段 → 退役階段
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 開發階段 (Development)
|
||||
|
||||
### 2.1 新處理器創建流程
|
||||
|
||||
#### 步驟 1: 需求分析
|
||||
1. **功能定義**:明確處理器要實現的功能
|
||||
2. **輸入輸出規範**:定義輸入參數和輸出格式
|
||||
3. **依賴分析**:識別所需的 AI 模型、庫和工具
|
||||
|
||||
#### 步驟 2: 技術選型
|
||||
1. **執行類型**:選擇 Python、Shell、CLI App 等
|
||||
2. **模型選擇**:選擇合適的 AI 模型
|
||||
3. **性能評估**:評估計算資源需求
|
||||
|
||||
#### 步驟 3: 代碼開發
|
||||
1. **腳本編寫**:編寫處理器核心邏輯
|
||||
2. **錯誤處理**:實現健壯的錯誤處理機制
|
||||
3. **日誌記錄**:添加詳細的日誌記錄
|
||||
|
||||
### 2.2 開發標準
|
||||
|
||||
#### Python 處理器標準:
|
||||
```python
|
||||
# 1. 必要的導入
|
||||
import json
|
||||
import sys
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
|
||||
# 2. 參數解析
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--uuid", required=True, help="Video UUID")
|
||||
parser.add_argument("--output", required=True, help="Output path")
|
||||
args = parser.parse_args()
|
||||
|
||||
# 3. 主處理邏輯
|
||||
def process_video(video_uuid, output_path):
|
||||
# 處理邏輯
|
||||
result = {
|
||||
"status": "success",
|
||||
"metadata": {...},
|
||||
"chunks": [...]
|
||||
}
|
||||
|
||||
# 4. 結果保存
|
||||
with open(output_path, "w") as f:
|
||||
json.dump(result, f, indent=2)
|
||||
|
||||
# 5. 主函數
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
process_video(args.uuid, args.output)
|
||||
sys.exit(0)
|
||||
except Exception as e:
|
||||
print(f"Error: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 測試階段 (Testing)
|
||||
|
||||
### 3.1 測試類型
|
||||
|
||||
#### 單元測試:
|
||||
- 測試處理器核心邏輯
|
||||
- 驗證輸入輸出格式
|
||||
- 測試錯誤處理
|
||||
|
||||
#### 集成測試:
|
||||
- 測試與其他組件的集成
|
||||
- 驗證數據流完整
|
||||
- 測試性能表現
|
||||
|
||||
#### 回歸測試:
|
||||
- 確保新版本不破壞現有功能
|
||||
- 測試兼容性
|
||||
- 驗證性能改進
|
||||
|
||||
### 3.2 測試數據
|
||||
|
||||
#### 測試視頻:
|
||||
| 類型 | 用途 | 示例 |
|
||||
|------|------|------|
|
||||
| 短視頻(<1分鐘) | 快速測試 | test_video.mp4 |
|
||||
| 中等視頻(1-5分鐘) | 功能測試 | demo_video.mp4 |
|
||||
| 長視頻(>10分鐘) | 性能測試 | long_video.mp4 |
|
||||
|
||||
#### 測試環境:
|
||||
1. **本地開發環境**:快速迭代
|
||||
2. **測試服務器**:集成測試
|
||||
3. **生產模擬環境**:性能測試
|
||||
|
||||
---
|
||||
|
||||
## 4. 部署階段 (Deployment)
|
||||
|
||||
### 4.1 部署流程
|
||||
|
||||
#### 步驟 1: 版本管理
|
||||
1. **版本號**:遵循語義化版本規範(SemVer)
|
||||
2. **構建時間**:記錄構建/部署時間戳
|
||||
3. **變更日誌**:記錄版本變更內容
|
||||
|
||||
#### 步驟 2: 配置管理
|
||||
1. **環境變量**:配置處理器運行環境
|
||||
2. **模型文件**:管理 AI 模型文件
|
||||
3. **依賴庫**:管理 Python 依賴
|
||||
|
||||
#### 步驟 3: 數據庫註冊
|
||||
```sql
|
||||
-- 註冊新處理器到數據庫
|
||||
INSERT INTO processors (
|
||||
id, name, category, execution_type,
|
||||
entry_point, version, build_time,
|
||||
description, technical_details,
|
||||
output_spec, runtime_config, is_active
|
||||
) VALUES (
|
||||
'uuid', 'face_processor', 'visual', 'python',
|
||||
'scripts/face_processor.py', '1.2.0', NOW(),
|
||||
'人臉識別處理器,使用 InsightFace 模型',
|
||||
'基於 InsightFace 的深度學習人臉識別',
|
||||
'{"type": "object", "properties": {...}}'::jsonb,
|
||||
'{"venv_path": "...", "timeout_secs": 3600}'::jsonb,
|
||||
TRUE
|
||||
);
|
||||
```
|
||||
|
||||
### 4.2 部署檢查清單
|
||||
|
||||
- [ ] 處理器腳本已測試通過
|
||||
- [ ] 依賴庫已正確安裝
|
||||
- [ ] 模型文件已下載並配置
|
||||
- [ ] 環境變量已設置
|
||||
- [ ] 數據庫註冊已完成
|
||||
- [ ] 權限設置正確
|
||||
- [ ] 日誌配置完整
|
||||
|
||||
---
|
||||
|
||||
## 5. 運行階段 (Runtime)
|
||||
|
||||
### 5.1 調度與執行
|
||||
|
||||
#### 任務調度流程:
|
||||
```
|
||||
1. 任務創建 → 2. 處理器選擇 → 3. 資源分配
|
||||
→ 4. 執行監控 → 5. 結果收集 → 6. 狀態更新
|
||||
```
|
||||
|
||||
#### 執行監控:
|
||||
1. **進程監控**:監控處理器進程狀態
|
||||
2. **資源監控**:監控 CPU、內存、GPU 使用
|
||||
3. **性能監控**:監控處理速度和進度
|
||||
|
||||
### 5.2 錯誤處理與恢復
|
||||
|
||||
#### 錯誤類型:
|
||||
1. **可恢復錯誤**:臨時性問題,可重試
|
||||
2. **配置錯誤**:配置問題,需要修復
|
||||
3. **系統錯誤**:系統級問題,需要干預
|
||||
|
||||
#### 重試策略:
|
||||
```rust
|
||||
// Rust 中的重試機制示例
|
||||
let result = run_with_retry(
|
||||
|| python_executor.execute(&script, &args),
|
||||
RetryConfig {
|
||||
max_attempts: 3,
|
||||
initial_delay: Duration::from_secs(2),
|
||||
max_delay: Duration::from_secs(30),
|
||||
backoff_multiplier: 2.0,
|
||||
},
|
||||
).await;
|
||||
```
|
||||
|
||||
### 5.3 性能優化
|
||||
|
||||
#### 優化策略:
|
||||
1. **並行處理**:同時處理多個視頻
|
||||
2. **批處理**:批量處理相關任務
|
||||
3. **緩存優化**:重用計算結果
|
||||
4. **資源調度**:智能分配計算資源
|
||||
|
||||
---
|
||||
|
||||
## 6. 維護階段 (Maintenance)
|
||||
|
||||
### 6.1 日常維護
|
||||
|
||||
#### 監控項目:
|
||||
1. **處理器狀態**:運行狀態、健康狀態
|
||||
2. **性能指標**:處理速度、成功率
|
||||
3. **資源使用**:CPU、內存、存儲
|
||||
4. **錯誤率**:各種錯誤的發生頻率
|
||||
|
||||
#### 維護任務:
|
||||
1. **日誌分析**:定期分析處理器日誌
|
||||
2. **性能調優**:根據監控數據進行調優
|
||||
3. **安全更新**:更新依賴庫修復安全漏洞
|
||||
4. **數據清理**:清理臨時文件和緩存
|
||||
|
||||
### 6.2 版本升級
|
||||
|
||||
#### 升級流程:
|
||||
1. **兼容性檢查**:檢查新版本與現有系統的兼容性
|
||||
2. **回滾計劃**:制定升級失敗時的回滾計劃
|
||||
3. **分階段部署**:分階段逐步升級
|
||||
4. **驗證測試**:升級後進行全面測試
|
||||
|
||||
#### 版本兼容性矩陣:
|
||||
| 處理器版本 | 系統版本 | 模型版本 | 狀態 |
|
||||
|------------|----------|----------|------|
|
||||
| v1.0.x | v0.1.0 | insightface==0.7.3 | ✅ 兼容 |
|
||||
| v1.1.x | v0.2.0 | insightface==0.7.5 | ⚠️ 需要測試 |
|
||||
| v2.0.x | v0.3.0 | insightface==0.8.0 | ❌ 不兼容 |
|
||||
|
||||
---
|
||||
|
||||
## 7. 退役階段 (Retirement)
|
||||
|
||||
### 7.1 退役原因
|
||||
|
||||
1. **技術過時**:技術棧過時,需要替換
|
||||
2. **功能重疊**:與其他處理器功能重疊
|
||||
3. **性能問題**:性能無法滿足需求
|
||||
4. **維護成本**:維護成本過高
|
||||
|
||||
### 7.2 退役流程
|
||||
|
||||
#### 步驟 1: 退役計劃
|
||||
1. **替代方案**:確定替代處理器
|
||||
2. **數據遷移**:計劃數據遷移方案
|
||||
3. **時間安排**:安排退役時間表
|
||||
|
||||
#### 步驟 2: 數據遷移
|
||||
1. **歷史數據**:遷移歷史處理結果
|
||||
2. **配置數據**:遷移配置信息
|
||||
3. **依賴關係**:處理依賴關係
|
||||
|
||||
#### 步驟 3: 正式退役
|
||||
1. **停止服務**:停止處理器服務
|
||||
2. **數據清理**:清理相關數據
|
||||
3. **文檔更新**:更新系統文檔
|
||||
|
||||
### 7.3 退役檢查清單
|
||||
|
||||
- [ ] 替代處理器已部署並測試
|
||||
- [ ] 數據遷移已完成
|
||||
- [ ] 依賴關係已處理
|
||||
- [ ] 系統配置已更新
|
||||
- [ ] 用戶通知已發送
|
||||
- [ ] 退役文檔已更新
|
||||
|
||||
---
|
||||
|
||||
## 8. 相關處理器示例
|
||||
|
||||
### 8.1 已部署處理器
|
||||
|
||||
| 處理器 | 類型 | 狀態 | 版本 |
|
||||
|--------|------|------|------|
|
||||
| asr_processor | Python | ✅ 生產 | v1.3.2 |
|
||||
| face_processor | Python | ✅ 生產 | v1.1.5 |
|
||||
| yolo_processor | Python | ⚠️ 測試 | v0.9.1 |
|
||||
| scene_processor | Python | ⚠️ 開發 | v0.5.0 |
|
||||
|
||||
### 8.2 處理器開發計劃
|
||||
|
||||
| 處理器 | 優先級 | 預計完成時間 | 狀態 |
|
||||
|--------|--------|--------------|------|
|
||||
| ocr_processor | P1 | 2026-05-31 | 🚧 開發中 |
|
||||
| lip_processor | P2 | 2026-06-30 | 📅 計劃中 |
|
||||
| audio_classifier | P3 | 2026-07-31 | 💡 設計中 |
|
||||
|
||||
---
|
||||
|
||||
## 9. 最佳實踐
|
||||
|
||||
### 9.1 開發最佳實踐
|
||||
|
||||
1. **模塊化設計**:保持處理器模塊化和可重用
|
||||
2. **配置驅動**:使用配置文件而非硬編碼
|
||||
3. **完善的日誌**:記錄詳細的處理日誌
|
||||
4. **錯誤處理**:實現健壯的錯誤處理機制
|
||||
|
||||
### 9.2 部署最佳實踐
|
||||
|
||||
1. **版本控制**:嚴格管理處理器版本
|
||||
2. **環境隔離**:使用虛擬環境隔離依賴
|
||||
3. **配置管理**:使用配置管理工具
|
||||
4. **監控預警**:設置監控和預警機制
|
||||
|
||||
### 9.3 運維最佳實踐
|
||||
|
||||
1. **定期備份**:定期備份處理器配置和數據
|
||||
2. **性能監控**:持續監控處理器性能
|
||||
3. **安全更新**:及時更新安全補丁
|
||||
4. **文檔維護**:保持文檔與實際情況一致
|
||||
|
||||
---
|
||||
|
||||
## 10. 相關文件
|
||||
|
||||
| 文件 | 描述 | 相關性 |
|
||||
|------|------|--------|
|
||||
| [PROCESSOR_REGISTRY_ARCHITECTURE.md](./PROCESSOR_REGISTRY_ARCHITECTURE.md) | 處理器資源管理架構 | 核心架構 |
|
||||
| [SERVICE_REGISTRY_ARCHITECTURE.md](./SERVICE_REGISTRY_ARCHITECTURE.md) | 服務資源管理架構 | 依賴管理 |
|
||||
| [ARCHITECTURE_ROADMAP.md](./ARCHITECTURE_ROADMAP.md) | 架構發展路線圖 | 發展規劃 |
|
||||
|
||||
---
|
||||
|
||||
## 11. 最後更新記錄
|
||||
|
||||
| 版本 | 日期 | 主要變更 | 操作人 |
|
||||
|------|------|----------|--------|
|
||||
| V1.0 | 2026-04-22 | 創建處理器生命週期管理文檔 | OpenCode |
|
||||
|
||||
**最後更新日期**: 2026-04-22
|
||||
@@ -0,0 +1,330 @@
|
||||
---
|
||||
document_type: "architecture_design"
|
||||
service: "MOMENTRY_CORE"
|
||||
title: "Momentry Core 處理器資源管理架構 (v1.0)"
|
||||
date: "2026-04-21"
|
||||
version: "V1.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
tags:
|
||||
- "momentry"
|
||||
- "core"
|
||||
- "處理器資源管理架構"
|
||||
ai_query_hints:
|
||||
- "查詢 Momentry Core 處理器資源管理架構 (v1.0) 的內容"
|
||||
- "Momentry Core 處理器資源管理架構 (v1.0) 的主要目的是什麼?"
|
||||
- "如何操作或實施 Momentry Core 處理器資源管理架構 (v1.0)?"
|
||||
---
|
||||
|
||||
# Momentry Core 處理器資源管理架構 (v1.0)
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | OpenCode |
|
||||
| 建立時間 | 2026-04-21 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-04-21 | 創建處理器資源管理架構文件 | OpenCode | OpenCode / Qwen3.6-Plus |
|
||||
|
||||
---
|
||||
|
||||
## 0. 設計目標
|
||||
|
||||
將所有影片處理腳本與程式(Processors)視為**標準化可執行資源**,實現:
|
||||
|
||||
1. **插件化架構**: 支援 Python, Shell, CLI App 及未來 Docker/HTTP 擴展。
|
||||
2. **版本追溯**: 精確記錄處理器版本號與構建時間 (Build Time)。
|
||||
3. **產出標準化**: 定義 JSON 輸出規範,確保上下游系統相容。
|
||||
4. **動態調度**: 排程器根據處理器類型與狀態分配任務。
|
||||
|
||||
---
|
||||
|
||||
## 1. 核心架構
|
||||
|
||||
### 1.1 處理器分類 (Execution Types)
|
||||
|
||||
| 類型 | 說明 | 範例 | 執行指令範例 |
|
||||
|------|------|------|--------------|
|
||||
| `python` | 依賴 Python 環境的腳本 | ASR (WhisperX), Face (InsightFace), OCR | `python3 script.py --uuid ...` |
|
||||
| `shell` | Bash 腳本,用於系統工具串接 | Smart Thumbnail (ffmpeg) | `bash script.sh --uuid ...` |
|
||||
| `cli_app` | 編譯後的二進位程式 | 高效能向量計算器 | `./bin/processor --uuid ...` |
|
||||
| `docker` | 容器化執行 (未來擴展) | 隔離環境的 AI 推論 | `docker run --rm image ...` |
|
||||
| `http` | 遠端 API 呼叫 (未來擴展) | 外部雲端服務 | `POST /api/process` |
|
||||
|
||||
### 1.2 處理器與服務的關係
|
||||
|
||||
```
|
||||
處理器 (Processors)
|
||||
│
|
||||
├── 依賴 ──> [服務資源] (Services: Ollama, Qdrant, GPU)
|
||||
│
|
||||
├── 讀取 ──> [資產] (Assets: Video Files)
|
||||
│
|
||||
└── 產出 ──> [文件] (JSON Results in Storage)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 資料庫設計
|
||||
|
||||
### 2.1 `processors` 表結構
|
||||
|
||||
```sql
|
||||
CREATE TABLE processors (
|
||||
id UUID PRIMARY KEY, -- 處理器唯一標識符
|
||||
name VARCHAR(100) NOT NULL, -- 顯示名稱
|
||||
category VARCHAR(50) NOT NULL, -- 分類: preprocessing, audio, visual, text
|
||||
execution_type VARCHAR(50) NOT NULL, -- 執行型態: python, shell, cli_app, docker, http
|
||||
entry_point VARCHAR(255) NOT NULL, -- 腳本路徑或二進位檔名
|
||||
version VARCHAR(20) DEFAULT '1.0.0', -- 語義化版本號
|
||||
build_time TIMESTAMPTZ DEFAULT NOW(), -- 構建/部署時間
|
||||
|
||||
description TEXT, -- 功能說明
|
||||
technical_details TEXT, -- 技術手段描述
|
||||
output_spec JSONB, -- 產出規範 (JSON Schema)
|
||||
runtime_config JSONB, -- 執行環境配置 (如 venv, timeout, gpu)
|
||||
|
||||
is_active BOOLEAN DEFAULT TRUE, -- 是否啟用
|
||||
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_processors_category ON processors(category);
|
||||
CREATE INDEX idx_processors_type ON processors(execution_type);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 欄位詳細說明
|
||||
|
||||
### 3.1 執行環境配置 (runtime_config)
|
||||
|
||||
根據 `execution_type` 不同,此欄位內容也會不同。
|
||||
|
||||
**Python**:
|
||||
```json
|
||||
{
|
||||
"venv_path": "/Users/accusys/momentry_core_0.1/venv",
|
||||
"timeout_secs": 7200,
|
||||
"requirements": ["torch", "insightface", "easyocr"]
|
||||
}
|
||||
```
|
||||
|
||||
**Shell**:
|
||||
```json
|
||||
{
|
||||
"timeout_secs": 300,
|
||||
"dependencies": ["ffmpeg", "ffprobe"]
|
||||
}
|
||||
```
|
||||
|
||||
**Docker**:
|
||||
```json
|
||||
{
|
||||
"image": "registry.gitlab.com/momentry/ocr:v1.2",
|
||||
"gpu": true,
|
||||
"shm_size": "4g"
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 產出規範 (output_spec)
|
||||
|
||||
定義處理器執行成功後應生成的 JSON 結構。
|
||||
|
||||
**ASR (WhisperX)**:
|
||||
```json
|
||||
{
|
||||
"format": "json",
|
||||
"structure": {
|
||||
"segments": [
|
||||
{
|
||||
"start": "float",
|
||||
"end": "float",
|
||||
"text": "string",
|
||||
"speaker": "string (optional)"
|
||||
}
|
||||
]
|
||||
},
|
||||
"naming_convention": "{uuid}_asr_{timestamp}.json"
|
||||
}
|
||||
```
|
||||
|
||||
**Smart Thumbnail**:
|
||||
```json
|
||||
{
|
||||
"format": "image/jpeg",
|
||||
"resolution": "320x(width/height ratio)",
|
||||
"storage_path": "thumbnails/{uuid}.jpg",
|
||||
"metadata_key": "thumbnail_generated_at"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 完整註冊範例
|
||||
|
||||
### 4.1 Smart Thumbnail (Shell)
|
||||
|
||||
```sql
|
||||
INSERT INTO processors (
|
||||
id, name, category, execution_type, entry_point,
|
||||
description, technical_details, output_spec, runtime_config
|
||||
) VALUES (
|
||||
'550e8400-e29b-41d4-a716-446655440001',
|
||||
'Smart Thumbnail Extractor',
|
||||
'preprocessing',
|
||||
'shell',
|
||||
'scripts/smart_thumbnail.sh',
|
||||
'Detects black screens to find the first valid frame of the main content.',
|
||||
'Uses FFmpeg `blackdetect` filter to scan first 60s; applies 0.5s offset to avoid transitions.',
|
||||
'{
|
||||
"format": "image/jpeg",
|
||||
"naming_convention": "{uuid}.jpg"
|
||||
}'::jsonb,
|
||||
'{
|
||||
"timeout_secs": 300,
|
||||
"dependencies": ["ffmpeg"]
|
||||
}'::jsonb
|
||||
);
|
||||
```
|
||||
|
||||
### 4.2 ASR WhisperX (Python)
|
||||
|
||||
```sql
|
||||
INSERT INTO processors (
|
||||
id, name, category, execution_type, entry_point,
|
||||
version, build_time, description, technical_details, output_spec, runtime_config
|
||||
) VALUES (
|
||||
'550e8400-e29b-41d4-a716-446655440002',
|
||||
'WhisperX Speech Recognition',
|
||||
'audio',
|
||||
'python',
|
||||
'scripts/asr_processor.py',
|
||||
'2.1.0',
|
||||
'2026-04-20 10:00:00+08', -- 真實構建時間
|
||||
'High-accuracy speech-to-text with word-level timestamps and speaker diarization.',
|
||||
'WhisperX (faster-whisper) + pyannote-audio for speaker diarization.',
|
||||
'{
|
||||
"format": "json",
|
||||
"structure": {
|
||||
"segments": [{"start": "f64", "end": "f64", "text": "str", "speaker": "str"}]
|
||||
},
|
||||
"naming_convention": "{uuid}_asr_{timestamp}.json"
|
||||
}'::jsonb,
|
||||
'{
|
||||
"venv_path": "/Users/accusys/momentry_core_0.1/venv",
|
||||
"timeout_secs": 7200,
|
||||
"gpu": true
|
||||
}'::jsonb
|
||||
);
|
||||
```
|
||||
|
||||
### 4.3 OCR (Python)
|
||||
|
||||
```sql
|
||||
INSERT INTO processors (
|
||||
id, name, category, execution_type, entry_point,
|
||||
description, technical_details, output_spec, runtime_config
|
||||
) VALUES (
|
||||
'550e8400-e29b-41d4-a716-446655440003',
|
||||
'EasyOCR Text Recognition',
|
||||
'visual',
|
||||
'python',
|
||||
'scripts/ocr_processor.py',
|
||||
'Extracts text blocks with coordinates from video frames.',
|
||||
'Uses EasyOCR (local model) with English language support.',
|
||||
'{
|
||||
"format": "json",
|
||||
"structure": {
|
||||
"frames": [
|
||||
{
|
||||
"frame": "int",
|
||||
"timestamp": "float",
|
||||
"texts": [{"text": "str", "bbox": "object", "confidence": "float"}]
|
||||
}
|
||||
]
|
||||
},
|
||||
"naming_convention": "{uuid}_ocr_{timestamp}.json"
|
||||
}'::jsonb,
|
||||
'{
|
||||
"venv_path": "/Users/accusys/momentry_core_0.1/venv",
|
||||
"timeout_secs": 3600,
|
||||
"sample_interval_frames": 30
|
||||
}'::jsonb
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 標準化執行介面 (Execution Interface)
|
||||
|
||||
為了讓排程器 (Scheduler) 能統一呼叫所有類型的處理器,所有處理器必須遵循以下參數規範:
|
||||
|
||||
| 參數 | 說明 | 範例值 |
|
||||
|:---|:---|:---|
|
||||
| `--uuid` | 影片/任務唯一標識符 | `--uuid 384b0ff4...` |
|
||||
| `--input` | 輸入媒體檔案路徑 | `--input /data/raw/video.mp4` |
|
||||
| `--output` | 產出 JSON/檔案目錄 | `--output /data/output/384b...` |
|
||||
| `--config` | (選用) 額外 JSON 配置路徑 | `--config settings.json` |
|
||||
|
||||
**Rust 執行分發邏輯 (Dispatcher)**:
|
||||
|
||||
```rust
|
||||
match processor.execution_type.as_str() {
|
||||
"python" => {
|
||||
Command::new(venv_python)
|
||||
.arg(&entry_point)
|
||||
.args(common_args)
|
||||
.spawn()?
|
||||
}
|
||||
"shell" => {
|
||||
Command::new("bash")
|
||||
.arg(&entry_point)
|
||||
.args(common_args)
|
||||
.spawn()?
|
||||
}
|
||||
"cli_app" => {
|
||||
Command::new(&entry_point)
|
||||
.args(common_args)
|
||||
.spawn()?
|
||||
}
|
||||
_ => bail!("Unsupported execution type")
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 處理器與服務整合 (Integration)
|
||||
|
||||
處理器在執行時,需要查詢「服務註冊中心」來獲取依賴資源的配置。
|
||||
|
||||
**流程範例**:
|
||||
1. 排程器啟動 `asr_processor.py`。
|
||||
2. Python 腳本查詢本地配置檔 (由排程器生成,內容來自 `services` 表)。
|
||||
3. 腳本獲取 Ollama 的 `endpoint` 與 `model_name`。
|
||||
4. 腳本執行 Embedding 任務。
|
||||
|
||||
這樣實現了**處理器與基礎設施配置的解耦**。
|
||||
|
||||
---
|
||||
|
||||
## 7. 總結
|
||||
|
||||
本設計確立了 Momentry 處理器管理的標準:
|
||||
|
||||
| 管理維度 | 實作方式 |
|
||||
|----------|----------|
|
||||
| **唯一標識** | UUID (`id` 欄位) |
|
||||
| **多態執行** | `execution_type` (Python/Shell/CLI/Docker...) |
|
||||
| **版本控制** | `version` + `build_time` |
|
||||
| **品質保證** | `output_spec` (JSON Schema 驗證) |
|
||||
| **環境隔離** | `runtime_config` (Venv, Docker Image) |
|
||||
| **依賴管理** | 啟動時注入 `services` 配置 |
|
||||
|
||||
此架構支持未來無限擴展,新的 AI 模型或工具只需編寫腳本並註冊即可納入系統管轄。
|
||||
@@ -0,0 +1,120 @@
|
||||
# Resource Monitoring Specification (資源監控規範)
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | OpenCode |
|
||||
| 建立時間 | 2026-04-25 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-04-25 | 定義 Processor/Agent 的註冊與心跳協定 (僅限監控) | OpenCode | OpenCode |
|
||||
|
||||
---
|
||||
|
||||
## 1. 核心概念
|
||||
|
||||
本階段資源註冊機制 (Resource Registry) **僅用於監控 (Monitoring)**,不介入動態任務調度。
|
||||
所有 Processor (YOLO, ASR...) 和 Agent (Translation, Summary...) 啟動時應主動註冊。
|
||||
|
||||
### 1.1 註冊時機
|
||||
* **Processor**: 在 Python 腳本啟動時,呼叫 HTTP Endpoint 註冊。
|
||||
* **Agent**: 在服務啟動時呼叫 HTTP Endpoint 註冊。
|
||||
|
||||
---
|
||||
|
||||
## 2. 註冊協定 (Registration Protocol)
|
||||
|
||||
### 2.1 API Endpoint
|
||||
|
||||
`POST /api/v1/resources/register`
|
||||
|
||||
### 2.2 Request Payload
|
||||
|
||||
```json
|
||||
{
|
||||
"resource_id": "unique_id",
|
||||
"resource_type": "processor | agent",
|
||||
"name": "Yolo Object Detector",
|
||||
"capabilities": ["detect_object", "detect_face"],
|
||||
"config": {
|
||||
"model_version": "v8n",
|
||||
"gpu_enabled": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
* **resource_id**: 建議格式 `{type}_{name}_{uuid}`,例如 `processor_yolo_a1b2c3`。
|
||||
|
||||
### 2.3 Response
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Resource registered"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 心跳協定 (Heartbeat Protocol)
|
||||
|
||||
資源應定期發送心跳,回報當前狀態與進度。
|
||||
|
||||
### 3.1 API Endpoint
|
||||
|
||||
`POST /api/v1/resources/{resource_id}/heartbeat`
|
||||
|
||||
### 3.2 Request Payload
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "idle | busy | error",
|
||||
"job_uuid": "current_video_uuid",
|
||||
"progress": 0.45,
|
||||
"last_frame_index": 12500
|
||||
}
|
||||
```
|
||||
|
||||
* **progress**: 0.0 到 1.0 之間的浮點數。
|
||||
* **job_uuid**: 當前正在處理的任務 ID。
|
||||
|
||||
---
|
||||
|
||||
## 4. 監控用途
|
||||
|
||||
系統後台 (Portal Dashboard) 可透過查詢 Registry 實現:
|
||||
1. **即時儀表板**: 顯示目前有幾個 Processor 在運行 (`busy` 數量)。
|
||||
2. **進度條**: 透過 `last_frame_index` 與影片總幀數計算百分比。
|
||||
3. **健康檢查**: 若資源超過 60 秒未發送心跳,標記為 `offline`。
|
||||
|
||||
---
|
||||
|
||||
## 5. Rust Worker 整合建議
|
||||
|
||||
在 `src/worker/processor.rs` 的 `run_processor` 函數中:
|
||||
|
||||
```rust
|
||||
// 1. 生成唯一的 Resource ID
|
||||
let resource_id = format!("processor_{}_{}", processor_type, job.uuid);
|
||||
|
||||
// 2. 註冊資源
|
||||
register_resource(&resource_id, processor_type).await;
|
||||
|
||||
// 3. 執行腳本 (腳本內部應定期發送心跳,或由 Rust Wrapper 發送)
|
||||
run_python_script(...);
|
||||
|
||||
// 4. 登出資源 (可選,或由 TTL 自動清理)
|
||||
deregister_resource(&resource_id).await;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 版本資訊
|
||||
|
||||
- 版本: V1.0
|
||||
- 建立日期: 2026-04-25
|
||||
@@ -0,0 +1,500 @@
|
||||
---
|
||||
document_type: "architecture_design"
|
||||
service: "MOMENTRY_CORE"
|
||||
title: "Momentry Core 全域服務資源管理架構 (v1.0)"
|
||||
date: "2026-04-21"
|
||||
version: "V1.0"
|
||||
status: "active"
|
||||
owner: "Warren"
|
||||
created_by: "OpenCode"
|
||||
tags:
|
||||
- "momentry"
|
||||
- "core"
|
||||
- "全域服務資源管理架構"
|
||||
ai_query_hints:
|
||||
- "查詢 Momentry Core 全域服務資源管理架構 (v1.0) 的內容"
|
||||
- "Momentry Core 全域服務資源管理架構 (v1.0) 的主要目的是什麼?"
|
||||
- "如何操作或實施 Momentry Core 全域服務資源管理架構 (v1.0)?"
|
||||
---
|
||||
|
||||
# Momentry Core 全域服務資源管理架構 (v1.0)
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | OpenCode |
|
||||
| 建立時間 | 2026-04-21 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-04-21 | 創建全域服務資源管理架構文件 | OpenCode | OpenCode / Qwen3.6-Plus |
|
||||
|
||||
---
|
||||
|
||||
## 0. 設計目標
|
||||
|
||||
將所有基礎設施服務(Infrastructure Services)視為**可管理資源**,實現:
|
||||
|
||||
1. **動態發現**: 處理器不再寫死服務 IP,而是從註冊中心查詢可用服務
|
||||
2. **健康監控**: 自動探活服務狀態,故障時標記並尋找備用節點
|
||||
3. **版本追溯**: 精確記錄模型檔案、配置、依賴關係,確保可重現性
|
||||
4. **運維自動化**: 統一管理備份、日誌、儲存路徑,降低人工維護成本
|
||||
|
||||
---
|
||||
|
||||
## 1. 核心架構
|
||||
|
||||
### 1.1 服務分類 (Service Types)
|
||||
|
||||
| 類型 | 說明 | 範例 |
|
||||
|------|------|------|
|
||||
| `embedding_engine` | 語意向量生成 | Ollama (nomic-embed-text-v2-moe) |
|
||||
| `llm_engine` | 文字生成/推理 | llama.cpp (gemma-4) |
|
||||
| `vector_db` | 向量儲存與搜尋 | Qdrant |
|
||||
| `cache` | 快取與隊列 | Redis |
|
||||
| `database` | 關聯式資料庫 | PostgreSQL |
|
||||
| `storage` | 檔案管理 | SFTPGo |
|
||||
| `api_server` | API 閘道 | Momentry Core Server |
|
||||
|
||||
### 1.2 服務資源關聯圖
|
||||
|
||||
```
|
||||
使用者/API
|
||||
│
|
||||
├──> [Momentry Core API Server] (api_server)
|
||||
│ │
|
||||
│ ├──> [Qdrant] (vector_db) ─── 向量搜尋
|
||||
│ │
|
||||
│ ├──> [Ollama] (embedding_engine) ─── 768-dim Embedding
|
||||
│ │
|
||||
│ ├──> [llama.cpp] (llm_engine) ─── Gemma4 推理
|
||||
│ │
|
||||
│ ├──> [PostgreSQL] (database) ─── 關聯資料
|
||||
│ │
|
||||
│ └──> [Redis] (cache) ─── 快取與隊列
|
||||
│
|
||||
└──> [SFTPGo] (storage) ─── 檔案上傳/管理
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 資料庫設計
|
||||
|
||||
### 2.1 `services` 表結構
|
||||
|
||||
```sql
|
||||
CREATE TABLE services (
|
||||
id UUID PRIMARY KEY,
|
||||
name VARCHAR(100) NOT NULL, -- 服務名稱 (e.g., ollama-embedding-nomic-v2-moe)
|
||||
type VARCHAR(50) NOT NULL, -- 服務類型 (見 1.1)
|
||||
endpoint VARCHAR(255), -- 基礎連接點 (e.g., http://127.0.0.1:11434)
|
||||
status VARCHAR(20) DEFAULT 'unknown', -- online, offline, degraded, unknown
|
||||
metadata JSONB, -- 技術細節 (模型版本、維度等)
|
||||
|
||||
-- 1. 網路與端口
|
||||
port_config JSONB, -- 主端口、範圍、協議
|
||||
|
||||
-- 2. 存取控制
|
||||
access_policy JSONB, -- 認證方式、允許的使用者
|
||||
|
||||
-- 3. 依賴關係
|
||||
dependency_graph JSONB, -- 上游/下游依賴
|
||||
|
||||
-- 4. 業務上下文
|
||||
business_purpose TEXT, -- 用途說明
|
||||
reference_docs JSONB, -- 文檔連結
|
||||
|
||||
-- 5. 儲存與日誌
|
||||
storage_paths JSONB, -- 配置、數據、log、error_log
|
||||
|
||||
-- 6. 備份策略
|
||||
backup_policy JSONB, -- 備份週期、方法、目標
|
||||
|
||||
-- 7. 健康檢查
|
||||
health_check_path VARCHAR(255), -- 探活路徑 (e.g., /health)
|
||||
health_check_method VARCHAR(10), -- HTTP 方法 (GET/POST)
|
||||
health_check_match TEXT, -- 預期回應 (Status 200 or JSON content)
|
||||
check_interval_secs INT DEFAULT 60, -- 檢查頻率 (秒)
|
||||
|
||||
last_check_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 欄位詳細說明
|
||||
|
||||
### 3.1 技術細節 (metadata)
|
||||
|
||||
根據服務類型記錄不同的技術參數。
|
||||
|
||||
**Ollama (Embedding Engine)**:
|
||||
```json
|
||||
{
|
||||
"provider": "ollama",
|
||||
"model_name": "nomic-embed-text-v2-moe",
|
||||
"model_tag": "latest",
|
||||
"gguf_file": "nomic-embed-text-v2-moe-Q4_0.gguf",
|
||||
"gguf_sha256": "sha256:xxxxx...",
|
||||
"source_url": "https://huggingface.co/nomic-ai/nomic-embed-text-v2-moe-GGUF",
|
||||
"dimensions": 768,
|
||||
"capabilities": ["embedding", "text-similarity", "multilingual"],
|
||||
"context_length": 2048,
|
||||
"architecture": "Mixture of Experts (MoE)"
|
||||
}
|
||||
```
|
||||
|
||||
**llama.cpp (LLM Engine)**:
|
||||
```json
|
||||
{
|
||||
"provider": "llama.cpp",
|
||||
"model_name": "gemma-4-12b-it",
|
||||
"model_file": "gemma-4-12b-it-Q4_K_M.gguf",
|
||||
"source": "https://huggingface.co/bartowski/gemma-4-12b-it-GGUF",
|
||||
"sha256": "sha256:yyyyy...",
|
||||
"capabilities": ["text-generation", "chat"],
|
||||
"parameters": "12B",
|
||||
"quantization": "Q4_K_M",
|
||||
"gpu_layers": -1
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 網路與端口 (port_config)
|
||||
|
||||
```json
|
||||
{
|
||||
"main_port": 11434,
|
||||
"range": "11434-11435",
|
||||
"protocol": "HTTP",
|
||||
"bind_address": "127.0.0.1"
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3 存取控制 (access_policy)
|
||||
|
||||
```json
|
||||
{
|
||||
"auth_type": "none",
|
||||
"allowed_users": ["momentry_core", "vectorize_worker"],
|
||||
"api_key_env": null,
|
||||
"rate_limit": "unlimited",
|
||||
"cors_origin": "localhost"
|
||||
}
|
||||
```
|
||||
|
||||
### 3.4 依賴關係 (dependency_graph)
|
||||
|
||||
```json
|
||||
{
|
||||
"upstream": ["gpu_driver", "cuda_toolkit"],
|
||||
"downstream": ["qdrant_ingestion", "search_api", "smart_synonym_expander"],
|
||||
"criticality": "high"
|
||||
}
|
||||
```
|
||||
|
||||
### 3.5 儲存與日誌 (storage_paths)
|
||||
|
||||
```json
|
||||
{
|
||||
"data_dir": "/Users/accusys/.ollama/models",
|
||||
"config_dir": "/Users/accusys/.ollama/modelfiles",
|
||||
"log_file": "/Users/accusys/Library/Logs/ollama/ollama.log",
|
||||
"error_log_file": "/Users/accusys/Library/Logs/ollama/ollama.error.log",
|
||||
"env_file": "/Users/accusys/.ollama/.env"
|
||||
}
|
||||
```
|
||||
|
||||
### 3.6 備份策略 (backup_policy)
|
||||
|
||||
```json
|
||||
{
|
||||
"enabled": true,
|
||||
"method": "rsync",
|
||||
"schedule": "daily",
|
||||
"destination": "/Volumes/BackupDrive/momentry_services/ollama",
|
||||
"retention_days": 30,
|
||||
"pre_hook": "launchctl stop com.ollama.service",
|
||||
"post_hook": "launchctl start com.ollama.service",
|
||||
"exclude_patterns": ["*.tmp", "logs/*"]
|
||||
}
|
||||
```
|
||||
|
||||
### 3.7 健康檢查 (health_check)
|
||||
|
||||
| 欄位 | 說明 | 範例 |
|
||||
|------|------|------|
|
||||
| `health_check_path` | 探活路徑 | `/health` 或 `/` |
|
||||
| `health_check_method` | HTTP 方法 | `GET` |
|
||||
| `health_check_match` | 預期回應內容 | `Ollama is running` |
|
||||
| `check_interval_secs` | 檢查頻率 | `60` |
|
||||
|
||||
---
|
||||
|
||||
## 4. 完整註冊範例
|
||||
|
||||
### 4.1 Ollama Embedding Engine
|
||||
|
||||
```sql
|
||||
INSERT INTO services (
|
||||
id, name, type, endpoint, status, metadata,
|
||||
port_config, access_policy, dependency_graph,
|
||||
business_purpose, reference_docs,
|
||||
storage_paths, backup_policy,
|
||||
health_check_path, health_check_method, health_check_match, check_interval_secs
|
||||
) VALUES (
|
||||
'550e8400-e29b-41d4-a716-446655440100',
|
||||
'ollama-embedding-nomic-v2-moe',
|
||||
'embedding_engine',
|
||||
'http://127.0.0.1:11434',
|
||||
'online',
|
||||
'{"provider": "ollama", "model_name": "nomic-embed-text-v2-moe", "model_tag": "latest", "dimensions": 768}'::jsonb,
|
||||
'{"main_port": 11434, "protocol": "HTTP"}'::jsonb,
|
||||
'{"auth_type": "none", "allowed_users": ["momentry_core", "vectorize_worker"]}'::jsonb,
|
||||
'{"upstream": ["gpu_driver"], "downstream": ["qdrant_ingestion", "search_api"], "criticality": "high"}'::jsonb,
|
||||
'Generate 768-dim multilingual embeddings for chunks and semantic search.',
|
||||
'{"model_url": "https://ollama.com/library/nomic-embed-text-v2-moe", "wiki": "docs/PROCESSING_PIPELINE.md"}'::jsonb,
|
||||
'{
|
||||
"data_dir": "/Users/accusys/.ollama/models",
|
||||
"config_dir": "/Users/accusys/.ollama/modelfiles",
|
||||
"log_file": "/Users/accusys/Library/Logs/ollama/ollama.log",
|
||||
"error_log_file": "/Users/accusys/Library/Logs/ollama/ollama.error.log"
|
||||
}'::jsonb,
|
||||
'{
|
||||
"enabled": true,
|
||||
"method": "rsync",
|
||||
"destination": "/Volumes/BackupDrive/ollama_models",
|
||||
"retention_days": 30
|
||||
}'::jsonb,
|
||||
'/', 'GET', 'Ollama is running', 60
|
||||
);
|
||||
```
|
||||
|
||||
### 4.2 llama.cpp LLM Engine
|
||||
|
||||
```sql
|
||||
INSERT INTO services (
|
||||
id, name, type, endpoint, status, metadata,
|
||||
port_config, access_policy, dependency_graph,
|
||||
business_purpose, reference_docs,
|
||||
storage_paths, backup_policy,
|
||||
health_check_path, health_check_method, health_check_match, check_interval_secs
|
||||
) VALUES (
|
||||
'550e8400-e29b-41d4-a716-446655440099',
|
||||
'llama-server-gemma4',
|
||||
'llm_engine',
|
||||
'http://127.0.0.1:8081',
|
||||
'online',
|
||||
'{"provider": "llama.cpp", "model_name": "gemma-4-12b-it", "model_file": "gemma-4-12b-it-Q4_K_M.gguf", "capabilities": ["text-generation", "chat"], "parameters": "12B"}'::jsonb,
|
||||
'{"main_port": 8081, "protocol": "HTTP"}'::jsonb,
|
||||
'{"auth_type": "none", "allowed_users": ["momentry_core"]}'::jsonb,
|
||||
'{"upstream": ["gpu_driver"], "downstream": ["smart_synonym_expander", "query_parser"], "criticality": "medium"}'::jsonb,
|
||||
'Provide text generation and instruction following for synonym expansion and query analysis.',
|
||||
'{"model_url": "https://huggingface.co/bartowski/gemma-4-12b-it-GGUF"}'::jsonb,
|
||||
'{
|
||||
"data_dir": "/Users/accusys/momentry/models",
|
||||
"config_dir": "/Users/accusys/momentry/config",
|
||||
"log_file": "/Users/accusys/momentry/logs/llama_server.log",
|
||||
"error_log_file": "/Users/accusys/momentry/logs/llama_server.error.log"
|
||||
}'::jsonb,
|
||||
'{
|
||||
"enabled": true,
|
||||
"method": "rsync",
|
||||
"destination": "/Volumes/BackupDrive/llama_models",
|
||||
"retention_days": 30
|
||||
}'::jsonb,
|
||||
'/health', 'GET', 'OK', 30
|
||||
);
|
||||
```
|
||||
|
||||
### 4.3 Qdrant Vector DB
|
||||
|
||||
```sql
|
||||
INSERT INTO services (
|
||||
id, name, type, endpoint, status, metadata,
|
||||
port_config, access_policy, dependency_graph,
|
||||
business_purpose, reference_docs,
|
||||
storage_paths, backup_policy,
|
||||
health_check_path, health_check_method, health_check_match, check_interval_secs
|
||||
) VALUES (
|
||||
'550e8400-e29b-41d4-a716-446655440003',
|
||||
'qdrant-vector-store',
|
||||
'vector_db',
|
||||
'http://127.0.0.1:6333',
|
||||
'online',
|
||||
'{"version": "1.7.0", "collections": ["momentry_rule1", "momentry_rule2", "momentry_rule3"], "vector_dim": 768, "distance": "Cosine"}'::jsonb,
|
||||
'{"main_port": 6333, "grpc_port": 6334, "protocol": "HTTP/REST+gRPC"}'::jsonb,
|
||||
'{"auth_type": "api_key", "api_key_env": "QDRANT_API_KEY", "allowed_users": ["momentry_core", "vectorize_worker"]}'::jsonb,
|
||||
'{"upstream": ["ollama-embedding-nomic-v2-moe"], "downstream": ["search_api"], "criticality": "critical"}'::jsonb,
|
||||
'Store and search 768-dim embeddings for all chunk rules.',
|
||||
'{"docs": "https://qdrant.tech/documentation"}'::jsonb,
|
||||
'{
|
||||
"data_dir": "/opt/qdrant/storage",
|
||||
"config_dir": "/opt/qdrant/config",
|
||||
"log_file": "/var/log/qdrant/qdrant.log",
|
||||
"error_log_file": "/var/log/qdrant/qdrant.error.log"
|
||||
}'::jsonb,
|
||||
'{
|
||||
"enabled": true,
|
||||
"method": "snapshot",
|
||||
"schedule": "daily",
|
||||
"destination": "/Volumes/BackupDrive/qdrant_snapshots",
|
||||
"retention_days": 14
|
||||
}'::jsonb,
|
||||
'/healthz', 'GET', '', 30
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 健康監控機制 (Health Monitor)
|
||||
|
||||
### 5.1 監控流程
|
||||
|
||||
```
|
||||
1. Worker 掃描 services 表 (status != 'disabled')
|
||||
↓
|
||||
2. 對每個服務發送 health_check
|
||||
- URL: endpoint + health_check_path
|
||||
- Method: health_check_method
|
||||
↓
|
||||
3. 驗證回應
|
||||
- HTTP Status: 200 OK?
|
||||
- Content: 包含 health_check_match?
|
||||
↓
|
||||
4. 更新狀態
|
||||
- success → status = 'online'
|
||||
- fail → status = 'offline'
|
||||
- timeout → status = 'degraded'
|
||||
↓
|
||||
5. 記錄 last_check_at
|
||||
```
|
||||
|
||||
### 5.2 Rust 實作範例
|
||||
|
||||
```rust
|
||||
pub async fn run_health_checks(pool: &PgPool) -> anyhow::Result<()> {
|
||||
let services = sqlx::query!(
|
||||
"SELECT id, endpoint, health_check_path, health_check_method,
|
||||
health_check_match, check_interval_secs
|
||||
FROM services WHERE status != 'disabled'"
|
||||
)
|
||||
.fetch_all(pool)
|
||||
.await?;
|
||||
|
||||
for svc in services {
|
||||
let url = format!("{}{}", svc.endpoint, svc.health_check_path);
|
||||
let new_status = match check_service_health(&url, &svc.health_check_method).await {
|
||||
Ok(body) => {
|
||||
if let Some(expected) = &svc.health_check_match {
|
||||
if body.contains(expected) { "online" } else { "degraded" }
|
||||
} else { "online" }
|
||||
}
|
||||
Err(_) => "offline"
|
||||
};
|
||||
|
||||
sqlx::query!(
|
||||
"UPDATE services SET status = $1, last_check_at = NOW() WHERE id = $2",
|
||||
new_status,
|
||||
svc.id
|
||||
)
|
||||
.execute(pool)
|
||||
.await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 依賴影響分析
|
||||
|
||||
### 6.1 故障傳播查詢
|
||||
|
||||
```sql
|
||||
-- 查詢受 Ollama 故障影響的所有服務
|
||||
SELECT name, type, status
|
||||
FROM services
|
||||
WHERE dependency_graph->'upstream' @> '["ollama-embedding-nomic-v2-moe"]';
|
||||
|
||||
-- 查詢 Qdrant 依賴的所有上游服務
|
||||
SELECT name, type, status
|
||||
FROM services
|
||||
WHERE 'qdrant-vector-store' = ANY(
|
||||
ARRAY(
|
||||
SELECT jsonb_array_elements_text(
|
||||
dependency_graph->'downstream'
|
||||
)
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
### 6.2 啟動順序
|
||||
|
||||
根據 `dependency_graph` 的 `upstream` 字段,系統可自動計算服務啟動順序:
|
||||
|
||||
```
|
||||
1. gpu_driver → cuda_toolkit
|
||||
2. ollama-embedding-nomic-v2-moe (需要 gpu_driver)
|
||||
3. llama-server-gemma4 (需要 gpu_driver)
|
||||
4. qdrant-vector-store
|
||||
5. redis-cache
|
||||
6. postgres-main
|
||||
7. momentry-core-api (依賴以上所有)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 備份管理 (Backup Manager)
|
||||
|
||||
### 7.1 備份排程查詢
|
||||
|
||||
```sql
|
||||
-- 找出今日需要備份的服務
|
||||
SELECT name, backup_policy
|
||||
FROM services
|
||||
WHERE backup_policy->>'enabled' = 'true'
|
||||
AND (backup_policy->>'schedule' = 'daily'
|
||||
OR backup_policy->>'schedule' LIKE '%* * *');
|
||||
```
|
||||
|
||||
### 7.2 備份執行邏輯
|
||||
|
||||
```
|
||||
1. Worker 掃描 backup_policy.enabled = true
|
||||
↓
|
||||
2. 執行 pre_hook (如停止服務)
|
||||
↓
|
||||
3. 執行備份方法
|
||||
- rsync: rsync -a --exclude="*.tmp" data_dir destination
|
||||
- pg_dump: pg_dump dbname > destination/dump.sql
|
||||
- snapshot: qdrant CLI create-snapshot
|
||||
↓
|
||||
4. 壓縮 (gzip)
|
||||
↓
|
||||
5. 執行 post_hook (如重啟服務)
|
||||
↓
|
||||
6. 清理超過 retention_days 的舊備份
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 總結
|
||||
|
||||
本設計將所有基礎設施服務納管為**可發現、可監控、可備份、可追溯**的資源實體。
|
||||
|
||||
| 管理維度 | 實作方式 |
|
||||
|----------|----------|
|
||||
| **服務發現** | `services` 表 + `endpoint` 欄位 |
|
||||
| **版本追溯** | `metadata` (模型檔案 SHA256, 版本號) |
|
||||
| **健康監控** | `health_check_*` 欄位 + 背景 Worker |
|
||||
| **依賴管理** | `dependency_graph` (upstream/downstream) |
|
||||
| **存取控制** | `access_policy` (認證方式、允許使用者) |
|
||||
| **儲存管理** | `storage_paths` (配置、數據、分離日誌) |
|
||||
| **備份恢復** | `backup_policy` (排程、方法、保留期、Hooks) |
|
||||
|
||||
透過此架構,Momentry 可實現從「手動運維」到「自動化服務治理」的升級。
|
||||
@@ -0,0 +1,162 @@
|
||||
# 統一資源註冊架構 (Unified Resource Registry Architecture)
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | OpenCode |
|
||||
| 建立時間 | 2026-04-25 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||||
|------|------|------|--------|-----------|
|
||||
| V1.0 | 2026-04-25 | 定義 Service、Processor、Agent 為統一資源 (Resource) 的註冊與管理架構 | OpenCode | OpenCode |
|
||||
|
||||
---
|
||||
|
||||
## 1. 核心設計理念
|
||||
|
||||
在 Momentry Core 系統中,所有用於處理、分析和管理數據的組件,統一抽象為 **「資源 (Resource)」**。
|
||||
這種設計允許系統以統一的方式發現、調度、監控和管理不同類型的組件。
|
||||
|
||||
### 1.1 資源三大分類 (Resource Types)
|
||||
|
||||
| 資源類型 | 英文代號 | 定義 | 特性 | 範例 |
|
||||
|----------|----------|------|------|------|
|
||||
| **服務** | **Service** | 系統運行依賴的基礎設施或長駐進程。 | 長生命週期 (Long-lived), 狀態保持。 | PostgreSQL, Redis, TMDB API |
|
||||
| **處理器** | **Processor** | 執行確定性數據轉換的模組。 | 短生命週期 (Task-based), 輸入 A -> 輸出 B, 無狀態。 | FFmpeg (Probe), YOLO, Whisper |
|
||||
| **智能體** | **Agent** | 依賴 LLM 進行語義推論或決策的模組。 | 短生命週期 (Task-based), 機率性輸出, 依賴 Prompt/Context。 | 5W1H Inference, Summarization, Identity Resolution |
|
||||
|
||||
---
|
||||
|
||||
## 2. 通用資源模型 (Universal Resource Model)
|
||||
|
||||
所有資源在註冊表中共享以下核心結構:
|
||||
|
||||
```json
|
||||
{
|
||||
"resource_id": "unique_identifier",
|
||||
"resource_type": "processor | agent | service",
|
||||
"category": "visual | speech | metadata | logic",
|
||||
|
||||
"capabilities": ["capability_1", "capability_2"],
|
||||
"status": "idle | busy | offline | error",
|
||||
|
||||
"config": {
|
||||
"model": "yolov8n",
|
||||
"timeout": 60,
|
||||
"gpu_required": false
|
||||
},
|
||||
|
||||
"health_check": {
|
||||
"endpoint": "/health",
|
||||
"interval_seconds": 30,
|
||||
"last_success": "2026-04-25T10:00:00Z"
|
||||
},
|
||||
|
||||
"metadata": {
|
||||
"version": "1.0.0",
|
||||
"description": "..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 資源生命週期 (Resource Lifecycle)
|
||||
|
||||
1. **註冊 (Registration)**:
|
||||
* 組件啟動時向 **Resource Registry** 報到,聲明其 ID、類型和能力。
|
||||
* *範例*: Agent 啟動,註冊 `resource_type: "agent"`, `capabilities: ["summarize_text"]`。
|
||||
2. **發現 (Discovery)**:
|
||||
* 調度器 (Scheduler) 根據任務需求查詢 Registry 尋找合適的資源。
|
||||
* *範例*: 任務需要「語音轉文字」,查詢 `capabilities: ["audio_to_text"]`。
|
||||
3. **分配與執行 (Allocation & Execution)**:
|
||||
* 狀態變為 `busy`,接收任務並執行。
|
||||
4. **健康檢查 (Health Monitoring)**:
|
||||
* Registry 定期 Ping 資源。若無回應,標記為 `offline`。
|
||||
5. **登出 (Deregistration)**:
|
||||
* 組件關閉或崩潰時從 Registry 移除。
|
||||
|
||||
---
|
||||
|
||||
## 4. 資源註冊表設計 (Registry Schema)
|
||||
|
||||
### 4.1 資料庫表結構 (SQL)
|
||||
|
||||
```sql
|
||||
CREATE TABLE resources (
|
||||
resource_id VARCHAR(64) PRIMARY KEY,
|
||||
resource_type VARCHAR(20) NOT NULL, -- 'processor', 'agent', 'service'
|
||||
category VARCHAR(50), -- 'visual', 'speech', 'logic'
|
||||
|
||||
name VARCHAR(100) NOT NULL,
|
||||
description TEXT,
|
||||
|
||||
capabilities JSONB, -- Array of strings
|
||||
config JSONB, -- Resource specific config
|
||||
metadata JSONB, -- Version, author, etc.
|
||||
|
||||
status VARCHAR(20) DEFAULT 'offline',
|
||||
last_heartbeat TIMESTAMPTZ,
|
||||
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- 索引優化查詢
|
||||
CREATE INDEX idx_res_type ON resources(resource_type);
|
||||
CREATE INDEX idx_res_status ON resources(status);
|
||||
CREATE INDEX idx_res_caps ON resources USING GIN(capabilities);
|
||||
```
|
||||
|
||||
### 4.2 API 端點設計
|
||||
|
||||
| Method | Endpoint | 說明 |
|
||||
|--------|----------|------|
|
||||
| `POST` | `/api/v1/resources/register` | 資源啟動時註冊 |
|
||||
| `POST` | `/api/v1/resources/:id/heartbeat` | 發送心跳 |
|
||||
| `GET` | `/api/v1/resources` | 查詢所有資源 (支援過濾) |
|
||||
| `GET` | `/api/v1/resources?capability=summarize` | 查詢具備特定能力的資源 |
|
||||
| `POST` | `/api/v1/resources/:id/deregister` | 資源關閉時登出 |
|
||||
|
||||
---
|
||||
|
||||
## 5. 實作建議
|
||||
|
||||
### 5.1 Processor 實作 (確定性)
|
||||
* 通常由 Python 腳本或 Rust 二進制執行。
|
||||
* 啟動時呼叫 `POST /resources/register`,宣告如 `["video_to_frames", "detect_objects"]`。
|
||||
|
||||
### 5.2 Agent 實作 (機率性)
|
||||
* 通常封裝為具備 LLM Context 的服務。
|
||||
* 啟動時呼叫 `POST /resources/register`,宣告如 `["summarize_text", "extract_5w1h"]`。
|
||||
* **重點**: 在 `metadata` 中記錄使用的 LLM 模型名稱 (e.g., `gpt-4o`, `llama3`)。
|
||||
|
||||
### 5.3 Service 實作 (基礎設施)
|
||||
* 通常由 Docker Compose 或 Systemd 管理。
|
||||
* 可透過 Sidecar 或定期腳本進行註冊與心跳更新。
|
||||
|
||||
---
|
||||
|
||||
## 6. 與其他架構的關係
|
||||
|
||||
* **Job/Task Scheduler**: 任務調度器依賴 Resource Registry 來尋找誰能執行任務。
|
||||
* **Configuration Management**: 資源的詳細參數 (如 API Key, Threshold) 應存在 Config 中心,Registry 僅儲存引用或摘要。
|
||||
* **Monitoring**: Prometheus/Grafana 應抓取 Registry 狀態來展示系統資源健康度儀表板。
|
||||
|
||||
## 7. 關聯文檔
|
||||
|
||||
本目錄整合了原有的 Processor 與 Service 架構,並納入新的 Agent 架構:
|
||||
- `PROCESSOR_REGISTRY_ARCHITECTURE.md` - 舊版處理器註冊設計 (已整合)。
|
||||
- `SERVICE_REGISTRY_ARCHITECTURE.md` - 舊版服務註冊設計 (已整合)。
|
||||
- `PROCESSOR_LIFECYCLE.md` - 處理器生命週期 (資源生命週期的子集)。
|
||||
|
||||
---
|
||||
|
||||
## 版本資訊
|
||||
|
||||
- 版本: V1.0
|
||||
- 建立日期: 2026-04-25
|
||||
@@ -0,0 +1,195 @@
|
||||
# API Key Management System Architecture
|
||||
|
||||
## System Overview
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ API Key Management System │
|
||||
├─────────────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
||||
│ │ CLI │ │ HTTP API │ │ Service │ │ External │ │
|
||||
│ │ Layer │────▶│ Layer │────▶│ Layer │────▶│ Services │ │
|
||||
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │
|
||||
│ │ │ │ │ │
|
||||
│ │ │ │ │ │
|
||||
│ ▼ ▼ ▼ ▼ │
|
||||
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ Core Modules │ │
|
||||
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
|
||||
│ │ │ Service │ │Validator│ │ Anomaly │ │Rotation │ │ Cleanup │ │ │
|
||||
│ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │
|
||||
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
|
||||
│ │ │ Webhook │ │Encrypt │ │Blacklist│ │ Report │ │ Error │ │ │
|
||||
│ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │
|
||||
│ └─────────────────────────────────────────────────────────────────────────┘ │
|
||||
│ │ │ │ │
|
||||
│ ▼ ▼ ▼ │
|
||||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
||||
│ │ PostgreSQL │ │ Redis │ │ External │ │
|
||||
│ │ (Storage) │ │ (Cache) │ │ (Gitea/n8n)│ │
|
||||
│ └─────────────┘ └─────────────┘ └─────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Module Dependencies
|
||||
|
||||
```
|
||||
┌──────────────┐
|
||||
│ models.rs │
|
||||
│ (Types) │
|
||||
└──────┬───────┘
|
||||
│
|
||||
┌──────────────────┼──────────────────┐
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
|
||||
│ service.rs │ │ error.rs │ │ validator.rs │
|
||||
│ (Core CRUD) │ │ (Errors) │ │ (Cache+Rate) │
|
||||
└───────┬───────┘ └───────────────┘ └───────────────┘
|
||||
│
|
||||
│ ┌───────────────────────────────┐
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
|
||||
│ anomaly.rs │ │ rotation.rs │ │ blacklist.rs │
|
||||
│ (Detection) │ │ (Rotation) │ │ (IP Block) │
|
||||
└───────────────┘ └───────────────┘ └───────────────┘
|
||||
```
|
||||
|
||||
## Request Flow
|
||||
|
||||
```
|
||||
Client Request
|
||||
│
|
||||
▼
|
||||
┌─────────────┐
|
||||
│ CLI/API │
|
||||
└──────┬──────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────┐ ┌─────────────┐
|
||||
│ Rate Limit │────▶│ IP Blacklist│
|
||||
│ Check │ │ Check │
|
||||
└──────┬──────┘ └──────┬──────┘
|
||||
│ │
|
||||
└─────────┬─────────┘
|
||||
│
|
||||
▼
|
||||
┌───────────────┐
|
||||
│ Hash API Key │
|
||||
└───────┬───────┘
|
||||
│
|
||||
▼
|
||||
┌───────────────┐ ┌───────────────┐
|
||||
│ Cache Lookup │────▶│ PostgreSQL │
|
||||
└───────┬───────┘ │ Lookup │
|
||||
│ └───────┬───────┘
|
||||
│ │
|
||||
└──────────┬──────────┘
|
||||
│
|
||||
▼
|
||||
┌───────────────┐
|
||||
│ Validate │
|
||||
│ (Status, │
|
||||
│ Expiry) │
|
||||
└───────┬───────┘
|
||||
│
|
||||
┌─────────────┼─────────────┐
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌──────────┐ ┌──────────┐ ┌──────────┐
|
||||
│ Valid │ │ Invalid │ │ Error │
|
||||
│ Response│ │ Response │ │ Response │
|
||||
└──────────┘ └──────────┘ └──────────┘
|
||||
```
|
||||
|
||||
## Database Schema
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ PostgreSQL │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─────────────────┐ ┌─────────────────┐ │
|
||||
│ │ api_keys │ │ api_key_audit_ │ │
|
||||
│ ├─────────────────┤ │ log │ │
|
||||
│ │ id │ ├─────────────────┤ │
|
||||
│ │ key_id │─────▶│ id │ │
|
||||
│ │ key_hash │ │ key_id (FK) │ │
|
||||
│ │ name │ │ action │ │
|
||||
│ │ key_type │ │ ip_address │ │
|
||||
│ │ status │ │ details │ │
|
||||
│ │ expires_at │ └─────────────────┘ │
|
||||
│ │ ... │ │
|
||||
│ └─────────────────┘ ┌─────────────────┐ │
|
||||
│ │ api_key_anomalies│ │
|
||||
│ ┌─────────────────┐ ├─────────────────┤ │
|
||||
│ │ gitea_tokens │ │ id │ │
|
||||
│ ├─────────────────┤ │ key_id (FK) │ │
|
||||
│ │ id │ │ anomaly_type │ │
|
||||
│ │ gitea_token_id │ │ severity │ │
|
||||
│ │ token_name │ │ details │ │
|
||||
│ │ scopes │ └─────────────────┘ │
|
||||
│ └─────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────────┐ │
|
||||
│ │ n8n_api_keys │ │
|
||||
│ ├─────────────────┤ │
|
||||
│ │ id │ │
|
||||
│ │ n8n_key_id │ │
|
||||
│ │ label │ │
|
||||
│ └─────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## External Integrations
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ External Integrations │
|
||||
├─────────────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
|
||||
│ │ Gitea │ │ n8n │ │ Webhook │ │
|
||||
│ ├─────────────────┤ ├─────────────────┤ ├─────────────────┤ │
|
||||
│ │ • Create Token │ │ • Create API Key│ │ • Key Created │ │
|
||||
│ │ • List Tokens │ │ • List API Keys │ │ • Key Revoked │ │
|
||||
│ │ • Delete Token │ │ • Delete API Key│ │ • Anomaly │ │
|
||||
│ │ • Verify Token │ │ • Verify │ │ • Rate Limited │ │
|
||||
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Security Layers
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ Security Layers │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ Layer 1: Network │
|
||||
│ ┌─────────────────────────────────────────────────────────┐ │
|
||||
│ │ • IP Blacklist │ │
|
||||
│ │ • Rate Limiting │ │
|
||||
│ └─────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ Layer 2: Authentication │
|
||||
│ ┌─────────────────────────────────────────────────────────┐ │
|
||||
│ │ • API Key Hash (SHA256) │ │
|
||||
│ │ • Constant-time Comparison │ │
|
||||
│ │ • Key Validation (Status, Expiry) │ │
|
||||
│ └─────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ Layer 3: Monitoring │
|
||||
│ ┌─────────────────────────────────────────────────────────┐ │
|
||||
│ │ • Anomaly Detection │ │
|
||||
│ │ • Audit Logging (Encrypted) │ │
|
||||
│ │ • Webhook Notifications │ │
|
||||
│ └─────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
@@ -0,0 +1,461 @@
|
||||
# Momentry API 使用流程
|
||||
|
||||
> **目標**: 從影片上傳到搜尋的完整流程
|
||||
> **適用**: WordPress / n8n 整合
|
||||
> **版本**: V1.0 | **日期**: 2026-03-25
|
||||
|
||||
---
|
||||
|
||||
## 流程總覽
|
||||
|
||||
```
|
||||
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
||||
│ 1. 上傳 │ → │ 2. 註冊 │ → │ 3. 確認 │ → │ 4. 處理 │ → │ 5. 搜尋 │
|
||||
│ SFTPGo │ │ 自動完成 │ │ UUID │ │ 查詢進度 │ │ 測試 │
|
||||
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 1: 上傳影片
|
||||
|
||||
### 方式 A: SFTP 上傳(推薦)
|
||||
|
||||
```bash
|
||||
# 連線資訊
|
||||
主機: sftpgo.momentry.ddns.net
|
||||
連接埠: 2022
|
||||
用戶名: demo
|
||||
密碼: demopassword123
|
||||
```
|
||||
|
||||
使用 FileZilla 或 SFTP 客戶端上傳到 `/` 目錄
|
||||
|
||||
### 方式 B: SFTP 命令列
|
||||
|
||||
```bash
|
||||
sshpass -p "demopassword123" sftp -P 2022 demo@sftpgo.momentry.ddns.net
|
||||
```
|
||||
|
||||
上傳後確認檔案在 SFTPGo 中的位置
|
||||
|
||||
---
|
||||
|
||||
## Step 2: 自動註冊
|
||||
|
||||
上傳後,系統會自動:
|
||||
1. 偵測新檔案
|
||||
2. 計算 UUID(SHA256)
|
||||
3. 建立資料庫記錄
|
||||
|
||||
**無需手動操作**
|
||||
|
||||
---
|
||||
|
||||
## Step 3: 確認註冊成功
|
||||
|
||||
### 查詢所有影片
|
||||
|
||||
```bash
|
||||
curl -s -H "X-API-Key: YOUR_API_KEY" \
|
||||
"https://api.momentry.ddns.net/api/v1/videos" | jq '.videos | length'
|
||||
```
|
||||
|
||||
### 查詢特定檔案
|
||||
|
||||
```bash
|
||||
curl -s -H "X-API-Key: YOUR_API_KEY" \
|
||||
"https://api.momentry.ddns.net/api/v1/videos" | jq '.videos[] | select(.file_name | contains("你的檔案名"))'
|
||||
```
|
||||
|
||||
### 預期回應
|
||||
|
||||
```json
|
||||
{
|
||||
"uuid": "952f5854b9febad1",
|
||||
"file_path": "/Users/accusys/momentry/var/sftpgo/data/demo/你的檔案.mp4",
|
||||
"file_name": "你的檔案.mp4",
|
||||
"duration": 123.45,
|
||||
"width": 1920,
|
||||
"height": 1080
|
||||
}
|
||||
```
|
||||
|
||||
**確認要點**:
|
||||
- ✅ UUID 已產生(16位 hex)
|
||||
- ✅ `file_path` 正確
|
||||
- ✅ `duration` > 0
|
||||
|
||||
---
|
||||
|
||||
## Step 4: 查詢處理進度
|
||||
|
||||
### 取得任務 UUID
|
||||
|
||||
```bash
|
||||
# 從影片資訊取得 job_id
|
||||
curl -s -H "X-API-Key: YOUR_API_KEY" \
|
||||
"https://api.momentry.ddns.net/api/v1/videos" | \
|
||||
jq '.videos[] | select(.file_name == "你的檔案.mp4") | {uuid, job_id}'
|
||||
```
|
||||
|
||||
### 查詢任務狀態
|
||||
|
||||
```bash
|
||||
curl -s -H "X-API-Key: YOUR_API_KEY" \
|
||||
"https://api.momentry.ddns.net/api/v1/jobs/{uuid}"
|
||||
```
|
||||
|
||||
### 任務狀態說明
|
||||
|
||||
| status | 說明 | 動作 |
|
||||
|--------|------|------|
|
||||
| `pending` | 等待處理 | 等待中 |
|
||||
| `processing` | 處理中 | 繼續輪詢 |
|
||||
| `completed` | 已完成 | 可進入 Step 5 |
|
||||
| `failed` | 處理失敗 | 檢查錯誤 |
|
||||
|
||||
### n8n 輪詢範例
|
||||
|
||||
```javascript
|
||||
// n8n Workflow: 檢查處理狀態
|
||||
const jobUuid = $input.item.json.job_uuid;
|
||||
|
||||
const response = await fetch(
|
||||
`https://api.momentry.ddns.net/api/v1/jobs/${jobUuid}`,
|
||||
{
|
||||
headers: {
|
||||
"X-API-Key": "YOUR_API_KEY"
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const job = await response.json();
|
||||
|
||||
// 狀態檢查
|
||||
if (job.status === 'completed') {
|
||||
return [{ json: { done: true, video_uuid: job.video_uuid } }];
|
||||
} else {
|
||||
return [{ json: { done: false, status: job.status } }];
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 5: 搜尋測試
|
||||
|
||||
處理完成後,資料會入庫到向量資料庫,可進行搜尋測試。
|
||||
|
||||
### 測試向量搜尋
|
||||
|
||||
```bash
|
||||
curl -s -X POST "https://api.momentry.ddns.net/api/v1/search" \
|
||||
-H "X-API-Key: YOUR_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"query": "測試關鍵字",
|
||||
"limit": 5
|
||||
}'
|
||||
```
|
||||
|
||||
### 取得分段(Chunk)內容
|
||||
|
||||
搜尋結果會返回影片分段(Chunk),包含可播放的時間軸資訊:
|
||||
|
||||
```json
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"uuid": "39567a0eb16f39fd",
|
||||
"chunk_id": "sentence_1471",
|
||||
"chunk_type": "sentence",
|
||||
"start_time": 5309.08,
|
||||
"end_time": 5311.08,
|
||||
"text": "influenced by a vital way,",
|
||||
"score": 0.68
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Chunk 欄位說明**:
|
||||
| 欄位 | 說明 |
|
||||
|------|------|
|
||||
| `uuid` | 影片 UUID(用於取得影片網址) |
|
||||
| `chunk_id` | 分段 ID |
|
||||
| `chunk_type` | 分段類型(sentence/cut/time/trace/story) |
|
||||
| `start_time` | 開始時間(秒) |
|
||||
| `end_time` | 結束時間(秒) |
|
||||
| `text` | 語音內容文字 |
|
||||
| `score` | 相似度分數(0-1) |
|
||||
|
||||
### 播放分段
|
||||
|
||||
取得 Chunk 後可組合成播放網址:
|
||||
|
||||
```
|
||||
影片網址?start={start_time}&end={end_time}
|
||||
```
|
||||
|
||||
範例:
|
||||
```
|
||||
https://wp.momentry.ddns.net/video.mp4?start=5309.08&end=5311.08
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 完整 n8n Workflow 範例
|
||||
|
||||
```
|
||||
┌──────────────┐
|
||||
│ 觸發 (定時) │
|
||||
└──────┬───────┘
|
||||
▼
|
||||
┌──────────────┐ ┌──────────────┐
|
||||
│ 查詢影片 │────►│ 比對新檔案 │
|
||||
│ /videos │ │ │
|
||||
└──────┬───────┘ └──────────────┘
|
||||
│ │
|
||||
▼ ▼
|
||||
┌──────────────┐ ┌──────────────┐
|
||||
│ 等待處理 │◄────│ 輪詢任務狀態 │
|
||||
│ /jobs/:uuid │ │ /jobs/:uuid │
|
||||
└──────┬───────┘ └──────────────┘
|
||||
│
|
||||
▼ (completed)
|
||||
┌──────────────┐
|
||||
│ 搜尋測試 │
|
||||
│ /search │
|
||||
└──────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 快速參考
|
||||
|
||||
| 步驟 | API | 用途 |
|
||||
|------|-----|------|
|
||||
| 查詢影片 | `GET /api/v1/videos` | 確認上傳成功 |
|
||||
| 查詢任務 | `GET /api/v1/jobs/:uuid` | 查看處理進度 |
|
||||
| 搜尋內容 | `POST /api/v1/search` | 測試搜尋功能 |
|
||||
|
||||
---
|
||||
|
||||
## WordPress PHP 範例
|
||||
|
||||
### 基本設定
|
||||
|
||||
```php
|
||||
<?php
|
||||
class Momentry_API {
|
||||
private const API_URL = 'https://api.momentry.ddns.net';
|
||||
private const API_KEY = 'YOUR_API_KEY';
|
||||
|
||||
public static function request(string $method, string $endpoint, ?array $data = null): array {
|
||||
$url = self::API_URL . $endpoint;
|
||||
|
||||
$args = [
|
||||
'method' => $method,
|
||||
'headers' => [
|
||||
'X-API-Key' => self::API_KEY,
|
||||
'Content-Type' => 'application/json',
|
||||
],
|
||||
'timeout' => 30,
|
||||
];
|
||||
|
||||
if ($data !== null) {
|
||||
$args['body'] = json_encode($data);
|
||||
}
|
||||
|
||||
$response = wp_remote_request($url, $args);
|
||||
|
||||
if (is_wp_error($response)) {
|
||||
throw new Exception($response->get_error_message());
|
||||
}
|
||||
|
||||
return json_decode(wp_remote_retrieve_body($response), true);
|
||||
}
|
||||
|
||||
public static function getVideos(): array {
|
||||
return self::request('GET', '/api/v1/videos');
|
||||
}
|
||||
|
||||
public static function getVideo(string $uuid): array {
|
||||
return self::request('GET', "/api/v1/videos/{$uuid}");
|
||||
}
|
||||
|
||||
public static function getJob(string $uuid): array {
|
||||
return self::request('GET', "/api/v1/jobs/{$uuid}");
|
||||
}
|
||||
|
||||
public static function search(string $query, int $topK = 5): array {
|
||||
return self::request('POST', '/api/v1/search', [
|
||||
'query' => $query,
|
||||
'top_k' => $topK,
|
||||
]);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Step 3: 確認註冊成功
|
||||
|
||||
```php
|
||||
<?php
|
||||
// 查詢所有影片
|
||||
$videos = Momentry_API::getVideos();
|
||||
|
||||
foreach ($videos['videos'] as $video) {
|
||||
echo "UUID: " . $video['uuid'] . "\n";
|
||||
echo "檔案: " . $video['file_name'] . "\n";
|
||||
echo "時長: " . $video['duration'] . " 秒\n";
|
||||
echo "---\n";
|
||||
}
|
||||
|
||||
// 查詢特定影片
|
||||
$video = Momentry_API::getVideo('952f5854b9febad1');
|
||||
print_r($video);
|
||||
```
|
||||
|
||||
### Step 4: 查詢處理進度
|
||||
|
||||
```php
|
||||
<?php
|
||||
// 取得任務狀態
|
||||
$job = Momentry_API::getJob('9760d0820f0cf9a7');
|
||||
|
||||
switch ($job['status']) {
|
||||
case 'pending':
|
||||
echo "等待處理中...\n";
|
||||
break;
|
||||
case 'processing':
|
||||
echo "處理中: " . $job['progress'] . "%\n";
|
||||
break;
|
||||
case 'completed':
|
||||
echo "處理完成!\n";
|
||||
break;
|
||||
case 'failed':
|
||||
echo "處理失敗: " . ($job['error'] ?? '未知錯誤') . "\n";
|
||||
break;
|
||||
}
|
||||
```
|
||||
|
||||
### Step 5: 搜尋內容並取得 Chunk
|
||||
|
||||
```php
|
||||
<?php
|
||||
// 搜尋相關片段
|
||||
$results = Momentry_API::search('測試關鍵字', 5);
|
||||
|
||||
foreach ($results['results'] as $result) {
|
||||
echo "影片 UUID: " . $result['uuid'] . "\n";
|
||||
echo "Chunk ID: " . $result['chunk_id'] . "\n";
|
||||
echo "類型: " . $result['chunk_type'] . "\n";
|
||||
echo "開始: " . $result['start_time'] . "s\n";
|
||||
echo "結束: " . $result['end_time'] . "s\n";
|
||||
echo "內容: " . ($result['text'] ?? '') . "\n";
|
||||
echo "相似度: " . $result['score'] . "\n";
|
||||
echo "---\n";
|
||||
}
|
||||
```
|
||||
|
||||
### WordPress Shortcode 範例(可點擊播放)
|
||||
|
||||
```php
|
||||
<?php
|
||||
// 在 functions.php 中加入
|
||||
add_shortcode('momentry_search', function($atts) {
|
||||
$atts = shortcode_atts([
|
||||
'query' => '',
|
||||
'limit' => 10,
|
||||
], $atts);
|
||||
|
||||
if (empty($atts['query'])) {
|
||||
return '<p>請輸入搜尋關鍵字</p>';
|
||||
}
|
||||
|
||||
try {
|
||||
$results = Momentry_API::search($atts['query'], $atts['limit']);
|
||||
|
||||
if (empty($results['results'])) {
|
||||
return '<p>找不到相關結果</p>';
|
||||
}
|
||||
|
||||
$html = '<div class="momentry-results">';
|
||||
$html .= '<h3>搜尋結果: ' . esc_html($atts['query']) . '</h3>';
|
||||
$html .= '<ul>';
|
||||
|
||||
foreach ($results['results'] as $result) {
|
||||
$video_uuid = $result['uuid'];
|
||||
$start = $result['start_time'] ?? 0;
|
||||
$end = $result['end_time'] ?? 0;
|
||||
$text = $result['text'] ?? '無文字描述';
|
||||
|
||||
$html .= '<li>';
|
||||
$html .= '<a href="/player?uuid=' . esc_attr($video_uuid) .
|
||||
'&start=' . esc_attr($start) .
|
||||
'&end=' . esc_attr($end) . '">';
|
||||
$html .= '播放 ' . $start . 's - ' . $end . 's';
|
||||
$html .= '</a>';
|
||||
$html .= '<br>';
|
||||
$html .= '<small>相似度: ' . round($result['score'] * 100) . '%</small>';
|
||||
$html .= '<br>';
|
||||
$html .= esc_html($text);
|
||||
$html .= '</li>';
|
||||
}
|
||||
|
||||
$html .= '</ul></div>';
|
||||
return $html;
|
||||
|
||||
} catch (Exception $e) {
|
||||
return '<p>搜尋服務暫時無法使用</p>';
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
**使用方式**:
|
||||
```html
|
||||
[momentry_search query="關鍵字" limit="5"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 完整 n8n Workflow 範例
|
||||
|
||||
```
|
||||
┌──────────────┐
|
||||
│ 觸發 (定時) │
|
||||
└──────┬───────┘
|
||||
▼
|
||||
┌──────────────┐ ┌──────────────┐
|
||||
│ 查詢影片 │────►│ 比對新檔案 │
|
||||
│ /videos │ │ │
|
||||
└──────┬───────┘ └──────────────┘
|
||||
│ │
|
||||
▼ ▼
|
||||
┌──────────────┐ ┌──────────────┐
|
||||
│ 等待處理 │◄────│ 輪詢任務狀態 │
|
||||
│ /jobs/:uuid │ │ /jobs/:uuid │
|
||||
└──────┬───────┘ └──────────────┘
|
||||
│
|
||||
▼ (completed)
|
||||
┌──────────────┐
|
||||
│ 搜尋測試 │
|
||||
│ /search │
|
||||
└──────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**注意**:
|
||||
- 處理時間視影片長度而定(1分鐘影片約需 2-5 分鐘處理)
|
||||
- 大量影片時建議分批上傳
|
||||
|
||||
---
|
||||
|
||||
## 附錄:版本歷史
|
||||
|
||||
| 版本 | 日期 | 內容 | 操作人 |
|
||||
|------|------|------|--------|
|
||||
| V1.0 | 2026-03-25 | 初版建立 | OpenCode |
|
||||
| V1.1 | 2026-03-25 | 新增 Chunk 取得與播放說明、Shortcode 範例 | OpenCode |
|
||||
| V1.2 | 2026-03-25 | 修正 SFTPGo 主機名稱為 sftpgo.momentry.ddns.net | OpenCode |
|
||||
@@ -0,0 +1,331 @@
|
||||
# 架構優化待評估事項
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 建立者 | OpenCode |
|
||||
| 建立時間 | 2026-03-21 |
|
||||
| 文件版本 | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## 版本歷史
|
||||
|
||||
| 版本 | 日期 | 目的 | 操作人 |
|
||||
|------|------|------|--------|
|
||||
| V1.0 | 2026-03-21 | 創建文件 | OpenCode |
|
||||
| V1.1 | 2026-03-22 | 新增 TigerGraph/GraphRAG 說故事評估 | OpenCode |
|
||||
|
||||
---
|
||||
|
||||
## 架構優化項目
|
||||
|
||||
### 1. PostgreSQL → Redis 故障轉移
|
||||
|
||||
**說明**: 當 PostgreSQL 不可用時,降級到 Redis 作為臨時存儲
|
||||
|
||||
**複雜度**: 中
|
||||
|
||||
**影響範圍**:
|
||||
- `src/core/db/postgres_db.rs`
|
||||
- `src/core/db/redis_client.rs`
|
||||
|
||||
**風險**:
|
||||
- 數據一致性問題
|
||||
- 需要定義轉移策略
|
||||
|
||||
**優先級**: 待評估
|
||||
|
||||
---
|
||||
|
||||
### 2. 連接池監控
|
||||
|
||||
**說明**: 添加 PostgreSQL 和 Redis 連接池指標到 Prometheus
|
||||
|
||||
**複雜度**: 低
|
||||
|
||||
**影響範圍**:
|
||||
- `src/core/db/postgres_db.rs`
|
||||
- `src/core/db/redis_client.rs`
|
||||
- `src/api/` (新增 metrics endpoint)
|
||||
|
||||
**風險**: 低
|
||||
|
||||
**優先級**: 待評估
|
||||
|
||||
---
|
||||
|
||||
### 3. Processor 重試機制
|
||||
|
||||
**說明**: 當 processor 失敗時自動重試
|
||||
|
||||
**複雜度**: 中
|
||||
|
||||
**影響範圍**:
|
||||
- `src/core/processor/executor.rs` (新增 `run_with_retry` 方法)
|
||||
- `src/core/processor/mod.rs` (導出 `RetryConfig`)
|
||||
|
||||
**風險**:
|
||||
- 無限重試風險 → 已通過 `max_attempts` 控制
|
||||
- 需要指數退避 → 已實現
|
||||
|
||||
**優先級**: ✅ 已完成 (2026-03-21)
|
||||
|
||||
**實作內容**:
|
||||
- `RetryConfig` 結構體 (可配置重試次數、初始延遲、最大延遲、退避倍數)
|
||||
- `run_with_retry()` 方法 (自動重試 + 指數退避)
|
||||
- 單元測試覆蓋
|
||||
|
||||
**使用範例**:
|
||||
```rust
|
||||
use crate::core::processor::{PythonExecutor, RetryConfig};
|
||||
|
||||
let executor = PythonExecutor::new()?;
|
||||
let config = RetryConfig::new(3).with_delay(1000).with_max_delay(30000);
|
||||
|
||||
executor.run_with_retry(
|
||||
"asr_processor.py",
|
||||
&["--input", "/path/to/video"],
|
||||
Some(&uuid),
|
||||
"asr",
|
||||
Some(Duration::from_secs(3600)),
|
||||
Some(config),
|
||||
).await?;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. PyO3 整合
|
||||
|
||||
**說明**: Python/Rust 直接調用,移除子進程調用
|
||||
|
||||
**複雜度**: 高
|
||||
|
||||
**影響範圍**:
|
||||
- `src/core/processor/executor.rs` (重寫)
|
||||
- Python 模組 (修改為可直接 import)
|
||||
|
||||
**風險**:
|
||||
- Python GIL 問題
|
||||
- 依賴版本兼容性
|
||||
- 需要大量重寫
|
||||
|
||||
**優先級**: 低 (長期目標)
|
||||
|
||||
---
|
||||
|
||||
### 5. HTTP 健康端點
|
||||
|
||||
**說明**: 添加 `/health` API 用於外部監控
|
||||
|
||||
**複雜度**: 低
|
||||
|
||||
**影響範圍**:
|
||||
- `src/api/server.rs` (新增路由)
|
||||
|
||||
**風險**: 低
|
||||
|
||||
**優先級**: ✅ 已完成 (2026-03-21)
|
||||
|
||||
**實作內容**:
|
||||
- `GET /health` - 基本健康檢查 (status, version, uptime)
|
||||
- `GET /health/detailed` - 詳細健康檢查 (PostgreSQL, Redis, Qdrant 狀態和延遲)
|
||||
|
||||
---
|
||||
|
||||
### 6. Gitea Actions CI/CD
|
||||
|
||||
**說明**: 配置 Gitea Actions 自動化 CI/CD,在合併前執行檢查
|
||||
|
||||
**複雜度**: 中
|
||||
|
||||
**影響範圍**:
|
||||
- `.gitea/workflows/` (新增 workflow 文件)
|
||||
|
||||
**優點**:
|
||||
- 強制執行檢查,無法跳過
|
||||
- 跨設備一致
|
||||
- PR 審查前自動檢查
|
||||
|
||||
**風險**: 低
|
||||
|
||||
**優先級**: 待評估
|
||||
|
||||
---
|
||||
|
||||
### 7. Commit Message Lint
|
||||
|
||||
**說明**: 規範化提交訊息格式 (Conventional Commits)
|
||||
|
||||
**複雜度**: 低
|
||||
|
||||
**影響範圍**:
|
||||
- `.git/hooks/commit-msg` (新增 hook)
|
||||
- `~/dotfiles/hooks/commit-msg`
|
||||
|
||||
**風險**: 低
|
||||
|
||||
**優先級**: ✅ 已完成 (2026-03-21)
|
||||
|
||||
**實作內容**:
|
||||
- 驗證格式: `<type>(<scope>): <description>`
|
||||
- 有效類型: feat, fix, docs, style, refactor, test, chore, perf, ci, build, revert
|
||||
- 警告: 第一行超過 72 字符
|
||||
|
||||
**範例**:
|
||||
```
|
||||
feat(api): add health check endpoint
|
||||
fix(db): resolve connection pool issue
|
||||
docs: update README
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 8. 自動化安裝腳本
|
||||
|
||||
**說明**: 創建腳本一次安裝所有開發工具
|
||||
|
||||
**複雜度**: 低
|
||||
|
||||
**影響範圍**:
|
||||
- `scripts/install-dev-tools.sh` (新增)
|
||||
|
||||
**風險**: 低
|
||||
|
||||
**優先級**: 待評估
|
||||
|
||||
---
|
||||
|
||||
## 評估標準
|
||||
|
||||
| 標準 | 說明 |
|
||||
|------|------|
|
||||
| 業務價值 | 對用戶有何幫助 |
|
||||
| 技術風險 | 實現難度和潛在問題 |
|
||||
| 維護成本 | 未來維護負擔 |
|
||||
| 依賴性 | 對其他系統的影響 |
|
||||
|
||||
---
|
||||
|
||||
## 評估記錄
|
||||
|
||||
| 項目 | 評估日期 | 決策 | 原因 |
|
||||
|------|----------|------|------|
|
||||
| PostgreSQL → Redis 故障轉移 | 待評估 | - | - |
|
||||
| 連接池監控 | 待評估 | - | - |
|
||||
| Processor 重試機制 | 2026-03-21 | 已完成 | - |
|
||||
| PyO3 整合 | 待評估 | - | - |
|
||||
| HTTP 健康端點 | 2026-03-21 | 已完成 | - |
|
||||
| Gitea Actions CI/CD | 待評估 | - | - |
|
||||
| Commit Message Lint | 2026-03-21 | 已完成 | - |
|
||||
| 自動化安裝腳本 | 待評估 | - | - |
|
||||
|
||||
---
|
||||
|
||||
## 9. TigerGraph / Knowledge Graph 圖譜說故事
|
||||
|
||||
**說明**: 使用知識圖譜 (Knowledge Graph) 增強視頻敘事 (Storytelling) 和 RAG 檢索
|
||||
|
||||
**複雜度**: 高
|
||||
|
||||
**研究來源**:
|
||||
- [TigerGraph Agentic GraphRAG](https://www.tigergraph.com/blog/agentic-graphrag-gives-ai-a-playbook-for-smarter-retrieval/) (2025-12-15)
|
||||
- [TigerGraph GraphRAG GitHub](https://github.com/tigergraph/graphrag) (v1.2.0, 2026-03-11)
|
||||
- [GraphRAG in 2026: Practitioner's Guide](https://medium.com/graph-praxis/graph-rag-in-2026-a-practitioners-guide-to-what-actually-works-dca4962e7517) (2026-02-22)
|
||||
- [GraphRAG Complete Guide](https://medium.com/@brian-curry-research/graphrag-the-complete-guide-to-graph-powered-retrieval-augmented-generation-eeb58a6bb4d1) (2026-02-11)
|
||||
|
||||
### 核心概念
|
||||
|
||||
| 概念 | 說明 |
|
||||
|------|------|
|
||||
| **GraphRAG** | 結合知識圖譜與 RAG,比傳統向量檢索更智能 |
|
||||
| **知識圖譜** | 實體 (Entity) + 關係 (Relationship) 的結構化表示 |
|
||||
| **多跳推理** | Multi-hop traversal,可連接多個相關節點 |
|
||||
| **混合檢索** | Graph traversal + Vector similarity 結合 |
|
||||
|
||||
### 對 Momentry 的潛在應用
|
||||
|
||||
```
|
||||
視頻場景 → 實體識別 → 關係建立 → 故事圖譜
|
||||
↓ ↓ ↓ ↓
|
||||
CUT [人物, 物品, 動作] [誰做了什麼, 什麼導致什麼] [敘事鏈]
|
||||
```
|
||||
|
||||
**1. 敘事圖譜構建 (Narrative Graph)**
|
||||
- 從 Story/Chunks 模組提取實體
|
||||
- 建立場景之間的因果關係
|
||||
- 追蹤角色互動和情節發展
|
||||
|
||||
**2. 故事檢索增強**
|
||||
```python
|
||||
# 現有: Parent-child chunks
|
||||
parent_chunk: "場景描述"
|
||||
child_chunks: [詳細內容]
|
||||
|
||||
# 加入圖譜:
|
||||
場景A --led_to--> 場景B
|
||||
角色X --interacted_with--> 角色Y
|
||||
主題Y --related_to--> 主題Z
|
||||
```
|
||||
|
||||
**3. 查詢模式**
|
||||
|
||||
| 查詢類型 | 傳統 RAG | GraphRAG |
|
||||
|----------|----------|----------|
|
||||
| 事實查找 | ✅ "這個場景在說什麼" | ✅ |
|
||||
| 主題推理 | ❌ "這個視頻的主要情節" | ✅ Global search |
|
||||
| 多跳關係 | ❌ | ✅ "A導致B,B導致C" |
|
||||
| 可解釋性 | ❌ | ✅ 關係路徑可追溯 |
|
||||
|
||||
### 實作方案
|
||||
|
||||
**方案 A: TigerGraph Cloud (推薦)**
|
||||
- ✅ 原生 Graph + Vector 混合查詢
|
||||
- ✅ GraphRAG 官方支援
|
||||
- ✅ 200GB 免費額度
|
||||
- ❌ 雲端依賴,延遲敏感場景需考慮
|
||||
|
||||
**方案 B: Neo4j + Qdrant**
|
||||
- ✅ 成熟開源生態
|
||||
- ✅ LangChain/LlamaIndex 整合
|
||||
- ❌ 需要維護兩個系統
|
||||
|
||||
**方案 C: 自建混合架構**
|
||||
- PostgreSQL + Neo4j (或Typesense)
|
||||
- 利用現有 BM25 + 向量檢索基礎
|
||||
- ❌ 開發成本高
|
||||
|
||||
### 技術棧整合建議
|
||||
|
||||
```rust
|
||||
// 現有架構
|
||||
Vector Search (Qdrant) ← BM25 (PostgreSQL)
|
||||
|
||||
// 加入 GraphRAG
|
||||
Knowledge Graph (TigerGraph/Neo4j)
|
||||
↓
|
||||
混合檢索 ← Vector + Graph traversal
|
||||
```
|
||||
|
||||
### 優先級: 待評估
|
||||
|
||||
**考慮因素**:
|
||||
- 用戶是否需要複雜的故事情節查詢?
|
||||
- 實體識別 (NER) 成本是否可以接受?
|
||||
- 與現有 BM25 + Vector 混合搜索的比較優勢?
|
||||
|
||||
---
|
||||
|
||||
## 10. LazyGraphRAG / FastGraphRAG 成本優化
|
||||
|
||||
**說明**: GraphRAG 索引成本高昂,LazyGraphRAG 推遲圖譜構建到查詢時
|
||||
|
||||
**來源**: [GraphRAG in 2026](https://medium.com/graph-praxis/graph-rag-in-2026-a-practitioners-guide-to-what-actually-works-dca4962e7517)
|
||||
|
||||
**Microsoft GraphRAG 問題**: $33K 索引大型數據集
|
||||
|
||||
**替代方案**:
|
||||
- **LazyGraphRAG**: 按需構建,查詢時再建立子圖
|
||||
- **FastGraphRAG**: 優化索引管道,10-90% 成本節省
|
||||
- **HippoRAG**: 使用 Personalised PageRank 優化遍歷
|
||||
|
||||
**優先級**: 待評估 (作為 GraphRAG 的一部分)
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user