本文 AI 產出,尚未審核

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. 向量化流程概覽

  1. 文件切分(Chunking)
    • 大段文字需要切成適合模型輸入長度的 chunk(例如 500~1000 token)。
  2. Embedding 計算
    • 使用 Embeddings 類別把每個 chunk 轉成向量。
  3. 向量儲存
    • 把向量與原始 chunk 的 metadata(如檔名、段落編號)寫入向量資料庫(FAISS、Pinecone、Milvus 等)。
  4. 相似度檢索
    • 查詢時先把問題 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-largesentence-transformers/paraphrase-multilingual-MiniLM-L12-v2
忽略向量正規化 相似度計算時向量長度差異會影響結果 大多數向量資料庫會自動正規化;若自行計算,使用 cosine similarity 而非 Euclidean

最佳實踐

  1. 先切分再 embed:切分策略(大小、重疊、段落邊界)直接影決向量品質。
  2. 使用批次 embed:一次呼叫 embedDocuments 能減少網路往返次數。
  3. 保存完整 metadata:包括檔案路徑、段落號、來源時間,方便後續追蹤與 UI 呈現。
  4. 定期重建向量庫:當模型升級或資料有大幅變更時,重新 embed 能提升檢索效果。
  5. 監控相似度分數:設定一個分數門檻(如 >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、靈活的切分工具與多樣的向量資料庫介面,只要遵守以下幾點,就能在實務專案中快速上手:

  1. 適當切分:確保每個 chunk 符合模型 token 限制且保留語意完整性。
  2. 選對模型:根據語言、成本與部署需求挑選 OpenAI、HuggingFace 或自建模型。
  3. 批次處理與正規化:減少 API 呼叫、避免向量維度不匹配。
  4. 完整 Metadata:檢索結果才能回溯到原始資料。
  5. 持續監控與重建:模型升級或資料變動時,重新 embed 以維持檢索品質。

掌握了 向量化流程與注意事項,您就能利用 LangChain 建立 高效、可擴展且語意準確 的檢索與生成系統,從聊天機器人到企業搜尋,皆能快速落地。祝開發順利,期待看到您打造的下一個 AI 產品!