API 開發
embedding 與向量檢索是怎麼運作的?看懂 RAG 的搜尋心臟
embedding 與向量檢索是怎麼運作的?看懂 RAG 的搜尋心臟
文件切好塊之後,下一個問題來了:使用者提問時,要怎麼從一堆切塊裡找出最相關的那幾塊?這本質上是個搜尋問題。
RAG 最常用的答案是語意搜尋(semantic search):不比對字面,而是比對意思。而讓意思可以被比對的關鍵技術,就是這篇的主角 embedding。
你將學到什麼
embedding 是什麼
把文字的語意變成一串數字的表示法。
動手產生 embedding
用 VoyageAI 的 API 幾行程式就能做到。
cosine similarity
向量資料庫怎麼用角度判斷誰跟誰相似。
完整檢索流程
從切塊、存庫到提問檢索的六個步驟。
找相關段落,其實是個搜尋問題
上一篇我們把文件切成了塊,現在得在使用者提問時,從所有切塊裡挑出跟問題相關的。傳統的關鍵字搜尋比對的是「有沒有出現同樣的字」,而語意搜尋走的是另一條路:把提問與每個切塊都轉成 embedding,比較它們在意思上的接近程度。
兩條路各有擅場,之後談檢索品質時會再回來比較;這篇先把語意搜尋這條路走通。
embedding:把語意變成一串數字
embedding 是一段文字所含語意的數值表示。你把文字餵進 embedding 模型,它吐回一長串數字,每個數字介於負 1 到正 1 之間,各自代表輸入文字的某種特質或特徵。
這裡有個重要的但書:我們並不知道每個數字精確代表什麼。你可以想像某個數字代表「這段文字有多開心」或「有多常談到海洋」,但這只是幫助理解的比喻。每個維度的實際意義是模型在訓練過程中自己學出來的,人類無法直接解讀。
動手產生 embedding
Anthropic 目前不提供 embedding 生成,這裡選的供應商是 VoyageAI:註冊帳號、拿到 API key(入門免費),放進環境變數,然後安裝套件、寫一個產生 embedding 的函式:
# .env
VOYAGE_API_KEY="your_key_here"
# pip install voyageai
from dotenv import load_dotenv
import voyageai
load_dotenv()
client = voyageai.Client()
def generate_embedding(text, model="voyage-3-large", input_type="query"):
result = client.embed([text], model=model, input_type=input_type)
return result.embeddings[0]
對任何一段文字呼叫它,就會拿回一串浮點數。產生 embedding 本身很快也很簡單,真正的功夫在於接下來怎麼比較這些數字。
用一個想像的模型,看懂整個流程
真實的 embedding 有成百上千個維度,很難視覺化。這裡換一個好懂的想像:假設有個完美的 embedding 模型,永遠只回傳兩個數字,而且我們知道第一個數字代表「談醫療的程度」,第二個代表「談軟體工程的程度」。
拿兩個切塊來看。醫學研究的段落可能得到 [0.97, 0.34]:非常醫療,但因為出現了「bug」這個字而帶點軟體味。軟體工程的段落得到 [0.30, 0.97]:重度軟體,但「infection vectors」這種詞又帶點醫療的影子。embedding API 通常會做正規化,把每個向量縮放成長度 1.0,於是變成 [0.944, 0.331] 與 [0.295, 0.955],可以畫在單位圓上。
接著使用者提問:「我想了解這家公司,特別是軟體工程部門今年做了什麼?」這句話跑過同一個 embedding 模型,得到類似 [0.1, 0.89] 的結果:醫療分數低、軟體分數高,正規化後是 [0.112, 0.993]。
cosine similarity:向量資料庫怎麼比相似
切塊的 embedding 會存進向量資料庫:一種專門儲存、比較、搜尋這種長串數字的資料庫。到這裡為止都是可以事先做完的前處理,接下來就等使用者提問。
提問進來、轉成 embedding 之後,向量資料庫用 cosine similarity(餘弦相似度)找最接近的向量:計算兩個向量夾角的餘弦值。數值越接近 1 代表越相似,接近負 1 代表非常不同,0 則代表垂直、沒什麼關係。在剛才的例子裡,提問與軟體段落的相似度是 0.983,跟醫療段落只有 0.398,高下立判。
很多向量資料庫的文件會改用 cosine distance(餘弦距離),也就是 1 減掉 cosine similarity:距離越接近 0 越相似,數值越大越不相關。同一件事,換個方向表達而已。
把流程串起來,並且記得存原文
整條實作流程就是五步:切塊、為每塊產生 embedding、存進向量庫、把使用者提問轉成 embedding、搜尋最相近的切塊。程式骨架長這樣:
chunks = chunk_by_section(text)
embeddings = generate_embedding(chunks)
store = VectorIndex()
for embedding, chunk in zip(embeddings, chunks):
store.add_vector(embedding, {"content": chunk})
user_embedding = generate_embedding(
"What did the software engineering dept do last year?"
)
results = store.search(user_embedding, 2)
拿範例文件實測,問軟體工程部門做了什麼,搜尋回傳軟體工程章節(距離 0.71,最接近)與方法論章節(距離 0.72,次接近)。距離越小代表越相關,檢索結果合情合理。
說到底,RAG 的核心就是:把文字變成數字、把數字存好,再用數學上的相似度找回相關的內容。不過語意搜尋不是萬能的,有些情境它會失手。下一篇我們就來補上它的盲點:關鍵字搜尋與混合檢索。
延伸學習
做出你的第一個 Skill
一場快閃直播的完整重製。從搞懂 Skill 的五個層級開始,帶你把一件你每天在做的重複工作,寫成一支 AI 真的會照做的 Skill ── 命名、description、輸入拆解、Workflow 訪談、Output 與 Checks,最後組成一份能通過格式檢查的 SKILL.md。
NT$ 999
ChatGPT 很強,但真正讓你下班的是 Google
六小時完整實錄。從「AI 很厲害,為什麼你還是每天加班」這個問題出發,把 Google Workspace 當成真正的工作平台重新設計一次流程 ── Sheets 的資料結構、Drive 與 Docs 的文件流、Gmail 與 Calendar 的通知系統,再用 Apps Script 讓它自己跑起來,最後收斂成一張屬於你自己的 AI 工作能力地圖。
NT$ 4,599

