LangChain 課程 – Embedding:向量嵌入
向量化流程與注意事項
簡介
在 LLM(大型語言模型) 與 向量資料庫 的結合中,向量嵌入(Embedding) 是最關鍵的第一步。它把原始文字、文件、甚至程式碼轉換成固定長度的向量,使得相似度搜尋、語意比對與檢索成為可能。若向量化的品質不佳,後續的檢索、問答或生成都會受到嚴重影響。
LangChain 作為一套 LLM 應用框架,提供了完整的「文字 → 向量 → 向量資料庫」流程封裝。掌握向量化的原理、流程與常見陷阱,才能在實務專案中發揮 LangChain 的最大效能。
核心概念
1. 什麼是 Embedding?
Embedding 是一種 將離散文字映射到連續向量空間 的技術。向量的每個維度代表了文字在語意空間中的某種特徵,向量之間的 餘弦相似度(或 Euclidean 距離)即可反映語意相似度。
- 稠密向量(Dense Vector):如 OpenAI 的
text-embedding-ada-002,維度 1536。 - 稀疏向量(Sparse Vector):如 BM25、TF‑IDF,維度通常非常大且多為 0。
- 多模態向量:同時包含文字、圖片或音訊的特徵。
2. LangChain 中的 Embedding 抽象
LangChain 把 embedding 抽象成 Embeddings 類別,透過 embed_documents(批次文件)與 embed_query(單一查詢)兩個介面與底層模型互動。常見的實作有:
| 實作類別 | 背後模型 | 特色 |
|---|---|---|
OpenAIEmbeddings |
OpenAI text-embedding-ada-002 |
高品質、即時、付費 |
HuggingFaceEmbeddings |
本地模型(如 sentence-transformers/all-MiniLM-L6-v2) |
可自行部署、免 API 費 |
CohereEmbeddings |
Cohere API | 支援多語言、可調整維度 |
3. 向量化流程概覽
- 文件切分(Chunking)
- 大段文字需要切成適合模型輸入長度的 chunk(例如 500~1000 token)。
- Embedding 計算
- 使用
Embeddings類別把每個 chunk 轉成向量。
- 使用
- 向量儲存
- 把向量與原始 chunk 的 metadata(如檔名、段落編號)寫入向量資料庫(FAISS、Pinecone、Milvus 等)。
- 相似度檢索
- 查詢時先把問題 embed 成向量,再在資料庫中找最近的 k 個向量,取得對應的原文回傳給 LLM。
下面的程式碼示範了這整個流程。
程式碼範例
以下範例使用 LangChainJS(Node.js),若您習慣 Python,只要把類別換成
langchain.embeddings即可。
1. 安裝必要套件
npm install langchain @langchain/openai @langchain/community faiss-node
2. 基本向量化流程
// 1️⃣ 載入套件
import { OpenAIEmbeddings } from "@langchain/openai";
import { RecursiveCharacterTextSplitter } from "@langchain/textsplitters";
import { FAISS } from "@langchain/community/vectorstores/faiss";
// 2️⃣ 初始化 Embedding 模型(使用 OpenAI)
const embeddings = new OpenAIEmbeddings({
// 需要在環境變數 OPENAI_API_KEY 中設定金鑰
});
// 3️⃣ 文件切分(每段 500 token,重疊 50 token)
const splitter = new RecursiveCharacterTextSplitter({
chunkSize: 500,
chunkOverlap: 50,
});
// 假設有一段長文字
const rawText = `
LangChain 是一個用於構建 LLM 應用的框架...
(此處省略大量內容)
`;
// 4️⃣ 產生 chunk
const docs = await splitter.createDocuments([rawText]);
// 5️⃣ 計算每個 chunk 的向量
const vectors = await embeddings.embedDocuments(docs.map(d => d.pageContent));
// 6️⃣ 建立 FAISS 向量資料庫(內存版)
const vectorStore = await FAISS.fromTexts(
docs.map(d => d.pageContent), // 原始文字
docs.map((d, i) => ({ id: i })), // metadata
embeddings
);
// 7️⃣ 查詢測試
const query = "LangChain 的核心概念是什麼?";
const queryVector = await embeddings.embedQuery(query);
const results = await vectorStore.similaritySearchVectorWithScore(queryVector, 3);
// 輸出前 3 個最相似的段落
results.forEach(([doc, score]) => {
console.log(`Score: ${score.toFixed(4)} | Text: ${doc.pageContent.slice(0, 80)}...`);
});
重點說明
RecursiveCharacterTextSplitter會自動處理 重疊,避免關鍵資訊在切分時被切斷。FAISS.fromTexts同時完成 向量計算 與 資料庫建置,非常適合快速原型。similaritySearchVectorWithScore回傳的是 [Document, similarityScore],分數越接近 1 表示越相似。
2. 使用本地 HuggingFace 模型(免 API)
import { HuggingFaceInferenceEmbeddings } from "@langchain/community/embeddings/hf";
import { MemoryVectorStore } from "@langchain/community/vectorstores/memory";
const hfEmbeddings = new HuggingFaceInferenceEmbeddings({
model: "sentence-transformers/all-MiniLM-L6-v2", // 輕量模型
// 若有自行部署的 inference endpoint,可設定 endpointUrl
});
// 建立記憶體向量庫
const memoryStore = new MemoryVectorStore(hfEmbeddings);
// 假設有多篇文章
const articles = [
"向量資料庫可以加速語意搜尋。",
"LangChain 支援多種資料來源,包括 PDF、CSV、網頁。",
"Embedding 的品質直接影響檢索結果的相關性。",
];
// 直接加入向量庫(內部會自動 embed)
await memoryStore.addDocuments(articles.map(text => ({ pageContent: text })));
// 查詢
const answer = await memoryStore.similaritySearch("什麼是向量資料庫?", 2);
console.log(answer.map(d => d.pageContent));
提示:本地模型在 GPU 環境下速度會大幅提升;若只有 CPU,建議使用
all-MiniLM-L6-v2這類 22M 參數的模型。
3. 大規模資料上線:使用 Pinecone 作為向量雲端服務
import { PineconeStore } from "@langchain/community/vectorstores/pinecone";
import { PineconeClient } from "@pinecone-database/pinecone";
// 初始化 Pinecone
const pinecone = new PineconeClient();
await pinecone.init({
environment: "us-west1-gcp", // 依照你的 region 設定
apiKey: process.env.PINECONE_API_KEY,
});
const index = pinecone.Index("langchain-demo");
// 使用同樣的 OpenAI Embeddings
const pineconeStore = await PineconeStore.fromExistingIndex(
embeddings,
{ pineconeIndex: index, namespace: "course-embedding" }
);
// 假設已有 10,000 份文件的 chunk 已經 embed 完成
// 直接上傳到 Pinecone(批次上傳)
await pineconeStore.addDocuments(docs.map(d => ({
pageContent: d.pageContent,
metadata: { source: "pdf", id: d.metadata.id },
})));
// 查詢
const queryVector = await embeddings.embedQuery("LangChain 如何結合向量搜尋?");
const topK = await pineconeStore.similaritySearchVectorWithScore(queryVector, 5);
topK.forEach(([doc, score]) => {
console.log(`Score ${score.toFixed(3)} | ${doc.metadata.id}: ${doc.pageContent.slice(0, 60)}...`);
});
注意:
- 上傳速度 受限於每次批次大小(建議 100~500 條)與網路頻寬。
- 向量維度 必須與 Pinecone index 建立時的維度相同(如 1536)。
- 使用 namespace 可以在同一 index 中分離不同專案或版本的向量。
4. 多模態向量:同時嵌入文字與圖片
import { OpenAIEmbeddings } from "@langchain/openai";
import { OpenAIImageEmbeddings } from "@langchain/openai/image";
import { MultiModalRetriever } from "@langchain/community/retrievers/multimodal";
// 文字嵌入
const textEmb = new OpenAIEmbeddings();
// 圖片嵌入(使用 CLIP)
const imageEmb = new OpenAIImageEmbeddings();
// 建立多模態檢索器
const retriever = new MultiModalRetriever({
textEmbeddings: textEmb,
imageEmbeddings: imageEmb,
vectorStore: vectorStore, // 共享同一個向量資料庫
});
// 查詢文字 + 圖片
const queryText = "這張圖說明的機器學習流程是什麼?";
const queryImagePath = "./assets/pipeline.png";
const resultsMM = await retriever.getRelevantDocuments({
text: queryText,
image: queryImagePath,
});
console.log("Multi‑modal top result:", resultsMM[0].pageContent);
應用:在檔案管理、電商搜尋或教育平台,文字+圖片 的混合檢索能提供更精準的結果。
常見陷阱與最佳實踐
| 陷阱 | 為什麼會發生 | 解決方式 |
|---|---|---|
| 切分過大 | 超過模型 token 上限(OpenAI 8192 token)會拋出錯誤或截斷 | 使用 RecursiveCharacterTextSplitter,設定 chunkSize ≤ 1000 且適度 chunkOverlap |
| 向量維度不匹配 | 向量資料庫建立時的維度與實際 embed 的維度不同 | 建立資料庫前先 console.log(embedding.dimensions),或在 FAISS/Pinecone 建立時明確指定 |
| 忘記儲存 metadata | 檢索結果只能得到向量,無法對應原始檔案位置 | 在 addDocuments 時傳入 { metadata: { source, page, ... } } |
| 大量文件一次上傳 | API 限流或記憶體 OOM | 分批(batchSize 100~500)上傳,或使用 流式(streaming)方式 |
| 不同語言混雜 | 某些 embedding 模型對非英語支援較差,導致相似度偏低 | 選擇支援多語言的模型(如 text-embedding-3-large 或 sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2) |
| 忽略向量正規化 | 相似度計算時向量長度差異會影響結果 | 大多數向量資料庫會自動正規化;若自行計算,使用 cosine similarity 而非 Euclidean |
最佳實踐:
- 先切分再 embed:切分策略(大小、重疊、段落邊界)直接影決向量品質。
- 使用批次 embed:一次呼叫
embedDocuments能減少網路往返次數。 - 保存完整 metadata:包括檔案路徑、段落號、來源時間,方便後續追蹤與 UI 呈現。
- 定期重建向量庫:當模型升級或資料有大幅變更時,重新 embed 能提升檢索效果。
- 監控相似度分數:設定一個分數門檻(如 >0.78)過濾噪聲,避免把不相關結果交給 LLM。
實際應用場景
| 場景 | 目標 | LangChain + Embedding 的角色 |
|---|---|---|
| 企業內部文件搜尋 | 讓員工快速查到政策、手冊或技術文件 | 把 PDF、Word 轉成文字 → 切分 → embed → 存入 Pinecone → 前端使用 LangChain 的 RetrievalQAChain |
| 聊天機器人 FAQ | 依照使用者問句返回最相關的答案段落 | 預先把常見問答對 embed → 使用 FAISS 本地檢索 → 把檢索結果當作 LLM 的上下文 |
| 多語言客服 | 同時支援中文、英文、日文等語言的查詢 | 使用 多語言 embedding(如 text-embedding-3-large)建立單一向量庫,語言自動對齊 |
| 電商商品相似推薦 | 根據商品描述與圖片找相似商品 | 文字嵌入 + 圖片 CLIP 嵌入 → 合併向量 → 用 FAISS 近似最近鄰搜索 |
| 法律文件比對 | 判斷兩份合約條款的相似度 | 把條款切成句子 → embed → 計算 cosine similarity → 高於門檻即標記為相似 |
總結
向量嵌入是 LLM 應用 的基礎建設,LangChain 為我們提供了 統一的 API、靈活的切分工具與多樣的向量資料庫介面,只要遵守以下幾點,就能在實務專案中快速上手:
- 適當切分:確保每個 chunk 符合模型 token 限制且保留語意完整性。
- 選對模型:根據語言、成本與部署需求挑選 OpenAI、HuggingFace 或自建模型。
- 批次處理與正規化:減少 API 呼叫、避免向量維度不匹配。
- 完整 Metadata:檢索結果才能回溯到原始資料。
- 持續監控與重建:模型升級或資料變動時,重新 embed 以維持檢索品質。
掌握了 向量化流程與注意事項,您就能利用 LangChain 建立 高效、可擴展且語意準確 的檢索與生成系統,從聊天機器人到企業搜尋,皆能快速落地。祝開發順利,期待看到您打造的下一個 AI 產品!