---
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 模組 |
