# ANE Embedding 整合測試計畫

**作者**：M4  
**日期**：2026-05-07  
**待測版本**：M5 完成全面 text embedding ANE CoreML 整合後  

---

## 1. 測試範圍

M5 正在將所有 text embedding 從 Ollama GPU 改為 CoreML ANE。本計畫驗證改動後的正确性與效能。

### 受影響的程式碼路徑

| 路徑 | 目前使用 | 改為 |
|------|---------|------|
| `search.rs` → `smart_search` | Ollama `POST /api/v1/search/smart` | CoreML ANE |
| `universal_search.rs` → chunk search | Ollama `POST /api/v1/search/universal` | CoreML ANE |
| 任何其他 text embedding 呼叫 | Ollama | CoreML ANE |

---

## 2. 測試案例

### TC-1：Embedding 正確性

**目的**：確認 ANE CoreML embedding 輸出與 Ollama 一致

**方法**：
1. 對同一段 text，分別用 Ollama 和 CoreML ANE 產生 embedding
2. 計算 cosine similarity

**預期**：similarity > 0.99（兩位小數精度內一致）

**測試用 text**：
```
"Hello and welcome to the old time movie show"
"Cary Grant and Audrey Hepburn star in Charade"
"歡迎收看老電影節目"
"感謝您的觀看"
```

**指令範例**：
```bash
# Ollama
curl -s http://localhost:11434/api/embeddings \
  -H "Content-Type: application/json" \
  -d '{"model":"mxbai-embed-large","prompt":"Hello world"}' | jq '.embedding[:5]'

# CoreML ANE（透過 Python）
python3.11 -c "
import coremltools as ct
from transformers import AutoTokenizer
m = ct.models.MLModel('models/mxbai-embed-large-v1.mlpackage')
t = AutoTokenizer.from_pretrained('mixedbread-ai/mxbai-embed-large-v1')
tok = t('Hello world', return_tensors='np', padding='max_length', truncation=True, max_length=512)
r = m.predict({'input_ids': tok['input_ids'].astype(np.float32), 'attention_mask': tok['attention_mask'].astype(np.float32)})
print(list(r.values())[0][0,:5])
"
```

### TC-2：搜尋結果一致性

**目的**：確認改用 ANE 後，search API 的回傳結果與原本一致或更好

**方法**：
1. 對同一影片執行 `POST /api/v1/search/universal` 使用相同 query
2. 比較 ANE 版與 Ollama 版的 top-5 結果

**測試 queries**：
| Query | 預期包含 |
|-------|---------|
| `"Cary Grant"` | 含有 Cary Grant 的 chunk |
| `"how do you shave"` | 對話原文 chunk |
| `"Audrey Hepburn"` | 含有 Audrey Hepburn 的 chunk |
| `"thank you"` | 含有 "thank you" 的對話 |

**指令範例**：
```bash
curl -s -X POST http://localhost:3003/api/v1/search/universal \
  -H "Content-Type: application/json" \
  -d '{"query":"Cary Grant","uuid":"417a7e93860d70c87aee6c4c1b715d70","limit":5}' | jq '.results[] | {score, text: .text[:60]}'
```

### TC-3：效能基準

**目的**：確認 ANE embedding latency

**方法**：對不同長度 text 各執行 10 次，取平均 latency

| Text 長度 | 內容範例 |
|-----------|---------|
| short (10B) | `"Hello world"` |
| medium (50B) | `"Cary Grant and Audrey Hepburn star in Charade."` |
| long (500B) | `"word " * 100` |
| 中文 short | `"你好世界"` |
| 中文 long | `"歡迎收看老電影節目 " * 20` |

**預期 latency**（M4 Mac Mini）：
| 方案 | short | medium | long | 中文 |
|------|-------|--------|------|------|
| **CoreML ANE** | ~60ms | ~60ms | ~60ms | ~60ms |
| Ollama (基準) | ~50ms | ~80ms | ~150ms | ~100ms |

> ANE latency 理論上不隨 text length 變化（固定 padding 到 512 tokens）

### TC-4：搜尋 API 端點功能

**目的**：確認所有相關 API 端點在 ANE 整合後仍正常運作

| Endpoint | Method | 測試方式 |
|----------|--------|---------|
| `/api/v1/search/smart` | POST | 執行 query + 確認回傳 200 + 有 results |
| `/api/v1/search/universal` | POST | 執行 query + 確認回傳 200 + 有 results |
| `/api/v1/search/frames` | POST | 執行 query + 確認回傳 200 |

### TC-5：邊界情況

| 測試 | 方法 | 預期 |
|------|------|------|
| 空字串 query | `{"query":""}` | 回傳 400 或空結果 |
| 超長 query | 5000+ chars | 正常截斷回傳 |
| 無 uuid | `{"query":"test"}` | 回傳 400（uuid required） |
| 不存在的 uuid | `{"query":"test","uuid":"xxx"}` | 回傳空結果或 404 |

---

## 3. 接受標準

| # | 標準 | 必須 |
|---|------|------|
| 1 | ANE embedding output 與 Ollama cosine similarity > 0.99 | ✅ |
| 2 | 搜尋結果 top-3 與改動前一致 | ✅ |
| 3 | API endpoint 回傳 200 + 有效 JSON | ✅ |
| 4 | ANE latency 不高於 Ollama latency | ✅ |
| 5 | 空字串 / 超長 query 不 crash | ✅ |

---

## 4. 測試環境

| 項目 | M4 | M5 |
|------|-----|-----|
| 角色 | 測試者 | 開發者 |
| Playground | port 3003 | port 3003 |
| CoreML ANE model | `models/mxbai-embed-large-v1.mlpackage` | ✅ |
| Ollama 基準 | `localhost:11434` | `192.168.110.201:11434` |

---

## 5. 注意事項

1. ANE 首次推論可能較慢（model loading），需 warmup
2. CoreML model 的 `compute_units` 需設為 `ComputeUnit.ALL` 以啟用 ANE
3. 若 ANE 不可用，應自動 fallback 到 CPU/GPU（ComputeUnit.ALL 會自動選擇）
