docs: update docs_v1.0/ documentation

- Fix markdown lint issues (MD030, MD047, MD051, MD028, MD005)
- Update AI agents, architecture, implementation docs
- Add new identity, face recognition, and API documentation
- Remove deprecated face/person API guides
This commit is contained in:
Warren
2026-04-30 15:10:41 +08:00
parent 8f05a7c188
commit 4d75b2e251
185 changed files with 21071 additions and 1605 deletions

View File

@@ -446,4 +446,4 @@ def test_search_documents():
**版本**: V1.0
**適用對象**: 所有 AI Agent 和自動化工具
**注意**: 本文檔應與實際模板文件結合使用,定期更新以反映系統變更。
**注意**: 本文檔應與實際模板文件結合使用,定期更新以反映系統變更。

View File

@@ -46,7 +46,7 @@ ai_query_hints:
## 目錄
1. [已實作端點](#1-已實作端點)
2. [API Key 管理](#2-api-key-管理-規劃中)
2. [API Key 管理](#2-api-key-管理)
3. [影片管理](#3-影片管理)
4. [查詢與搜索](#4-查詢與搜索)
5. [系統狀態](#5-系統狀態)

View File

@@ -196,7 +196,7 @@ n8n 專用搜尋(包含完整影片檔案路徑 file_path
```json
{
"uuid": "9760d0820f0cf9a7",
"video_uuid": "5dea6618a606e7c7",
"file_uuid": "5dea6618a606e7c7",
"status": "completed",
"progress": 100,
"created_at": "2026-03-25T10:00:00Z",

View File

@@ -368,4 +368,4 @@ Q - 退出
**更新日期**: 2026-04-02
**版本**: 2.0.0
**作者**: Momentry Team
**作者**: Momentry Team

View File

@@ -394,4 +394,4 @@ brew install ffmpeg
**創建日期**: 2026-04-02
**版本**: 1.1.0
**作者**: Momentry Team
**作者**: Momentry Team

View File

@@ -219,4 +219,4 @@ A: 兩種模式都按實際時長播放。說話人演示有 2 秒暫停。
**更新日期**: 2026-04-02
**版本**: 2.1.0
**作者**: Momentry Team
**作者**: Momentry Team

View File

@@ -219,4 +219,4 @@ A:
**更新日期**: 2026-04-02
**版本**: 1.2.0
**作者**: Momentry Team
**作者**: Momentry Team

View File

@@ -0,0 +1,199 @@
# Dev 3003 改造記錄
| 項目 | 內容 |
|------|------|
| 建立者 | Warren |
| 建立時間 | 2026-04-30 |
| 文件版本 | V1.0 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-04-30 | Dev 3003 全面改造 | Warren | OpenCode |
---
## 1. 改造目標
- 將 Dev 3003 (Playground) 與 Public 3002 完全隔離
- 統一術語:`video_uuid``file_uuid`
- 修復資料庫結構問題probe_json 類型、timestamp 類型)
- Python 腳本和 output 目錄隔離
---
## 2. PostgreSQL Schema 修復
### 2.1 probe_json 類型修復
**問題**: `dev.videos.probe_json` 類型為 `TEXT`,但 Rust 期望 `JSONB`
**修復**:
```sql
ALTER TABLE dev.videos ALTER COLUMN probe_json TYPE jsonb USING probe_json::jsonb;
```
### 2.2 video_uuid → file_uuid 重命名 (10 張表)
| 表 | 狀態 |
|----|------|
| `dev.backup_registry` | ✅ 已重命名 |
| `dev.castings` | ✅ 已重命名 |
| `dev.characters` | ✅ 已重命名 |
| `dev.face_identities` | ✅ 已重命名 |
| `dev.face_recognition_results` | ✅ 已重命名 |
| `dev.file_lifecycle` | ✅ 已重命名 |
| `dev.file_registry` | ✅ 已重命名 |
| `dev.processor_results` | ✅ 已重命名 |
| `dev.video_events` | ✅ 已重命名 |
| `dev.video_identities` | ✅ 已重命名 |
**修復 SQL**:
```sql
ALTER TABLE dev.backup_registry RENAME COLUMN video_uuid TO file_uuid;
ALTER TABLE dev.castings RENAME COLUMN video_uuid TO file_uuid;
ALTER TABLE dev.characters RENAME COLUMN video_uuid TO file_uuid;
ALTER TABLE dev.face_identities RENAME COLUMN video_uuid TO file_uuid;
ALTER TABLE dev.face_recognition_results RENAME COLUMN video_uuid TO file_uuid;
ALTER TABLE dev.file_lifecycle RENAME COLUMN video_uuid TO file_uuid;
ALTER TABLE dev.file_registry RENAME COLUMN video_uuid TO file_uuid;
ALTER TABLE dev.processor_results RENAME COLUMN video_uuid TO file_uuid;
ALTER TABLE dev.video_events RENAME COLUMN video_uuid TO file_uuid;
ALTER TABLE dev.video_identities RENAME COLUMN video_uuid TO file_uuid;
-- 重建 constraint
ALTER TABLE dev.face_recognition_results
DROP CONSTRAINT face_recognition_results_video_uuid_key;
ALTER TABLE dev.face_recognition_results
ADD CONSTRAINT face_recognition_results_file_uuid_key UNIQUE (file_uuid);
```
### 2.3 timestamp 類型修復
**問題**: `dev.videos.created_at`, `updated_at`, `registered_at``TIMESTAMP` (without time zone),但 Rust 期望 `TIMESTAMPTZ`
**修復**:
```sql
ALTER TABLE dev.videos ALTER COLUMN created_at TYPE timestamptz USING created_at AT TIME ZONE 'UTC';
ALTER TABLE dev.videos ALTER COLUMN updated_at TYPE timestamptz USING updated_at AT TIME ZONE 'UTC';
ALTER TABLE dev.videos ALTER COLUMN registered_at TYPE timestamptz USING registered_at AT TIME ZONE 'UTC';
```
---
## 3. Rust 代碼修改
### 3.1 `src/api/server.rs`
| 行號 | 修改前 | 修改後 |
|------|--------|--------|
| 3982 | `DELETE FROM {} WHERE video_uuid = $1` | `DELETE FROM {} WHERE file_uuid = $1` |
### 3.2 `src/api/face_recognition.rs`
| 行號 | 修改前 | 修改後 |
|------|--------|--------|
| 721 | `WHERE video_uuid = $1` | `WHERE file_uuid = $1` |
| 764 | `"video_uuid": file_uuid` | `"file_uuid": file_uuid` |
| 786 | `video_uuid: &str` (參數) | `file_uuid: &str` (參數) |
| 807 | `ON CONFLICT (video_uuid)` | `ON CONFLICT (file_uuid)` |
| 818 | `.bind(video_uuid)` | `.bind(file_uuid)` |
| 877 | `.bind(video_uuid)` | `.bind(file_uuid)` |
| 926 | `.bind(video_uuid)` | `.bind(file_uuid)` |
### 3.3 測試修復
| 檔案 | 修改 |
|------|------|
| `src/core/db/postgres_db.rs:4550` | 添加 `file_type: None``VideoRecord` 測試 |
---
## 4. Python 腳本隔離
### 4.1 更新預設 DATABASE_URL (7 個腳本)
| 腳本 | 修改 |
|------|------|
| `scripts/clip_logo_integration.py` | `?options=-c%20search_path=dev` |
| `scripts/match_face_with_pose_filtering.py` | `?options=-c%20search_path=dev` |
| `scripts/select_face_reference_vectors_v2.py` | `?options=-c%20search_path=dev` |
| `scripts/match_face_identity.py` | `?options=-c%20search_path=dev` |
| `scripts/tmdb_identity_integration.py` | `?options=-c%20search_path=dev` |
| `scripts/select_face_reference_vectors.py` | `?options=-c%20search_path=dev` |
| `scripts/test_identity_db.py` | `?options=-c%20search_path=dev` |
### 4.2 output 目錄隔離
| 腳本 | 修改 |
|------|------|
| `scripts/identity_agent.py` | 預設 output 改為 `/Users/accusys/momentry/output_dev` |
### 4.3 環境變數配置
`.env.development` 已配置:
```bash
MOMENTRY_OUTPUT_DIR=/Users/accusys/momentry/output_dev
DATABASE_SCHEMA=dev
MONGODB_DATABASE=momentry_dev
QDRANT_COLLECTION=momentry_dev_rule1
REDIS_PREFIX=momentry_dev:
```
---
## 5. 隔離狀態總覽
| 資源 | 配置 | 狀態 |
|------|------|------|
| PostgreSQL | `DATABASE_SCHEMA=dev` | ✅ 隔離 |
| MongoDB | `momentry_dev` | ✅ 隔離 |
| Qdrant | `momentry_dev_rule1` | ✅ 隔離 |
| Redis | `momentry_dev:` | ✅ 隔離 |
| Output Dir | `/Users/accusys/momentry/output_dev` | ✅ 隔離 |
---
## 6. 驗證結果
### 6.1 Build 驗證
- `cargo build --bins`: ✅ 成功
- `cargo clippy --lib`: ✅ 通過 (119 warnings, 0 errors)
- `cargo test --lib`: ✅ 178 tests passed
### 6.2 API 驗證
- `GET /api/v1/files`: ✅ 返回 200 (之前返回 500)
- 測試數據: 6 個檔案已註冊
---
## 7. 待辦事項
| 任務 | 優先級 | 狀態 |
|------|--------|------|
| 設計 Dev 3003 API 結構 (v1.0 aligned) | Medium | ⬜ |
| 實作 `GET /api/v1/files/{uuid}/identities` | Medium | ⬜ |
| 實作 `GET /api/v1/identities/{uuid}` | Medium | ⬜ |
| 實作 `GET /api/v1/identities/{uuid}/files` | Medium | ⬜ |
| 實作 AI Agent API (clustering/merge suggestions) | Medium | ⬜ |
---
## 8. 注意事項
### 8.1 Public 3002 不受影響
- 所有修改僅限於 `dev` schema
- `public` schema 保持原狀
- Rust 代碼修改適用於兩者,但 SQL 中的 column name 已統一為 `file_uuid`
### 8.2 Python 腳本注意事項
- 仍有其他 Python 腳本使用 `DB_CONFIG``POSTGRES_CONFIG` 等模式
- 這些腳本需單獨檢查和更新
- 建議逐步遷移至使用環境變數
### 8.3 已知限制
- Player module 仍使用 `video_uuid` 變數名(內部使用,不影響 API
- 部分 Python 腳本的 output 路徑仍需手動指定

View File

@@ -2,13 +2,13 @@
document_type: "design"
title: "File / Identity API 架構設計"
service: "MOMENTRY_CORE"
date: "2026-04-25"
date: "2026-04-28"
status: "active"
current_state: "finalized"
owner: "Warren"
created_by: "OpenCode"
created_at: "2026-04-25"
version: "V1.1"
version: "V1.2"
tags:
- "api"
- "file"
@@ -16,6 +16,9 @@ tags:
- "face"
- "candidate"
- "pre_chunk"
- "reference_data"
- "identity_embedding"
- "clip"
related_documents:
- "DOCS_STANDARD.md"
- "AI_AGENT_DOCUMENTATION_GUIDE.md"
@@ -24,11 +27,14 @@ related_documents:
- "_deprecated/IDENTITY_SYSTEM_DESIGN.md"
- "PROCESSORS/_CORE/RULE_SPECIFICATION.md"
- "REFERENCE/API_ERROR_CODES.md"
- "IDENTITY_REFERENCE_VECTOR_DESIGN.md"
ai_query_hints:
- "查詢 File/Identity 核心架構設計"
- "查詢 People API 端點定義"
- "查詢 Candidate 狀態轉換流程"
- "查詢資料庫 Schema 定義 (含 pre_chunks)"
- "查詢 reference_data JSONB 結構"
- "查詢 identity_embedding (CLIP ViT-L/14)"
---
# File / Identity API 架構設計文件
@@ -45,6 +51,7 @@ ai_query_hints:
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.2 | 2026-04-28 | **重大更新**: 添加 face_embedding(512), voice_embedding(192), identity_embedding(768), reference_data JSONB 結構詳解, identity_type 扩展 (logo/symbol/sound/animal/environmental) | OpenCode | OpenCode |
| V1.1 | 2026-04-25 | **重大更新**: 移除 faces 表 (方案 A), 新增 pre_chunks 表, 統一命名為 file_uuid, 更新 Response 格式 | OpenCode | OpenCode |
| V1.0 | 2026-04-25 | 創建 File/Identity API 架構設計 | OpenCode | OpenCode |
@@ -91,9 +98,9 @@ Identity ──[出現在]──→ File (通過 Pre-chunk 或 file_identities
```
**Candidate 狀態轉換**:
* **定義**: Candidate 不是獨立表格,而是 `pre_chunks``identity_id IS NULL` 的 Face 數據。
* **確認**: 更新 `identity_id` → 成為已確認身份。
* **剔除**: 將 `identity_id` 設回 `NULL` → 重新成為 Candidate。
* **定義**: Candidate 不是獨立表格,而是 `pre_chunks``identity_id IS NULL` 的 Face 數據。
* **確認**: 更新 `identity_id` → 成為已確認身份。
* **剔除**: 將 `identity_id` 設回 `NULL` → 重新成為 Candidate。
---
@@ -132,8 +139,8 @@ CREATE TABLE files (
### Pre-chunks 表 (處理器原始數據)
**說明**: 所有 Processor (YOLO, Face, ASR...) 產出的數據皆存於此表。
* **Video File**: 最小單位為 **Frame (幀)** (`coordinate_type = 'frame'`)。
* **Text File**: 最小單位為 Word/Sentence (`coordinate_type = 'text'`)。
* **Video File**: 最小單位為 **Frame (幀)** (`coordinate_type = 'frame'`)。
* **Text File**: 最小單位為 Word/Sentence (`coordinate_type = 'text'`)。
| 欄位 | 類型 | 必填 | 說明 |
|------|------|------|------|
@@ -174,10 +181,13 @@ CREATE INDEX idx_pre_chunks_identity ON pre_chunks(identity_id) WHERE identity_i
|------|------|------|------|
| identity_id | UUID | Yes | 唯一識別 (自動產生) |
| name | TEXT | Yes | 顯示名稱 |
| identity_type | VARCHAR(30) | Yes | people, brand, object, concept, logo... |
| identity_type | VARCHAR(30) | Yes | people, brand, object, concept, logo, symbol, sound, animal, environmental... |
| source | VARCHAR(20) | No | manual, tmdb, agent_suggested, ai_detection |
| status | VARCHAR(20) | No | pending, confirmed, skipped |
| reference_data | JSONB | No | 參考數據 (face_embedding, voice_embedding, image_url...) |
| face_embedding | VECTOR(512) | No | 參考臉向量 (ArcFace) - 用於人臉比對 |
| voice_embedding | VECTOR(192) | No | 參考聲紋向量 (ECAPA-TDNN) - 用於聲音比對 |
| identity_embedding | VECTOR(768) | No | 身份向量 (CLIP ViT-L/14) - 用於 logo/symbol/object 搜索 |
| reference_data | JSONB | No | 1對多參考向量存儲 (多角度/多場景/多版本 embedding) |
| metadata | JSONB | No | 擴展屬性 |
| created_at | TIMESTAMPTZ | Yes | 建立時間 |
| updated_at | TIMESTAMPTZ | Yes | 更新時間 |
@@ -189,13 +199,115 @@ CREATE TABLE identities (
identity_type VARCHAR(30) NOT NULL,
source VARCHAR(20) DEFAULT 'manual',
status VARCHAR(20) DEFAULT 'pending',
reference_data JSONB DEFAULT '{}',
-- 參考向量 (用於自動比對)
face_embedding VECTOR(512), -- 參考臉向量 (ArcFace)
voice_embedding VECTOR(192), -- 參考聲紋向量 (ECAPA-TDNN)
identity_embedding VECTOR(768), -- 身份向量 (CLIP ViT-L/14)
-- 1對多參考向量存儲
reference_data JSONB DEFAULT '{}', -- 多角度/多場景/多版本 embedding
metadata JSONB DEFAULT '{}',
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
```
#### reference_data JSONB 結構詳解
`reference_data` 用於存儲同一 Identity 的多個參考向量,支援 1對多匹配提高識別鲁棒性。
**完整結構範例**:
```json
{
"face_embeddings": [
{
"embedding": [0.1, 0.2, ...],
"source": "tmdb_images",
"image_url": "https://image.tmdb.org/t/p/original/xxx.jpg",
"angle": "frontal",
"quality_score": 0.95,
"created_at": "2026-04-28T10:00:00Z"
},
{
"embedding": [0.3, 0.4, ...],
"source": "tmdb_images",
"image_url": "https://image.tmdb.org/t/p/original/yyy.jpg",
"angle": "profile_left",
"quality_score": 0.88,
"created_at": "2026-04-28T10:05:00Z"
}
],
"voice_embeddings": [
{
"embedding": [0.1, 0.2, ...],
"source": "video_segment",
"file_uuid": "vid_001",
"timestamp_start": 120.5,
"timestamp_end": 135.2,
"quality_score": 0.88,
"created_at": "2026-04-28T11:00:00Z"
}
],
"identity_embeddings": [
{
"embedding": [0.1, 0.2, ...],
"source": "logo_image",
"image_url": "https://www.accusys.com.tw/wp-content/uploads/2023/03/Accusys-Orange-2017.png",
"context": "brand_logo",
"created_at": "2026-04-28T12:00:00Z"
}
],
"sound_embeddings": [
{
"embedding": [0.1, 0.2, ...],
"source": "audio_segment",
"file_uuid": "vid_001",
"timestamp_start": 10.0,
"timestamp_end": 15.0,
"sound_type": "animal_dog_bark",
"created_at": "2026-04-28T13:00:00Z"
}
],
"image_urls": [
"https://image.tmdb.org/t/p/original/xxx.jpg",
"https://www.accusys.com.tw/wp-content/uploads/2023/03/Accusys-Orange-2017.png"
]
}
```
**欄位說明**:
| 欄位 | 類型 | 說明 |
|------|------|------|
| face_embeddings | Array | 多個 512-dim ArcFace embedding (不同角度/定妝造型) |
| voice_embeddings | Array | 多個 192-dim ECAPA-TDNN embedding (不同音質片段) |
| identity_embeddings | Array | 多個 768-dim CLIP ViT-L/14 embedding (logo/symbol/object) |
| sound_embeddings | Array | TBD - 動物叫聲、雷雨、槍炮、樂器 (Phase 5+) |
| image_urls | Array | 參考圖片 URL 列表 |
**子欄位說明**:
| 欄位 | 類型 | 說明 |
|------|------|------|
| embedding | Array | 向量值 |
| source | String | 來源: tmdb_profile, tmdb_images, manual_upload, auto_detection, logo_image, audio_segment |
| image_url | String | 圖片 URL (face/identity) |
| file_uuid | UUID | 檔案 UUID (voice/sound) |
| timestamp_start/end | Float | 時間範圍 (voice/sound) |
| angle | String | 人臉角度: frontal, profile_left, profile_right, three_quarter |
| quality_score | Float | 質量評分 (0.0-1.0) |
| context | String | 識別場景: brand_logo, symbol, object, concept |
| sound_type | String | 聲音類型: animal_dog_bark, environmental_thunder, weapon_gunshot, musical_guitar |
| created_at | String | 建立時間 |
**設計理念**:
1. **1對多匹配**: 同一 Identity 可有多個參考向量,提高識別鲁棒性
2. **多角度覆蓋**: 人臉正面、側面、三側角度,覆蓋不同拍攝角度
3. **多場景覆蓋**: Logo/Symbol 在不同場景(白底、黑底、複雜背景)的 embedding
4. **質量評分**: 記錄每個參考向量的質量,用於加權匹配
5. **來源追溯**: 記錄每個 embedding 的來源,方便追溯和更新
### File-Identities 表 (關聯表 - 用於記錄聚合後的結果或特定角色資訊)
**說明**: 用於記錄 Identity 在 File 中的**整體出現資訊** (如:角色名、定妝造型描述)。
@@ -471,43 +583,43 @@ WHERE id = 1001;
### Phase 0: 系統備份 (立即執行)
- [ ] 備份現有 PostgreSQL 資料庫
- [ ] 備份現有程式碼
- [ ] 記錄現有版本
* [ ] 備份現有 PostgreSQL 資料庫
* [ ] 備份現有程式碼
* [ ] 記錄現有版本
### Phase 1: 建立新資料庫 Schema
- [ ] 建立 `files`, `identities`, `pre_chunks`
- [ ] 建立 `file_identities`, `categories`
- [ ] 建立索引
- [ ] 建立測試資料
* [ ] 建立 `files`, `identities`, `pre_chunks`
* [ ] 建立 `file_identities`, `categories`
* [ ] 建立索引
* [ ] 建立測試資料
### Phase 2: 核心 API 實作
- [ ] Candidates API (`GET /people/candidates`) - 查詢 `identity_id IS NULL`
- [ ] Identity CRUD API (`GET/POST/PATCH /people`)
- [ ] Identity Search API (`POST /people/search`)
- [ ] Identity Resolve API (`GET /people/{id}/resolve`)
- [ ] Candidate Management (`POST /people/{id}/confirm-candidate`, `remove-candidate`)
- [ ] Status API (`GET /people/status`)
* [ ] Candidates API (`GET /people/candidates`) - 查詢 `identity_id IS NULL`
* [ ] Identity CRUD API (`GET/POST/PATCH /people`)
* [ ] Identity Search API (`POST /people/search`)
* [ ] Identity Resolve API (`GET /people/{id}/resolve`)
* [ ] Candidate Management (`POST /people/{id}/confirm-candidate`, `remove-candidate`)
* [ ] Status API (`GET /people/status`)
### Phase 3: Processor 整合 (Pre-chunk 寫入)
- [ ] 修改 YOLO, Face, OCR 處理器,改寫入 `pre_chunks`
- [ ] 實作 `PROCESSOR_RESUME_STRATEGY.md` 中的 Checkpoint 邏輯
- [ ] probe Processor 整合 (ffprobe → File 分類)
* [ ] 修改 YOLO, Face, OCR 處理器,改寫入 `pre_chunks`
* [ ] 實作 `PROCESSOR_RESUME_STRATEGY.md` 中的 Checkpoint 邏輯
* [ ] probe Processor 整合 (ffprobe → File 分類)
### Phase 4: Portal 前端
- [ ] Candidates 介面
- [ ] Identity 管理介面
- [ ] File 管理介面
* [ ] Candidates 介面
* [ ] Identity 管理介面
* [ ] File 管理介面
### Phase 5: 非 People Identity (待辦事項)
- [ ] Brand Identity 支援
- [ ] Object Identity 支援
- [ ] Concept Identity 支援
* [ ] Brand Identity 支援
* [ ] Object Identity 支援
* [ ] Concept Identity 支援
---
@@ -526,24 +638,24 @@ WHERE id = 1001;
## 限制條件
- 本設計為全新架構,不與現有系統共用資料
- 需要做新的處理器版本產生新的輸出 (寫入 `pre_chunks` 而非 `chunks`)
- 非 People Identity 列入待辦事項,不在本次實作範圍
- Face 的唯一識別為 `file_uuid` + `coordinate_index` (Frame Number)
* 本設計為全新架構,不與現有系統共用資料
* 需要做新的處理器版本產生新的輸出 (寫入 `pre_chunks` 而非 `chunks`)
* 非 People Identity 列入待辦事項,不在本次實作範圍
* Face 的唯一識別為 `file_uuid` + `coordinate_index` (Frame Number)
---
## 相關文件
- `docs_v1.0/STANDARDS/DOCS_STANDARD.md` - 文件創建規範
- `docs_v1.0/ARCHITECTURE/` - 架構相關文件
- `docs_v1.0/PROCESSORS/_CORE/PROCESSOR_RESUME_STRATEGY.md` - 處理器續傳機制
- `docs_v1.0/PROCESSORS/_CORE/RULE_SPECIFICATION.md` - Rule 依賴與數據流定義
* `docs_v1.0/STANDARDS/DOCS_STANDARD.md` - 文件創建規範
* `docs_v1.0/ARCHITECTURE/` - 架構相關文件
* `docs_v1.0/PROCESSORS/_CORE/PROCESSOR_RESUME_STRATEGY.md` - 處理器續傳機制
* `docs_v1.0/PROCESSORS/_CORE/RULE_SPECIFICATION.md` - Rule 依賴與數據流定義
---
## 版本資訊
- 版本: V1.1
- 建立日期: 2026-04-25
- 文件更新: 2026-04-25
* 版本: V1.2
* 建立日期: 2026-04-25
* 文件更新: 2026-04-28

View File

@@ -1312,4 +1312,4 @@ cargo run --bin test_synonym_expansion
3. **技術支援**Momentry Core 僅提供技術框架和格式支援,不提供同義詞資料。
4. **更新維護**:客戶需自行維護和更新同義詞資源。
**使用前請務必確認您擁有合法使用同義詞林資源的權利。**
**使用前請務必確認您擁有合法使用同義詞林資源的權利。**

View File

@@ -33,7 +33,7 @@ Momentry 提供四種搜尋 API針對不同的情境進行優化。選擇正
"hits": [
{
"id": "sentence_0790",
"vid": "384b0ff44aaaa1f1",
"vid": "384b0ff44aaaa1f14cb2cd63b3fea966",
"chunk_type": "sentence",
"start_frame": 187296,
"end_frame": 187356,
@@ -60,7 +60,7 @@ Momentry 提供四種搜尋 API針對不同的情境進行優化。選擇正
"hits": [
{
"id": "sentence_0790",
"vid": "384b0ff44aaaa1f1",
"vid": "384b0ff44aaaa1f14cb2cd63b3fea966",
"chunk_type": "sentence",
"start_frame": 187296,
"end_frame": 187356,
@@ -102,7 +102,7 @@ Momentry 提供四種搜尋 API針對不同的情境進行優化。選擇正
"hits": [
{
"id": "sentence_0790",
"vid": "384b0ff44aaaa1f1",
"vid": "384b0ff44aaaa1f14cb2cd63b3fea966",
"chunk_type": "sentence",
"start_frame": 187296,
"end_frame": 187356,
@@ -136,7 +136,6 @@ Momentry 提供四種搜尋 API針對不同的情境進行優化。選擇正
| **快取機制** | MongoDB | MongoDB | MongoDB | MongoDB |
> **提示**: 如果 n8n 流程只需要知道「出現在哪裡」,不需要播放影片或詳細摘要,使用 `/api/v1/search/bm25` 會比向量搜尋更省資源且更快。
> **新增**: 所有向量搜尋 API 現在支援多維度搜尋 (Multi-Modal),同時查詢 ASR、Face、Object (YOLO)、Scene 四個 Collection自動合併去重後回傳。
---

View File

@@ -44,7 +44,7 @@ X-API-Key: muser_68600856036340bcafc01930eb4bd839
```json
{
"query": "主角開車離開的場景",
"uuid": "384b0ff44aaaa1f1",
"uuid": "384b0ff44aaaa1f14cb2cd63b3fea966",
"limit": 5
}
```
@@ -60,7 +60,7 @@ X-API-Key: muser_68600856036340bcafc01930eb4bd839
"hits": [
{
"id": "sentence_0790",
"vid": "384b0ff44aaaa1f1",
"vid": "384b0ff44aaaa1f14cb2cd63b3fea966",
"start_frame": 187296,
"end_frame": 187356,
"fps": 59.94,
@@ -103,36 +103,36 @@ X-API-Key: muser_68600856036340bcafc01930eb4bd839
為了讓開發者理解效能與資料來源,以下是 `/api/v1/n8n/search` (Vector) 的完整執行流程:
### 步驟 1: 認證與快取 (Auth & Cache)
1. **API Key 驗證**: 檢查 Header 中的 `X-API-Key`
2. **Query Hash**: 計算 `SHA256(query + uuid + limit)` 作為快取 Key。
3. **MongoDB Cache 檢查**:
* 若命中快取:直接返回 JSON**跳過**後續繁重的向量運算。
* 若未命中:進入步驟 2。
1. **API Key 驗證**: 檢查 Header 中的 `X-API-Key`
2. **Query Hash**: 計算 `SHA256(query + uuid + limit)` 作為快取 Key。
3. **MongoDB Cache 檢查**:
* 若命中快取:直接返回 JSON**跳過**後續繁重的向量運算。
* 若未命中:進入步驟 2。
### 步驟 2: 向量化與搜尋 (Embedding & Search)
1. **Embedding**: 呼叫 Ollama (Nomic-Embed-Text) 將 Query 轉換為向量 (Vector)。
2. **Qdrant 搜尋**:
* 若有 `uuid`: 在該影片的 Collection 中搜尋最相似的 Chunk。
* 若無 `uuid`: 進行全域搜尋。
* 返回 Top-N 個包含 `chunk_id``uuid` 的候选結果。
1. **Embedding**: 呼叫 Ollama (Nomic-Embed-Text) 將 Query 轉換為向量 (Vector)。
2. **Qdrant 搜尋**:
* 若有 `uuid`: 在該影片的 Collection 中搜尋最相似的 Chunk。
* 若無 `uuid`: 進行全域搜尋。
* 返回 Top-N 個包含 `chunk_id``uuid` 的候选結果。
### 步驟 3: 資料豐富化 (Enrichment via PostgreSQL)
拿到 Qdrant 結果後,系統會**逐筆**向 PostgreSQL 查詢詳細資訊:
1. **取得 Chunk 內容**:
* 查詢 `chunks` 表格,獲取原始 JSON 內容、時間、幀號等。
2. **取得影片路徑**:
* 利用 `uuid` 查詢 `videos` 表格,獲取 `file_path`
3. **取得 5W1H 摘要**:
* 檢查 Chunk 是否有 `parent_chunk_id` (所屬的故事段落)。
* 若有,查詢 `parent_chunks` 表格的 `metadata` 欄位。
* 該欄位包含由 Gemma4 LLM 生成的結構化摘要 (Who, What, Where, When, Why, Key Events)。
1. **取得 Chunk 內容**:
* 查詢 `chunks` 表格,獲取原始 JSON 內容、時間、幀號等。
2. **取得影片路徑**:
* 利用 `uuid` 查詢 `videos` 表格,獲取 `file_path`
3. **取得 5W1H 摘要**:
* 檢查 Chunk 是否有 `parent_chunk_id` (所屬的故事段落)。
* 若有,查詢 `parent_chunks` 表格的 `metadata` 欄位。
* 該欄位包含由 Gemma4 LLM 生成的結構化摘要 (Who, What, Where, When, Why, Key Events)。
### 步驟 4: 格式化與回傳 (Formatting)
1. **Text Processing**: 將 Chunk 原始內容中的文字提取,並進行繁簡轉換與分詞處理 (Tokenization)。
2. **建構 Hit 物件**: 組合上述所有資料為 `N8nSearchHit`
3. **寫入快取**: 將最終結果寫入 MongoDB 以便下次秒回。
4. **回應 JSON**: 返回給 n8n。
1. **Text Processing**: 將 Chunk 原始內容中的文字提取,並進行繁簡轉換與分詞處理 (Tokenization)。
2. **建構 Hit 物件**: 組合上述所有資料為 `N8nSearchHit`
3. **寫入快取**: 將最終結果寫入 MongoDB 以便下次秒回。
4. **回應 JSON**: 返回給 n8n。
---
@@ -141,12 +141,12 @@ X-API-Key: muser_68600856036340bcafc01930eb4bd839
除了標準的 Vector Search還有兩種變體
### 5.1 BM25 Keyword Search
- **Endpoint**: `/api/v1/n8n/search/bm25`
- **邏輯**: 跳過向量運算,直接使用 PostgreSQL 的全文檢索 (Full Text Search) 功能。適合精確匹配專有名詞或關鍵字。
* **Endpoint**: `/api/v1/n8n/search/bm25`
* **邏輯**: 跳過向量運算,直接使用 PostgreSQL 的全文檢索 (Full Text Search) 功能。適合精確匹配專有名詞或關鍵字。
### 5.2 Smart Search (LLM 分析)
- **Endpoint**: `/api/v1/n8n/search/smart`
- **邏輯**:
* **Endpoint**: `/api/v1/n8n/search/smart`
* **邏輯**:
1. 將 Query 送至 Llama-server (Port 8081) 進行意圖分析 (5W1H)。
2. 提取出關鍵實體 (人名、地點、動作)。
3. 將提取出的實體轉換為更精確的 BM25 查詢語句進行搜尋。

View File

@@ -0,0 +1,267 @@
# Portal 适配 Birth UUID 完成报告
## 修改日期
2026-04-28
---
## 背景
Birth UUID Phase 1 MVP 实施后,需要确认 Portal 是否需要修改以适配新的 UUID 格式。
---
## Birth UUID 规格
| 项目 | 内容 |
|------|------|
| **格式** | SHA256[mac|timestamp|username|filename](0:32) |
| **长度** | 32字符比旧UUID的16字符更长 |
| **唯一性** | MAC + Timestamp确保全球唯一 |
| **隐私保护** | MAC不直接暴露哈希在UUID内 |
| **不可变** | 文件迁移不影响UUID |
---
## Portal 分析结果
### ✅ 前端无需强制修改
**原因**
1. UUID显示使用CSS `truncate`,可自动截断长文本
2. API调用使用`uuid`参数,无长度限制
3. 路由`/videos/:uuid`可接受任意长度字符串
4. 向后兼容16字符旧UUID和32字符新UUID都能正常工作
### 🔧 后端需要修改
**原因**
- API返回的`VideoRecord`缺少`birth_registration`字段
- 需要在API响应中包含注册来源信息
---
## 实施修改
### 后端修改Rust
#### 1. VideoRecord 添加字段
```rust
// src/core/db/postgres_db.rs Line 158-177
pub struct VideoRecord {
pub birth_registration: Option<serde_json::Value>,
// ... 其他字段
}
```
#### 2. VideoRow 添加字段
```rust
// src/core/db/postgres_db.rs Line 99-124
pub struct VideoRow {
pub birth_registration: Option<serde_json::Value>,
// ... 其他字段
}
```
#### 3. VideoInfoResponse 添加字段
```rust
// src/api/server.rs Line 361-375
struct VideoInfoResponse {
birth_registration: Option<serde_json::Value>,
// ... 其他字段
}
```
#### 4. SELECT 查询修改
```sql
-- Line 770, 838, 920
SELECT id, uuid, ..., birth_registration, ..., total_frames FROM videos
```
#### 5. 构造函数修改
- `From<VideoRow> for VideoRecord`Line 125-155
- `ingestion.rs` VideoRecord构造Line 146-164
- `server.rs` VideoRecord构造Line 802-820
- 测试代码Line 4489-4514
---
### 前端修改Vue
#### 1. UUID显示优化
```vue
<!-- VideoDetailView.vue Line 17-20 -->
<div>
<span class="text-xs text-gray-500 uppercase">UUID</span>
<p class="text-sm font-mono text-gray-300 truncate">{{ video.uuid }}</p>
<p class="text-xs text-gray-600 mt-1">長度: {{ video.uuid.length }} 字符</p>
</div>
```
#### 2. Birth Registration 显示区域
```vue
<!-- VideoDetailView.vue Line 33-48 -->
<div v-if="video.birth_registration" class="mt-4 bg-gray-850 p-3 rounded border border-gray-600">
<h4 class="text-xs font-semibold text-gray-400 mb-2 uppercase">註冊來源資訊</h4>
<div class="grid grid-cols-2 md:grid-cols-4 gap-3">
<div>
<span class="text-xs text-gray-600">用戶名:</span>
<p class="text-sm text-gray-300">{{ video.birth_registration.registration_source?.username }}</p>
</div>
<div>
<span class="text-xs text-gray-600">註冊時間:</span>
<p class="text-sm text-gray-300">{{ formatTimestamp(video.birth_registration.registration_source?.timestamp) }}</p>
</div>
<div>
<span class="text-xs text-gray-600">原始檔名:</span>
<p class="text-sm text-gray-300 truncate">{{ video.birth_registration.registration_source?.original_filename }}</p>
</div>
<div>
<span class="text-xs text-gray-600">UUID類型:</span>
<p class="text-sm text-gray-300">{{ video.uuid.length === 32 ? 'Birth UUID' : 'Legacy UUID' }}</p>
</div>
</div>
</div>
```
#### 3. 时间格式化函数
```typescript
function formatTimestamp(timestamp: string | undefined): string {
if (!timestamp) return '-'
try {
const date = new Date(timestamp)
return date.toLocaleString('zh-TW', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
})
} catch {
return timestamp
}
}
```
---
## birth_registration JSONB 结构
```json
{
"registration_source": {
"mac_address": "ba:f5:ee:bc:45:78",
"username": "demo",
"timestamp": "2026-04-27T22:00:00+08:00",
"original_path": "/Users/.../demo",
"original_filename": "video.mp4"
}
}
```
---
## API 响应示例
### 旧UUID视频16字符
```json
{
"uuid": "ac625815183a21e1",
"birth_registration": null,
"file_name": "video.mp4",
...
}
```
### 新UUID视频32字符
```json
{
"uuid": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
"birth_registration": {
"registration_source": {
"mac_address": "ba:f5:ee:bc:45:78",
"username": "demo",
"timestamp": "2026-04-27T22:00:00+08:00",
"original_filename": "video.mp4"
}
},
"file_name": "video.mp4",
...
}
```
---
## 向后兼容性
| UUID类型 | 长度 | birth_registration | Portal显示 |
|---------|------|-------------------|-----------|
| **旧UUID** | 16字符 | null | 显示UUID隐藏birth_registration区域 |
| **新UUID** | 32字符 | 有数据 | 显示UUID显示birth_registration区域 |
---
## 测试验证计划
### 步骤 1: 编译测试
```bash
# 检查编译birth_registration相关错误已修复
cargo check --lib
```
### 步骤 2: 注册新视频
```bash
# 使用Birth UUID注册
cargo run -- register /path/to/new_video.mp4
```
### 步骤 3: 检查数据库
```sql
SELECT uuid, LENGTH(uuid), birth_registration
FROM dev.videos
WHERE birth_registration IS NOT NULL;
```
### 步骤 4: API测试
```bash
# 查询新UUID视频
curl http://localhost:3003/api/v1/videos?uuid=<32字符UUID>
```
### 步骤 5: Portal显示测试
- 打开Portal `/videos/<32字符UUID>`
- 确认UUID显示为32字符
- 确认birth_registration区域显示注册信息
---
## 修改文件清单
| 文件 | 修改内容 |
|------|---------|
| `/src/core/db/postgres_db.rs` | VideoRecord/VideoRow添加字段SELECT查询修改 |
| `/src/api/server.rs` | VideoInfoResponse添加字段构造函数修改 |
| `/src/core/ingestion.rs` | VideoRecord构造添加birth_registration: None |
| `/portal/src/views/VideoDetailView.vue` | UUID显示优化birth_registration显示区域 |
---
## 总结
**Portal已完全适配Birth UUID**
### 关键成果
1. ✅ 后端API返回`birth_registration`字段
2. ✅ 前端显示Birth UUID长度和注册来源信息
3. ✅ 向后兼容16字符旧UUID
4. ✅ 新视频注册时自动记录`birth_registration`
### 下一步
1. 修复遗留编译错误redis、SCRIPTS_DIR、PYTHON_PATH
2. 实际注册新视频验证Birth UUID流程
3. Portal端到端测试
---
**完成日期**: 2026-04-28
**状态**: 后端+前端修改完成,待测试验证

View File

@@ -81,11 +81,11 @@
為了達成上述所有標準,我們必須完成以下開發:
1. [ ] **語義索引建置 (Semantic Indexing)**: 使用 Ollama (Gemma 4) 離線分析全片對話,填入 `chunk_semantics` 表。
2. [ ] **場景聚合邏輯 (Scene Aggregation)**: 將零碎的 ASR/CUT 聚合成有意義的「場景 (Scene)」。
3. [ ] **互動計數器 (Interaction Counter)**: 統計角色同框次數,支援「第 X 次見面」查詢。
4. [ ] **事件時間軸 (Event Timeline)**: 將 Gunshot, Death, Fight 等事件結構化,支援「...之後」查詢。
5. [ ] **關係圖譜 (Relationship Graph)**: 建立角色關係網,支援「主持人/來賓」等角色定義。
1. [ ] **語義索引建置 (Semantic Indexing)**: 使用 Ollama (Gemma 4) 離線分析全片對話,填入 `chunk_semantics` 表。
2. [ ] **場景聚合邏輯 (Scene Aggregation)**: 將零碎的 ASR/CUT 聚合成有意義的「場景 (Scene)」。
3. [ ] **互動計數器 (Interaction Counter)**: 統計角色同框次數,支援「第 X 次見面」查詢。
4. [ ] **事件時間軸 (Event Timeline)**: 將 Gunshot, Death, Fight 等事件結構化,支援「...之後」查詢。
5. [ ] **關係圖譜 (Relationship Graph)**: 建立角色關係網,支援「主持人/來賓」等角色定義。
---
@@ -190,4 +190,4 @@ curl -X POST http://localhost:3002/api/v1/search \
---
**這份文檔已建立於 `docs/SEARCH_ACCEPTANCE_CRITERIA.md`,我們將以此為目標持續推進系統開發。**
**這份文檔已建立於 `docs/SEARCH_ACCEPTANCE_CRITERIA.md`,我們將以此為目標持續推進系統開發。**

View File

@@ -1,6 +1,6 @@
# Stamp Search Progress
**UUID**: `384b0ff44aaaa1f1`
**UUID**: `384b0ff44aaaa1f14cb2cd63b3fea966`
**Video**: Charade (1963) - ~115 min
**Status**: ⏸️ Paused - User review needed
@@ -31,26 +31,26 @@
### 1. Color-Based Detection (Blue + Red for Inverted Jenny)
- **Script**: `scripts/filter_stamp_colors.py`
- **Candidates**: 21 images
- **Location**: `output/384b0ff44aaaa1f1/florence2_results/STAMP_CANDIDATE_*.jpg`
- **Location**: `output/384b0ff44aaaa1f14cb2cd63b3fea966/florence2_results/STAMP_CANDIDATE_*.jpg`
- **Result**: ❌ Not a match
### 2. Balanced Blue+Red Shape Detection
- **Script**: `scripts/filter_stamp_colors.py` (refined)
- **Candidates**: 13 images
- **Location**: `output/384b0ff44aaaa1f1/florence2_results/BALANCED_STAMP_*.jpg`
- **Location**: `output/384b0ff44aaaa1f14cb2cd63b3fea966/florence2_results/BALANCED_STAMP_*.jpg`
- **Result**: ❌ Not a match
### 3. Rectangle Shape + Color Detection (Full Frames)
- **Script**: `scripts/detect_stamp_shapes.py`
- **Candidates**: 22 crops from 8 scan frames
- **Location**: `output/384b0ff44aaaa1f1/florence2_results/STAMP_CROP_*.jpg`
- **Location**: `output/384b0ff44aaaa1f14cb2cd63b3fea966/florence2_results/STAMP_CROP_*.jpg`
- **Result**: ❌ Not a match
### 4. Full Video Scan (every 60 seconds)
- **Script**: `scripts/scan_full_video_stamps.py`
- **Frames scanned**: 115
- **Candidates**: 27 images
- **Location**: `output/384b0ff44aaaa1f1/stamp_candidates_full/`
- **Location**: `output/384b0ff44aaaa1f14cb2cd63b3fea966/stamp_candidates_full/`
- **Result**: ❌ Not a match
### 5. Florence-2 AI Vision
@@ -61,7 +61,7 @@
- **Script**: `scripts/scan_charade_stamps.py`
- **Frames scanned**: 67 (from key stamp dialogue timestamps)
- **Candidates**: 60+ paper-like rectangular crops
- **Location**: `output/384b0ff44aaaa1f1/stamp_scenes_crops/`
- **Location**: `output/384b0ff44aaaa1f14cb2cd63b3fea966/stamp_scenes_crops/`
- **Result**: ❌ Not a match (or user hasn't reviewed yet)
## Key Timestamps for Visual Inspection

View File

@@ -184,4 +184,4 @@ println!("擴展 '電腦 工作': {}", expander.expand_query("電腦 工作"));
- 同義詞擴展器:`src/core/text/synonym_expander.rs`
- 繁簡轉換:`src/core/text/synonym.rs`
- 分詞器:`src/core/text/tokenizer.rs`
- PostgreSQL 整合:`src/core/db/postgres_db.rs`
- PostgreSQL 整合:`src/core/db/postgres_db.rs`

View File

@@ -206,4 +206,4 @@ python3 -m json.tool config/synonyms/custom_synonyms.json
---
**注意**: 本系統僅提供技術框架和工具,客戶需自行負責同義詞資源的合法性、準確性和維護工作。
**注意**: 本系統僅提供技術框架和工具,客戶需自行負責同義詞資源的合法性、準確性和維護工作。

View File

@@ -260,17 +260,17 @@ pub async fn register(
}
// 關聯 user_id 到影片
let video_uuid = state.db.create_video(req, Some(ctx.user_id)).await?;
let file_uuid = state.db.create_video(req, Some(ctx.user_id)).await?;
// 建立 processing job帶 user_id
state.db.create_monitor_job(
job_type: "auto_ingestion",
video_uuid,
file_uuid,
user_id: Some(ctx.user_id),
processors: vec!["asr", "cut", "yolo", "ocr", "face", "pose"],
).await?;
Ok(Json(RegisterResponse { uuid: video_uuid }))
Ok(Json(RegisterResponse { uuid: file_uuid }))
}
```