01

總覽與痛點

先問「為什麼 keyword 不夠」,再談向量資料庫——語意相近,但用字不同時怎麼找?

假設知識庫裡有一段話:「如何在分散式系統裡保證資料不遺失?」使用者卻搜尋「多機備份會不會掉資料」。若只做關鍵字比對(倒排索引 + BM25:依詞頻/稀有度打分的關鍵字排序),兩邊幾乎沒有共同詞,結果可能是空的——但對人來說,這兩句意思很接近。

向量搜尋的做法是:先把文字(或圖片)轉成一串數字——Embedding(嵌入向量)——讓「意思接近」的內容在數字空間裡距離也接近;查詢時同樣轉成向量,找出距離最近的 Top-K 筆。承擔這件事、並在大規模下仍能毫秒級回應的系統,就是常說的向量資料庫(Vector Database)。

座標它和你熟悉的東西差在哪

• OLTP(PostgreSQL):WHERE id = ?、交易——不擅長「跟這句話意思像不像」。
• 全文搜尋(Elasticsearch):關鍵字 / BM25——擅長用字重疊,不擅長純語意近似。
• 向量搜尋:高維距離 Top-K——擅長語意,專有名詞、型號、精確詞常仍要靠 keyword 互補。
實務上兩者常並用(Hybrid Search),不是互相取代。

何時不該用先排除這些需求

若查詢本質是精確 ID、固定欄位過濾、或「必須出現某個型號字串」——先用 SQL / 倒排索引。向量搜尋適合「意思像不像」;語料極小(幾百筆)時,甚至直接算全量距離就夠,不必急著上專用向量庫。

範圍本篇講什麼、不講什麼

• 本篇聚焦:Embedding → 索引(ANN)→ 檢索品質(recall)→ 何時用 pgvector/專用庫。
• 名詞對齊:業界常把「存向量 + 做近鄰搜尋」的元件叫 Vector Store(應用/框架用語)或 Vector Database(獨立服務)。兩者都在做同一件事;本篇用「向量資料庫」統稱。
• 輕觸即可:量化壓縮、寫入後索引何時可查——在 §05 用最短路徑交代,不當成完整儲存引擎專題。

一句話答法向量搜尋把內容變成高維座標,用距離表達語意相近;它補的是 keyword 做不到的「用字不同、意思相近」,常與全文搜尋一起用。

02

向量長什麼樣子

先建立畫面:向量就是座標;相近的意思會聚在一起。先别管 768 維,用 2D 想即可。

把每段文字想成平面上的一個點。Embedding 模型的工作,是讓「語意相近」的點靠得近、「不相干」的點離得遠。查詢時:把問題也變成一個點(下圖的 ★),再找離它最近的幾個點——這就是 Nearest Neighbor / Top-K。

內容(示意) 向量(簡化成 2 維) 與 Query 的距離
Query:「多機備份會不會掉資料」 (0.62, 0.55) ★ —
「分散式系統如何保證資料不遺失」 (0.60, 0.58) 很近 → 應命中
「資料庫主從複寫與 failover」 (0.58, 0.50) 近 → 常進 Top-K
「今天午餐吃什麼」 (0.12, 0.90) 很遠 → 不該出現

真實系統是幾百~上千維,人眼看不見,但「距離近 = 較相似」的直覺相同。§06 Demo 會用 2D 畫布讓你親手點 query。

常見距離/相似度

  • L2(歐氏距離):就是平面上兩點的直線距離——上表用的直覺。圖像 embedding 常見。
  • Cosine Similarity:比「方向」像不像,不太在乎向量長短;文字最常用。實務常先做 L2 normalize(把長度收成 1),再比角度。
  • Inner Product(內積):向量已 normalize 時與 Cosine 等價;若沒 normalize,長度會干擾分數,別跟 Cosine 混用。
場景 常見選法 記住一句
文字/RAG chunk 先 L2 normalize,再用 Cosine 或 IP 比方向,不比長短
圖像 embedding 常直接 L2 空間距離本身有意義
索引與查詢混用不同度量 — 分數不可比,等於換了一個空間

§06 Demo 用 L2 在 2D 示意「近/遠」;文字系統多半是 normalize 後的 Cosine/IP,但「愈近愈像」的決策邏輯相同。

關鍵為什麼不能「掃全部算距離」就結束?

資料量小時可以——這種精確全掃下文統一叫 Flat(也常寫成 brute-force kNN),命中率是滿分。但當向量到數百萬~數億,每次查詢都算 N 次距離會變成秒級。於是需要 ANN(Approximate Nearest Neighbor,近似最近鄰):用索引少看一些點,用略降的命中率換毫秒級延遲。

名詞Recall@K 是什麼

假設「真正最近的 5 個點」是標準答案,ANN 也回傳 5 個。若其中 4 個落在標準答案裡,則 recall@5 = 4/5 = 80%。後面說 recall↑,就是指「該中的有沒有被撈回來」——跟 API 延遲是兩條不同的 SLA。

一句話答法Embedding 把內容放進高維空間;查詢 = 找離 query 最近的 Top-K。小資料用 Flat 全掃,大資料才需要 ANN。

03

Embedding 管線:從文件到可搜尋

第 2 節的「點」在實務上通常代表一段文字,不是整本書——所以要先切段再 embed。

真實文件往往太長:若整本手冊只變成一個向量,查詢時只能說「這本書有點相關」,無法指出哪一段。因此要先 Chunk(切段),讓空間裡的每個點對應可引用的片段。

Index-time
文件
PDF、Wiki、工單
──切段──▸
Chunk + Embed
片段轉成 d 維向量
──寫入──▸
索引 + metadata
向量、doc_id、tenant_id
Query-time
使用者問題
同一個 embedding 模型
──Query Embed──▸
索引 + metadata
同一份索引,這次是讀
──ANN 近鄰──▸
Top-K chunk
可再 rerank,交給 LLM

這是兩條各自獨立觸發的路徑:上面一條每份文件只跑一次,下面一條每次提問跑一次,兩者在索引處交會——而且必須用同一個 embedding 模型、同一維度,否則距離根本不可比。這也就是下方「換模型 = 重建世界」的具體意思:兩條路徑都得整條重跑。

  1. Chunking:長文切成片段。模型內部用 token(子詞單位,可粗想成「比字元大、比句子小的計長單位」)限制長度;實務常切成約數百 token——大約半頁~一頁說明文字的量級。一頁 PDF 常見會變成數個 chunk。切太細語意破碎;切太粗一次撈回太多雜訊。邊界常留 overlap(重疊):實務常見約 10%~20% 的長度,讓跨段關鍵句兩邊都留得到。
  2. Embedding:呼叫模型把 chunk 轉成 d 維向量(常見 768~1536)。建索引與查詢必須同一模型、同一維度。
  3. Metadata:一併存 doc_id、tenant_id、source 等,供過濾(權限、租戶、時間)。注意:若先 ANN 取 Top-10 再過濾,可能只剩 1~2 筆——看起來像「搜尋很空」。能 pre-filter(先縮小候選再搜)就先縮小;細節見 §07。
  4. Query path:使用者問題同樣 embed → ANN 取 Top-K →(可選)rerank(用更重的模型把候選再排一次)→ 把原文 chunk 當上下文交給 LLM。
名詞RAG 在這條管線的哪一段

RAG(Retrieval-Augmented Generation)=先檢索相關片段,再把片段塞進 LLM 提示詞裡生成答案。本篇聚焦前面的檢索(向量/ANN);生成品質是下一層問題。所以「RAG 答錯」時,要先問:正解 chunk 有沒有被撈到?

Chunk 切壞的後果(反例)

切法 發生什麼 使用者感受
切太細(一句一切) 「主從複寫」與「會不會丟資料」拆成兩段向量 問完整問題時,兩段都不夠像 → 漏檢
切太粗(整章一段) 一段向量混雜多主題 撈得到文件,但上下文又臭又長 → LLM 答得糊
合理 + 少量 overlap 邊界句子兩邊都留一點(約 10%~20%) 較不易在段落接縫處漏掉關鍵句
陷阱換 embedding 模型 = 重建世界

不同模型的向量不在同一空間(768 維 A 與 1024 維 B 不能比距離)。Production 換模型 = 全量 re-embed + 重建索引,需要雙寫或 alias 切換期。別以為「只改 API 名稱」。

白話答案不對,先別只怪 LLM

很多「胡謅」其實是檢索沒把正確 chunk 撈上來(recall@K 低)。先查 chunk 策略、ANN 參數、要不要加 keyword hybrid,往往比換更大生成模型有效。

一句話答法管線是 Chunk → Embed → Index → Query Embed → ANN;index/query 必須同模型同維度,chunk 品質直接決定能不能撈到正解。

04

ANN:為什麼要近似,HNSW 與 IVF 差在哪

精確全掃太慢時,用「少看一些點」換速度;漏看就可能漏檢——這就是 recall vs latency。

精確 kNN:對每個向量算一次距離,複雜度約 O(N·d)。ANN 透過資料結構剪枝,讓查詢只檢查一部分候選,目標是:recall@K 仍夠高,P99 延遲可接受。

兩種常見思路(先建立機制,再記產品名)

IVF · 先分區再搜 離線用分群(可想成 K-means)把空間切成多個 cluster/桶。每個桶裡裝的是「落在這區的向量 ID」——文獻有時叫 inverted file list,但不是 Elasticsearch 那種「詞 → 文件」的關鍵字倒排索引。查詢時先找離 query 最近的幾個桶(參數 nprobe),只在這些桶裡算距離。nprobe 愈大,看得愈多 → recall@K↑、延遲↑。
HNSW · 在圖上走路 事先把相近的向量連成鄰近圖(實作常有多層;§06 Demo 用單層 + 少量跨區跳接邊示意)。查詢可以想成三步:① 選一個入口點;② 看它的鄰居,留下較近的候選;③ 再從候選往外擴——參數 ef 就是「候選名單最多留多寬」。ef 愈大,走得愈勤 → recall@K↑、延遲↑。§06 Demo 的 SEARCH TRACE 會顯示「擴張了幾步、訪問了幾點」。
索引 你在少看什麼 調什麼 直覺取捨
Flat 什麼都不少看 — recall@K = 100%;大資料不可行
IVF 沒被 probe 到的 cluster nprobe 實作直覺、可搭配量化省記憶體
HNSW 沒走到的圖節點 ef(查)、M(建圖) 高 recall、低延遲常見首選;較吃記憶體
陷阱只報 latency、不報 recall

把參數壓到「1ms」很漂亮,但 recall@10 = 70% 表示每 10 個該中的大約漏 3 個——檢索層先壞了。SLA 應同時寫:recall@K ≥ X% 且 P99 ≤ Y ms。§06 Demo 會讓你同時看到命中、漏檢與「實際比較了幾次距離」。

一句話答法ANN 用少算一些距離換速度;IVF 是少探一些桶,HNSW 是少走一些圖節點——參數決定 recall-latency 曲線。

05

pgvector 與專用向量庫:何時升級

多數後端團隊的務實路徑:先在 PostgreSQL 驗證,量級與 SLA 撞牆再上分散式向量庫。

pgvector:你已經會的資料庫加一欄向量

pgvector 是 PostgreSQL extension:表上加 vector(d),可建 HNSW / IVFFlat,並用 SQL 做 metadata filter + 排序。優點是零新基礎設施、ACID、JOIN;限制是單機記憶體/寫入天花板,到億級向量通常不夠。

CREATE EXTENSION vector;
CREATE TABLE doc_chunks (
  id BIGSERIAL PRIMARY KEY,
  content TEXT,
  embedding vector(1536),
  tenant_id INT
);
CREATE INDEX ON doc_chunks
  USING hnsw (embedding vector_cosine_ops)
  WITH (m = 16, ef_construction = 64);
-- ef_construction:建圖時的搜尋寬度(愈大圖通常愈好,建置愈慢)
-- 查詢時另有 ef:控制「這次走路走多勤」,影響 recall / 延遲

SELECT id, content
FROM doc_chunks
WHERE tenant_id = 42
ORDER BY embedding <=> $1   -- <=> = cosine 距離,愈小愈像
LIMIT 10;

專用向量庫:為規模而生

Milvus、FAISS(常作函式庫嵌入服務,而非獨立 DB)這類系統把「向量集合、分片、多種 ANN、水平擴展」當一等公民。適合億級向量、高 QPS,或需要把索引/向量壓進更少記憶體時。代價是多一套運維,metadata 也不如 SQL 靈活。內部還有 collection/segment 等名詞——決策階段可先略過,當成「可水平切的索引分片」即可。

名詞量化(Quantization)在省什麼

原始 float32 × 1536 維很吃記憶體。量化用較少位元近似每個向量(常見:SQ 標量量化、 PQ 乘積量化——把高維切段後各段用 code book 編碼)。
效果:記憶體↓、可快取更多向量 → QPS↑;代價是距離變成近似值,recall 可能略降,需要用更高的 nprobe/ef 或 rerank 補回來。

流程寫入之後,索引何時「可查」

• Flat:寫入即可比距離,無額外索引結構。
• HNSW:新點可增量插入圖,但大量寫入後圖品質會漂,偶發需要 rebuild 才能維持 recall。
• IVF:centroid 是離線分群結果;資料分布大變時,舊桶邊界失準 → 常需定期重訓 centroid 再重建 list。
Production 要問清楚:upsert 是「立刻可搜」還是「進 buffer,背景合併後才進 ANN」。

決策線索 偏 pgvector 偏專用 Vector DB
規模 大約 < 數百萬 chunk 億級、要水平擴
查詢型態 強依賴 SQL JOIN / 交易 檢索為主、metadata 過濾即可
團隊成本 已有 PG,想快驗證 願意養叢集或買託管
陷阱Day 1 就上分散式向量叢集

內部 10 萬 chunk 的知識庫,運維成本常大於收益。先 pgvector 或託管 Serverless 驗證產品;出現 CPU/記憶體飽和、建索引超時、P99 破 SLA、需要跨機 shard 再遷。

實務遷移時記得什麼

雙寫(或 CDC)→ 同一組 query 比 recall 與 P99 → 讀流量灰度。常見切法:PG 繼續當 metadata/權限的 source of truth,向量檢索切到專用庫。

一句話答法pgvector 適合百萬級 MVP 與複雜 SQL;專用向量庫適合億級與水平擴展——先驗證再遷移,避免過早優化。

06

互動演示:空間結果與搜尋過程

左側看命中/漏檢;右側看這次查詢「探了哪些桶/走了哪些候選」以及實際距離比較次數。

畫布上有一組 2D 點(模擬向量)。點一下可移動 Query ★。左側列表的 GROUND TRUTH=用 Flat 全掃算出的「正確 Top-5」,用來對照 ANN 有沒有漏。預設已選 Flat,請依序試:

  1. Flat——全掃,recall@5 應為 100%,比較次數 = 全部點。
  2. IVF + 小 nprobe——畫布出現 cluster 圓;實線粗圓 = 有 probe。只探少數桶時,看漏檢(橘)與比較次數下降。
  3. IVF + 大 nprobe——recall 回升、比較次數上升。
  4. HNSW——切換後預設 ef=16(教學起點,通常已接近滿分);再把 ef 往下調到 2~4,觀察漏檢與比較次數下降。淡線是鄰近邊,亮一點的是走過的邊;右側 TRACE 顯示擴張步數與訪問點數。
互動 · ANN 空間與搜尋過程 點畫布移動 Query;調參數觀察 recall 與比較次數
SEARCH TRACE · 這次少看了什麼
GROUND TRUTH (Top-5)
ANN RESULT (Top-5)
METRICS
就緒:選索引 → 調參數 → 點畫布或按「隨機 Query」。延遲由實際距離比較次數推估,不是亂數。
資料點(淡=本次沒算到) Query ★ 命中(ANN 與 GT 都有) 漏檢(GT 有、ANN 沒撈到) 誤報(ANN 有、不在 GT)

這是 2D 教學模型:IVF 真的只掃被 probe 的 cluster;HNSW 真的只沿預建鄰近邊(含少量跨區跳接)擴張。真實高維 HNSW 有多層、曲線更陡,仍需離線 benchmark 定參。

一句話答法調大 nprobe / ef = 願意多做距離比較換更高 recall;Production 用標注集畫 recall-latency 曲線找 SLA 甜蜜點。

07

面試時怎麼講:RAG 檢索與折衷

把前面串成可答的架構故事:檢索差先查 recall,再決定調 ANN、chunk 或 hybrid。

  • Ingestion:文件 → chunk → embed → upsert(+ metadata)。寫入後索引是否立刻可搜,見 §05。
  • Retrieval:query embed → ANN Top-K →(可選)rerank → 當 LLM 上下文(RAG 的檢索段)。
  • Hybrid:BM25 與向量各取一截,再把兩路排名合併(常見手法叫 RRF:兩邊名次互相加權,不必調分數量綱)——專有名詞靠 keyword,語意靠向量。
  • Filter:假設 ANN 取 Top-10,再按 tenant_id 過濾只剩 2 筆——使用者會覺得「搜尋很空」。能 pre-filter(先縮小再 ANN)就先縮小;只能 post-filter 時,要把 K 加大或換支援過濾的索引路徑。

RRF 合併:一個最小例子

假設向量路與 BM25 路各回傳排名(名次愈小愈好)。RRF 常用分數:1 / (60 + rank),兩路加總後再排序——重點是用名次、不用原始分數,所以不必把 Cosine 與 BM25 硬齊到同一量綱。

chunk 向量名次 BM25 名次 RRF(示意)
A(語意很像,但沒出現型號字) 1 5 1/61 + 1/65 ≈ 0.0318
B(有精確型號,語意普通) 4 1 1/64 + 1/61 ≈ 0.0320 → 常會浮上來
C(兩路都普通) 3 4 1/63 + 1/64 ≈ 0.0315

上表只示範「名次相加」的直覺;常數 60 可調,但面試講清楚「為何不用原始分數相加」比背數字重要。

流程pre-filter vs post-filter

• pre-filter:先用 metadata(租戶、ACL、時間)縮小集合,再跑 ANN——Top-K 幾乎都是「合法且相關」。
• post-filter:先 ANN 再丟棄不合格——若過濾很嚴,要把 K 加大(例如先取 50 再濾),否則結果會空。
• 產品若強制多租戶隔離,優先選支援過濾感知索引(filtered HNSW/分 collection)的路徑,而不是只靠應用層事後丟棄。

考點「RAG 檢索效果不好怎麼查?」

1. 用標注 query–chunk 量 recall@K。
2. 低 recall → 調 ef/nprobe、檢查 chunk/overlap、試 hybrid。
3. recall 高仍答錯 → reranker 或 prompt/模型問題。
4. 延遲高 → 減 K、量化、或擴 query 節點——但先確認沒為了速度犧牲太多 recall。

一句話答法向量庫是 RAG 檢索層核心:先保證 recall,再用參數與 hybrid 換延遲;pgvector 驗證、量大再遷專用庫。