# Embedding

查看 pending chunk 如何透過 OpenAI 產生 embedding、無 race 地套用至 SQLite，並載入記憶體 vector cache。

## Embedding scheduler

每五秒，`runEmbedder` 會依 ID 由小到大選取最多 64 筆符合下列條件的 row：

```sql
is_embed = FALSE AND dismiss = FALSE
```

Scheduler 將其 content 送至 `EmbedBatch`。超過 8,000 個 Unicode rune 的 input 會在 request 前截斷。Client 使用 `text-embedding-3-small`、512 dimensions 與一分鐘 HTTP timeout。

若 OpenAI 回傳錯誤數量的 vector，或任一 vector 維度錯誤，整個 batch 會被拒絕。被拒絕或失敗的 batch 只記錄 log，row 維持 pending，下一個 tick 會重試。

## Race-safe 套用

`UpdateEmbedding` 在同一 transaction 中執行，只會在下列條件都仍相符時更新 row：

- Row ID 未變。
- `dismiss = FALSE`。
- 儲存的 content 與送去 embedding 的 content 相同。

此設計可避免緩慢 OpenAI response 在檔案變更後附加過期 vector。Function 只回傳實際套用更新的 ID。

## Vector cache 更新

對已套用的 ID，KuraDB 會將每個 vector 儲存至 database bucket、追蹤 source-to-chunk relation，並重建每個受影響的 source vector。Source vector 是有效且同維度 chunk vector 的 normalized sum。

執行中的 cache 只會新增、不會淘汰。已 dismiss 的 row 的 vector 會留在記憶體直到重啟；content 已變更的 chunk 在新 vector 套用前仍保留舊 vector。Semantic 結果的內容仍正確，因為 hydration 以 `dismiss = FALSE` 讀取 SQLite，會丟棄已 dismiss 的 row 並回傳目前文字。

Daemon 啟動時，cache 會從 SQLite 中 active 且已 embedding 的 row 重建，再重建所有 source vector。記憶體 cache 遺失時，SQLite 仍是權威來源。
