本文 AI 產出,尚未審核

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

  1. 文本前處理(斷詞、去除噪音)
  2. 呼叫 Embedding API 產生向量(float[]
  3. 向量正規化(L2 正規化)提升相似度計算的穩定性
  4. 儲存至向量資料庫(如 Pinecone、Chroma、Weaviate)
  5. 檢索 & 重排序(向量相似度 + LLM 重新生成)

程式碼範例

以下範例均以 Node.jslangchainjs)為基礎,展示從前處理到向量檢索的完整流程。請先安裝相依套件:

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,失去語意細節。 使用 jiebackip 等斷詞工具,或選擇 詞粒度的中文模型(如 text2vec-base-chinese)。
跨語言檢索語言偏見 多語模型在高資源語言(英文)上表現較好,中文相對較弱。 若中文需求高,先以中文模型建立向量庫,再使用多語模型做跨語言映射(如使用 sentence-transformers/LaBSE 做語言對齊)。
API 費用失控 大量文件一次性嵌入會產生高額費用。 批次 分段嵌入(例如每 1000 筆一次),同時使用 本地模型(如 HuggingFace)降低成本。
向量資料庫過期 向量庫未同步最新文件,導致檢索結果過時。 建立 增量更新機制:文件變更時只重新嵌入變更部分,使用 upsert 功能。

小技巧

  1. 使用 metadata 標記語言與領域:檢索時可加入 filter,只在相同語言或相同主題的向量中搜尋。
  2. 混合檢索:先用向量相似度快速篩選 Top‑k,再用 BM25LLM 重排序 完成精細化。
  3. 向量壓縮:若向量維度過高(例如 1536),可使用 PCAProduct Quantization 降低儲存成本,對檢索精度影響有限。

實際應用場景

場景 需求 推薦模型與流程
中文客服機器人 即時回應、支援多領域(商品、物流) text-embedding-3-large + Chroma + LLM 重排序;斷詞前處理 + metadata 標記領域。
跨語言文件搜尋(公司內部多國語言文件) 中文、英文、日文同時檢索 sentence-transformers/LaBSEtext-embedding-3-large;向量庫加入 lang metadata,使用語言過濾。
電商商品相似度推薦 高精度中文商品描述相似度 text2vec-base-chinese(開源)+ L2 正規化;使用 IVF‑FlatHNSW 索引加速。
學術文獻多語檢索 支援中文、英文、德文摘要比對 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 的世界裡看到更多 中文智慧應用! 🚀