LangChain
單元:Retrieval 與資料查詢
主題:向量查詢(Vector Search)基礎
簡介
在現代的 LLM(大型語言模型)應用中,檢索(Retrieval) 已成為提升模型回答正確性與上下文相關性的關鍵技術。
傳統的關鍵字搜尋只能捕捉字面上的匹配,卻無法理解語意的相似度;而 向量查詢(Vector Search)則透過把文字、圖像或其他資料轉換成高維向量,利用 相似度度量 直接找出語意最接近的內容。
LangChain 作為一套將 LLM 與外部工具(如向量資料庫)結合的框架,已內建多種 VectorStore 實作,讓開發者可以在 幾行程式碼 內完成向量化、索引、以及檢索的全流程。掌握向量查詢的基礎概念與實作方式,是建置 問答系統、知識庫、文件搜尋 等應用的第一步。
核心概念
1. 向量空間模型與 Embedding
- 向量空間模型(Vector Space Model):將每一筆文字(句子、段落、文件)映射到一個多維向量。向量的每個維度代表某種語意特徵,距離越近代表語意越相似。
- Embedding:產生向量的具體演算法。常見的有 OpenAI 的
text-embedding-ada-002、OpenAI 的text-embedding-3-large、以及開源的sentence‑transformers。
重點:Embedding 的品質直接決定後續檢索的效果,選擇與應用領域相符的模型非常重要。
2. 向量資料庫(Vector Store)
向量資料庫負責 儲存向量、建立索引(如 IVF、HNSW)以及 快速相似度搜尋。常見的開源實作包括:
| 資料庫 | 特色 | 適用情境 |
|---|---|---|
| FAISS | 高效、支援 GPU、可自行調校索引類型 | 大規模(上億筆)向量檢索 |
| Chroma | 本地端易部署、支援 metadata | 小至中型專案、快速原型 |
| Pinecone / Weaviate / Qdrant | 雲端服務、即時擴展、內建過期策略 | 需要高可用與彈性伸縮的商業系統 |
LangChain 抽象出 VectorStore 介面,讓開發者可以 無縫切換 背後的資料庫,只需更換實例化的類別即可。
3. 相似度度量
- Cosine Similarity(餘弦相似度)是最常用的度量方式,計算兩向量的夾角。
- Inner Product(內積)在某些索引(如 IVF‑PQ)中更高效。
- 若向量已正規化(單位向量),兩者等價。
LangChain 的 Retriever 預設使用 cosine similarity,但可自行透過 search_type 參數調整。
4. LangChain 中的 Retriever 流程
- Embedding → 把原始文字轉成向量。
- 向量寫入 VectorStore → 建立或更新索引。
- 檢索 → 從 VectorStore 取得相似向量,回傳原始文件或片段。
- 傳遞給 LLM → LLM 以檢索結果作為上下文產生答案。
下面的程式碼範例會一步步示範上述流程。
程式碼範例
說明:以下範例皆使用 Python,因為 LangChain 官方支援最完整。若您偏好其他語言,可參考 LangChain 社群的對應 SDK(如
langchainjs)。
範例 1:使用 OpenAI Embedding 產生向量
from langchain.embeddings import OpenAIEmbeddings
# 取得 OpenAI 的文字嵌入模型(text-embedding-ada-002)
embeddings = OpenAIEmbeddings(model="text-embedding-ada-002")
# 測試文字
texts = [
"LangChain 是一個可以讓 LLM 與外部工具互動的框架。",
"向量搜尋能夠根據語意相似度找出相關文件。",
]
# 產生向量(list of list[float])
vectors = embeddings.embed_documents(texts)
print(vectors[0][:5]) # 顯示前 5 個維度的值
重點:
embed_documents會一次處理多筆文件,回傳的vectors與texts的順序相對應。
範例 2:將向量寫入 Chroma(本地向量資料庫)
from langchain.vectorstores import Chroma
from langchain.docstore.document import Document
# 建立 Document 物件,方便後續檢索時返回原始文字
docs = [Document(page_content=text) for text in texts]
# 使用剛才產生的 embedding 以及 Chroma 建立向量庫
vectorstore = Chroma.from_documents(
documents=docs,
embedding=embeddings, # 直接傳入 Embedding 物件,LangChain 會自動呼叫 embed_documents
collection_name="langchain_demo" # Chroma 內部的 collection 名稱
)
print("向量庫已建立,文件數量:", vectorstore._collection.count())
提示:Chroma 會把向量與
Document的metadata一起儲存,之後檢索得到的結果會回傳完整的Document物件。
範例 3:使用 FAISS 建立大規模向量索引
from langchain.vectorstores import FAISS
# 假設有 10,000 筆文件(此處僅示範重複使用前面的 texts)
large_texts = texts * 5000 # 產生大量測試資料
large_docs = [Document(page_content=t) for t in large_texts]
# 建立 FAISS 向量庫(會自動選擇 Inner Product 作為相似度度量)
faiss_store = FAISS.from_documents(
documents=large_docs,
embedding=embeddings
)
print("FAISS 向量庫文件數量:", faiss_store.index.ntotal)
注意:FAISS 需要安裝
faiss-cpu(或faiss-gpu)套件;在雲端環境務必確認相容的 Python 版本。
範例 4:結合 Retriever 與 LLM,完成「問答」流程
from langchain.llms import OpenAI
from langchain.chains import RetrievalQA
# 以剛剛的 Chroma 向量庫作為檢索器
retriever = vectorstore.as_retriever(search_kwargs={"k": 3}) # 取前 3 筆最相似結果
# 建立 LLM(使用 GPT-3.5 Turbo)
llm = OpenAI(model_name="gpt-3.5-turbo", temperature=0)
# 建立 RetrievalQA chain
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff", # 最簡單的「直接把檢索結果塞進 prompt」方式
retriever=retriever,
return_source_documents=True # 同時回傳來源文件,方便除錯
)
# 向系統提問
question = "什麼是向量搜尋?它跟關鍵字搜尋有什麼不同?"
answer = qa_chain(question)
print("回答:", answer["result"])
print("\n來源文件:")
for doc in answer["source_documents"]:
print("- ", doc.page_content[:60], "...")
說明:
RetrievalQA自動把檢索到的文件(最多k筆)拼接成 Prompt,送給 LLM。若需要更複雜的 Prompt 結構,可改用chain_type="map_reduce"或自行撰寫 Chain。
範例 5:Hybrid Search – 同時使用向量相似度與 Metadata 篩選
# 假設每筆文件都有分類 metadata
docs_with_meta = [
Document(page_content="LangChain 可以串接多種向量資料庫。", metadata={"category": "framework"}),
Document(page_content="向量搜尋的核心在於高維空間的相似度計算。", metadata={"category": "concept"}),
Document(page_content="FAISS 支援 GPU 加速檢索。", metadata={"category": "tool"}),
]
# 重新建立向量庫,並保留 metadata
vectorstore_meta = Chroma.from_documents(
documents=docs_with_meta,
embedding=embeddings,
collection_name="hybrid_demo"
)
# 建立 Retriever,加入 metadata filter
retriever_hybrid = vectorstore_meta.as_retriever(
search_kwargs={
"k": 5,
"filter": {"category": "concept"} # 只搜尋屬於 concept 類別的文件
}
)
# 測試檢索
results = retriever_hybrid.get_relevant_documents("向量搜尋的原理")
for r in results:
print("[", r.metadata["category"], "]", r.page_content)
關鍵:透過 metadata filter,可以在向量相似度基礎上加入結構化條件,實現 Hybrid Search,適合多層級知識庫或需要權限控制的情境。
常見陷阱與最佳實踐
| 陷阱 | 說明 | 最佳實踐 |
|---|---|---|
| 向量維度不一致 | 不同模型產生的向量維度不同,若混用會導致索引錯誤。 | 只使用同一套 Embedding 模型,或在切換模型時重新建索引。 |
| 未正規化向量 | 直接使用內積計算相似度,若向量未正規化會產生偏差。 | 在建立索引前呼叫 embeddings.embed_documents(..., normalize=True)(或自行除以 L2 norm)。 |
| Metadata 過大 | 把大量文字放在 metadata 會拖慢檢索速度。 | 只保留關鍵欄位(如 id、category、timestamp),大文本仍放在 page_content。 |
| 檢索結果過少 | k 設太小或向量庫過於稀疏,導致 LLM 無足夠上下文。 |
根據文件長度調整 k,必要時使用 retriever.combine_documents 合併多筆結果。 |
| 向量庫更新不即時 | 實務上常需要新增文件,但忘記重新 persist(持久化)或 refresh 索引。 | 使用 vectorstore.add_documents(new_docs) 後立即呼叫 vectorstore.persist()(對於 Chroma、Pinecone 等)。 |
| 成本控制不足 | 使用雲端向量服務(如 Pinecone)時,未設定 TTL 或容量上限,成本可能失控。 | 設定 metadata={"expires_at": ts} 或使用服務提供的 quota 功能。 |
實際應用場景
- 企業內部知識庫
- 把公司手冊、FAQ、會議紀要等文件向量化,讓員工透過聊天機器人即時取得相關資訊。
- 產品說明文件搜尋
- 結合 Metadata(產品類別、版本號)做 Hybrid Search,讓技術支援人員快速定位特定版本的說明。
- 法律條文比對
- 法律文件往往長度龐大且條款相似,向量檢索能找出語意相近的條款,協助律師做前案比對。
- 多語言客服
- 透過多語言 Embedding(如
text-embedding-3-large支援 95 種語言),同一向量庫即可服務不同語言的使用者。
- 透過多語言 Embedding(如
- 推薦系統
- 把商品描述向量化,使用相似度搜尋即時產生「類似商品」或「相關文章」的推薦。
總結
向量查詢是 語意檢索 的核心技術,而 LangChain 為開發者提供了 統一的抽象層,讓我們可以在不同的向量資料庫之間自由切換,同時結合 LLM 完成 RAG(Retrieval‑Augmented Generation) 流程。
本文從 Embedding、向量資料庫、相似度度量 三大概念出發,示範了從 產生向量 → 建立索引 → 檢索 → 與 LLM 整合 的完整程式碼流程,並列出常見的陷阱與最佳實踐,幫助讀者在實務上快速落地。
掌握向量查詢的基礎後,您就可以開始構建 問答系統、知識庫、推薦引擎 等多樣化應用,讓 LLM 的能力在真實業務中發揮最大效益。祝開發順利,期待看到您用 LangChain 打造的下一代智慧應用!