Skip to content

06.1 - LangChain 实践指南

定位: 最流行的 RAG 开发框架——LangChain 的核心概念、RAG 构建方式和最佳实践 面试高频度: ⭐⭐⭐⭐ 考查方式: LangChain 架构、LCEL、组件使用

一、这是什么?为什么需要它?

是什么

LangChain 是一个面向 LLM 应用的开发框架,提供了构建 RAG 系统所需的全套组件:文档加载、文本分块、向量存储、检索器、Prompt 模板、语言模型等。

没有 LangChain 的 RAG:从头写每个组件
  文档加载器 -> 自己写
  文本分块器 -> 自己写
  Embedding 封装 -> 自己写
  ... 

使用 LangChain 的 RAG:组装预制组件
  document_loaders.PyMuPDFLoader()
  text_splitter.RecursiveCharacterTextSplitter()
  embeddings.OpenAIEmbeddings()
  vectorstores.Chroma()
  chains.RetrievalQA()

为什么使用 LangChain?

1. 组件化:模块化设计,每个环节可替换
2. 生态丰富:支持几乎所有主流模型和数据库
3. LCEL:声明式 Chain 定义
4. 社区活跃:大量文档和示例
5. 工具链完善:LangSmith(调试) + LangServe(部署)

二、原理拆解

2.1 LangChain 核心组件

+------------------------------------------------------------------+
|                    LangChain RAG 核心组件                          |
|                                                                   |
|  Loaders:     | TextLoader, PyMuPDFLoader, WebBaseLoader          |
|  Splitters:   | RecursiveCharacterTextSplitter                    |
|  Embeddings:  | OpenAIEmbeddings, HuggingFaceEmbeddings           |
|  Vectorstores:| Chroma, FAISS, Pinecone, Milvus                  |
|  Retrievers:  | VectorStoreRetriever, EnsembleRetriever           |
|  Llms/Chat:   | ChatOpenAI, ChatAnthropic, Ollama                |
|  Chains:      | RetrievalQA, ConversationalRetrievalChain         |
|  LCEL:        | RunnableSequence, RunnableParallel               |
+------------------------------------------------------------------+

2.2 LCEL(LangChain Expression Language)

LCEL 是 LangChain 的声明式 Chain 定义语言,用 | 操作符连接组件:

python
# 传统方式 vs LCEL

# 传统方式(命令式)
qa_chain = RetrievalQA.from_chain_type(
    llm=llm,
    retriever=retriever,
)

# LCEL(声明式)——更灵活
from langchain_core.runnables import RunnablePassthrough

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

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

2.3 LangChain RAG 完整示例

python
from langchain_community.document_loaders import PyMuPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_community.vectorstores import Chroma
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
from langchain_core.prompts import ChatPromptTemplate

# 1. 加载文档
loader = PyMuPDFLoader("documents/manual.pdf")
docs = loader.load()

# 2. 分块
splitter = RecursiveCharacterTextSplitter(
    chunk_size=500, chunk_overlap=100
)
chunks = splitter.split_documents(docs)

# 3. 向量化 + 存储
vectorstore = Chroma.from_documents(
    documents=chunks,
    embedding=OpenAIEmbeddings()
)

# 4. 创建检索器
retriever = vectorstore.as_retriever(search_kwargs={"k": 5})

# 5. 定义 Prompt 模板
prompt = ChatPromptTemplate.from_messages([
    ("system", "基于以下文档回答问题。如果找不到答案,说不知道。\n{context}"),
    ("human", "{question}")
])

# 6. LCEL 构建链
def format_docs(docs):
    return "\n\n".join(doc.page_content for doc in docs)

rag_chain = (
    {"context": retriever | format_docs, "question": RunnablePassthrough()}
    | prompt
    | ChatOpenAI(model="gpt-4o-mini")
    | StrOutputParser()
)

# 7. 调用
result = rag_chain.invoke("年假政策是什么?")
print(result)

2.4 LangChain 高级 RAG 配置

python
# 高级配置:混合检索 + Reranker + 历史对话

from langchain.retrievers import EnsembleRetriever
from langchain_community.retrievers import BM25Retriever
from langchain.retrievers.document_compressors import CrossEncoderReranker
from langchain.retrievers import ContextualCompressionRetriever
from langchain.chains import create_history_aware_retriever

# 混合检索
ensemble_retriever = EnsembleRetriever(
    retrievers=[
        vectorstore.as_retriever(search_kwargs={"k": 30}),
        BM25Retriever.from_documents(chunks, k=30),
    ],
    weights=[0.5, 0.5]
)

# 加 Reranker
reranker = CrossEncoderReranker(
    model_name="BAAI/bge-reranker-v2-m3",
    top_n=5
)
retriever = ContextualCompressionRetriever(
    base_compressor=reranker,
    base_retriever=ensemble_retriever
)

# 历史对话感知
from langchain.chains import create_history_aware_retriever
from langchain_core.prompts import MessagesPlaceholder

qa_prompt = ChatPromptTemplate.from_messages([
    ("system", "基于文档回答问题..."),
    MessagesPlaceholder(variable_name="chat_history"),
    ("human", "{input}")
])

chain = create_history_aware_retriever(
    llm, retriever, qa_prompt
)

三、图解全景

LangChain RAG 组件关系

+------------------------------------------------------------------+
|                    LangChain RAG 组件关系图                        |
|                                                                   |
|  文档加载        分块          Embedding          向量存储         |
|  [Loader] --> [Splitter] --> [Embeddings] --> [VectorStore]      |
|                                                      |           |
|  查询           检索器         复用                  |           |
|  [Query] --> [Retriever] ---------------------------+           |
|                |                                                   |
|                v                                                   |
|           +----+----+                                              |
|           | Prompt  |                                              |
|           +----+----+                                              |
|                |                                                   |
|                v                                                   |
|           [LLM/Chat]                                               |
|                |                                                   |
|                v                                                   |
|           [Output Parser]                                          |
|                |                                                   |
|                v                                                   |
|           [结果输出]                                                |
+------------------------------------------------------------------+

四、面试视角

问题答案要点
LangChain 的 LCEL 有什么优势?声明式、可组合、可流式输出、异步支持、内置重试/回退、易于调试(LangSmith)。比传统 Chain API 更灵活
LangChain 和 LlamaIndex 的核心区别?LangChain 面向通用 LLM 应用(Chain/Agent/Tool),RAG 是其一。LlamaIndex 专注 RAG 数据层(索引类型多样、查询引擎强大)。两者可互补使用
LangChain RAG 的调试方式?1) LangSmith 链路追踪;2) verbose=True 查看中间步骤;3) RunnableLambda 注入调试输出;4) 单元测试各组件
LangChain 的局限?1) API 变动频繁(版本升级可能 break);2) 过度封装导致难以定位问题;3) 自定义组件需要理解内部机制;4) 大规模生产需配合其他工具

📚 相关链接

  • **LlamaIndex实践指南** — 另一主流框架
  • **RAG系统部署与监控** — RAG 系统的生产部署
  • **检索系统索引** — LangChain 检索组件详解
  • <- 返回 **工程化索引**

Knowledge4J — Java 知识库