LangChain 中如何将 VectorStoreRetriever 加入到链(Chain)中

在基于 LLM 的问答系统(RAG,Retrieval-Augmented Generation)中,检索器(Retriever) 是关键组件。本文将系统讲解:

  • 什么是 VectorStoreRetriever
  • 它在 LangChain 架构中的位置
  • 如何把 VectorStoreRetriever 接入到链(Chain)中
  • 常见的组合模式与最佳实践

本文基于 LangChain v0.1+ 以及 LCEL(LangChain Expression Language)范式。


一、VectorStoreRetriever 是什么?

在 LangChain 中,VectorStoreRetriever 是对向量数据库的抽象封装。

它的职责非常单一:

输入:查询字符串
输出:相关文档列表(List[Document])

其底层通常依赖向量数据库,例如:

  • FAISS
  • Chroma
  • Milvus
  • Weaviate
  • Pinecone

典型流程:

用户问题
   ↓
Embedding
   ↓
向量相似度搜索
   ↓
返回相关文档

二、创建一个 VectorStoreRetriever

假设我们使用 FAISS:

from langchain.vectorstores import FAISS
from langchain.embeddings import OpenAIEmbeddings

# 1. 初始化 embedding
embeddings = OpenAIEmbeddings()

# 2. 构建向量数据库
vectorstore = FAISS.from_texts(
    ["LangChain is a framework for LLM applications."],
    embedding=embeddings
)

# 3. 创建 retriever
retriever = vectorstore.as_retriever(
    search_type="similarity",
    search_kwargs={"k": 3}
)

此时,retriever 已经是一个 VectorStoreRetriever 实例。


三、将 VectorStoreRetriever 加入到链中(核心部分)

在 LangChain 中,推荐使用 LCEL(| 管道操作符)构建链。

目标结构:

用户问题
   ↓
Retriever
   ↓
拼接上下文
   ↓
PromptTemplate
   ↓
LLM
   ↓
输出

方法一:使用 Runnable 组合(推荐方式)

from langchain.chat_models import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
from langchain.schema.runnable import RunnablePassthrough
from langchain.schema.output_parser import StrOutputParser

llm = ChatOpenAI()

prompt = ChatPromptTemplate.from_template("""
请根据以下上下文回答问题:

{context}

问题:
{question}
""")

# 构建 RAG 链
rag_chain = (
    {
        "context": retriever,
        "question": RunnablePassthrough()
    }
    | prompt
    | llm
    | StrOutputParser()
)

调用:

rag_chain.invoke("什么是 LangChain?")

解释:

  • RunnablePassthrough() 保留原始问题
  • retriever 自动接收 question 作为输入
  • 返回的文档会被自动格式化为字符串拼入 prompt

这是最清晰、可扩展性最强的方式。


方法二:使用 create_retrieval_chain

LangChain 提供了高级封装:

from langchain.chains import create_retrieval_chain
from langchain.chains.combine_documents import create_stuff_documents_chain

llm = ChatOpenAI()

prompt = ChatPromptTemplate.from_template("""
根据上下文回答问题:

{context}

问题:
{input}
""")

combine_docs_chain = create_stuff_documents_chain(llm, prompt)

retrieval_chain = create_retrieval_chain(
    retriever,
    combine_docs_chain
)

retrieval_chain.invoke({"input": "什么是 LangChain?"})

优点:

  • 更模块化
  • 清晰分离“检索”和“生成”

四、VectorStoreRetriever 在链中的数据流解析

在 LCEL 里,retriever 本质是:

Callable[str] → List[Document]

当你写:

{
    "context": retriever,
    "question": RunnablePassthrough()
}

LangChain 会自动:

  1. 把输入字符串传给 retriever
  2. 把返回的 Document 列表转成字符串
  3. 填充到 prompt 中的 {context}

这就是 LangChain 的 Runnable 设计优势 —— 所有组件都是可组合的。


五、高级用法

1️⃣ 控制检索策略

retriever = vectorstore.as_retriever(
    search_type="mmr",
    search_kwargs={"k": 5, "lambda_mult": 0.7}
)

支持:

  • similarity
  • mmr
  • similarity_score_threshold

2️⃣ 自定义文档格式化

可以插入一个格式化步骤:

from langchain.schema.runnable import RunnableLambda

def format_docs(docs):
    return "\n\n".join(doc.page_content for doc in docs)

rag_chain = (
    {
        "context": retriever | RunnableLambda(format_docs),
        "question": RunnablePassthrough()
    }
    | prompt
    | llm
)

3️⃣ 多路检索合并

multi_retriever = retriever1 | retriever2

或者使用 EnsembleRetriever


六、常见问题

❓ Retriever 接收的输入是什么?

默认是:

str

如果你传入 dict,需要:

retriever = retriever.with_config(
    run_name="my_retriever"
)

或者手动提取字段。


❓ 为什么推荐 LCEL 而不是传统 Chain?

原因:

  • 更灵活
  • 可并行
  • 可追踪
  • 易于调试
  • 可组合

传统 RetrievalQA 已经逐步被 LCEL 方式替代。


七、完整示例(生产可用版本)

from langchain.chat_models import ChatOpenAI
from langchain.vectorstores import FAISS
from langchain.embeddings import OpenAIEmbeddings
from langchain.prompts import ChatPromptTemplate
from langchain.schema.runnable import RunnablePassthrough
from langchain.schema.output_parser import StrOutputParser

# embedding
embeddings = OpenAIEmbeddings()

# 向量数据库
vectorstore = FAISS.from_texts(
    ["LangChain 是一个构建 LLM 应用的框架。"],
    embedding=embeddings
)

retriever = vectorstore.as_retriever()

# LLM
llm = ChatOpenAI()

# Prompt
prompt = ChatPromptTemplate.from_template("""
根据上下文回答问题:

{context}

问题:
{question}
""")

# 构建 RAG
rag_chain = (
    {
        "context": retriever,
        "question": RunnablePassthrough()
    }
    | prompt
    | llm
    | StrOutputParser()
)

print(rag_chain.invoke("LangChain 是什么?"))

八、总结

VectorStoreRetriever 加入链的本质是:

把“检索”作为一个 Runnable 节点插入到数据流中。

在 LangChain 新架构中:

  • Retriever 是 Runnable
  • Prompt 是 Runnable
  • LLM 是 Runnable
  • Parser 也是 Runnable

因此所有组件都可以自由组合。

如果你正在构建 RAG 系统,建议:

  • 使用 LCEL
  • 明确分离 retriever 和 combine_docs
  • 控制检索策略
  • 做好文档格式化
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐