---
document_type: "architecture_design"
service: "MOMENTRY_CORE"
title: "Momentry Core Chunk Rule 1: 句子級檢索 (Sentence 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 1: 句子級檢索 (Sentence Chunk) (v1.0) 的內容"
  - "Momentry Core Chunk Rule 1: 句子級檢索 (Sentence Chunk) (v1.0) 的主要目的是什麼？"
  - "如何操作或實施 Momentry Core Chunk Rule 1: 句子級檢索 (Sentence Chunk) (v1.0)？"
---

# Momentry Core Chunk Rule 1: 句子級檢索 (Sentence Chunk) (v1.0)

| 項目 | 內容 |
|------|------|
| 建立者 | OpenCode |
| 建立時間 | 2026-04-21 |
| 文件版本 | V1.0 |

---

## 版本歷史

| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-04-21 | 定義 Rule 1: 單一語句為 Chunk 的數據結構與搜尋邏輯 | OpenCode | OpenCode / Qwen3.6-Plus |

---

## 0. 設計目標

**Rule 1** 的核心概念是**「原子化語義」**。將影片內容切分到「一句話」的粒度，以便進行最高精度的語意搜尋與元數據過濾。

- **核心原則**: One Sentence = One Chunk。
- **時間權威**: 基於 `frame_number` (由 `ffprobe` 定義的 FPS 計算)。
- **多模態關聯**: 每個句子 Chunk 必須攜帶該時間區間內的 **Speaker (說話者)** 與 **Faces (出現人物)** 資訊。

---

## 1. 數據源與聚合邏輯

Rule 1 的生成依賴三個上游處理器的產出：

1. **ASR (Primary)**: 提供文本內容 (`text`)、起始時間 (`start_time`)、結束時間 (`end_time`)。
2. **ASRX (Speaker)**: 提供說話者 ID (`speaker_id`)。
    - *聚合策略*: 使用 ASR 的時間區間去對齊 ASRX，取該區間內**佔比最高**的 `speaker_id`。
3. **Face (Visual)**: 提供幀級別的人物 ID (`face_id`)。
    - *聚合策略*: 在 ASR 的 `[start_frame, end_frame]` 區間內，收集所有出現的 `face_id`。若同一 ID 出現多次，去重後形成 `face_ids` 陣列。

---

## 2. Chunk 結構定義

### 2.1 資料庫結構 (PostgreSQL)

```sql
CREATE TABLE chunks_rule1 (
    id UUID PRIMARY KEY,
    asset_uuid UUID NOT NULL,              -- 關聯 assets 表
    chunk_type VARCHAR(20) DEFAULT 'sentence',  -- 對應 ChunkType::Sentence
    
    -- 時間軸 (幀為權威)
    start_frame INT NOT NULL,
    end_frame INT NOT NULL,
    start_time_sec DOUBLE PRECISION,       -- 參考值: start_frame / fps
    end_time_sec DOUBLE PRECISION,         -- 參考值: end_frame / fps
    fps DOUBLE PRECISION NOT NULL,

    -- 核心內容
    content TEXT NOT NULL,                 -- ASR 識別出的文字
    
    -- 關聯元數據 (Metadata)
    speaker_id VARCHAR(50),                -- ASRX 產出
    face_ids JSONB,                        -- Face 產出，例如 ["face_01", "face_02"]
    
    -- 向量與索引
    embedding vector(768),                 -- nomic-embed-text-v2-moe
    search_vector tsvector,                -- PostgreSQL BM25

    created_at TIMESTAMPTZ DEFAULT NOW()
);
```

### 2.2 JSON 產出範例 (供前端 API 返回)

當 API 搜尋 Rule 1 時，返回結構如下：

```json
{
  "chunk_id": "550e...0001",
  "type": "sentence",
  "content": "這是一個關於尋找真相的故事。",
  "start_frame": 1200,
  "end_frame": 1250,
  "start_time_sec": 40.04,
  "end_time_sec": 41.71,
  "metadata": {
    "speaker": "SPEAKER_00",
    "faces": [
      { "face_id": "person_a", "confidence": 0.98 }
    ]
  },
  "highlight": "這是一個關於<span class='highlight'>尋找真相</span>的故事。"
}
```

---

## 3. 搜尋能力定義

Rule 1 支援三種主要搜尋模式：

### 3.1 語意搜尋 (Vector Search)
- **場景**: "有人提到錢嗎？" (即使影片沒說 "錢"，而是說 "鈔票" 也能搜到)。
- **邏輯**: 
    1. 將 Query 透過 Ollama (`nomic-v2-moe`) 轉為 768-dim 向量。
    2. 在 Qdrant (`collection: momentry_rule1`) 中進行 Cosine 相似度比對。
    3. **Filter**: 可加入 `metadata.speaker == "SPEAKER_00"`。

### 3.2 關鍵字搜尋 (BM25 Search)
- **場景**: "搜尋確切字串 'Charade 1963'"。
- **邏輯**: 
    1. 使用 PostgreSQL `tsvector` 進行全文檢索。
    2. 適合精確匹配專有名詞。

### 3.3 過濾搜尋 (Faceted Search)
- **場景**: "找出 **Audrey Hepburn (Face)** 說話的所有片段"。
- **邏輯**:
    1. `face_ids` 包含 "Audrey Hepburn" 的 ID。
    2. `speaker_id` 不為空 (代表她在說話)。
    3. 檢索符合條件的 Chunks。

---

## 4. 處理流程 (Processing Pipeline)

### 4.1 聚合演算法 (Pseudocode)

```python
# 輸入: asr_segments (List), asrx_segments (List), face_frames (List)
# 常數: FPS (來自 ffprobe)

for seg in asr_segments:
    # 1. 確定時間範圍 (Frames)
    start_f = int(seg.start_time * FPS)
    end_f = int(seg.end_time * FPS)
    
    # 2. 匹配 Speaker (取重疊時間最長的)
    speaker = find_majority_speaker(start_f, end_f, asrx_segments)
    
    # 3. 聚合 Faces (收集區間內出現過的所有唯一 ID)
    faces = get_unique_faces(start_f, end_f, face_frames)
    
    # 4. 建立 Chunk
    chunk = {
        "content": seg.text,
        "start_frame": start_f,
        "end_frame": end_f,
        "speaker_id": speaker,
        "face_ids": faces
    }
    
    # 5. 寫入 DB
    store_chunk_rule1(chunk)
```

### 4.2 時間邊界處理

若 ASR 的 `end_time` 與 ASRX 的 `start_time` 有微小誤差 (例如 0.05s)，系統應容忍 **±2 frames** 的誤差範圍進行匹配。

---

## 5. 向量嵌入策略

- **嵌入模型**: `nomic-embed-text-v2-moe` (768-dim)。
- **嵌入內容**: 僅使用 `content` (句子文字)。
  - *原因*: 避免 speaker 或 face 的 metadata 干擾語意向量空間，確保語意純淨。Metadata 僅用於過濾 (Filter)。

---

## 6. 總結

Rule 1 提供了**最細緻**的影片理解層級。

| 特性 | 實作方式 |
|------|----------|
| **粒度** | 句子 (Sentence) |
| **時間精度** | Frame 級別 (由 FPS 換算) |
| **人物標記** | 自動關聯 Face ID (Visual) |
| **說話者標記** | 自動關聯 Speaker ID (Audio) |
| **適用場景** | 尋找特定台詞、某人說了什麼、特定鏡頭對話 |

此規範確保了所有 Rule 1 Chunk 在進入資料庫前，都已經完成了多模態數據的融合 (Audio + Visual + Text)。
