索引管線
追蹤檔案如何從監控 inbox,經過 parsing、SQLite persistence、OpenAI embedding,最後重建 vector cache。
Pipeline stage
inbox → watcher diff → parser → Upsert → pending rows → OpenAI → UpdateEmbedding → vector rebuild
每個已載入資料庫都會在 daemon 共用的 cancellation context 下執行一個 watcher goroutine 與一個 embedding-scheduler goroutine。
Watcher snapshot
每 10 秒,runWatcher 會對資料庫 inbox 呼叫 filesystem.WalkFiles。Walker 會遞迴建立包含下列資料的 snapshot:
| Field | 用途 |
|---|---|
Size |
偵測檔案大小變更 |
ModTime |
偵測內容或直接 entry 變更 |
IsDir |
區分檔案與遞迴 directory node |
Children |
保留前次 subtree,供比較與移除偵測 |
成功掃描後,daemon 會非同步將 snapshot 寫入 {db}/record.json。下次啟動時會載入已儲存的 snapshot。
Watcher 依賴直接 directory entry 的 mtime 行為,只針對支援的本機 macOS 與 Linux filesystem 設計。
Parser dispatch
變更的非目錄 entry 會依 extension 或 text sniffing 選擇:
| 輸入 | Parser path | Chunking 行為 |
|---|---|---|
.pdf |
go_pkg_parser.PDF |
Document parser chunks |
.docx |
go_pkg_parser.Docx |
Document parser chunks |
.pptx |
go_pkg_parser.PPTX |
Presentation parser chunks |
.csv, .tsv |
經 parseTabular 呼叫 go_pkg_parser.CSV |
依 header,每五筆 data row 一組 |
.xlsx |
經 parseTabular 呼叫 go_pkg_parser.XLSX |
依 header,每五筆 data row 一組 |
| 其他有效 UTF-8 文字 | go_pkg_parser.Markdown |
Markdown/text chunks |
Text sniffing 最多讀取 8,192 bytes,拒絕包含 NUL、無效 UTF-8 或空白的 sample。
略過的輸入
.DS_Store 會依名稱略過。已知的 image、video、audio、archive、executable、database、design、font 與其他 binary extension 會在 parsing 前略過,避免 image base64 與不支援的 binary data 進入文字 embedding pipeline。
Transactional upsert
databaseHandler.Upsert 是 filesystem layer 使用的唯一內容寫入入口。它會在同一 SQLite transaction 中:
- 將此 source 目前所有 active row 標記為
dismiss = TRUE。 - 插入每個目前 chunk,或更新
(source, chunk)conflict。 - 將目前 chunk 恢復為
dismiss = FALSE。 - 只有在 chunk content 未變時保留 embedding。
- 清除已變更的 embedding,並重設
is_embed = FALSE。
此順序可處理縮短的檔案,不會回傳過期的尾端 chunk,也不會重新 embedding 未變更的 chunk。
已移除檔案
掃描目前 entry 後,walker 會比較先前 snapshot。缺少的 file node 會遞迴傳給 databaseHandler.Dismiss,透過設定 dismiss = TRUE 將 active row soft-delete。資料仍保留在 SQLite,但 indexing 與 query read 都會排除。
Embedding scheduler
每五秒,runEmbedder 會選取最多 64 筆符合下列條件的 row:
is_embed = FALSE AND dismiss = FALSE
Scheduler 將其 content 送至 EmbedBatch。過大的 input 會在 request 前截斷為 8,000 個 Unicode rune。Client 使用 text-embedding-3-small、512 dimensions 與一分鐘 HTTP timeout。
若 OpenAI 回傳錯誤數量的 vector,或任一 vector 維度錯誤,整個 batch 會被拒絕。
Race-safe 套用
UpdateEmbedding 只會在下列條件都仍相符時更新 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。
Daemon 啟動時,cache 會從 SQLite 中 active 且已 embedding 的 row 重建,再重建所有 source vector。記憶體 cache 遺失時,SQLite 仍是權威來源。
操作時間
Pipeline 為非同步。正常狀況下,變更檔案可能等待最多一個 watcher interval 才開始 parsing,再等待最多一個 embedding interval 才被選取,另加 OpenAI request 時間。Keyword retrieval 可在 parsing 後、embedding 前使用 content;semantic retrieval 必須等待 vector 成功套用。