回到頂部
Embedding 向量嵌入實戰 — 封面

Embedding 向量嵌入實戰

Embedding 把文字轉成向量——語意搜尋怎麼做、RAG 的核心原理、向量資料庫怎麼選,一篇搞懂 AI 的「語意理解」基礎。

🧮 Embedding 是什麼?

💡 一句話理解 Embedding = 把文字轉成一串數字(向量),讓電腦能「理解」文字的意思而不只是文字本身。

生活化比喻

想像一個超大的地圖,每個詞都有一個位置:

  • 「貓」和「狗」在地圖上很近(都是寵物)
  • 「貓」和「汽車」在地圖上很遠(毫無關聯)
  • 「國王」和「皇后」很近,「男人」和「女人」也很近

Embedding 就是把文字放到這張地圖上的過程。每個文字的位置用一組坐標(向量)表示。

技術本質

"今天天氣很好" → [0.023, -0.156, 0.892, ..., 0.045]
                    ↑ 一個 1536 維的向量(以 text-embedding-3-small 為例)

"天氣晴朗"    → [0.019, -0.148, 0.901, ..., 0.038]
                    ↑ 和上面的向量很接近!因為意思相近

"量子力學"    → [-0.234, 0.567, 0.012, ..., -0.891]
                    ↑ 完全不同的向量方向,因為意思無關

🎯 Embedding 能做什麼?

應用場景說明案例
語意搜尋搜「水果」能找到「蘋果」「香蕉」企業知識庫搜尋
RAG在問 AI 前,先找到最相關的文件段落客服機器人
推薦系統找到和使用者興趣相近的內容推薦文章、商品
重複檢測找出意思相同但用詞不同的文件客服工單去重
分類自動計算文字屬於哪個類別信件分類、情感分析
聚類把相似的文件自動分組客戶回饋主題分析

📊 Embedding 模型比較(2026)

模型開發者維度價格/M tokens特色
text-embedding-3-smallOpenAI1536$0.02性價比最高
text-embedding-3-largeOpenAI3072$0.13精度最高
voyage-3Voyage AI1024$0.06長文 + 程式碼最強
embed-v4Cohere1024$0.10多語言最好
bge-m3BAAI1024免費(開源)開源最強、多語言
nomic-embed-textNomic AI768免費(開源)可用 Ollama 本地跑

怎麼選?

  • 💰 預算有限text-embedding-3-small(最便宜、品質夠用)
  • 🏆 要求最高精度text-embedding-3-largevoyage-3
  • 🇹🇼 中文為主bge-m3cohere embed-v4
  • 🔒 資料不能外傳bge-m3nomic-embed-text(本地離線跑)

💻 實作:產生 Embedding

OpenAI

from openai import OpenAI
import os

client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])

def get_embedding(text, model="text-embedding-3-small"):
    """取得文字的 embedding 向量"""
    response = client.embeddings.create(
        input=text,
        model=model
    )
    return response.data[0].embedding

# 單一文字
vector = get_embedding("今天天氣很好")
print(f"維度: {len(vector)}")  # 1536
print(f"前 5 個值: {vector[:5]}")

# 批次處理(更高效)
texts = ["蘋果很好吃", "iPhone 16 上市了", "香蕉也不錯"]
response = client.embeddings.create(
    input=texts,
    model="text-embedding-3-small"
)
vectors = [item.embedding for item in response.data]

本地模型(用 Sentence-Transformers)

from sentence_transformers import SentenceTransformer

# 下載並載入模型(第一次會自動下載)
model = SentenceTransformer("BAAI/bge-m3")

texts = ["今天天氣很好", "天氣晴朗", "量子力學"]
vectors = model.encode(texts)

print(f"維度: {vectors.shape}")  # (3, 1024)

📏 計算相似度

有了 Embedding,怎麼比較兩段文字的「像不像」?

Cosine Similarity(餘弦相似度)

import numpy as np

def cosine_similarity(a, b):
    """計算兩個向量的餘弦相似度(-1 ~ 1,越接近 1 越相似)"""
    return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))

# 範例
v1 = get_embedding("蘋果很好吃")
v2 = get_embedding("水果真美味")
v3 = get_embedding("量子力學原理")

print(cosine_similarity(v1, v2))  # ~0.85(很相似!)
print(cosine_similarity(v1, v3))  # ~0.12(不相關)

相似度讓你建立語意搜尋

def semantic_search(query, documents, top_k=3):
    """語意搜尋:找出和查詢最相關的文件"""
    query_vec = get_embedding(query)
    doc_vecs = [get_embedding(doc) for doc in documents]

    # 計算所有文件和查詢的相似度
    scores = [cosine_similarity(query_vec, dv) for dv in doc_vecs]

    # 按相似度排序,取前 k 筆
    ranked = sorted(zip(documents, scores), key=lambda x: x[1], reverse=True)
    return ranked[:top_k]

# 範例
docs = [
    "我們的退貨政策是 7 天內無條件退貨",
    "運費滿 500 元免運,未滿收 60 元",
    "營業時間為週一到週五 9:00-18:00",
    "如需退貨請聯繫客服,無需提供理由",
    "我們接受信用卡、LinePay 和銀行轉帳"
]

results = semantic_search("怎麼退貨?", docs)
for doc, score in results:
    print(f"{score:.3f} | {doc}")
# 0.891 | 如需退貨請聯繫客服,無需提供理由
# 0.856 | 我們的退貨政策是 7 天內無條件退貨
# 0.423 | 運費滿 500 元免運...

✂️ Chunking(文件切割策略)

要把長文件做 Embedding,必須先切割成小段落。切法直接決定 RAG 的品質

三種切法比較

策略做法優點缺點
固定長度每 500 字切一段簡單快速可能從句子中間斷開
句子分段按句號/段落分段語意完整段落長度不一
遞迴切割先段落 → 再句子 → 再固定長度平衡語意和長度實作稍複雜

遞迴切割實作

from langchain.text_splitter import RecursiveCharacterTextSplitter

splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,        # 每段最大 500 字
    chunk_overlap=50,      # 相鄰段重疊 50 字(保持上下文連續)
    separators=["\n\n", "\n", "。", "!", "?", ",", " "],
    # 優先在段落邊界切,其次句子邊界,最後才切字
)

document = "這是一篇很長的文件...(略)"
chunks = splitter.split_text(document)

print(f"切成 {len(chunks)} 段")
for i, chunk in enumerate(chunks):
    print(f"段落 {i+1} ({len(chunk)} 字): {chunk[:50]}...")

Chunking 最佳實踐

  • 🎯 Chunk size 500-1000 字(中文場景)——太短語意不完整,太長檢索不精確
  • 🔄 Overlap 10-15%——防止關鍵資訊被切在邊界
  • 📑 保留 metadata——每個 chunk 記住它來自哪個文件、哪個章節
  • 🧪 實際測試——沒有萬能參數,必須用你的資料測試

🗄️ 存入向量資料庫

Embedding 產生後,要存入專門的向量資料庫才能高效搜尋。

用 ChromaDB(最簡單、本地)

import chromadb

# 建立本地資料庫
client = chromadb.Client()
collection = client.create_collection("my_docs")

# 存入文件
collection.add(
    documents=["退貨政策是 7 天", "運費 500 免運", "營業時間 9-18"],
    ids=["doc1", "doc2", "doc3"],
    metadatas=[
        {"source": "faq.pdf", "page": 1},
        {"source": "faq.pdf", "page": 2},
        {"source": "about.pdf", "page": 1},
    ]
)

# 搜尋
results = collection.query(
    query_texts=["怎麼退貨"],
    n_results=2
)
print(results["documents"])
# [['退貨政策是 7 天', ...]]

用 Pinecone(雲端、生產)

from pinecone import Pinecone

pc = Pinecone(api_key=os.environ["PINECONE_API_KEY"])
index = pc.Index("my-index")

# 存入向量
index.upsert(vectors=[
    {"id": "doc1", "values": get_embedding("退貨政策是 7 天"),
     "metadata": {"source": "faq", "text": "退貨政策是 7 天"}},
    {"id": "doc2", "values": get_embedding("運費 500 免運"),
     "metadata": {"source": "faq", "text": "運費 500 免運"}},
])

# 搜尋
results = index.query(
    vector=get_embedding("怎麼退貨"),
    top_k=3,
    include_metadata=True
)

⚠️ Embedding 的限制

限制說明應對策略
最大長度大部分模型限 8192 tokens先切割再 embed
語意漂移太長的文字 embedding 會「模糊」切成 500-1000 字的小段
跨語言部分模型中文效果差選中文優化的模型(bge-m3, Cohere)
精準匹配搜 “PO-2024-0831” 可能找不到搭配關鍵字搜尋(Hybrid Search)
即時性模型的知識有截止日期結合 RAG 補充最新資訊

→ 學會 Embedding 後,你已經具備建構 RAG 系統的核心能力。


🗺️ 直觀理解:座標系統怎麼運作?

很多人卡在「一串數字怎麼代表意思」這一步,我們用三維空間舉例(實際上是上千維,但原理一樣):

          ↑ 動物性
          |
    🐱(0.9, 0.8, 0.1)
    🐶(0.85, 0.9, 0.1)
          |
          |_________→ 寵物性
         /
        /
    🚗(0.1, 0.05, 0.9) ← 交通工具
       ↙ 機械性

貓和狗的向量在「動物性」和「寵物性」維度上都很高、很接近——所以 cosine similarity 會是 0.9+。 汽車的向量在「機械性」維度很高,在「動物性」上接近 0——和貓的相似度只剩 0.1。

真實的 embedding 模型有 1024-3072 個維度,每一維代表某個抽象語意特徵(通常人類看不懂)。但數學邏輯一樣——語意相近 → 座標相近 → 向量夾角小


📊 2026 主流 Embedding 模型深度比較

上面的表格是概覽,這邊給生產級的選型細節。

模型維度價格/M tokens多語言最大 token最適合的場景
text-embedding-3-large3072(可降維)$0.138192需要最高精度的企業 RAG
text-embedding-3-small1536$0.028192預算敏感的一般 RAG
voyage-31024$0.0632000長文件、程式碼、金融法律
BGE-M31024免費(開源)最強中文8192中文為主、資料不外傳
Cohere embed-v41024$0.10多語言512跨語言搜尋(中英日韓混用)

三個不常被提的選型因素

1. Matryoshka 降維能力:OpenAI 3-large 可以從 3072 維「截斷」到 256 維仍保留多數效能。這對成本和搜尋速度影響巨大——256 維的向量 DB 成本只有 3072 維的 1/12。

2. 最大 token 長度:Voyage-3 的 32K 對法律、論文、程式碼 repo 是殺手鐧——減少切 chunk 的必要。

3. Instruction tuning:BGE-M3、Voyage 支援「query 和 document 用不同 prefix」,實測能提升 3-5% recall。別忽略這個小設定。


🔬 實戰 Mini RAG 範例(完整走一次)

為了把原理打通,我們手算一個 5 文件的 mini RAG。

知識庫(5 個文件)

docs = [
    "d1: 退貨政策為 7 天內無條件退貨,需保留完整包裝",
    "d2: 運費滿 500 元免運,未滿收取 60 元運費",
    "d3: 營業時間週一至週五 9:00 到 18:00",
    "d4: 付款方式支援信用卡、LinePay、銀行轉帳",
    "d5: 若商品有瑕疵請在 3 天內聯繫客服申請換貨"
]

查詢:「我買的東西壞掉了怎麼辦?」

query = "我買的東西壞掉了怎麼辦?"

# 1. 產生 embedding
query_vec = get_embedding(query)
doc_vecs = [get_embedding(d) for d in docs]

# 2. 計算 cosine similarity
sims = [cosine_similarity(query_vec, dv) for dv in doc_vecs]

# 實際跑出來的分數(text-embedding-3-small)
# d1 (退貨): 0.624
# d2 (運費): 0.312
# d3 (營業時間): 0.198
# d4 (付款): 0.287
# d5 (瑕疵換貨): 0.812  ← 最高!

# 3. 取 top-2 丟給 LLM
top_k = sorted(zip(docs, sims), key=lambda x: -x[1])[:2]
context = "\n".join([d for d, _ in top_k])

# 4. 組 prompt 問 LLM
prompt = f"根據以下資訊回答:\n{context}\n\n問題:{query}"

注意重點:使用者用「壞掉」,文件寫「瑕疵」——關鍵字搜尋會 miss,但 embedding 理解「壞掉 ≈ 瑕疵」,所以 d5 拿到 0.812 的高分。這就是語意搜尋贏關鍵字搜尋的地方。


✂️ Chunking 策略深入:什麼時候用哪種?

上面提到三種切法,這邊給更實戰的選擇標準。

三種策略的真實應用場景

Fixed-size(固定長度)

  • 什麼時候用:資料量巨大(100 萬+ 文件)、延遲敏感、內容結構均勻
  • 實際 case:新聞網站的文章全文檢索
  • 陷阱:會把句子從中間切斷,相似度會下降 5-10%

Semantic chunking(語意切割)

  • 什麼時候用:內容高度異質(產品文件、混合型知識庫)
  • 實際 case:技術文件同時包含「概念說明」+「程式碼」+「表格」
  • 陷阱:慢且貴——要先跑一次 embedding 判斷邊界,成本約 2-3 倍

Recursive(遞迴切割)

  • 什麼時候用99% 的情況,這個就是你的預設答案
  • 實際 case:公司 FAQ、客服知識庫、一般 RAG
  • 優勢:優先在段落/句子邊界切,只在必要時才動到字元層級

一個被忽略的細節:保留 parent document

好的 chunking 策略會存「子 chunk(用於檢索)+ 父 chunk(用於給 LLM 的上下文)」。檢索用精細的 200 字 chunk 提高精度,找到後擴展回 1000 字的段落給 LangChainMCP,LLM 才有足夠 context 生成答案。


🗄️ 向量資料庫選型快速指南

市面上向量 DB 很多,給你一個不迷路的決策樹

選擇適合情境優點缺點
pgvector(Postgres 擴充)已用 Postgres、中小資料量(< 100 萬向量)不用新增 infra、可和關聯資料 join大規模(千萬級)性能會掉
Qdrant自架、高效能、中大型Rust 寫的快、filter 功能強、可 self-host要自己運維
Pinecone完全託管、不想碰 infra零運維、scale 無痛貴、資料鎖死在他們家
ChromaDB原型、本地開發三行程式碼起跑不適合生產
Weaviate需要混合搜尋(keyword + semantic)BM25 + vector 內建 hybrid學習曲線較陡

我的私房建議

  • 新專案、已用 Postgres → pgvector,簡單到哭。
  • 專門做 AI 產品、資料量會爆 → Qdrant
  • 公司不差錢、工程師少 → Pinecone,花錢買時間。

更多選型思考可以搭配 MCP 開發實戰 一起看,把向量搜尋封裝成 MCP server 是 2026 的常見架構。


💥 常見錯誤(血淚踩坑)

這些錯誤我在輔導企業 RAG 時看到最多次:

1. Chunk size 亂設

  • 太大(2000+ 字):檢索回來一堆無關內容,Answer Relevancy 直接掉到 0.6 以下。
  • 太小(< 100 字):語意碎片化,上下文全失。
  • 正確做法:中文 500-1000 字起步,用 LLM 評估 跑 Ragas 看 context precision/recall 調整。

2. Query 和 Document 語言不一致

使用者用繁中問「如何申請退貨」,但知識庫是英文,用 text-embedding-3-small 會準嗎?——精度會掉 15-20%。解法:選支援 cross-lingual 的模型(Cohere embed-v4、BGE-M3)或先用 LLM 翻譯 query。

3. 沒有 reranker

Embedding 搜尋抓 top-20 相關的,但前 3 名不一定是最好的。加上 Cohere Rerank 或 BGE-reranker 重排序,通常能把 top-3 精度拉高 10-15%。這是 CP 值最高的單一優化

4. 忘記 normalize 向量

有些模型回傳的向量需要 L2 normalize 才能用 cosine similarity(BGE 系列),沒 normalize 分數會失真。讀官方文件確認。

5. 用 embedding 做精確匹配

搜「PO-2024-0831」這種單號,embedding 會「感覺它看起來像某個單號」而回錯。精確 ID 必須搭配關鍵字/SQL 搜尋,這就是 Hybrid Search 存在的理由。


🔗 延伸閱讀

  • RAG 完整指南——Embedding 是 RAG 的引擎,整個系統怎麼組請看這篇
  • LangChain 實戰——LangChain 的 VectorStore 抽象幫你少寫 80% 樣板
  • MCP 開發實戰——把向量搜尋封裝成 MCP tool,給 Claude/Cursor 直接用
  • LLM 評估指南——Embedding 調完了怎麼知道變好?看這篇

❓ FAQ

Embedding 和 Token 有什麼關係?

Token 是 LLM 處理文字的基本單位。Embedding 是把一整段文字轉成一個固定長度的向量。一段 100 tokens 的文字會被轉成一個 1536 維(text-embedding-3-small)的向量。

Embedding 模型需要 GPU 嗎?

用 API(OpenAI、Cohere)不需要 GPU。自行跑開源模型(bge-m3)用 CPU 也行但較慢,有 GPU 會快 10-50 倍。一般電腦可用 Ollama 跑小型 embedding 模型。

Chunk size 該設多大?

中文場景建議 500-1000 字。太短(< 200)語意不完整,太長(> 2000)搜尋不精確。沒有萬能答案,務必用你的實際資料測試比較。

向量資料庫和普通資料庫差在哪?

普通資料庫用關鍵字搜尋(SQL WHERE),向量資料庫用語意搜尋(找最接近的向量)。搜「想退貨」在傳統 DB 找不到「退貨政策」,但向量 DB 可以。兩者可結合使用(Hybrid Search)。

pgvector 撐得住生產環境嗎?

100 萬向量以下完全沒問題,延遲和精度都夠用,且省掉另外架一套 infra 的成本。超過這個量級會開始遇到 HNSW index 建構慢、記憶體吃重的問題,這時候再考慮 Qdrant 或 Pinecone。2026 年 pgvector 0.8 已經支援 HNSW,性能比一年前好非常多——不要再聽「pgvector 不能上生產」的過時建議

Embedding 要不要自己 fine-tune?

90% 的場景不用。通用模型(OpenAI 3-large、Voyage-3、BGE-M3)已經足夠強。只有在極度垂直的領域(醫學術語、法律條文、金融衍生品)才值得投資 fine-tune。Fine-tune 一個 embedding 模型至少要 5000 組 positive/negative pair,而且你要有持續維護資料集的人力。先用通用模型 + reranker,真的不夠再談 fine-tune。

Reranker 是什麼?一定要用嗎?

Reranker 是在 embedding 搜尋後再加一層精排。Embedding 搜尋快但粗(抓 top-20),reranker 慢但精(把 top-20 重新排序選 top-3)。一定要用——尤其是 RAG 場景,加上 Cohere Rerank 或 BGE-reranker 通常能把最終答案品質拉高 10-20%,成本增加有限。這是我看過 CP 值最高的單一優化。

中文場景用哪個 embedding 模型最好?

首選 BGE-M3(開源、中文最強、可本地跑)。如果要 API 不想自架,Cohere embed-v4 的繁體中文也很強。OpenAI 3-large 中文堪用但不是最佳,若已經在 OpenAI 生態可直接用。避免只用 text-embedding-3-small 做繁中 RAG——精度明顯不如 BGE-M3,省的那點錢不值得。

Embedding 存了之後,換模型怎麼辦?

要全部重新 embed——不同模型的向量空間不相容,不能混用。這是選型時最容易忽略的成本:10 萬文件從 3-small 換到 3-large,光 API 費就要 $200-500,還不算重建索引的時間。建議:新專案先用便宜的 small 跑通流程,確定品質不夠再升級;或是一開始就用可降維的 Matryoshka 模型保留彈性。

📚 延伸閱讀