搜尋與檢索
了解 KuraDB 如何準備 query、執行 keyword 與 semantic retrieval,並建立安全的 API result。
Strategy 選擇
GET /api/search 接受可選的 target parameter:
target |
執行工作 | Response key |
|---|---|---|
| 省略或空值 | 並行執行 keyword 與 semantic branch | keyword, semantic |
keyword |
只執行 keyword branch | keyword |
semantic |
只執行 semantic branch | semantic |
兩個 branch 使用相同的 loaded database 與 request context。若任何已選 branch 回傳 error,handler 會回傳 HTTP 500,而不是 partial data。
Keyword retrieval
Keyword branch 會呼叫 segmenter.Tokenize,其流程如下:
- Trim 完整 query。
- 使用內嵌的 gse
zh_sdictionary 與 stop-word data。 - 執行 search-mode segmentation。
- Trim token、轉為 lowercase、移除空值,並依遇到順序去重。
SearchKeyword 會為每個 token 建立 case-insensitive SQLite LIKE predicate。Row 必須符合 dismiss = FALSE,並至少命中一個 token。
Ranking 具確定性:
- 依 matched-token count 降冪。
- 依 row ID 升冪。
- 套用 request limit,預設 10。
Semantic query embedding
Semantic branch 先使用完整 query string 查詢 openai.Cache。Cache miss 時,會向 OpenAI 傳送單項 batch。成功的 vector 會存入記憶體,並透過 cache 的 OnSet callback 非同步持久化至 global.db。
啟動時只預載 byte length 等於 openai.Dim() * 4 的 query-cache blob。Preload 會繞過 OnSet,因此不會重寫同一 entry。
兩階段 vector search
每個 database bucket 都儲存 chunk vector,以及每個 source 的一個 derived vector。
階段一: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。工作最多拆分至 CPU count - 1 個 worker,每個 worker 目標至少 200 chunks。Hit 依 score 降冪排序,再截斷為 topK。
若沒有 source vector,search 會 fallback 為直接排序所有 chunk vector。
Score 過濾與 hydration
API layer 會移除 score 低於 0.3 的 semantic hit,接著以 dismiss = FALSE 從 SQLite 取得其餘 ID、恢復 vector ranking order,並丟棄已無法解析為 active row 的 ID。
即使記憶體 state 同時發生變更,此 final hydration 仍維持 SQLite 的權威性。
分組
兩個 flat result set 都依首次出現的 source 分組。External representation 為:
[
{
"source": "/path/to/document.md",
"matches": [
{"chunk": 1, "content": "..."},
{"chunk": 2, "content": "..."}
]
}
]
已選 branch 無結果時表示為 []。
私有 ranking data
Handler 在 serialization 前會刻意移除內部 field:
| 內部 field | 保持私有的原因 |
|---|---|
| Row ID | Storage implementation detail |
| Semantic score | Threshold 與 ranking 可能調整 |
| Keyword hit count | 內部 ranking signal |
| Chunk total | 不屬於 consumer contract |
| Overall total | API 不提供 pagination metadata |
Consumer 應只依賴 source、matches、chunk 與 content。
一致性行為
- Keyword SQL 一律過濾 dismissed row。
- Semantic hydration 一律過濾 dismissed row。
- 維度與 query 不同的 vector 會被忽略。
- Query cache 以完整 query text 為 key,不做 normalization。
- Keyword 與 semantic result array 維持獨立;KuraDB 不會混合其 score。
- 只有目前 daemon 啟動時載入的 database 可供搜尋。