向量、檢索與 RAG:一套「照著資料說話」的搜尋系統是怎麼搭起來的
一、字面 vs 意思:老式搜尋輸在哪
傳統的站內搜尋、政府門戶的檢索框,絕大多數是關鍵字檢索:把你的問句拆成詞,去文件裡找「包含相同字詞」的條目。它的死穴是——使用者說話的方式,和文件寫字的方式,幾乎從不一致。
你問「東西弄壞了要賠多少」,法律文件寫的是「損害賠償」「侵權責任」——一個字都對不上。關鍵字檢索於是漏掉了最該命中的兩篇,反而因為「賠償」二字命中了一篇《賠償金計算器使用說明》,答非所問。

語義檢索換了個問法。它不問「有沒有相同的字」,改問「兩段話的意思像不像」。同義改寫、跨語言、舊式表述,只要意思一致,它都能撈回來。這不是玄學,背後是一套很硬的數學,核心叫 embedding。
二、Embedding:把每段文字放進一張「意思地圖」
一個 embedding 模型,做的事情是把任意一段文字,壓成一串固定長度的數字——比如 1024 個數。這串數字不是隨機的,它是這段話在一個 1024 維空間裡的座標。
模型經過海量訓練,學會了一件事:意思相近的文字,座標點靠得近;意思無關的,離得遠。於是「損害賠償」和「侵權責任」這兩個點在地圖上擠成一團,「紅燒肉」「火候」則落在遙遠的另一片區。

到這一步,「搜尋」就被翻譯成了一道幾何題:把使用者問句也變成一個座標點,然後量它離哪些文件座標最近。近的就是相關的。字面對不對得上,從此無關緊要。
好的 embedding 模型還是雙語的(比如 bge-m3),中文問句和英文文件能落在同一片區——這意味著中文提問也能召回英文資料,這是關鍵字檢索永遠做不到的。
三、RAG:讓大模型「照著資料說話」
光有檢索還不夠。使用者要的不是一堆文件連結,是一個直接的答案。但你又不能讓大模型自由發揮——它會一本正經地編造事實(幻覺)。RAG(檢索增強生成)就是把「檢索」和「大模型」焊在一起的方案:先檢索出真實資料,再逼著模型只能照著這些資料回答。
整條鏈路分兩段:一段離線建庫,一段線上問答。

離線段(內容更新時才跑一次):原始資料 → 清洗切塊 → 每塊 embed 成向量 → 連同「出處」一起存進向量庫。這一段是重活,但不用天天做。
線上段(每次提問都跑):問句 embed → 向量庫召回最相關的幾段 → 把這幾段原文塞進提示詞當「依據」→ 大模型照著依據串流作答 → 附上來源連結。
這裡藏著零幻覺的關鍵設計:來源連結是「檢索結果」直接給出的,不經過大模型。模型只負責把話說得像人,不負責報出處——所以它編不出一個指向不存在文章的連結。這一條分工,是「AI 客服敢掛在專業場景上」的底氣:答錯一句可能有責任的行業(律所、醫療、政務),靠的就是「每句話都能點回原文」。
一句話概括:檢索負責「找對料」,大模型負責「說人話」,兩件事拆開,各司其職,才既準又像人。
四、向量資料庫在算什麼:量角度、排序、卡門檻
「向量庫召回」這一步,聽著神秘,拆開只有三個動作。
相似度用的是餘弦相似度——只看兩個向量的方向像不像,不管它們多長。方向完全一致是 1,毫不相干是 0。為什麼用角度不用長度?因為這樣長文件和短文件能公平比,不會因為一篇文章字多就佔便宜。

拿到所有候選的相似度後,再過兩道關:
| 關卡 | 做什麼 | 為什麼 |
|---|---|---|
| topK = 6 | 按相似度從高到低排,最多只取前 6 段 | 控制餵給模型的量:太多會讓噪音淹沒重點,也費錢 |
| 閾值 0.35 | 低於這個相似度的,一律丟棄 | 寧可回答「資料裡沒有」,也不硬湊不相關的段來充數 |
這兩個數字(6 和 0.35)是可調的旋鈕。調高閾值 → 答得更保守但可能漏;調低 → 召回更多但可能帶進噪音。這是每個 RAG 系統都要根據自己語料去磨的手感。
五、系統架構:一套內核,兩副「手」
原理講完,看工程。一個真正跑在生產上的這類系統,最值錢的架構決策是:讓核心邏輯徹底不認得自己跑在哪。
做法叫依賴注入 + 介面抽象。核心(core)是一堆純函數——頁面渲染、RAG 編排、表單處理——它們不直接呼叫任何具體的雲端服務,只對著三個抽象介面編程:AI 模型、向量庫、存檔。至於這三個介面背後是誰在實現,由部署時注入的「手」決定。

於是同一份程式碼,能落在兩種完全不同的地基上:
| 手 A · 雲端託管(Cloudflare) | 手 B · 自託管(Node / VPS) | |
|---|---|---|
| AI 模型 | Workers AI(平台內建) | 任意 OpenAI 相容 API(DeepSeek/自建 vLLM) |
| 向量庫 | Vectorize(託管服務) | sqlite-vec(本地檔案) |
| 存檔 | D1(託管 SQL) | SQLite 檔案 |
| 運維 | 零運維,全球邊緣,按量計費 | 自己管一台機器一個行程 |
| 身分面 | 三個服務都掛在同一個雲端帳戶下,集中 | 不綁雲端帳戶,資料在自己機器上,可換非實名主機 |
| 切換方式 | deploy --target=cf | deploy --target=node |
換客戶站,只改一張租戶配置表(網域/品牌/知識庫來源/系統提示詞);換地基,只換一副適配器。核心 core 一個字不用動。這就是「可移植」的全部含義——你的系統不再是某一家雲端的人質。
實測更正一筆:此前專案內部文件一直寫著「Node 適配器本地跑不了」。2026-07-13 我實際把它跑起來驗證了:
TENANT=demo node server.js零安裝依賴直接起,首頁/檢索/搜尋/健康檢查全部 200。文件那句是舊 note,已訂正。真實AI_API_KEY下的端到端問答、sqlite-vec 在真實 VPS 上的編譯、以及帶 TLS/行程守護的正式部署,這三項還沒驗證——誠實標注,不吹成「已完成」。
六、優雅降級:小體量根本不需要重裝備
這是整套設計裡我最欣賞的一處工程克制,也是性價比的題眼。
「向量資料庫」聽起來是個必須專門部署的重傢伙(Pinecone、Weaviate 那一檔)。但自託管這副手做了件聰明事:啟動時先試著裝專業向量引擎(sqlite-vec);裝不上,就自動降級——把所有向量讀進記憶體,每次查詢挨個算餘弦。

關鍵在於規模拐點:
- 幾十到幾百條向量:記憶體暴力算,快得和專業向量庫沒區別。今天實測的 demo 站就走這條路,零依賴,照跑不誤。
- 上萬條起:暴力算開始線性變慢,這時才真正需要 ANN(近似最近鄰)索引跳過大部分候選。
大多數現實場景落在左區。一個縣志館、一份地方報的歷史存量、一本雜誌的全部往期、一家諮詢公司的案例庫——通常就是幾百到幾千篇。為一個你永遠到不了的規模,提前扛來一套分散式向量叢集,是典型的過度工程。這套「裝得上就用好的,裝不上照樣跑」的降級設計,把「你現在到底需要多重的裝備」這個判斷,交還給了真實的資料量,而不是交給焦慮。
七、撞牆之後:效能牆與精度牆怎麼補
第六節講了小體量根本不用重裝備。可萬一體量真漲上去(第六節那個拐點的右側),或者內容天生就精確、答錯有後果,怎麼辦?這裡有兩堵牆,性質截然不同——效能牆是純工程問題,成熟套路一堆,且本架構自帶解藥;精度牆是「生成 + 近似」的固有屬性,只能重壓,不能根治。
效能牆:向量漲到十萬、百萬後怎麼辦
按性價比從高到低排,這些都是業界跑熟的標準手段:
| 方案 | 做什麼 | 代價 / 在本架構裡 |
|---|---|---|
| ①分區 / 分租戶預過濾 | 只搜相關分區(租戶/品類/年款),不搜全庫 | 幾乎零成本;本架構天生分租戶,等於白送——最先該用 |
| ②換 ANN 索引(HNSW / IVF) | 把「挨個算」換成「跳著找」,百萬向量也能亞 10ms | 約 95–99% 召回率;手 A(CF Vectorize)就是託管 ANN,core 一字不動直接畢業 |
| ③量化壓縮(PQ / 二值化) | 向量瘦身:1024 維 float32=4KB,二值化砍到 128B(32×),距離算得飛快 | 精度略降,配「粗篩→精排」補回 |
| ④兩段式檢索 | 廉價粗篩(量化 / 低維 / ANN)選出前幾百,再全精度精排 | 複雜度上升一層 |
| ⑤降維 / Matryoshka | 支援嵌套表示的模型可把 1024 維截到 256 維做首輪 | 召回略降,視 embedding 模型而定 |
| ⑥查詢 / 語義快取 | 高頻問題快取召回結果或整答案 | 省錢又降延遲 |
一條誠實提醒:自託管路徑若用 sqlite-vec,它走的是精確暴力掃描(據我所知 2026 年仍無 ANN 索引,以最新版為準),規模天花板約在百萬級、可接受延遲內;要再往上,得在 Node 側換 pgvector(HNSW) / Qdrant / Milvus。換句話說,效能牆對本系統的標準解法就一句:單機扛不住時切到 CF 手 A——這正是雙執行環境設計的紅利。
精度牆:別拿向量幹精確活
關鍵認知——精度牆不是靠把向量做得更好來補的,而是「把精確的交給精確手段,把語義的交給語義」:
| 補法 | 做什麼 | 專治 |
|---|---|---|
| ①混合檢索(向量 + BM25,RRF 融合) | 語義路撈「意思」,關鍵字路撈「M8×1.25」「第 47 條」這種一字不差的詞 | 型號 / 編號 / 法條 / 藥名 精確命中 |
| ②結構化路由(表格不進向量庫) | 建庫時把規格表 / 參數抽進 SQL,查「扭矩多少」直接查表取精確值 | 數值 / 參數 / 規格 |
| ③交叉編碼重排(bge-reranker 類) | 向量粗召 top-20,再用重排器把 query+段落一起讀、精打分 | 壓「召回了近義但錯的段」 |
| ④抽取式回答 + 強制引用 | 逼模型原樣引出那句 / 那個數並標源,答案裡的數字須逐字出現於召回塊,否則拒答 | 堵「檢索對了、生成時把數字改寫錯」 |
| ⑤元資料過濾 | chunk 掛上車型 / 年款 / 章節標籤,先過濾再檢索 | 防「2021 款問出 2019 規格」 |
| ⑥結構感知切塊 | 別把一張規格表攔腰切開,按標題 / 表邊界切 | 保住表格上下文完整 |
| ⑦圖表專門處理 | 圖紙 / 爆炸圖 → OCR+圖注,或直接返回原圖指向標號,不硬合成 | 誠實處理「看圖才能答」的部分 |
但這裡有條畫不掉的設計邊界:對真正安全關鍵的精確查詢(扭矩、藥物劑量、法條),上面全套壓下來也無法保證「一個會改寫文字的生成模型永遠不出錯」。終極正解不是讓 RAG 生成那個數字,而是讓它檢索並原樣呈現出處——把「說人話」降級為「精準找到並高亮」,把最終判斷交還給人。這是設計邊界,不是待修的 bug。
八、算總帳:這套方案的性價比到底體現在哪
把技術架構翻譯成錢和人力,分四個維度看。
| 維度 | 傳統機構方案(Java + Oracle + Elasticsearch 叢集) | 這套 RAG-on-edge 方案 |
|---|---|---|
| 初期開發 | 採購+整合多個系統,人月起步 | 一套內核 + 一張配置表,新站是「填表」不是「重寫」 |
| 運維 | 專人管資料庫/搜尋叢集/伺服器 | 雲端託管路徑近乎零運維;自託管也只有一台機器一個行程 |
| 擴充成本 | 加一個客戶站 = 又一套部署 | 加一個客戶站 = 加一個租戶配置(多租戶複用同一份 core) |
| 規模適配 | 起步就是重架構,小專案嚴重浪費 | 降級路徑讓小體量零額外成本,規模到了再上重裝備 |
| 供應商鎖定 | 綁死資料庫/中介軟體廠商 | 雙執行環境:雲端帳戶和自建機器之間可遷移,不被單一雲端綁架 |
真正稀缺的不是其中任何單項——聊天機器人有更精緻的 SaaS(Chatbase),靜態站有更成熟的生態(Astro),法律語料有更深的垂類玩家(Harvey AI)。稀缺的是把「零依賴 + RAG 原生 + 雙語 + 建置期清洗」這四件事,以近乎零邊際成本捏在一起。這是大廠懶得做(體量太小不值當)、小工具懶得做(每項都要重新整合好幾個 SaaS)的中間地帶。
但要誠實說一句反面:這門生意的規模天花板不是技術決定的,是商業模式決定的。「代客戶建站+營運、不做自助編輯後台」意味著每個客戶都要真人投入一次——能服務的客戶數是線性增長,不是 SaaS 那種邊際成本趨零的指數曲線。技術再漂亮,也改不掉這條。
尾聲:什麼場景適合,什麼不適合
一套系統的邊界,比它的能力更值得說清楚。而畫這條邊界,第一件事是拆穿一個被反覆誤傳的數字。

先糾一個錯覺:「上千篇」根本不是天花板
坊間總說這類系統「撐死幾千篇就到頂」。這句話把篇數當成了標尺,可篇數根本不是系統眼裡的單位。系統眼裡只有一樣東西:向量條數。
一篇文章進庫前會被切成若干塊(chunk),每塊約 300–600 字,各成一個向量。所以真正的標尺是這道換算:
向量條數 ≈ 總字數 ÷ 約 500 字
拿這把尺子量一量常被拿來試探邊界的幾個例子(字數為量級估算):
| 語料 | 量級字數 | 約合向量數 | 落在哪一檔 |
|---|---|---|---|
| 一個部落格/公眾號全部存量 | 30–100 萬字 | 數百–2 千 | 記憶體暴力算,秒回 |
| 王小波全集 | ~200 萬字 | ~4–5 千 | 記憶體暴力算,秒回 |
| 研究型學者論文全集(兩三百篇) | 150–300 萬字 | 數千 | 記憶體暴力算,秒回 |
| 一部縣志 / 一本雜誌全部往期 | 100–300 萬字 | 數千 | 記憶體暴力算,秒回 |
| 一部車的整套技術手冊 | ~150 萬字 | ~3–5 千 | 規模上毫無壓力(但見下文「精度牆」) |
| 中小律所/諮詢所案例+範本庫 | 數百–數千篇 | 數千–數萬 | 記憶體或 sqlite-vec,單機夠 |
| 大型智庫數十年全部報告 | 數千萬字,且持續增長 | 十萬級並上漲 | 逼近 ANN,且有「時效牆」 |
| 大型電商/新聞站 | 億級字 + 天天變 | 百萬級 | 必須專業向量基建 |
看出門道了:王小波全集、學者論文全集、整部車手冊——這些聽起來「很大」的東西,其實全擠在最左邊那一檔,幾千個向量,記憶體裡挨個算餘弦都是毫秒級。「上千篇天花板」是個心理數字,不是技術數字。
而且技術天花板本身也被低估了。第六節說「上萬條起變慢」是保守說法——那是把降級路徑當成樸素解釋型迴圈在算。若走 sqlite-vec(C 實作)或向量化記憶體算,單機扛到十萬級向量(約合幾千萬字、約兩百本書的純文字)都還在可用延遲內。真正逼你上分散式 ANN 的,是百萬級向量往上——那已是百科全書、全站新聞庫那個量級,中小應用一輩子到不了。
結論一句話:規模這堵牆,對中小語料幾乎不存在。 先撞上的從來是另外三堵。
真正先撞的三堵牆
規模夠不著天花板,那什麼會先勸退?按「先撞到」的順序排:
| 牆 | 什麼時候撞上 | 為什麼 RAG 在這兒吃力 |
|---|---|---|
| ①時效牆 | 內容天天改、要按「最新」排序 | 離線建庫是重活,高頻重建拖垮;語義相似度不認「誰更新」,新聞/智庫/情報這類「新鮮度>相似度」的場景水土不服 |
| ②精度牆 | 答案是精確數值/流程/表格,答錯有後果 | 語義召回是「意思最近」,不是「一字不差」。扭矩規格、藥物劑量、稅率檔位、法條編號——近似召回差一檔就是事故。一部車手冊規模上完全 OK,卻栽在這堵牆:它要的是「螺栓擰多少牛·米」的確切數字和分步圖解,不是「大意相近的一段話」 |
| ③聚合牆 | 問題要跨全庫統計/計算 | RAG 只會「撈回最相關的幾段」,不會「讀完全部再算總帳」。問「這批案例平均賠了多少」「近十年報告裡出現最多的政策詞」——這類聚合分析型問題,召回幾段原文根本答不了 |
三堵牆背後是同一句話:RAG 擅長「在一堆確定的資料裡,用人話問出定性的答案」,不擅長「求最新、求精確、求彙總」。(其中效能牆與精度牆怎麼補,已在第七節展開;這裡只談「撞不撞得上」。)
橫向對照:哪些中小場景天生合拍
把上面幾把尺子合起來用(體量小 + 內容穩 + 要定性問答 + 答錯不致命),下面這些中小應用幾乎是量身訂製:
| 場景 | 體量 | 契合點 | 要留神的地方 |
|---|---|---|---|
| 作家/學者全集問答(王小波、某教授論文集) | 幾千向量 | 內容永不再改、語義檢索正是文學/學術的強項 | 幾乎無短板,是「最純」的適配 |
| 縣志/地方報/老雜誌歷史文庫 | 數千向量 | 只增不改、變化極慢、雙語加分 | 外部約束小,正文已點名 |
| 中小律所合約範本 / 諮詢所案例庫 | 數千–數萬 | 「照著己方資料答、不許瞎編」剛需 | 責任敏感,須每句可點回原文 |
| 醫院科室 SOP / 診療指南問答 | 數百–數千 | 內容權威、更新緩慢、問答剛需 | 精度敏感,只答「指南怎麼說」不做診斷 |
| 政務辦事指南 / 12345 知識庫 | 數千 | 穩定、雙語、要「照章說話」 | 政策變更時需重建,屬可控低頻 |
| 中小企業售後/產品知識庫 | 數百–數千 | 客服照文件答,降人力 | 產品換代內容會變,屬中頻,尚可 |
| 企業內部 wiki / 員工手冊 / HR 政策 | 數百–數千 | 員工自助問答,省 HR 重複答疑 | 政策會改,需配輕量重建流程 |
| 一門課程全部講義 / 一套教材 | 數千 | 學生「問教材」,邊界清晰 | 版本迭代時重建 |
| Podcast/訪談全部逐字稿庫 | 隨年增長但慢 | 「我記得哪期聊過 X」的模糊召回 | 增長雖持續但低頻,長期觀察向量數 |
| 經典/宗教文本庫 | 數千–數萬 | 文本永恆不變,語義檢索天然適合 | 幾乎無短板 |
而下面這些,不是規模問題,是「用錯工具」問題,再漂亮的架構也是空轉:
| 場景 | 撞哪堵牆 | 該用什麼 |
|---|---|---|
| 一部車/一台設備技術手冊的精確查詢 | 精度牆 + 結構化 | 結構化檢索 + 精確欄位查詢(表格/編號/圖解本就不該塞進語義向量) |
| 新聞站 / 情報日報 / 輿情 | 時效牆 | 時序資料庫 + 新鮮度排序 |
| 大型智庫即時報告庫 | 時效 + 聚合 + 規模 | 專業向量基建 + 時序 + 聚合層混合 |
| 電商/預訂/購物車 | 根本不是問答系統 | 交易系統(正文已點名) |
| 「近十年資料統計/趨勢」類分析 | 聚合牆 | 資料倉儲 + BI,不是 RAG |
| 客戶要自助天天發文改文 | 無編輯後台 | 帶 CMS 的產品(正文已點名商業模式天花板) |
一句話收尾:語義檢索 + RAG 不是萬靈藥,它把「在一堆確定的資料裡,用人話問出準確答案」這件事做到了又準、又便宜、又不被雲端綁架。天花板不在「幾千篇」——中小語料幾乎撞不到規模牆;先勸退你的,永遠是「內容改不改、答案精不精、要不要算總帳」這三問。三問都答『否』,它的性價比碾壓老式方案;但凡有一個『是』,再漂亮的架構也是空轉。
本文技術判斷基於 2026-07-13 對 tryway.cc 背後 Praxis 系統真實生產程式碼的實測與調研。凡「實測」均已在本機復現;凡「未驗證」均如實標注,不以推測充結論。