我在 Google 做 AI 工程,也是面試官。
這是一份寫給準備 FDE 面試的人看的系列。
不是教科書,是我站在白板前問過你才懂的那種。
為什麼 RAG 是第一篇
FDE 的 JD 幾乎都明寫:
“Experience with Retrieval-Augmented Generation (RAG) architectures”
這不是裝飾。這是你第一關就會被問到的東西。
我面試過不少人,能把 RAG 說清楚的,比你想像中少。
RAG 是什麼
用一句話說完:
RAG = 讓 LLM 在回答前,先去查資料。
不讓它憑空捏造,而是給它上下文,再要求它根據上下文回答。
完整流程,五個步驟
使用者問題
↓
① Embedding(把問題變成向量)
↓
② Retrieval(從向量資料庫搜尋相關文件)
↓
③ Context Injection(把文件塞進 Prompt)
↓
④ Generation(LLM 根據 Prompt 生成回答)
↓
回答
① Embedding
把文字轉成一個數字向量,讓機器能比較「語意距離」。
1from sentence_transformers import SentenceTransformer
2
3model = SentenceTransformer("all-MiniLM-L6-v2")
4query_vector = model.encode("什麼是向量資料庫?")
5# → [0.12, -0.34, 0.87, ...] # 384 維向量
重點:兩個向量越接近,代表語意越相似。
② Retrieval(向量資料庫)
常見選擇:
| 工具 | 適合場景 |
|---|---|
| Pinecone | 雲端託管,快速上手 |
| Weaviate | 支援 hybrid search |
| pgvector | PostgreSQL 擴充,低門檻 |
| Chroma | 本地開發 / 原型 |
| Vertex AI Vector Search | GCP 全託管,生產首選 |
1import chromadb
2
3client = chromadb.Client()
4collection = client.get_or_create_collection("company_docs")
5
6# 查詢最相近的 5 筆
7results = collection.query(
8 query_embeddings=[query_vector.tolist()],
9 n_results=5
10)
③ Context Injection
把查到的文件塞進 Prompt:
1context = "\n\n".join(results["documents"][0])
2
3prompt = f"""
4根據以下資料回答問題。若資料不足,請說「我不知道」,不要猜測。
5
6資料:
7{context}
8
9問題:{user_question}
10"""
這個 不要猜測 很重要,後面會說為什麼。
④ Generation
1import anthropic
2
3client = anthropic.Anthropic()
4response = client.messages.create(
5 model="claude-sonnet-4-6",
6 max_tokens=1024,
7 messages=[{"role": "user", "content": prompt}]
8)
9print(response.content[0].text)
面試必考:RAG vs Fine-tuning
這是我最愛問的對比題。
很多人答得很模糊。讓我給你一個清楚的版本:
| 維度 | RAG | Fine-tuning |
|---|---|---|
| 知識更新 | 即時(改資料庫就好) | 需要重新訓練 |
| 成本 | 低(inference + retrieval) | 高(GPU 訓練費用) |
| 可引用來源 | 可以(查到哪篇文件) | 幾乎不行 |
| 私有資料 | 很適合 | 有資料外洩風險 |
| 推理格式 | 彈性 | 固定(訓練時決定) |
| 幻覺風險 | 相對低(有 context 約束) | 相對高 |
什麼時候選 Fine-tuning?
- 你需要改變模型的「說話方式」(語氣、格式、風格)
- 任務高度特化,且訓練資料充足(例如法律文件解析)
- 對 latency 要求極高,不允許每次查資料庫
什麼時候選 RAG?
- 資料頻繁更新(產品文件、政策、FAQ)
- 需要追溯來源(企業知識庫、客服系統)
- 預算有限,快速上線
面試必考:Chunk Size 怎麼選
這題問的人很多,但能說清楚 trade-off 的人不多。
Chunk 是什麼
在把文件放進向量資料庫之前,要先把它切成小塊(chunk),每塊各自 embedding。
問題是:切多大?
原始文件(10,000 tokens)
↓ 切成 chunks
[chunk_1: 500 tokens]
[chunk_2: 500 tokens]
[chunk_3: 500 tokens]
...
三種常見大小的差異
| Chunk Size | Recall | Precision | Cost | 適合 |
|---|---|---|---|---|
| 小(~200 tokens) | 低 | 高 | 低 | 精確問答、FAQ |
| 中(~500 tokens) | 中 | 中 | 中 | 通用首選 |
| 大(~1000 tokens) | 高 | 低 | 高 | 長段落理解 |
小 chunk:每塊資訊集中,查到的東西很精準,但可能漏掉需要跨段落的上下文。
大 chunk:查到的東西上下文豐富,但塞進 Prompt 的 token 多,成本高,也可能稀釋相關資訊。
實務建議
1from langchain.text_splitter import RecursiveCharacterTextSplitter
2
3splitter = RecursiveCharacterTextSplitter(
4 chunk_size=500, # 目標大小
5 chunk_overlap=50, # 重疊部分(避免邊界截斷)
6 separators=["\n\n", "\n", "。", " "] # 優先按段落切
7)
8
9chunks = splitter.split_text(document_text)
Overlap 很重要:沒有 overlap 的話,一個句子可能剛好被切到兩個 chunk 的邊界,語意就斷了。
我在面試中想聽到什麼
不是「500 tokens」這個數字,而是:
「我會先看文件的結構——是密集技術文件還是 FAQ?然後設計評估指標,跑幾組 chunk size,看 Recall@5 和 Answer Relevance,再決定。」
這才是工程師的思維。
面試必考:RAG 幻覺怎麼改善
RAG 的幻覺比純 LLM 少,但不是零。
常見的幻覺場景:
- 查到的文件和問題其實不相關,但 LLM 還是硬回答
- 文件有多個版本,LLM 混著用
- Prompt 太長,LLM 忽略了關鍵段落
五個改善方向
1. 更好的 Chunking
按語意切,而非固定長度:
1# 不好:固定切 500 個字
2text[:500], text[500:1000], ...
3
4# 好:按段落 / 標題切
5splitter = RecursiveCharacterTextSplitter(
6 separators=["\n## ", "\n### ", "\n\n", "\n"]
7)
2. Metadata Filtering
查資料時加過濾條件,避免查到不相關的文件:
1results = collection.query(
2 query_embeddings=[query_vector],
3 n_results=5,
4 where={
5 "department": "engineering", # 只查工程部門的文件
6 "version": {"$gte": "2024"}, # 只查 2024 年後的版本
7 "language": "zh-TW"
8 }
9)
3. Hybrid Search
純向量搜尋有時候抓不到特定關鍵字。結合全文搜尋(BM25)效果更好:
1# 語意搜尋:找「相似意思」的文件
2semantic_results = vector_db.query(query_embedding)
3
4# 關鍵字搜尋:找「包含這個詞」的文件
5keyword_results = bm25_index.search(query_text)
6
7# 結合(RRF: Reciprocal Rank Fusion)
8final_results = reciprocal_rank_fusion(semantic_results, keyword_results)
Google 自家的 Vertex AI Search 預設就有 hybrid search。
4. Reranking
第一輪搜尋取回 20 筆,再用更強的 cross-encoder 重新排序,只取前 5 筆:
1from sentence_transformers import CrossEncoder
2
3reranker = CrossEncoder("cross-encoder/ms-marco-MiniLM-L-6-v2")
4
5# 計算 (query, chunk) 的相關分數
6pairs = [(query, chunk) for chunk in candidate_chunks]
7scores = reranker.predict(pairs)
8
9# 取最高分的 5 筆
10top_chunks = [chunk for _, chunk in sorted(
11 zip(scores, candidate_chunks), reverse=True
12)][:5]
為什麼不直接用 cross-encoder 查? 因為它太慢,不適合掃整個資料庫。先用向量搜尋縮小範圍,再用 reranker 精選。
5. Evaluation Pipeline
沒有評估就沒有改善。
常用指標:
1# RAGAS 框架(開源)
2from ragas import evaluate
3from ragas.metrics import faithfulness, answer_relevancy, context_recall
4
5results = evaluate(
6 dataset=qa_pairs,
7 metrics=[faithfulness, answer_relevancy, context_recall]
8)
9
10# faithfulness:回答是否忠於查到的文件(最重要)
11# answer_relevancy:回答是否和問題相關
12# context_recall:有沒有把正確的文件查出來
完整 RAG Pipeline 範例
1import chromadb
2from sentence_transformers import SentenceTransformer
3import anthropic
4
5class SimpleRAG:
6 def __init__(self):
7 self.embedder = SentenceTransformer("all-MiniLM-L6-v2")
8 self.db = chromadb.Client()
9 self.collection = self.db.get_or_create_collection("docs")
10 self.llm = anthropic.Anthropic()
11
12 def add_documents(self, docs: list[dict]):
13 texts = [d["content"] for d in docs]
14 embeddings = self.embedder.encode(texts).tolist()
15 self.collection.add(
16 ids=[d["id"] for d in docs],
17 embeddings=embeddings,
18 documents=texts,
19 metadatas=[d.get("metadata", {}) for d in docs]
20 )
21
22 def query(self, question: str) -> str:
23 q_vec = self.embedder.encode([question]).tolist()
24 results = self.collection.query(query_embeddings=q_vec, n_results=5)
25 context = "\n\n---\n\n".join(results["documents"][0])
26
27 prompt = f"""根據以下資料回答問題。若資料不足,請說「資料中沒有相關資訊」。
28
29資料:
30{context}
31
32問題:{question}"""
33
34 response = self.llm.messages.create(
35 model="claude-sonnet-4-6",
36 max_tokens=1024,
37 messages=[{"role": "user", "content": prompt}]
38 )
39 return response.content[0].text
面試答題模板
如果面試官問「解釋一下你對 RAG 的理解」,我建議這樣組織你的回答:
- 一句話定義:RAG 讓 LLM 在生成前先查資料,以減少幻覺並支援知識更新
- 流程:Embedding → Retrieval → Context Injection → Generation
- vs Fine-tuning:RAG 適合知識頻繁更新、需要來源引用的場景
- 主要挑戰:Chunk 策略、Retrieval 品質、幻覺控制
- 你的實作經驗(如果有的話,要說具體數字)
小結
RAG 是 FDE 必考的第一題。
不要只背定義,面試官在意的是:
- 你知道每個環節的 trade-off
- 你有辦法設計評估,而不是憑感覺調參數
- 你遇到問題時,有系統性的改善方向
下一篇:Agent System Design — 如何設計一個不會失控的 AI Agent 系統。
