語意搜尋
說明 semantic branch 如何 embed query、以 source vector 縮小候選、排序 chunk,並從 SQLite hydrate 命中結果。
Semantic query embedding
Semantic branch 先使用完整 query string 查詢 openai.Cache。Cache miss 時,會向 OpenAI 傳送單項 batch(text-embedding-3-small、512 維),並把 vector 存入記憶體。
| Process | Cache miss 之後 |
|---|---|
| Daemon | 透過 cache 的 OnSet callback 非同步持久化至 global.db,timeout 5 秒 |
kura mcp |
未註冊 OnSet callback;vector 只存在該 session 的記憶體 |
兩種 process 啟動時都會從 global.db 預載 query cache,只保留 byte length 等於 openai.Dim() * 4 的 blob。Preload 會繞過 OnSet,因此不會重寫同一 entry。
兩階段 vector search
每個 database bucket 都儲存 chunk vector,以及每個 source 的一個 derived vector:該 source 同維度 chunk vector 加總後做 L2 正規化,chunk 變動時重建。
階段一:Source candidate
KuraDB 會計算 query 與每個同維度 source vector 的 cosine similarity。Candidate 數量為:
min(max(number of sources / 20, 20), number of sources)
大型 source set 約保留 5%;若數量足夠,至少保留 20 個。
階段二:Chunk ranking
KuraDB 收集 candidate source 的 chunk ID,再為每個 chunk 計算同維度 cosine similarity。Worker 數量為 min(CPU count - 1, chunk count / 200, chunk count),至少一個,因此資料量足夠時每個 worker 至少處理 200 chunks。Hit 依 score 降冪排序,再截斷為 topK(即 request limit)。
若沒有 source vector,search 會 fallback 為直接排序所有 chunk vector。
Score 過濾與 hydration
搜尋核心會移除 score 低於 0.3 的 semantic hit,接著以 dismiss = FALSE 從 SQLite 取得其餘 ID、恢復 vector ranking order,並丟棄已無法解析為 active row 的 ID。
即使記憶體 state 同時發生變更,此 final hydration 仍維持 SQLite 的權威性。留下的 row 如何分組與回傳,見搜尋結果。