Zvec 把本地向量搜尋變成函式庫
我把 Zvec 拆成一套可直接抄的本地向量搜尋做法:把檢索塞回應用程式、把混合搜尋和全文檢索放進同一個引擎。

我把 Zvec 拆成一套可直接抄的本地向量搜尋做法:把檢索塞回應用程式、把混合搜尋和全文檢索放進同一個引擎。
我用向量搜尋堆疊一陣子了,越用越火大。我要的明明只是把檢索功能做進產品,結果很快就變成我要顧另一台服務、調連線逾時、補權限、還要跟人解釋為什麼一個「簡單相似度搜尋」會長成半套平台。更煩的是,查詢一旦複雜起來,dense vector、metadata filter、全文檢索、fallback 路徑全部一起來,整個系統開始像在跟我道歉,而不是幫我做事。
我會注意到 Zvec,是因為它走的是反方向。它不是叫我再養一個 box,而是直接把資料庫塞回進程裡,讓延遲留在本地、設定保持單純。這種做法對很多應用才是正解,尤其是檢索層本來就是產品的一部分,不是獨立基礎設施。這篇我主要拆的是 Zvec GitHub 倉庫,以及它 README、文件與 release notes 透露出來的思路。
我先講清楚:我沒看到官方在頁面上明列 star 或 fork 數字,所以我不亂寫。對我來說,比數字更重要的是它怎麼把搜尋、儲存、過濾壓成一個本地單元,這才是它真正想解的問題。
別再把向量搜尋當成獨立服務養
訂閱 AI 趨勢週報
每週精選模型發布、工具應用與深度分析,直送信箱。不定期,不騷擾。
不會寄垃圾信,隨時可取消。
「Zvec 是一個開源、in-process 的向量資料庫,輕量、快速,並且設計成可以直接嵌進應用程式。」
這句話翻譯一下就是:我不用每次查資料都繞一圈網路。資料庫跟我的程式活在同一個進程裡,故障模型整個變簡單。我要是做桌面應用、邊緣工具、Notebook 工作流,或是某個本來就只需要本地檢索的服務,這種架構就很香。

我看過太多「先加向量搜尋」最後變成「要不要順便找個維運」的案例。問題不是向量不行,是大家一開始就把它當平台在養。Zvec 的 in-process 其實是在砍掉那些多餘儀式:不用另外部署、不用服務發現、不用先搞遠端連線池,才開始驗證功能值不值得做。
實操上,我會這樣判斷:如果檢索路徑本來就屬於你的應用邊界,而且資料量跟查詢量還在單機可控範圍內,那本地內嵌通常比較乾淨。要是你真的需要多租戶協調、跨節點共享、超大規模併發,那就是另一題,不要硬拿 embedded DB 硬拗。
- 適合:本地應用、agent、桌面工具、內部小工具、邊緣工作負載。
- 不適合:多節點共享服務,資料庫本身必須是中心平台。
Zvec 最有意思的地方,是它把這個取捨講得很直白。它不是想當你的分散式搜尋後端,它是想當你嵌進去就能用的那顆搜尋引擎。
混合搜尋才是我真的在乎的部分
「Hybrid Search:把向量相似度、全文檢索和結構化過濾合併到同一個查詢裡,得到更精準的結果。」
這句我很有感,因為純向量搜尋在真實產品裡常常太飄。使用者要的不是「語意上有點像」,而是「這件事要符合硬條件」。例如他想找的是「上週的 billing issue,而且在歐洲區」,不是「跟 billing 有關」。如果檢索層不能把語意、文字、條件一起處理,我最後就得在應用層自己補一堆很醜的後處理。
Zvec 的做法比較務實:dense vectors、sparse vectors、文字欄位、scalar filter 放在同一條查詢路徑裡。這就是我想要的檢索形狀,因為它同時回答兩件事:這東西內容上像不像,以及它有沒有符合規則。
我之前自己做過類似系統,最常犯的錯就是太迷信 embedding,卻把那些很 boring 的欄位丟掉。結果每個查詢都在補召回率,最後系統像在打補丁。混合搜尋的價值,就是讓每一種訊號做自己擅長的事。
實操寫法很簡單:先把你真的需要限制的欄位建模出來,不要什麼都丟進 embedding 期待模型自己懂。文字欄位保留全文檢索,結構化資料保留 filter,語意相似度才交給向量。這樣你的檢索流程才講得清楚,也比較不會把同事一起搞瘋。
- 向量搜尋負責語意相似。
- 全文檢索負責精確或接近精確的字詞。
- 過濾條件負責 tenant、日期、地區、類型這種硬限制。
同一個引擎內建全文檢索,才是真的省事
「原生全文檢索:可以替任何字串欄位掛上 FTS 索引,直接用自然語言或結構化表達式查詢,不需要外部搜尋引擎。」
這種功能很容易被低估,直到你真的被迫再接一套文字搜尋系統。然後事情就開始變複雜:資料要同步進向量庫和全文引擎,排名邏輯還可能各自漂掉。我看過這種架構,通常最後都很醜。

Zvec 的 FTS 之所以有價值,是因為它把檢索故事留在本地,而且還統一在同一個引擎裡。只要資料已經在 collection 裡,我就能把字串欄位掛上全文索引,不需要再去找另一個服務。這代表更少同步 bug、更少奇怪的「為什麼這筆資料在這個索引裡,卻不在那個索引裡」的鬼故事。
實操上,我會建議你從第一天就把文字搜尋算進去。就算你的主力是 embedding,使用者最後還是會想查精確片語、人名、ID、或某種結構化文字。不要把這件事當成之後再補,因為通常「之後」就會變成一個清理專案。
我自己的理解是這樣:embedding 回答「像不像」,FTS 回答「字面上有沒有」,filter 回答「允不允許」。Zvec 把這三件事塞進同一個引擎,才讓查詢邏輯不會散成一地。
磁碟型索引是它沒裝可愛的地方
「DiskANN 索引:新的磁碟上索引,把大部分索引放在磁碟上,大幅降低大規模資料集的記憶體使用量。」
我對任何號稱能擴充的向量資料庫都先半信半疑,因為錢通常是從記憶體那邊燒出來的。很多系統小的時候很漂亮,一旦索引長大,就變成你花錢買一台機器,只為了把資料硬塞進 RAM。這種事我真的看膩了。
Zvec 的 DiskANN 支援比較誠實,它直接承認:不是每個資料集都值得全塞進記憶體。把大部分索引留在磁碟上,對較大的 collection 來說是更務實的折衷,尤其是你還想維持本地嵌入與檢索,而不是再拉一個叢集出來。
實操上,我會這樣切:資料小、追求極低延遲時,先用 in-memory 索引;當你開始感受到記憶體壓力,就換成磁碟感知的索引。不要等到主機開始 swap 了才說「咦怎麼這麼慢」。
我喜歡它把這件事講成工程選項,而不是行銷口號。因為「快」不等於「永遠都必須把全部東西放在 RAM 裡」。
- 先用記憶體索引做原型和小型資料集。
- 資料量開始超出便宜 RAM 的承受範圍時,再切磁碟型索引。
持久化和併發,才是救你命的無聊功能
「寫前日誌(WAL)保證持久化:就算行程當掉或斷電,資料也不會消失。」
這種功能在 demo 裡最容易被忽略,然後你真的上線之後就會後悔。 local database 只有在重啟後還能活著,才真的有用。WAL 不帥,但它就是讓我敢把這東西放進真實應用的原因。
併發那段我也覺得講得很老實:多個行程可以同時讀同一個 collection,但寫入是單進程獨占。這個取捨很明確,我反而欣賞。它告訴我這套東西是為什麼場景設計的:共享讀、受控寫、本地擁有。
實操上,你要先決定你的應用到底是單寫者還是多寫者。如果能把寫入集中起來,模型就很簡單;如果你需要分散式寫入,那你就已經回到協調問題了,不要假裝一個內嵌資料庫會幫你把這題魔法解掉。
我看過團隊把持久化拖到最後才處理,第一次壞掉才開始認真備份。Zvec 的 WAL 至少讓你可以從一開始就把復原設計進去,而不是等事故來教育你。
SDK 和工具鏈一多,我才會相信它不是玩具
「官方提供 Go / Rust SDK、Zvec Studio 視覺化工具,還有 RISC-V 支援。」
一個專案如果有多種語言綁定,我才會開始認真看。這份倉庫除了主打的 Zvec 倉庫,還提到 Python、Node.js、Go、Rust、Dart/Flutter 等支援,外加一個視覺化工具。這很重要,因為真正的資料庫通常不是最潮的那個,而是你實際產品堆疊能直接呼叫的那個。
我也喜歡它把 zvec.org、release notes,以及 README 裡的文件和 benchmark 連結都放出來。這種專案通常不是只想秀一個 demo,而是真的希望你去測、去比、去用。
實操上,我會先選你正式上線會用的語言綁定,再去驗證查詢語意。不要在 Python 原型跑得很順,就以為 Go 版也會一模一樣。我以前就被這種假設坑過。概念一樣,手感不一定一樣,邊角也常常不一樣。
如果是我自己評估 Zvec,我會先用 Python 包快速試 schema 和查詢形狀,再把同一套結構搬到真正要出貨的語言。這樣才知道抽象到底是真的,還是 README 看起來很美而已。
我會怎麼真的把它用進產品
我的短版答案是:如果我想把檢索留在應用邊界內,而不是再養一個平台,我就會考慮 Zvec。像產品功能、agent 記憶層、本地語意索引、內部工具這些場景,都很適合。只要程式本身掌握資料生命週期,而且你想要低延遲搜尋,不想再多一層服務,它就有機會派上用場。
但我不會拿它當偷懶藉口,假裝自己不用思考規模問題。如果產品需要很多寫者、分散式協調、或多節點共享服務,我會把那當成另一種架構,不會硬把 embedded DB 塞進去。內嵌資料庫很強,但前提是你要把它放在對的位置。
這也是我覺得 Zvec 值得看的原因:它不像某些工具那樣硬裝成平台。它比較像一個邊界清楚、功能務實的工具,直接把本地、快速、混合搜尋、持久化這幾件事收進同一個地方。
可抄的模板
# Zvec 本地混合搜尋模板(繁中版,可直接改成你的專案格式)
## 1) 資料結構
- collection_name: "docs"
- 向量欄位:embedding(維度依模型決定)
- 文字欄位:title、body(啟用全文檢索)
- 結構化欄位:tenant_id、doc_type、created_at
## 2) 匯入規則
- 只讓單一寫入流程負責寫資料
- 開啟 WAL
- 原文內容與 embedding 一起存
- 先把 metadata 正規化,再寫入
## 3) 查詢規則
- 向量搜尋負責語意相似
- 全文檢索負責字面匹配
- filter 負責 tenant、日期、類型等硬條件
- 同一條查詢路徑內完成合併
## 4) 部署原則
- 本地延遲優先時,使用 in-process
- 記憶體開始吃緊時,改用磁碟型索引
- 讀取允許併發
- 寫入維持單寫者,除非你另外有協調層
## 5) 給團隊的說法
「我們把 Zvec 放進應用程式裡,讓檢索留在本地。
查詢要同時用語意、文字和條件過濾。
除非真的證明需要,不然不要再多拉一個搜尋服務。」
## 6) Python 風格偽碼
import zvec
schema = zvec.CollectionSchema(
name="docs",
vectors=zvec.VectorSchema("embedding", zvec.DataType.VECTOR_FP32, 768),
)
collection = zvec.create_and_open(path="./data/zvec", schema=schema)
collection.insert([
zvec.Doc(
id="doc_1",
vectors={"embedding": [0.1, 0.2, 0.3, 0.4]},
fields={
"title": "當機復原筆記",
"body": "WAL 可以讓本地資料在重啟後保持持久化",
"tenant_id": "acme",
"doc_type": "note",
"created_at": 1710000000,
},
)
])
results = collection.query(
zvec.MultiQuery(
vector={"field": "embedding", "value": [0.4, 0.3, 0.3, 0.1]},
text={"field": "body", "query": "當機復原"},
filters={"tenant_id": "acme", "doc_type": "note"},
),
topk=10,
)
print(results)這份模板是我根據 Zvec 的 README、文件連結和 release notes 重新整理出來的,屬於衍生整理,不是原文照抄。你可以直接拿去改成自己的 schema、查詢形狀和團隊規範。
原始來源 URL:https://github.com/alibaba/zvec。我這篇的拆解是原創整理,基礎觀點來自倉庫 README、文件與 release notes 的公開內容。