本文旨在理解向量檢索的實現(xiàn)機制、VectorStore 與 Retriever 在 LangChain 中的職責劃分,掌握 FAISS 與 Chroma 的實踐用法,以及如何把向量庫“作為 retriever” 接入檢索增強生成(RAG)流水線。
一、先把概念理清楚(最重要的點)
- 向量數據庫 / VectorStore:把文本或片段映射為向量并存儲、索引,用于快速近鄰搜索(ANN)。在 LangChain 中常見實現(xiàn)包括 FAISS、Chroma、Milvus、Pinecone 等。
- Embedding:把文本變?yōu)橄蛄康哪P停∣penAIEmbeddings / HuggingFaceEmbeddings / 本地 xinference 等)。Embedding 的語義質量直接決定檢索效果。
- Retriever(檢索器):一個抽象層(BaseRetriever),負責“給我最相關的 documents”。VectorStore 通常提供 .as_retriever() 把自己包裝成 Retriever。LangChain 的 Retriever 遵循 Runnable 接口(支持 .invoke() / .ainvoke() 等)。
關鍵區(qū)別:VectorStore 負責存與查;Retriever 負責把“查詢”映射成文檔列表并做業(yè)務側包裝(例如加過濾、返回 score、metadata 過濾等)。
二、FAISS 與 Chroma 的快速比較(實踐視角)
FAISS(Facebook AI Similarity Search)
優(yōu)點:極高的性能與靈活的索引(IVF、PQ、HNSW 等),適合大規(guī)模離線索引與高吞吐檢索。實現(xiàn)上常用于內存/本地服務化場景。
典型場景:批量構建索引后離線部署、需要自定義索引參數與性能調優(yōu)時。
Chroma
優(yōu)點:開源、易用、開發(fā)者友好,提供嵌入 + 向量存儲的一體化體驗,適合中小規(guī)模快速迭代。LangChain 提供了 Chroma 的一鍵集成。
典型場景:快速原型、POC、輕量級應用或與 metadata 結合做過濾。
選擇建議:如果你要生產級、海量檢索(億級向量),優(yōu)先 FAISS / Milvus / ANN 專有云;若只是開發(fā)迭代或中小數據,Chroma 更省心。
三、as_retriever() 的作用與行為
as_retriever() 是 VectorStore 提供的便捷方法,把 VectorStore 包裝成 BaseRetriever(或可直接當作 Runnable 使用)。調用后你得到的 retriever 常帶有 search_kwargs(比如 k),并可被 LangChain 的 create_retrieval_chain / RetrievalQA 直接消費。官方 How-to 有示例。
要點:
- vectorstore.as_retriever(search_kwargs={"k":4}) → 返回 top-k 文檔(含 metadata、score)。
- Retriever 遵循 Runnable 接口,可直接 .invoke({"input": "..."}) 或在 Chain 中被自動調用。
四、實戰(zhàn)示例(兩版:FAISS 與 Chroma,基于最新 create_retrieval_chain)
說明:下面示例基于 LangChain 最新 API(create_retrieval_chain(retriever, combine_docs_chain)),并展示如何用 as_retriever()。你可以把 embedding 換成本地模型(如 Xinference / HuggingFace)。
依賴(示例環(huán)境)
pip install -U langchain langchain_community faiss-cpu chromadb sentence-transformers
示例 A:FAISS + Xinference Embeddings(小數據集演示)
import os
from langchain_community.document_loaders import DirectoryLoader
from langchain_community.embeddings import XinferenceEmbeddings
from langchain_community.vectorstores.faiss import FAISS
from langchain.text_splitter import CharacterTextSplitter
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain.chains import create_retrieval_chain
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
# 1. 加載文本并切片
loader = DirectoryLoader("docs", glob="**/*.txt") # 你的文檔目錄
docs = loader.load()
splitter = CharacterTextSplitter(chunk_size=300, chunk_overlap=30)
chunks = splitter.split_documents(docs)
# 2. Embedding
embedding = XinferenceEmbeddings(
server_url="http://127.0.0.1:9997",
model_uid="bge-large-zh-v1.5" # 這里填你在 Xinference 加載的 embedding 模型的 uid
)
# 3. 建索引(FAISS)
vectorstore = FAISS.from_documents(chunks, embedding)
vectorstore.save_local('GPT5')
# 4. 構造 retriever(as_retriever)
retriever = vectorstore.as_retriever(search_kwargs={"k": 2})
# 5. LLM + combine chain(stuff)
llm = ChatOpenAI(
temperature=0,
model="glm-4.5",
openai_api_key=os.getenv("ZAI_API_KEY"),
openai_api_base="https://open.bigmodel.cn/api/paas/v4/"
) # 或本地 LLM 封裝
prompt_template = "根據下面文檔回答問題:\n\n{context}\n\n問題:{input}\n"
prompt = ChatPromptTemplate.from_template(prompt_template)
combine_chain = create_stuff_documents_chain(llm, prompt)
# 6. 組合成 retrieval chain
retrieval_chain = create_retrieval_chain(retriever=retriever,
combine_docs_chain=combine_chain)
# 7. 調用
resp = retrieval_chain.invoke({"input": "GPT-5有什么特點?"})
print(resp["answer"])
說明/要點:
- FAISS.from_documents 會調用 Embeddings,把 chunks 向量化并建立索引(可保存/load)。
-
retriever = vectorstore.as_retriever(...) 把向量存儲包裝成 Retriever,可直接給 create_retrieval_chain 使用。
image.png
示例 B:Chroma 快速原型
import os
from langchain_community.document_loaders import DirectoryLoader
from langchain_community.embeddings import XinferenceEmbeddings
from langchain_community.vectorstores import Chroma
from langchain.text_splitter import CharacterTextSplitter
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain.chains import create_retrieval_chain
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
loader = DirectoryLoader("docs", glob="**/*.txt")
docs = loader.load()
splitter = CharacterTextSplitter(chunk_size=400, chunk_overlap=40)
chunks = splitter.split_documents(docs)
emb = XinferenceEmbeddings(
server_url="http://127.0.0.1:9997",
model_uid="bge-large-zh-v1.5" # 這里填你在 Xinference 加載的 embedding 模型的 uid
)
db = Chroma.from_documents(chunks, embedding=emb)
retriever = db.as_retriever(search_kwargs={"k": 1})
llm = ChatOpenAI(
temperature=0,
model="glm-4.5",
openai_api_key=os.getenv("ZAI_API_KEY"),
openai_api_base="https://open.bigmodel.cn/api/paas/v4/"
)
prompt = ChatPromptTemplate.from_template(
"請基于下面上下文回答問題:\n\n{context}\n\n問題:{input}\n")
combine_chain = create_stuff_documents_chain(llm, prompt)
chain = create_retrieval_chain(retriever=retriever, combine_docs_chain=combine_chain)
print(chain.invoke({"input": "一句話總結GPT5的特點!"})["answer"])
說明:
-
Chroma 做為一體化 DB,使用體驗更簡單,適合迭代。
image.png
五、實現(xiàn)細節(jié)與調優(yōu)要點(工程級)
文本切分(chunking)
切分策略影響召回 vs 上下文連貫性。常見做法:chunk_size=300800,overlap=50200。
先把長文檔分段并摘要,再存入向量庫,這樣能減少 Token 消耗。向量歸一化(normalize)
當使用余弦相似度時,常將向量 L2 歸一化,檢索時只需計算 dot product。某些 VectorStore 提供 normalize_L2 參數(FAISS 支持)。距離度量
FAISS 支持歐氏(L2)/內積(dot)等;Chroma 默認用余弦/內積。根據 embedding 決定(embedding 是否已歸一化)。索引類型與性能(FAISS)
常見索引:IndexFlatL2(精確);IVF + PQ(大規(guī)模壓縮);HNSW(快速近鄰)。選擇取決于吞吐、內存、精度需求??稍趧?chuàng)建時通過 FAISS API 參數配置。Metadata 過濾
如果需要基于 metadata 做篩選(例如按 source、date),vectorstores / retrievers 常支持 filter 參數;在調用 retriever.get_relevant_documents 時可傳 filters。檢查具體 VectorStore API(Chroma/FAISS wrapper 支持程度不同)。持久化 / 重建索引
FAISS 索引與 docstore 需分開保存(FAISS.save_local / FAISS.load_local);Chroma 提供內置持久化方式。務必做好 embedding 版本與索引版本的同步策略。語義質量監(jiān)測
使用小集的查詢做 A/B(不同 embedding / chunking / index)評測。監(jiān)控召回率、平均相似度分布、用戶反饋。
六、如何自定義 Retriever(高級用法)
如果內置 retriever 不滿足你的需求(例如要做多階段檢索、語境壓縮、檢索融合多個 index),可以繼承 BaseRetriever:
from langchain_core.retrievers import BaseRetriever
from langchain_core.documents import Document
from langchain_core.callbacks import CallbackManagerForRetrieverRun
from langchain_core.vectorstores import VectorStore
class MyCustomRetriever(BaseRetriever):
vector_store: VectorStore
k: int
def _get_relevant_documents(
self, query: str, *, run_manager: CallbackManagerForRetrieverRun = None
) -> list[Document]:
# 可以先做 query expansion / rerank / metadata filter
docs = self.vector_store.similarity_search(query, k=self.k)
# 做二次排序或裁剪
docs = docs[:2]
return docs
retriever = MyCustomRetriever(vector_store=db, k=4)

自定義 retriever 的好處:可以把 檢索、排序和壓縮 一步完成,直接把處理干凈的文檔交給 Chain。官方有自定義檢索器的教程。
七、實操常見問題與排錯
- 檢索到的是無關片段:檢查 embedding model 是否適配你的語言域、chunking 是否過碎或過大。
- 向量大小不匹配 / 報錯:確認索引創(chuàng)建時的 embedding 維度與檢索時一致(避免模型升級而沒重建索引)。
- metadata 過濾不起作用:不同 VectorStore 對 filter 支持度不同(Chroma 支持較好),務必查看具體實現(xiàn)文檔。
八、快速參考代碼片段(保存/加載 FAISS)
from langchain_community.vectorstores.faiss import FAISS
# 3. 建索引(FAISS)
if os.path.exists("GPT5"):
vectorstore = FAISS.load_local("GPT5", embeddings=embedding,
allow_dangerous_deserialization=True)
else:
vectorstore = FAISS.from_documents(chunks, embedding)
vectorstore.save_local('GPT5')
九、小結
- 向量數據庫是 RAG 的底座,選擇 FAISS / Chroma 應基于規(guī)模、性能與開發(fā)效率權衡。
- as_retriever() 是把 VectorStore 變?yōu)?Retriever 的快捷方式,便于在 LangChain Chain 中直接消費。
- 生產系統(tǒng)需要關注切分、embedding 一致性、索引類型選擇、metadata 過濾與索引持久化等要點。

