LangChain 教學 – Embedding(向量嵌入)
中文/多語嵌入最佳化
簡介
在自然語言處理(NLP)與生成式 AI 的應用中,**向量嵌入(Embedding)**是把文字、句子甚至整段文件轉換成高維向量的核心技術。這些向量讓機器能以「距離」或「相似度」的方式比較語意,從而完成檢索、分類、摘要等任務。
對於中文或多語言環境,嵌入的品質直接決定搜尋結果的相關性、Chatbot 的回應正確度,以及跨語言資訊檢索的效果。若嵌入模型未針對中文特性(如詞彙切分、字形相似)或跨語言語意對齊進行優化,往往會出現「語意斷層」或「語言偏見」的問題。
本篇文章將以 LangChain 為框架,說明如何在 中文/多語 場景下選擇、調校與部署向量嵌入模型,並提供實作範例、常見陷阱與最佳實踐,幫助你快速打造高品質的語意檢索系統。
核心概念
1. 為什麼中文嵌入需要特別處理?
- 字/詞粒度差異:中文沒有明顯的空格分隔,若直接使用英文模型的 tokenization,會把整句當成單一 token,導致向量缺乏細節。
- 同形異義字:如「行」在不同語境下可能是「走路」或「公司」,模型需要足夠的語境資訊才能分辨。
- 語料偏差:許多開源嵌入模型主要訓練於英文語料,中文語料比例不足,會產生語意漂移。
2. 多語嵌入的挑戰
- 語言對齊:跨語言檢索需要不同語言的向量落在同一向量空間(shared embedding space)。
- 語言容量:一個模型若同時支援 100+ 語言,往往在單一語言(尤其是低資源語言)上的表現會被稀釋。
- 計算成本:大型多語模型(如 XLM‑R、mBERT)參數龐大,部署成本較高。
3. LangChain 中的 Embedding 接口
LangChain 把嵌入抽象成 Embedding 類別,提供 embed_documents(批量文件)與 embed_query(單筆查詢)兩個主要方法。以下是最常見的實作:
| 類別 | 來源 | 適用語言 | 特點 |
|---|---|---|---|
OpenAIEmbeddings |
OpenAI | 多語(支援中文) | 高品質、即時調整 temperature,不過受限於 API 費用 |
HuggingFaceEmbeddings |
HuggingFace | 多語 | 可自訂模型(如 sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2) |
CohereEmbeddings |
Cohere | 多語 | 低延遲、支援大量批次 |
AzureOpenAIEmbeddings |
Azure | 多語 | 企業級安全與 SLA |
重點:在中文/多語場景下,建議優先挑選 已在中文或多語語料上 fine‑tune 的模型,或自行在中文語料上微調(Fine‑tune)。
4. 向量化流程概覽
- 文本前處理(斷詞、去除噪音)
- 呼叫 Embedding API 產生向量(
float[]) - 向量正規化(L2 正規化)提升相似度計算的穩定性
- 儲存至向量資料庫(如 Pinecone、Chroma、Weaviate)
- 檢索 & 重排序(向量相似度 + LLM 重新生成)
程式碼範例
以下範例均以 Node.js(langchainjs)為基礎,展示從前處理到向量檢索的完整流程。請先安裝相依套件:
npm install langchain @langchain/community @langchain/openai @langchain/huggingface
1️⃣ 範例一:使用 OpenAI text-embedding-3-large(支援中文)產生向量
// embed_openai.js
import { OpenAIEmbeddings } from "@langchain/openai";
import { Document } from "@langchain/core/documents";
// 建立 Embedding 實例,設定模型與 API 金鑰
const embeddings = new OpenAIEmbeddings({
modelName: "text-embedding-3-large",
openAIApiKey: process.env.OPENAI_API_KEY,
});
// 範例中文句子
const docs = [
new Document({ pageContent: "台北市的天氣今天非常晴朗。" }),
new Document({ pageContent: "台北的美食以小吃聞名,例如鹽酥雞。" }),
];
// 批次產生向量
async function embedDocuments() {
const vectors = await embeddings.embedDocuments(
docs.map((d) => d.pageContent)
);
console.log("向量維度:", vectors[0].length);
console.log("第一筆向量 (前 5 維):", vectors[0].slice(0, 5));
}
embedDocuments();
說明:
embedDocuments會自動把中文句子切成 token,OpenAI 內部已處理中文語意對齊,適合快速原型開發。
2️⃣ 範例二:使用 HuggingFace 多語模型 sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2
// embed_hf.js
import { HuggingFaceEmbeddings } from "@langchain/huggingface";
import { Document } from "@langchain/core/documents";
// 使用 MiniLM 多語模型(開源、免費)
const embeddings = new HuggingFaceEmbeddings({
modelName: "sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2",
// 若本機 GPU 可用,設定 device: "cuda"
// device: "cpu",
});
const docs = [
new Document({ pageContent: "今天天氣真好,適合去淡水散步。" }),
new Document({ pageContent: "我想找一間好吃的咖啡廳。" }),
];
// 產生向量並正規化
async function embedAndNormalize() {
const rawVectors = await embeddings.embedDocuments(
docs.map((d) => d.pageContent)
);
// L2 正規化
const normalized = rawVectors.map((v) => {
const norm = Math.sqrt(v.reduce((sum, x) => sum + x * x, 0));
return v.map((x) => x / norm);
});
console.log("正規化後第一筆向量 (前 5 維):", normalized[0].slice(0, 5));
}
embedAndNormalize();
說明:此模型在 100 多種語言 上都有不錯的語意對齊,適合跨語言檢索。但在中文細節(如成語、地名)上略遜於專屬中文模型。
3️⃣ 範例三:在 LangChain 中結合向量資料庫(Chroma)與 LLM 重排序
// retrieval_with_llm.js
import { OpenAIEmbeddings } from "@langchain/openai";
import { OpenAI } from "@langchain/openai";
import { Chroma } from "@langchain/community/vectorstores/chroma";
import { PromptTemplate } from "@langchain/core/prompts";
// 步驟 1:建立嵌入與向量資料庫
const embeddings = new OpenAIEmbeddings({
modelName: "text-embedding-3-large",
openAIApiKey: process.env.OPENAI_API_KEY,
});
const chroma = await Chroma.fromTexts(
[
"台北101 是世界最高的摩天大樓之一。",
"台中市的彩虹眷村充滿彩繪藝術。",
"高雄的駁二藝術特區是文創聚落。",
],
// 每筆文件的 metadata,方便後續過濾
[{ city: "Taipei" }, { city: "Taichung" }, { city: "Kaohsiung" }],
embeddings
);
// 步驟 2:建立檢索器
const retriever = chroma.asRetriever({
k: 3, // 取前 3 筆最相似向量
});
// 步驟 3:使用 LLM 產生最終回應
const llm = new OpenAI({
temperature: 0,
openAIApiKey: process.env.OPENAI_API_KEY,
});
const prompt = PromptTemplate.fromTemplate(
`以下是與使用者問題相關的資訊,請根據這些資訊回答問題。
問題: {question}
資訊:\n{context}\n\n回答:`
);
async function answerQuestion(question) {
// 取得相關文件
const docs = await retriever.getRelevantDocuments(question);
const context = docs.map((d) => d.pageContent).join("\n---\n");
// 呼叫 LLM
const answer = await llm.invoke(
await prompt.format({ question, context })
);
console.log("LLM 回答:", answer);
}
answerQuestion("台北有哪些著名的觀光景點?");
說明:此範例展示 向量檢索 + LLM 重排序 的典型流程,適合中文問答、客服機器人等應用。透過
metadata可以在檢索階段加入 語言、領域 等過濾條件,提升多語環境的精準度。
4️⃣ 範例四:自訂斷詞前處理(結合 jieba)
// embed_with_jieba.js
import { HuggingFaceEmbeddings } from "@langchain/huggingface";
import jieba from "nodejieba";
// 先對中文句子做斷詞,減少模型內部切分錯誤
function preprocess(text) {
const tokens = jieba.cut(text);
// 重新組合成空格分隔的字串,讓模型視為「詞」而非單字
return tokens.join(" ");
}
const embeddings = new HuggingFaceEmbeddings({
modelName: "sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2",
});
const raw = "我想了解台北市的公共自行車租借系統。";
const processed = preprocess(raw);
console.log("斷詞後:", processed);
// 產生向量
async function run() {
const vector = await embeddings.embedQuery(processed);
console.log("向量維度:", vector.length);
}
run();
說明:對於 字粒度模型(如 MiniLM)而言,先把句子斷成詞可以提升語意捕捉的精細度,尤其在中文專有名詞或成語上效果顯著。
5️⃣ 範例五:跨語言檢索(中文 ↔ 英文)使用 text-embedding-3-large
// cross_lingual.js
import { OpenAIEmbeddings } from "@langchain/openai";
import { Chroma } from "@langchain/community/vectorstores/chroma";
const embeddings = new OpenAIEmbeddings({
modelName: "text-embedding-3-large",
openAIApiKey: process.env.OPENAI_API_KEY,
});
// 中文文件
const zhDocs = [
"台北故宮博物院收藏豐富的中國古代文物。",
];
// 英文文件
const enDocs = [
"The Louvre Museum in Paris houses many famous artworks.",
];
// 建立混合向量庫
const vectorStore = await Chroma.fromTexts(
[...zhDocs, ...enDocs],
[{ lang: "zh" }, { lang: "en" }],
embeddings
);
// 中文查詢,找出英文最相關的文件
const retriever = vectorStore.asRetriever({ k: 2 });
const query = "巴黎有哪些著名的博物館?";
const results = await retriever.getRelevantDocuments(query);
console.log("跨語言檢索結果:");
results.forEach((doc, i) => {
console.log(`${i + 1}. (${doc.metadata.lang}) ${doc.pageContent}`);
});
說明:
text-embedding-3-large已在多語語料上做過對齊,中文與英文的向量自然落在同一空間,可直接進行跨語言相似度搜尋,適合 多語知識庫、跨國客服 等情境。
常見陷阱與最佳實踐
| 陷阱 | 說明 | 解決方案/最佳實踐 |
|---|---|---|
| 向量維度不一致 | 同一資料庫中混用不同模型會導致維度不匹配,檢索失效。 | 只在同一專案內使用 單一模型(或確保所有模型維度相同),若需切換,必須重新重建向量庫。 |
| 未正規化向量 | 直接使用 raw 向量計算餘弦相似度,會因向量長度差異產生偏差。 | 在儲存前 L2 正規化,或在向量資料庫設定 metric: "cosine"(大多數資料庫會自動正規化)。 |
| 中文斷詞不當 | 直接使用字粒度模型會把整句視為單一 token,失去語意細節。 | 使用 jieba、ckip 等斷詞工具,或選擇 詞粒度的中文模型(如 text2vec-base-chinese)。 |
| 跨語言檢索語言偏見 | 多語模型在高資源語言(英文)上表現較好,中文相對較弱。 | 若中文需求高,先以中文模型建立向量庫,再使用多語模型做跨語言映射(如使用 sentence-transformers/LaBSE 做語言對齊)。 |
| API 費用失控 | 大量文件一次性嵌入會產生高額費用。 | 批次 分段嵌入(例如每 1000 筆一次),同時使用 本地模型(如 HuggingFace)降低成本。 |
| 向量資料庫過期 | 向量庫未同步最新文件,導致檢索結果過時。 | 建立 增量更新機制:文件變更時只重新嵌入變更部分,使用 upsert 功能。 |
小技巧
- 使用
metadata標記語言與領域:檢索時可加入filter,只在相同語言或相同主題的向量中搜尋。 - 混合檢索:先用向量相似度快速篩選 Top‑k,再用 BM25 或 LLM 重排序 完成精細化。
- 向量壓縮:若向量維度過高(例如 1536),可使用 PCA 或 Product Quantization 降低儲存成本,對檢索精度影響有限。
實際應用場景
| 場景 | 需求 | 推薦模型與流程 |
|---|---|---|
| 中文客服機器人 | 即時回應、支援多領域(商品、物流) | text-embedding-3-large + Chroma + LLM 重排序;斷詞前處理 + metadata 標記領域。 |
| 跨語言文件搜尋(公司內部多國語言文件) | 中文、英文、日文同時檢索 | sentence-transformers/LaBSE 或 text-embedding-3-large;向量庫加入 lang metadata,使用語言過濾。 |
| 電商商品相似度推薦 | 高精度中文商品描述相似度 | text2vec-base-chinese(開源)+ L2 正規化;使用 IVF‑Flat 或 HNSW 索引加速。 |
| 學術文獻多語檢索 | 支援中文、英文、德文摘要比對 | sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2 + BM25 結合;先向量檢索 Top‑50,再用 LLM 做摘要比對。 |
| 智慧城市資訊平台(即時天氣、交通) | 大量短文本(幾十字)快速檢索 | text-embedding-3-large(低延遲)+ RedisVector(內存)或 Chroma;斷詞可省略,直接使用字粒度模型。 |
總結
- 中文與多語向量嵌入 不是直接套用英文模型就能得到好結果,必須考慮斷詞、語料偏差與語言對齊等因素。
- LangChain 提供統一的
Embedding抽象,讓開發者可以輕鬆切換 OpenAI、HuggingFace、Cohere 等服務,同時結合向量資料庫與 LLM 完成 檢索‑生成(RAG) 流程。 - 最佳實踐 包括:選擇已在中文或多語語料上 fine‑tune 的模型、做向量正規化、利用
metadata做語言/領域過濾、結合 LLM 重排序提升答案品質。 - 透過上述範例與技巧,你可以快速構建 中文/多語向量檢索系統,無論是客服、知識庫、電商推薦或跨國資訊平台,都能在語意層面提供更精準、自然的使用者體驗。
祝開發順利,期待在 LangChain 的世界裡看到更多 中文智慧應用! 🚀