「我想做一個隱私優先的檢索增強生成應用,文檔永遠不離開瀏覽器。」開發者這樣開場。聽起來理想主義,但「搜索」這一步在5000個向量時就崩了。
這不是算法問題。余弦相似度不過是點積除以兩個范數,但10000個向量×1536維=單次查詢1500萬次浮點乘法。JavaScript的垃圾回收器才不管你是不是在熱循環里——它想停就停。
![]()
實測:純JS的硬天花板
他測遍了所有客戶端庫。10000向量×1536維,client-vector-search耗時37毫秒,MeMemo的HNSW降到17毫秒,但建索引要幾分鐘;VecLite做到8毫秒,仍是它的兩倍慢。突破10萬后,純JS開始吃癟——單次搜索1.5秒以上。
生態位的空缺很明顯:沒有東西能填上「玩具庫(上限1千向量)」和「生產級向量庫(需要服務器)」之間的鴻溝。他需要在瀏覽器里跑10萬向量、亞秒級延遲。
第一個抉擇:留在JavaScript,還是下沉到更底層?他選了Rust編譯成WebAssembly,后續所有決定都從這里 cascade(級聯)開來。
Rust/WASM的三張底牌
這不是推測。PGlite、DuckDB-WASM、Figma都在用同一套架構,工具鏈成熟。相比JavaScript,Rust/WASM提供三樣東西:
一是接近原生的速度,WASM的浮點運算沒有JS的解釋開銷;二是內存布局可控,向量用連續的Float32Array存儲,沒有JS對象的指針跳躍;三是沒有垃圾回收,內存生命周期由開發者管理,不會在搜索中途卡頓。
但架構有三層嚴格分界:TypeScript、WASM邊界、Rust。邊界的規則比算法更重要。
邊界開銷:一次調用 vs 一萬次調用
每次WASM穿越都有成本。調用一次WASM傳10000個向量,和調用10000次每次傳1個向量,差距是40毫秒對4秒——兩個數量級。
實際的上傳路徑被壓縮成單次穿越:TypeScript負責校驗、扁平化、序列化,然后一次跨過邊界。
所有向量以f32存儲和計算,絕不用f64。這是刻意選擇:f64的精度對語義搜索是浪費,但內存和帶寬成本翻倍。在瀏覽器里,每個字節都值錢。
存儲層:故意做得「蠢」
VecLite的存儲適配器只有四個方法:get、set、delete、clear。鍵值接口,越簡單越好。
這個設計把持久化策略完全踢給調用方。想用IndexedDB?自己包一層。需要加密?在外面做。存儲層不碰這些,只保證向量索引本身足夠快、足夠小。
這種「愚蠢」是刻意的。瀏覽器環境太碎片化,試圖內置智能存儲只會變成配置地獄。把復雜度推出去,核心庫保持原子性。
為什么這件事值得看
這不是又一個Rust重寫的故事。它戳中了一個被忽視的中間地帶:客戶端AI的「最后一公里」——嵌入模型可以在瀏覽器跑(Transformers.js),但向量搜索成了瓶頸。
他的選擇揭示了一條務實路徑:不用等瀏覽器原生支持,也不倒退回服務器,而是用成熟工具鏈把計算密度壓進客戶端。隱私優先的RAG、離線可用的語義搜索、零API成本的個人知識庫——這些場景突然變得可行。
當然,10萬向量在瀏覽器里仍是小眾需求。但生態位空著的時候,先占住的人定義標準。VecLite的存儲適配器設計尤其聰明:它不假裝能解決所有問題,而是讓能解決的人無縫接入。
下次有人再說「瀏覽器做不了重計算」,你可以把這篇甩過去——順便提醒他,垃圾回收器可不會看場合暫停。
特別聲明:以上內容(如有圖片或視頻亦包括在內)為自媒體平臺“網易號”用戶上傳并發布,本平臺僅提供信息存儲服務。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.