---
document_type: "spec"
service: "MOMENTRY_CORE"
title: "Data Schema: File & Identity V1.0"
date: "2026-05-05"
version: "V1.0"
status: "active"
owner: "Warren"
created_by: "OpenCode"
tags:
  - "momentry"
  - "core"
  - "schema"
  - "file"
  - "identity"
  - "v1.0"
ai_query_hints:
  - "File & Identity DB schema"
  - "face_detections.identity_id direct FK"
  - "identity multi-modal: face + voice + TMDb + manual"
related_documents:
  - "../DUAL_EMBEDDING_PIPELINE_V1.0.0.md"
  - "../UUID_ENCODING_RULES_V1.0.0.md"
---

# Data Schema: File & Identity V1.0

## 1. File Schema

### videos / files

| Column | Type | 說明 |
|--------|------|------|
| `id` | SERIAL PK | |
| `file_uuid` | VARCHAR(32) | Birth UUID |
| `file_path` | VARCHAR(512) | 檔案完整路徑 |
| `file_name` | VARCHAR(256) | |
| `probe_json` | JSONB | ffprobe raw output |
| `status` | VARCHAR(20) | ready / processing / completed |
| `processing_status` | JSONB | per-processor progress |
| `total_frames` | INTEGER | |
| `fps` | DOUBLE | |
| `duration` | DOUBLE | 影片長度（秒） |
| `width` / `height` | INTEGER | 解析度 |
| `registration_time` | TIMESTAMP | 註冊時間 |

### face_detections (per-file face data)

| Column | Type | 說明 |
|--------|------|------|
| `id` | SERIAL PK | |
| `file_uuid` | VARCHAR(32) | → videos.file_uuid |
| `frame_number` | BIGINT | 幀號 |
| `face_id` | VARCHAR(64) | per-file face identifier |
| `trace_id` | INTEGER | 跨幀追蹤 ID |
| `x, y, width, height` | INTEGER | bbox |
| `confidence` | REAL | 偵測信度 |
| `embedding` | REAL[] | 512D CoreML FaceNet |
| `identity_id` | INTEGER | → identities.id (V4.0 direct FK) |

### chunks (per-file parent/child chunks)

| Column | Type | 說明 |
|--------|------|------|
| `id` | SERIAL PK | |
| `chunk_id` / `old_chunk_id` | VARCHAR | chunk identifier |
| `file_uuid` | VARCHAR(32) | → videos.file_uuid |
| `chunk_type` | VARCHAR(32) | story_parent / story_child / rule1_sentence |
| `chunk_index` | INTEGER | per-file ordering |
| `start_time` / `end_time` | DOUBLE | time range |
| `content` | JSONB | metadata |
| `text_content` | TEXT | summary text → embedding target |
| `embedding` | VECTOR | pgvector 768D |
| `search_vector` | TSVECTOR | BM25 full-text |
| `parent_chunk_id` | VARCHAR | → chunks.chunk_id |

## 2. Identity Schema

### 概念

Identity 是可命名的任何識別標的，不限於人。

| identity_type | 範例 | 識別模型 |
|--------------|------|---------|
| `people` | Cary Grant, Audrey Hepburn | face, voice, name |
| `animal` | 電影中的狗、馬 | face, body, sound |
| `object` | 特定道具、車輛 | yolo, image embedding |
| `plant` | 場景中的特定植物 | image embedding |
| `building` | 艾菲爾鐵塔、特定建築 | image embedding, OCR |
| `place` | Paris, 咖啡廳 | scene classification |
| `concept` | "離婚", "復仇" | text embedding |
| `brand` | Coca-Cola | OCR, logo detection |

每種 identity_type 可以使用不同的識別模型組合。

### 識別模型

| model | dimension | source | 適用 identity_type |
|-------|-----------|--------|-------------------|
| `face` | 512D | CoreML FaceNet | people, animal |
| `voice` | 192D | SpeechBrain ECAPA-TDNN | people |
| `text` | 768D | Ollama nomic-embed | concept, place |
| `image` | 768D | — (future) | object, building, plant |
| `yolo_class` | — | YOLO label | object |

### Table

```sql
CREATE TABLE identities (
    id              SERIAL PRIMARY KEY,
    uuid            UUID,                  -- 32-char UUIDv5 (source:external_id)
    name            TEXT NOT NULL UNIQUE,
    identity_type   VARCHAR(30) DEFAULT 'people',  -- people/animal/object/building/place/concept
    source          VARCHAR(20) DEFAULT 'manual',  -- tmdb/manual/face_cluster/yolo
    status          VARCHAR(20) DEFAULT 'pending',

    -- Reference vectors per model (in JSONB for extensibility)
    reference_vectors JSONB DEFAULT '{}',
    -- {
    --   "face": [{"vec":[...], "pose":"frontal", "source":"video_trace"}],
    --   "voice": [{"vec":[...], "speaker_id":"SPEAKER_0"}],
    --   "image": [{"vec":[...], "source":"manual"}]
    -- }

    -- Legacy columns (migrating to reference_vectors)
    face_embedding      VECTOR(512),
    voice_embedding     VECTOR(192),
    identity_embedding  VECTOR(768),

    reference_data  JSONB DEFAULT '{}',
    metadata        JSONB DEFAULT '{}',
    tmdb_id         INTEGER,
    tmdb_profile    TEXT,
    created_at      TIMESTAMP DEFAULT now()
);
```

### 彈性設計

現有 `face_embedding` / `voice_embedding` column 維持向下相容。  
未來全部移入 `reference_vectors` JSONB，支援任意 model × 多個 reference vectors：

```json
{
  "reference_vectors": {
    "face": [
      {"vec": [0.1, 0.2, ...], "pose": "frontal", "source": "video_trace_0", "confidence": 0.95},
      {"vec": [0.3, 0.4, ...], "pose": "profile", "source": "video_trace_0", "confidence": 0.88}
    ],
    "voice": [
      {"vec": [0.5, 0.6, ...], "speaker_id": "SPEAKER_0", "source": "asrx"}
    ],
    "image": []
  }
}
```

### 識別 Agent 架構

每個識別模型由對應的 Agent 負責。Identity 本身只存 reference vectors，不綁定特定 model。

```
                    ┌─────────────────────────┐
                    │      identities          │
                    │  name, type, source      │
                    │  reference_vectors (JSONB)│
                    └──────────┬──────────────┘
                               │
          ┌────────────────────┼────────────────────┐
          │                    │                    │
     ┌────▼────┐         ┌────▼────┐         ┌────▼────┐
     │FaceAgent│         │VoiceAgent│        │ImageAgent│
     │         │         │         │         │ (future) │
     │ input:  │         │ input:  │         │ input:  │
     │ face_   │         │ asrx    │         │ image   │
     │ detect  │         │ segments│         │ features│
     │ ions    │         │         │         │         │
     │         │         │         │         │         │
     │ output: │         │ output: │         │ output: │
     │ face →  │         │ voice → │         │ img →   │
     │ identity│         │ identity│         │ identity│
     └─────────┘         └─────────┘         └─────────┘
```

### Agent 定義

| Agent | 輸入 | 模型 | 輸出 | 狀態 |
|-------|------|------|------|------|
| **FaceAgent** | `face_detections` | CoreML FaceNet 512D | `identity_id` on face_detections | ✅ |
| **VoiceAgent** | ASRX segments | ECAPA-TDNN 192D + MAR lip | `metadata.speaker_id` | ✅ |
| **ImageAgent** | — | — | — | ⬜ future |
| **YoloAgent** | YOLO detections | — | object → identity | ⬜ future |
| **TextAgent** | chunk text | nomic-embed 768D | concept → identity | ⬜ future |

### Agent 運作模式

```
1. Agent 讀取 raw detections（face / voice / yolo）
2. 對比 identities.reference_vectors[model]
3. 相似度達標 → bind to existing identity
4. 不達標 → create new identity
5. 更新 identities.reference_vectors（enrich reference set）
```

同一個 identity 可以被多個 Agent 同時更新。例如：
- FaceAgent 寫入 `reference_vectors.face`
- VoiceAgent 寫入 `reference_vectors.voice`
- 兩者指向同一個 identity (Cary Grant)

### Face → Identity 綁定（V4.0）

```
face_detections.identity_id ──── FK ────→ identities.id
```

Direct FK。不需要 intermediate table。操作 API：

```
POST /api/v1/identities/bind
  { "file_uuid": "...", "face_id": "face_1", "identity_uuid": "..." }
  → UPDATE face_detections SET identity_id = X

POST /api/v1/identities/unbind
  { "file_uuid": "...", "face_id": "face_1" }
  → UPDATE face_detections SET identity_id = NULL
```

### Voice/Speaker → Identity 綁定

透過 `identities.metadata.speaker_id`：

```
identities.metadata = {"speaker_id": "SPEAKER_0", "speaker_confidence": 0.85}
```

Voice embedding 直接寫入 `identities.voice_embedding`。

## 3. File-Identity 關聯

```
file (1a04db97...)                    identity (Cary Grant)
│                                     │
├── face_detections                   │
│   ├── face_id="face_1"              │
│   │   identity_id ──────────────────┤
│   ├── face_id="face_2"              │
│   │   identity_id ──────────────────┤
│   └── face_id="face_3"              │
│       identity_id = NULL            │  ← unbounded
│                                     │
├── chunks                            │
│   ├── story_parent                  │
│   │   content.metadata.characters   │
│   │   = ["Cary Grant", ...]         │
│   └── story_child                   │
│       content.metadata.speaker      │
│       = "Cary Grant"                │
│                                     │
└── asrx.json                          │
    └── segments[].speaker_id          │
        = "SPEAKER_0" ────────────────┘

file_identities (N:N junction, if needed)
  file_uuid → identity_uuid
```

## 4. Class 分層分類（參照 IPC + HS）

### 設計參考

IPC（國際專利分類）與 HS（海關稅則）的分層編碼體系。

| 標準 | 結構 |
|------|------|
| **IPC** | Section(A-H) → Class(2digits) → Subclass → Group/NNN |
| **HS** | Section → Chapter(2digits) → Heading(4digits) → Subheading(6digits) |

共通原則：**層級碼**、**數字越長越精細**、**全球通用**。

### 編碼格式

```
{SECTION}-{NNN}-{NNN}-{NNN}
   │       │      │      └─ subgroup
   │       │      └──────── main_group
   │       └─────────────── subclass
   └─────────────────────── section
```

| Section | 涵蓋 |
|---------|------|
| `P` | People |
| `O` | Object |
| `B` | Brand |
| `C` | Concept |
| `A` | Animal |
| `S` | Scene |
| `E` | Environment |
| `M` | Music/Sound |

### Table

```sql
CREATE TABLE classes (
    code            VARCHAR(20) PRIMARY KEY,     -- P-001-010/010
    name            TEXT NOT NULL,
    parent_code     VARCHAR(20) REFERENCES classes(code),
    section         CHAR(1),
    level           INTEGER DEFAULT 0,
    description     TEXT,
    created_at      TIMESTAMPTZ DEFAULT now()
);

CREATE TABLE identity_classes (
    identity_id     INTEGER REFERENCES identities(id),
    class_code      VARCHAR(20) REFERENCES classes(code),
    confidence      REAL DEFAULT 1.0,
    source          VARCHAR(20),
    PRIMARY KEY (identity_id, class_code)
);
```

## 版本歷史

| 版本 | 日期 | 變更 |
|------|------|------|
| V1.0 | 2026-05-05 | File & Identity schema，V4.0 direct FK binding |
| V1.1 | 2026-05-05 | Class 分層分類（IPC/HS），Agent 識別架構 |
