LangChain 设计原理分析¹⁰ | 向量数据库与 Retriever 机制

本文旨在理解向量检索的实现机制、VectorStore 与 Retriever 在 LangChain 中的职责划分,掌握 FAISS 与 Chroma 的实践用法,以及如何把向量库"作为 retriever" 接入检索增强生成(RAG)流水线。


一、先把概念理清楚(最重要的点)

  • 向量数据库 / VectorStore:把文本或片段映射为向量并存储、索引,用于快速近邻搜索(ANN)。在 LangChain 中常见实现包括 FAISS、Chroma、Milvus、Pinecone 等。
  • Embedding:把文本变为向量的模型(OpenAIEmbeddings / HuggingFaceEmbeddings / 本地 xinference 等)。Embedding 的语义质量直接决定检索效果。
  • Retriever(检索器) :一个抽象层(BaseRetriever),负责"给我最相关的 documents "。VectorStore 通常提供 .as_retriever() 把自己包装成 Retriever。LangChain 的 Retriever 遵循 Runnable 接口(支持 .invoke() / .ainvoke() 等)。

关键区别:VectorStore 负责存与查;Retriever 负责把"查询"映射成文档列表并做业务侧包装(例如加过滤、返回 score、metadata 过滤等)。


二、FAISS 与 Chroma 的快速比较(实践视角)

  • FAISS(Facebook AI Similarity Search)

    • 优点:极高的性能与灵活的索引(IVF、PQ、HNSW 等),适合大规模离线索引与高吞吐检索。实现上常用于内存/本地服务化场景。
    • 典型场景:批量构建索引后离线部署、需要自定义索引参数与性能调优时。
  • Chroma

    • 优点:开源、易用、开发者友好,提供嵌入 + 向量存储的一体化体验,适合中小规模快速迭代。LangChain 提供了 Chroma 的一键集成。
    • 典型场景:快速原型、POC、轻量级应用或与 metadata 结合做过滤。

选择建议:如果你要生产级、海量检索(亿级向量),优先 FAISS / Milvus / ANN 专有云;若只是开发迭代或中小数据,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 中被自动调用。

四、实战示例(两版:FAISS 与 Chroma,基于最新 create_retrieval_chain

说明:下面示例基于 LangChain 最新 API(create_retrieval_chain(retriever, combine_docs_chain)),并展示如何用 as_retriever()。你可以把 embedding 换成本地模型(如 Xinference / HuggingFace)。

依赖(示例环境)

bash 复制代码
pip install -U langchain langchain_community faiss-cpu chromadb sentence-transformers

示例 A:FAISS + Xinference Embeddings(小数据集演示)

python 复制代码
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 使用。

输出


示例 B:Chroma 快速原型

python 复制代码
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,使用体验更简单,适合迭代。

输出


五、实现细节与调优要点(工程级)

  1. 文本切分(chunking)

    • 切分策略影响召回 vs 上下文连贯性。常见做法:chunk_size=300~800overlap=50~200
    • 先把长文档分段并摘要,再存入向量库,这样能减少 Token 消耗。
  2. 向量归一化(normalize)

    • 当使用余弦相似度时,常将向量 L2 归一化,检索时只需计算 dot product。某些 VectorStore 提供 normalize_L2 参数(FAISS 支持)。
  3. 距离度量

    • FAISS 支持欧氏(L2)/内积(dot)等;Chroma 默认用余弦/内积。根据 embedding 决定(embedding 是否已归一化)。
  4. 索引类型与性能(FAISS)

    • 常见索引:IndexFlatL2(精确);IVF + PQ(大规模压缩);HNSW(快速近邻)。选择取决于吞吐、内存、精度需求。可在创建时通过 FAISS API 参数配置。
  5. Metadata 过滤

    • 如果需要基于 metadata 做筛选(例如按 sourcedate),vectorstores / retrievers 常支持 filter 参数;在调用 retriever.get_relevant_documents 时可传 filters。检查具体 VectorStore API(Chroma/FAISS wrapper 支持程度不同)。
  6. 持久化 / 重建索引

    • FAISS 索引与 docstore 需分开保存(FAISS.save_local / FAISS.load_local);Chroma 提供内置持久化方式。务必做好 embedding 版本与索引版本的同步策略。
  7. 语义质量监测

    • 使用小集的查询做 A/B(不同 embedding / chunking / index)评测。监控召回率、平均相似度分布、用户反馈。

六、如何自定义 Retriever(高级用法)

如果内置 retriever 不满足你的需求(例如要做多阶段检索、语境压缩、检索融合多个 index),可以继承 BaseRetriever

python 复制代码
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 是否过碎或过大。
  • 向量大小不匹配 / 报错:确认索引创建时的 embedding 维度与检索时一致(避免模型升级而没重建索引)。
  • metadata 过滤不起作用 :不同 VectorStore 对 filter 支持度不同(Chroma 支持较好),务必查看具体实现文档。

八、快速参考代码片段(保存/加载 FAISS)

python 复制代码
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 应基于规模、性能与开发效率权衡。
  • as_retriever() 是把 VectorStore 变为 Retriever 的快捷方式,便于在 LangChain Chain 中直接消费。
  • 生产系统需要关注切分、embedding 一致性、索引类型选择、metadata 过滤与索引持久化等要点。

接下来我们将把 Agent 工作流扩展为图式执行引擎,分析 LangGraph 的任务编排、异步执行与状态管理机制。

相关推荐
AI大模型9 小时前
提升LangChain开发效率:10个被忽视的高效组件,让AI应用性能翻倍
程序员·langchain·llm
大志说编程12 小时前
LangChain框架入门12:深入解析文本嵌入组件
人工智能·langchain
掘我的金14 小时前
11_LangGraph快速构建Agent工作流应用
langchain
掘我的金14 小时前
12_LangGraph基于RAG构建智能客服应用
langchain
都叫我大帅哥17 小时前
🔧 LangGraph的ToolNode:AI代理的“瑞士军刀”管家
python·langchain·ai编程
都叫我大帅哥1 天前
当RAG学会“思考”:Agentic RAG架构完全指南
langchain·ai编程
麦兜*2 天前
LangChain4j终极指南:Spring Boot构建企业级Agent框架
java·spring boot·spring·spring cloud·ai·langchain·ai编程
都叫我大帅哥2 天前
🧠 LangGraph状态管理:让AI工作流拥有“记忆”的超能力
python·langchain·ai编程
掘我的金3 天前
9_LangChain基于RAG实现文档问答
langchain