搜尋與 AI 練習題 (Practice - Search and AI)
Related Concepts
- 08-Search-and-AI/01-Search-System
- 08-Search-and-AI/02-RAG
- 08-Search-and-AI/03-Elasticsearch
- 08-Search-and-AI/04-Vector-Database
| 概念 | 預設做法 | 關鍵點 |
|---|---|---|
| 搜尋索引同步 | CDC + Kafka(非同步) | 解耦、可靠;延遲幾秒~幾十秒 |
| 相關性排序 | BM25 + Boosting + 業務指標 | TF 有上限、IDF 越罕見越高 |
| Autocomplete | Edge N-gram + Redis 快取 | 100ms 內回應 |
| Shard 數量 | 每 shard 10~50GB,寧可多設 | 建立後無法改,少了只能 Reindex |
| ES keyword/text | keyword 精確、text 分詞 | mapping 只放會搜的欄位 |
| ES 深分頁 | search_after / PIT cursor | from/size > 1 萬極貴 |
| ES segment | 不可變 + soft delete + merge | 高頻更新很吃力,讀多寫少 |
| 向量檢索 | ANN(HNSW 為主) | recall/latency/memory 三角 |
| 向量先簡單 | pgvector / ES kNN | > 100M 才上專門向量 DB |
| RAG 四元件 | Ingestion → Retrieval → Augmentation → Generation | 解 foundation model 限制 |
| RAG vs 替代方案 | RAG(比訓練/fine-tune/塞長 context 划算) | 可引用來源、獨立調整 |
Part 1:Search System(搜尋系統)
Q1(Recall):為什麼 LIKE '%running shoes%' 不適合做搜尋?兩個原因。
- 無法用索引 → full table scan,幾百萬筆要跑幾十秒
- 只做字串比對 → 「跑步鞋」不匹配「慢跑鞋」、「running」不匹配「run」
本質:搜尋做的是相關性排序,不是 DB 的精確比對。
Q2(Recall):倒排索引(Inverted Index)的核心是什麼?建索引前的「文字分析」包含哪些步驟?
核心:傳統 DB 是「文件 → 詞彙」;倒排索引反過來「詞彙 → 文件」。搜尋時找各詞的文件清單取交集。
文字分析步驟:
- Tokenization(斷詞):
"Nike Running Shoes"→["nike","running","shoes"] - Lowercasing(統一小寫)
- Stop word removal(移除「的」「the」)
- Stemming(
running/runs/ran→run)
查詢時用戶輸入也經過同樣分析,確保匹配。
Q3(Recall):BM25 排序的兩個核心因子 TF 和 IDF 各代表什麼?
- TF(Term Frequency):詞在文件出現越多分數越高,但有上限(非線性增長)
- IDF(Inverse Document Frequency):詞在所有文件越罕見分數越高(「鞋」IDF 低、「Gore-Tex」IDF 高、更有辨別力)
另加欄位長度因子:同樣出現一次,短標題權重 > 長描述。
Q4(Application):設計電商搜尋的 Indexing Pipeline。為什麼選 CDC 而不是雙寫?
「商品從 PostgreSQL 透過 CDC 同步到 Elasticsearch。」
CDC 優於雙寫:
- 雙寫:app 同時寫 DB 和 ES,但兩寫非原子 —— DB 成功 ES 失敗就不一致,複雜度推到應用層
- CDC:監聽 DB 的 WAL → 變更轉 Kafka 事件 → Indexer 消費寫 ES。app 只寫主 DB(解耦),Kafka 緩衝(ES 暫不可用不丟資料),可在 Indexer 做轉換
缺點:延遲幾秒~幾十秒(大多數場景可接受)。
Q5(Application):Autocomplete 要在用戶輸入每個字元後 100ms 內回應。怎麼設計?
- Edge N-gram:indexing 時把「running」拆成
r, ru, run, runn, runni...每個前綴建索引 → 查詢時用前綴精確匹配,速度極快 - 加 Redis 快取(或用 Elasticsearch 針對補全優化的 suggest API)
- 補全詞來源:靜態(所有商品名,不反映熱度)或動態(從搜尋歷史統計高頻詞)
Q6(Application):需要即時一致的庫存狀態,該不該放進搜尋索引?
不該。CDC 有幾秒~幾十秒延遲,庫存這種需即時一致的資料不放搜尋索引。
正確做法:從搜尋索引拿到候選結果後,再去主資料庫做最終的庫存驗證。搜尋負責「找到相關商品」,主 DB 負責「確認當下庫存」。
Q7(Analysis):Elasticsearch 的 Shard 數量為什麼必須在建索引時就算好?如何估算?
資料路由依賴 shard 數量,建立後無法修改,要改只能 Reindex(重建整個索引)。
估算:官方建議每 shard 10~50GB(太大查詢慢、太小浪費資源)。
Shard 數 = 預估一年資料量 ÷ 目標 shard 大小
例:預估 500GB、目標每 shard 25GB → 20 個 primary shard。寧可多設一點,因為少了只能 Reindex。
Q8(Analysis):如何做到零停機的 Reindex?
用 alias 做索引切換:
- 建立新索引(新 mapping / 分詞器)
- 把資料灌進新索引
- alias 從舊索引指向新索引(原子切換)
搜尋查詢一直打 alias,整個過程零停機。直接刪除重建會讓搜尋在重建期間失效。
Part 2:RAG(Retrieval-Augmented Generation)
Q9(Recall):Foundation model 的四大限制是什麼?
- 知識截止日(Cutoff):訓練資料凍結在某時間點 → 近期事件答錯
- 缺特定領域深度:專業資料出現頻率不足
- 缺私有/專有資料:你的公司資料不在公開領域
- 失去信任:無法引用來源
Hallucination 是這些限制的症狀(加上 sampling 隨機性、模型無法區分「知道/不知道」)。
Q10(Recall):RAG 的四個核心元件依序是什麼?
- Ingestion(攝取):權威資料載入 vector database(如 Pinecone)
- Retrieval(檢索):根據用戶 query 檢索相關資料
- Augmentation(增強):檢索結果 + query 組合成增強 prompt
- Generation(生成):模型根據增強 prompt 生成輸出
Q11(Recall):Ingestion 的三個步驟是什麼?
- Chunking(分塊):把文件切成更小片段(依資料類型、query 類型、結果用途選策略)
- 建立 Vector Embedding:用 embedding model 把每個 chunk 轉成 vector(含義的數值表示)
- 載入 Vector Database:通常 offline 進行,獨立於應用工作流程
(這步通常離線;資料有變動可即時更新 index。)
Q12(Application):客戶想讓 LLM 回答公司內部政策問題。為什麼 RAG 比 fine-tuning 或「把所有文件塞進 prompt」更好?
- vs Fine-tuning:fine-tune 成本高、需專業知識,且知識仍會過時、無法引用來源
- vs 塞長 context:送的 context 越大,每次請求成本越高,且有 context window 上限
- RAG 優勢:注入即時/專有知識、可引用來源建立信任、獨立調整每個元件、成本可控(foundation model 製作成本高昂)
Q13(Application):用戶用內部縮寫和產品代號查詢,純 semantic search 找不到。怎麼改善 retrieval?
改用 Hybrid Search:結合
- Semantic search(dense vector):擅長「用不同語言談同一主題」
- Lexical/keyword search(sparse vector):擅長「內部/特定領域語言」(縮寫、產品名、團隊名)
流程:查 dense + sparse index → 合併去重 → reranking model 依統一 relevance score 重排 → 回傳最相關結果。
Q14(Analysis):增強 prompt 裡為什麼要加「If the CONTEXT doesn't contain the answer, say you don't know」?
為了降低 hallucination。
把搜尋結果作為 context 提供給 LLM,鼓勵它把回答紮根在 context 的事實上、優先用紮實資訊,而不是憑機率生成。明確指示「不知道就說不知道」可避免模型在 context 不含答案時硬編造 —— 因為模型本身無法區分「知道」與「不知道」,預設會很自信地給答案。
Q15(Analysis):Agentic RAG 和傳統 RAG 的差異是什麼?Agent 扮演什麼角色?
傳統 RAG:簡單 —— vector DB + 一次帶 context 的 prompt。
Agentic RAG:AI agent 成為核心 RAG 元件的 orchestrator:
- 構建更有效的 query
- 存取額外的 retrieval 工具
- 評估檢索 context 的準確性/相關性
- 應用推理驗證資訊,決定相信或捨棄
重點從「搜尋正確資訊」轉移到「決定要問哪些問題、用哪些工具、何時使用,然後聚合結果紮實回答」。
Part 3:Elasticsearch
Q16(Recall):Elasticsearch 和 Lucene 是什麼關係?
Elasticsearch =建立在 Apache Lucene 之上的分散式協調層。
- Lucene:高度優化的底層搜尋函式庫,負責搜尋本身(inverted index、doc values、segment)。
- Elasticsearch:負責分散式那層——cluster 協調、REST API、aggregation、即時能力。ES 的 shard 與 Lucene index 一對一。
一句話:ES 是在一大群 Lucene index 之上加上可用性與擴展性。
Q17(Recall):mapping 裡 keyword 和 text 型別差在哪?
- keyword:整段值、不分詞,查詢像 hash 表的精確比對——適合 id、tag、category、排序。
- text:經 analyzer 分詞,查詢像倒排索引的全文檢索——適合 title、description。
額外重點:mapping 只放會被搜尋的欄位。多餘欄位會增加每個 index 的記憶體開銷與成本。
Q18(Recall):Elasticsearch 怎麼避免並行更新互相覆蓋?
用 _version 做樂觀並行控制。PUT /books/_doc/{id}?version=1 表示「只有 version 為 1 時才更新」,否則回錯 → client 處理衝突並重試。
因為 ES 是分散式、非同步、並行的,request 可能到多個 node 且順序不定,所以更新語意要寫清楚。
Q19(Application):搜尋結果要支援翻到第 5000 頁,from/size 為什麼不行?該用什麼?
from/size 在深分頁(例如超過一萬筆)時極貴:每次請求都要先排序並取出前面所有 document,cluster 處理的資料量很大。
改用 search_after:用「上一頁最後一筆的 sort 值」當下一頁起點,只取「在那之後」的 document。好處是不漏不重、即使期間有新增也 OK;代價是不能隨機跳頁、client 要自己記住 sort 值。需要翻頁期間一致視角時,用 PIT(Point in Time)+ search_after 做 cursor(成本最高)。
Q20(Analysis):為什麼 Elasticsearch 不適合「按讚數」這種高頻更新的欄位?
根因是 Lucene segment 不可變。
- 更新不是就地改,而是對舊 document soft delete + 插入一筆新的,舊的在之後 segment merge 時才清理。
- 更新比插入更耗效能(要處理 soft delete 簿記),merge 前一直帶著效能包袱。
所以 ES 偏向讀多寫少。高頻計數類欄位讓 ES 很吃力——這類資料應放別處(如 Redis 計數器),ES 只當搜尋層。不可變設計的回報是:寫入快、快取簡單、並行單純、壓縮好。
Q21(Analysis):搜 "bill nye",bill 命中幾百萬筆、nye 只有幾百筆。query planner 會怎麼做?這體現什麼設計觀念?
Query planner 用統計資訊(欄位型別、詞分布、document 長度)選最省策略:先建 nye 的 document 集合(幾百筆),再去 bill 裡找交集,比反過來先掃幾百萬筆 bill 快好幾個數量級。
設計觀念:靠統計 + 一層間接,讓系統依資料動態調整執行計畫——這正是資料庫/搜尋引擎強大的原因之一。
Q22(Application):要在電商系統用 Elasticsearch 做商品搜尋,怎麼跟主資料庫整合?有哪些限制要先講?
用 CDC 從 Postgres/DynamoDB 把權威資料同步進 ES(ES 當搜尋層,不是 source of truth)。
先講的限制:
- 別當唯一 DB:一致性/持久性不是它的設計目標。
- 最終一致:搜尋結果可能落後幾秒。
- 反正規化:攤平資料,讓一兩次查詢拿到結果。
- 同步一致性:ES 與底層資料同步失敗會造成資料漂移(常見 bug)。
- 即時一致的資料(庫存)不放搜尋索引,拿候選結果後回主 DB 驗證。
Part 4:Vector Database
Q23(Recall):向量資料庫解決什麼傳統資料庫做不到的問題?它的本質是什麼?
傳統 DB 擅長精確查詢(user_id = 123);向量資料庫擅長相似度搜尋(「找出和這篇文章語意最接近的 10 篇」)。
本質:它是一種索引(不是 source of truth),核心技術是 ANN(Approximate Nearest Neighbor)。權威資料仍放主 DB。
Q24(Recall):KNN 暴力搜尋的複雜度是多少?為什麼要改用 ANN?
暴力 KNN 對每個向量算相似度、排序、取前 K → O(n)。1M 向量 × 1536 維每次 query 約 60 億次浮點運算,即使 SIMD/GPU 也不夠。
ANN 容忍「不完全準確」,找到 95%+ 就夠,把搜尋變成在 Recall / Latency / Memory 三者間平滑調整。
Q25(Analysis):HNSW、IVF、LSH 三種索引怎麼選?預設用哪個、為什麼?
預設 HNSW(高維 skip list,多層 graph greedy search):O(log n)、95%+ recall、低延遲。代價是記憶體大(≈原始向量 ×2)、建索引慢、插入貴。
- IVF:k-means 分群,只搜最近的幾個 cluster(
nprobe越高 recall 越高但越慢)。建索引快、記憶體小,但 recall 通常較低。 - LSH:隨機 hyperplane 讓相似向量落同 bucket。建索引快、適合 streaming,但實務 recall 不如 HNSW。
除非有特定限制(寫入極頻繁/規模極大/記憶體受限),HNSW 是正解。
Q26(Application):「找最相似的 10 個,而且 in stock、價格 < $100」——查詢帶過濾條件怎麼處理?
兩種策略,實務常 benchmark 再決定:
- Post-filter:先找 top-N 再過濾 → 問題是過濾後可能不足 K 個。
- Pre-filter:先過濾再向量搜尋 → 問題是索引可能無法有效套用。
各系統做法不同:pgvector 走 query planner(可能 brute force);Elasticsearch 在 HNSW traversal 中整合 filter,並支援 BM25 + vector hybrid search;Pinecone 把 metadata 當 first-class。過濾條件越具選擇性,這個決策越重要。
Q27(Application):客戶要做知識庫問答,問你用哪種向量資料庫。怎麼回答?
先簡單:若已用 PostgreSQL,先試 pgvector(支援 HNSW/IVF、可 JOIN、ACID);需要 hybrid 就用 Elasticsearch kNN。只有當規模或需求超出(> 100M 向量)才上專門向量 DB(Pinecone/Weaviate/Milvus/Qdrant)。
很多人低估多維運一套新系統的複雜度。一致性可說「向量搜尋採最終一致,插入後不需要立刻搜得到」。
Q28(Analysis):什麼是 embedding drift?為什麼它是要事先規劃的營運問題?
Embedding drift:當你更換或升級 embedding 模型,舊的 embedding 全部失效、不相容(不同模型的向量空間不同,混用會搜出垃圾)。
後果與規劃:必須重新產生所有 embedding(大規模重建索引,成本高、1000 萬筆可能要一小時+),或在轉換期間維護多套索引。關鍵設計細節:API 要帶「哪個模型產生此 embedding」,避免用錯模型的向量搜尋。這影響部署與災難復原速度,要事先規劃切換流程。
| 問題 | 解法 |
|---|---|
為何不用 LIKE |
無法用索引 + 只字串比對 → 用獨立搜尋索引 |
| 搜尋索引同步 | CDC + Kafka(非雙寫,避免非原子不一致) |
| 相關性排序 | BM25(TF 有上限 / IDF 越罕見越高)+ Boosting + 業務指標 |
| Autocomplete 100ms | Edge N-gram + Redis 快取 |
| 庫存等即時一致資料 | 不放搜尋索引,候選結果再回主 DB 驗證 |
| Shard 數量 | 每 shard 10~50GB,建立後無法改,寧可多設 |
| 零停機 Reindex | alias 切換 |
| ES = ? | Lucene 之上的分散式協調層(shard ↔ Lucene index 一對一) |
| ES 高頻更新慢 | segment 不可變 → soft delete + merge;讀多寫少 |
| ES 深分頁 | search_after / PIT cursor(from/size > 1 萬極貴) |
| 向量檢索 | KNN O(n) → ANN(HNSW 為主,O(log n)、95%+ recall) |
| 向量帶過濾 | pre / post-filter;benchmark 決定 |
| 向量選型 | 先 pgvector / ES kNN,> 100M 才上專門向量 DB |
| embedding drift | 換模型要重建索引;API 記錄產生模型 |
| foundation model 限制 | cutoff / 缺領域深度 / 缺私有資料 / 無法引用 |
| RAG 四元件 | Ingestion → Retrieval → Augmentation → Generation |
| 縮寫/產品名查不到 | Hybrid Search(dense semantic + sparse lexical) |
| 降低 hallucination | 增強 prompt 紮根 context +「不知道就說不知道」 |
| RAG vs fine-tune/長 context | RAG 划算、可引用來源、知識即時、獨立調整 |