Skip to content

LangChain 与 LlamaIndex 实战教程

LangChain 和 LlamaIndex 是构建 LLM 应用最流行的两个框架。本文详细介绍它们的使用场景、核心概念和实战代码。

LangChain vs LlamaIndex:定位差异

特性LangChainLlamaIndex
核心理念通用 LLM 应用开发框架专门针对 RAG(检索增强生成)
主要用途Agents、链式调用、多模态、工具调用数据索引、检索、RAG 流水线
学习曲线陡峭(组件多,概念复杂)平缓(专注于 RAG)
灵活度极高(可任意组合)中等(RAG 最佳实践预设)
社区生态庞大(2000+ 集成)快速增长(专注数据连接器)

选择建议

  • Agent / Tool Use:选 LangChain
  • RAG / 知识库问答:LlamaIndex(或 LangChain 的 RAG 模块)
  • 混合:可以同时使用(LlamaIndex 做检索,LangChain 做下游处理)

LangChain 核心概念

LCEL(LangChain Expression Language)

LCEL 是 LangChain 0.1+ 推荐的编排方式,使用管道操作符 | 连接组件。

示例:简单问答链

python
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough

# 1. 定义 Prompt 模板
prompt = ChatPromptTemplate.from_messages([
    ("system", "你是一个有帮助的助手。基于以下上下文回答问题:\n\n{context}"),
    ("human", "{question}")
])

# 2. 定义 LLM
llm = ChatOpenAI(model="gpt-4-turbo", temperature=0)

# 3. 构建 RAG 链(检索 + 生成)
def format_docs(docs):
    return "\n\n".join(doc.page_content for doc in docs)

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

# 4. 调用
response = rag_chain.invoke("什么是 RAG?")
print(response)

为什么用 LCEL?

  • 流式支持:天然支持流式输出
  • 自动批处理:自动优化批量请求
  • 异步支持ainvoke, astream
  • 可视化rag_chain.get_graph().draw_mermaid_png()

Agents(智能体)

Agent 让 LLM 能调用外部工具(搜索、计算、API 等)。

定义工具

python
from langchain.tools import tool
from langchain.agents import AgentExecutor, create_tool_calling_agent

@tool
def search_web(query: str) -> str:
    """搜索网络获取最新信息"""
    # 使用 Tavily, SerpAPI, 或自定义搜索
    return search_tavily(query)

@tool
def calculate(expression: str) -> float:
    """计算数学表达式,如 '2+2*3'"""
    return eval(expression)

tools = [search_web, calculate]

创建 Agent

python
from langchain_openai import ChatOpenAI
from langchain.agents import create_tool_calling_agent

llm = ChatOpenAI(model="gpt-4-turbo")
prompt = ChatPromptTemplate.from_messages([
    ("system", "你是有帮助的助手。使用工具回答问题。"),
    ("placeholder", "{chat_history}"),
    ("human", "{input}"),
    ("placeholder", "{agent_scratchpad}"),
])

agent = create_tool_calling_agent(llm, tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)

result = agent_executor.invoke({"input": "今天北京天气如何?"})
print(result["output"])

输出示例(Verbose=True):

> 进入 AgentExecutor 链...
思考:用户问天气,我需要使用搜索工具
行动:搜索 "北京天气"
观察:今天北京晴,15°C到25°C
回答:今天北京天气晴朗,温度在15°C到25°C之间。

Memory(记忆)

LangChain 支持多种记忆类型:

python
from langchain.memory import ConversationBufferMemory, ConversationSummaryMemory

# 1. 缓冲记忆(记录所有对话)
memory = ConversationBufferMemory(
    memory_key="chat_history",
    return_messages=True
)

# 2. 总结记忆(定期总结对话,节省 token)
memory = ConversationSummaryMemory(
    llm=ChatOpenAI(),
    memory_key="chat_history"
)

使用示例

python
from langchain.chains import ConversationChain

conversation = ConversationChain(
    llm=llm,
    memory=memory,
    verbose=True
)

conversation.predict(input="我叫小明")
# 输出:你好小明!

conversation.predict(input="我叫什么名字?")
# 输出:你叫小明。 (记忆生效)

LlamaIndex 核心概念

LlamaIndex 专注于 RAG:从数据源 → 索引 → 检索 → 生成。

数据加载

python
from llama_index.core import SimpleDirectoryReader

# 加载本地文件(PDF, Markdown, TXT 等)
documents = SimpleDirectoryReader("data/").load_data()

# 或者使用特定加载器
from llama_index.readers.file import PDFReader
reader = PDFReader()
documents = reader.load_data(file="my_doc.pdf")

索引构建

LlamaIndex 支持多种索引策略:

1. VectorStoreIndex(默认)

python
from llama_index.core import VectorStoreIndex

index = VectorStoreIndex.from_documents(documents)

背后流程

  • 文档 → 文本分块(默认 256 tokens,重叠 10%)
  • 每个块 → Embedding 模型(默认 text-embedding-ada-002)
  • 向量存入向量数据库(默认内存,也可配置 Chroma/Milvus)

2. 树索引(Tree Index)

适用于文档内部有层次结构的情况:

python
from llama_index.core import TreeIndex
index = TreeIndex.from_documents(documents)

3. 关键词索引(Keyword Index)

基于关键词快速召回,适合关键词搜索场景:

python
from llama_index.core import KeywordTableIndex
index = KeywordTableIndex.from_documents(documents)

查询引擎

python
# 创建查询引擎(默认使用 VectorStoreIndex)
query_engine = index.as_query_engine(
    similarity_top_k=3,  # 检索前 3 个最相关 chunk
    response_mode="tree_summarize"  # 生成总结式回答
)

response = query_engine.query("什么是 RAG?")
print(response.response)
# 输出:RAG (Retrieval-Augmented Generation) 是一种结合检索和生成的技术...

# 查看来源
for node in response.source_nodes:
    print(f"来源: {node.node.metadata['file_name']}, 相关性: {node.score:.2f}")

实战:构建完整 RAG 应用

使用 LangChain 实现

python
from langchain_community.document_loaders import DirectoryLoader
from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain.text_splitter import RecursiveCharacterTextSplitter

# 1. 加载文档
loader = DirectoryLoader("./data/", glob="**/*.pdf")
documents = loader.load()

# 2. 文本分块
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=1000,
    chunk_overlap=200
)
texts = text_splitter.split_documents(documents)

# 3. 向量化并存储
embeddings = OpenAIEmbeddings()
vectorstore = Chroma.from_documents(texts, embeddings, persist_directory="./chroma_db")

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

# 5. 构建问答链(LCEL)
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from operator import itemgetter

prompt = ChatPromptTemplate.from_messages([
    ("system", "你是一个有帮助的助手。基于以下上下文回答问题:\n\n{context}"),
    ("human", "{question}")
])

chain = (
    {"context": retriever | (lambda docs: "\n\n".join(doc.page_content for doc in docs)),
     "question": itemgetter("question")}
    | prompt
    | ChatOpenAI(model="gpt-4-turbo")
    | StrOutputParser()
)

result = chain.invoke({"question": "什么是 LangChain?"})

使用 LlamaIndex 实现

python
from llama_index.core import SimpleDirectoryReader, VectorStoreIndex
from llama_index.core.node_parser import SentenceSplitter

# 1. 加载
documents = SimpleDirectoryReader("./data/").load_data()

# 2. 分块(更细粒度控制)
parser = SentenceSplitter(chunk_size=256, chunk_overlap=32)
nodes = parser.get_nodes_from_documents(documents)

# 3. 索引
index = VectorStoreIndex(nodes)

# 4. 查询
query_engine = index.as_query_engine(
    similarity_top_k=5,
    response_mode="compact"  # 或 "tree_summarize", "refine"
)
response = query_engine.query("什么是 LlamaIndex?")
print(response.response)

对比

  • LangChain 代码略多,但高度可定制,可以灵活组合各种工具
  • LlamaIndex 更简洁,RAG 专用,内置很多 RAG 优化策略(如自动选择最佳检索模式)

高级功能

LangChain:Streaming(流式输出)

python
chain = ...  # 前面的链

for chunk in chain.stream({"question": "给我写一首诗"}):
    print(chunk, end="", flush=True)

LlamaIndex:Hybrid Search(混合检索)

结合向量检索和关键词检索,提升召回率:

python
from llama_index.core.retrievers import HybridRetriever
from llama_index.core.query_engine import RetrieverQueryEngine

# 两个检索器
vector_retriever = index.as_retriever(similarity_top_k=5)
keyword_retriever = index.as_retriever(similarity_top_k=5, vector_store=None)

hybrid_retriever = HybridRetriever(
    vector_retriever=vector_retriever,
    keyword_retriever=keyword_retriever,
    weights=[0.6, 0.4]  # 向量60%,关键词40%
)

query_engine = RetrieverQueryEngine.from_args(hybrid_retriever)
response = query_engine.query("你的问题")

LlamaIndex:Citation 标注

让回答标注引用来源:

python
from llama_index.core.response.notebook_utils import display_response

query_engine = index.as_query_engine(
    similarity_top_k=5,
    response_mode="tree_summarize"
)
response = query_engine.query("解释一下注意力机制")
print(response.response)
print("\n--- 引用来源 ---")
for source in response.source_nodes:
    print(f"文件: {source.node.metadata.get('file_name')}")
    print(f"内容: {source.node.text[:100]}...")

最佳实践建议

  1. 文档分块策略

    • 按语义分割(Sentence/Tokens),不要跨段落
    • 重叠 10-20% 避免信息断裂
    • 特殊文档(法律、医学)考虑更大 chunk
  2. Embedding 模型选择

    • 通用场景:text-embedding-ada-002(OpenAI)
    • 中文:text2vec (BGE), m3e
    • 开源可自托管:all-MiniLM-L6-v2, bge-large-zh
  3. 检索数量

    • 通常 3-5 个 chunk 足够
    • 如果信息分散,可增至 10-15
    • 太多会引入噪音,降低质量
  4. Rerank(重排序) 先用宽松策略召回 20-50 个,再用 Cross-Encoder 重排序取前 3-5:

    python
    from llama_index.core.postprocessor import CohereRerank
    query_engine = index.as_query_engine(
        similarity_top_k=20,
        node_postprocessors=[CohereRerank(top_n=3)]
    )
  5. Prompt Engineering

    • 系统提示明确指令: "你是一个有帮助的助手,基于上下文回答"
    • 指示如答案不在上下文中,就说"我不知道"
    • 限制幻觉: "只使用提供的上下文"

性能对比

框架文档索引速度(100页 PDF)查询延迟(不含 LLM)易用性
LangChain (Chroma)~5s~50ms⭐⭐⭐
LlamaIndex~4s~40ms⭐⭐⭐⭐
纯手动 (Sentence Transformers)~3s~30ms⭐⭐

结论:性能差异不大,LlamaIndex 略快,LangChain 更灵活但稍复杂。


快速开始模板

LangChain RAG 模板

python
# requirements.txt
# langchain
# langchain-openai
# langchain-community
# chromadb
# openai

from langchain_community.document_loaders import DirectoryLoader
from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.chains import RetrievalQA
from langchain.prompts import PromptTemplate

# 加载 → 分块 → 向量化
loader = DirectoryLoader("./docs/", glob="**/*.md")
docs = loader.load()
splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
texts = splitter.split_documents(docs)

vectorstore = Chroma.from_documents(
    texts, 
    OpenAIEmbeddings(),
    persist_directory="./chroma_db"
)

# 构建 QA 链
template = """你是一个有帮助的助手。基于以下上下文回答问题。如果不知道,就说不知道。

上下文:
{context}

问题:{question}
回答:"""
prompt = PromptTemplate.from_template(template)

qa = RetrievalQA.from_chain_type(
    llm=ChatOpenAI(model="gpt-4-turbo"),
    chain_type="stuff",
    retriever=vectorstore.as_retriever(search_kwargs={"k": 3}),
    chain_type_kwargs={"prompt": prompt}
)

# 使用
result = qa.invoke({"query": "你的问题"})
print(result["result"])

LlamaIndex 模板

python
# requirements.txt
# llama-index
# llama-index-vector-stores-chroma
# openai

from llama_index.core import SimpleDirectoryReader, VectorStoreIndex
from llama_index.core.node_parser import SentenceSplitter
from llama_index.llms.openai import OpenAI

# 1. 加载与索引
documents = SimpleDirectoryReader("./docs/").load_data()
parser = SentenceSplitter(chunk_size=256, chunk_overlap=32)
nodes = parser.get_nodes_from_documents(documents)

index = VectorStoreIndex(nodes)

# 2. 创建查询引擎
query_engine = index.as_query_engine(
    similarity_top_k=3,
    response_mode="compact",
    llm=OpenAI(model="gpt-4-turbo")
)

# 3. 查询
response = query_engine.query("你的问题")
print(response.response)

推荐

  • 快速原型 → LlamaIndex
  • 复杂 Agent 应用 → LangChain
  • 生产环境 RAG → 两者皆可,按团队熟悉度选择

总结

  • LangChain 是"瑞士军刀",组件多,灵活但学习曲线陡
  • LlamaIndex 是"专业工具",RAG 场景开箱即用
  • 两者可以混合使用(如 LlamaIndex 检索 + LangChain 流式输出)

建议从一个简单 RAG 开始,逐步深入各个组件。