LangChain与LlamaIndex接入Gemini 3.5 Pro实战对比:RAG应用开发选型指南
1. 项目概述:当Gemini 3.5 Pro遇上两大编排框架
最近在折腾大模型应用开发的朋友,估计都绕不开两个名字:LangChain和LlamaIndex。它们就像是AI应用开发领域的“瑞士军刀”和“专业导航仪”,各有各的擅长领域。与此同时,Google的Gemini 3.5 Pro模型以其强大的多模态能力和在代码、推理任务上的出色表现,成为了许多开发者在构建复杂应用时的新宠。但问题来了,当你想把Gemini 3.5 Pro这颗强大的“大脑”接入到你的项目中时,是选择生态庞大、组件丰富的LangChain,还是选择专精于RAG(检索增强生成)、对数据索引有深度优化的LlamaIndex呢?这个选择直接关系到后续的开发效率、系统性能和维护成本。
我最近正好在两个不同的项目里,分别用LangChain和LlamaIndex接入了Gemini 3.5 Pro,完成了一些从简单对话到复杂文档问答的应用。整个过程下来,感触颇深。这不仅仅是调用一个API那么简单,它涉及到框架的设计哲学、对工作流的抽象方式、以及在你遇到坑时,哪个框架能更快地帮你填上。这篇文章,我就以一个一线开发者的视角,来详细拆解一下这两种接入方式的异同、各自的优劣,以及在不同场景下该如何选择。我会尽量抛开那些官方的、教科书式的对比,多聊聊实际编码、调试、部署时遇到的真实情况和我的选择逻辑。
2. 核心框架设计哲学与定位差异
在深入代码之前,我们必须先理解这两个框架的根本不同。这决定了它们处理问题的方式,也直接影响了我们接入Gemini时的体验。
2.1 LangChain:以“链”为核心的通用型编排器
你可以把LangChain想象成一个高度模块化的乐高工厂。它的核心设计思想是 “链”(Chain) 。任何复杂的大模型应用,都被拆解成一系列可组合的“环节”(Links),比如:调用模型、检索信息、处理输出、执行工具等。LangChain提供了海量的标准化“乐高积木”(组件),并定义了清晰的接口,让你可以通过“链”把这些积木以任意顺序和逻辑组装起来,构建出从简单到极其复杂的AI工作流。
它的优势在于 通用性和灵活性 。无论是构建一个简单的聊天机器人,还是一个需要调用数据库、搜索引擎、代码解释器、并具备记忆能力的多智能体(Agent)系统,LangChain都有相应的组件和设计模式来支持。它的生态极其繁荣,社区贡献了数以千计的集成工具、模板和第三方扩展。当你使用LangChain接入Gemini时,你不仅仅是在调用一个模型,你是在将一个强大的LLM嵌入到一个预设的、可无限扩展的自动化流水线中。
注意 :这种强大灵活性带来的一个副作用是“初期的认知负担”。新手面对LangChain众多的概念(Chain, Agent, Tool, Memory, Retrieval等)和更底层的API时,可能会感到无从下手。你需要花时间理解它的抽象层次。
2.2 LlamaIndex:以“数据”为中心的RAG专家
与LangChain的“通用编排”定位不同,LlamaIndex从诞生起就带着鲜明的使命: 成为连接私有数据与大模型的最佳桥梁 。它的核心不是“链”,而是“索引”(Index)。LlamaIndex将全部精力聚焦在RAG管道的“R”(检索)部分,致力于解决如何高效地加载、解析、索引你的私有数据(文档、数据库、API等),并在查询时,为LLM提供最相关、最精准的上下文。
它的设计哲学是 专精和开箱即用 。对于RAG应用,LlamaIndex提供了一套更高层、更声明式的API。你不需要像在LangChain里那样手动组装检索器、文本分割器、向量数据库连接器;在LlamaIndex中,你通常只需要定义数据源、选择索引类型(如向量索引、摘要索引、树状索引等),然后进行查询,框架内部帮你处理了大部分繁琐的细节。它对多种数据格式的支持非常友好,并且内置了复杂的检索策略,比如混合检索、重排序等。
因此,当你主要目标是构建一个高质量的文档问答、知识库聊天机器人时,LlamaIndex往往能让你用更少的代码,更快地达到一个效果不错的基线。它的学习曲线在RAG领域相对平缓。
2.3 哲学差异对接入的影响
这种根本性的差异,直接体现在我们接入Gemini 3.5 Pro的代码和思路上:
- 在LangChain中 ,
ChatGoogleGenerativeAI或GoogleGenerativeAI只是一个强大的“LLM组件”。你需要思考的是:如何将它与你链条中的其他组件(如提示模板、输出解析器、记忆模块、工具)连接起来。你的代码结构围绕“工作流的构建”展开。 - 在LlamaIndex中 ,Gemini 3.5 Pro被配置为一个“LLM后端”。你的核心工作是定义和优化你的数据索引。你的代码结构围绕“数据的加载与查询”展开。框架更关心如何用最好的方式,将你的数据“喂”给Gemini。
理解这一点,是做出正确技术选型的第一步。接下来,我们就进入实战环节,看看具体代码怎么写。
3. 环境准备与基础接入实战
无论选择哪个框架,第一步都是准备好环境和基础的模型调用。这里我会展示最精简的接入代码,并对比其中的异同。
3.1 通用前置步骤:安装与密钥配置
首先,你需要一个Gemini API密钥。前往Google AI Studio即可免费申请,目前有较为充裕的免费额度,非常适合开发和测试。
安装必要的Python包:
# 如果你打算两个框架都尝试,可以一次性安装
pip install langchain langchain-google-genai llama-index llama-index-llms-google
# 或者按需安装
# 仅LangChain: pip install langchain langchain-google-genai
# 仅LlamaIndex: pip install llama-index llama-index-llms-google
将你的API密钥设置为环境变量,这是安全且通用的做法:
# 在终端中设置(临时)
export GOOGLE_API_KEY="your_api_key_here"
# 或者在Python代码中设置(不推荐用于生产)
import os
os.environ["GOOGLE_API_KEY"] = "your_api_key_here"
3.2 LangChain 基础接入:将Gemini作为组件
在LangChain中,我们通常使用 langchain-google-genai 这个官方集成包。接入非常简单,核心是创建一个LLM实例。
from langchain_google_genai import ChatGoogleGenerativeAI
from langchain_core.messages import HumanMessage
# 1. 创建ChatGoogleGenerativeAI实例
# 这里使用的是Gemini 1.5 Pro,如需Gemini 2.0 Flash或其他模型,修改model参数即可
llm = ChatGoogleGenerativeAI(
model="gemini-1.5-pro-latest", # 或 "gemini-1.5-flash-latest" 获取更快响应
temperature=0.7, # 控制创造性,0-1,越高越随机
max_output_tokens=1024, # 限制最大输出长度
convert_system_message_to_human=True # 处理系统消息的兼容性选项
)
# 2. 直接调用(流式输出)
print("LangChain 直接调用(流式):")
for chunk in llm.stream("请用一句话介绍你自己。"):
print(chunk.content, end="", flush=True)
# 3. 使用LangChain的消息格式调用
messages = [
HumanMessage(content="谁是《三体》的作者?")
]
response = llm.invoke(messages)
print(f"\n\nLangChain 消息调用:\n{response.content}")
代码解读与注意事项 :
ChatGoogleGenerativeAI类是对应Gemini聊天模型的封装。如果你需要进行纯文本补全(虽然Gemini主打聊天),也有GoogleGenerativeAI类,但前者更通用。temperature和max_output_tokens是关键参数,需要根据你的应用场景调整。做创意写作可以调高temperature,做事实问答则调低。convert_system_message_to_human=True是一个重要的兼容性参数。因为Gemini的API原生对“系统消息”的支持方式与OpenAI不同,这个参数能确保LangChain格式的系统消息被正确转换。 这是初期容易踩的坑,如果发现系统指令不生效,首先检查这个参数。
3.3 LlamaIndex 基础接入:将Gemini配置为后端
在LlamaIndex中,模型的配置通常与索引的创建和查询绑定得更紧密。我们首先需要配置一个“LLM”对象。
from llama_index.llms.google import Gemini
from llama_index.core import Settings
# 1. 创建Gemini LLM实例
llm = Gemini(
model="models/gemini-1.5-pro-latest", # LlamaIndex中模型路径格式略有不同
temperature=0.7,
max_tokens=1024,
)
# 2. 将LLM设置为全局默认设置(推荐方式)
Settings.llm = llm
# 你也可以同时设置嵌入模型,例如使用Gemini的嵌入模型或OpenAI的
# Settings.embed_model = ...
# 3. 直接调用LLM(非流式)
print("LlamaIndex 直接调用:")
response = llm.complete("请用一句话介绍你自己。")
print(response.text)
# 4. 使用聊天接口
from llama_index.core.llms import ChatMessage
messages = [
ChatMessage(role="user", content="谁是《三体》的作者?")
]
chat_response = llm.chat(messages)
print(f"\nLlamaIndex 聊天调用:\n{chat_response.message.content}")
代码解读与注意事项 :
- LlamaIndex的
Gemini类来自llama_index.llms.google,其参数与LangChain版本大同小异。 - 一个关键区别是模型名称的格式 :LlamaIndex中通常使用
models/gemini-1.5-pro-latest这种完整路径,这与Google AI Studio的格式一致。而LangChain的langchain-google-genai做了一层简化。 Settings.llm = llm这行代码非常重要。它将该LLM实例设置为全局默认。这意味着之后你创建的任何索引或查询引擎,如果没有显式指定LLM,都会自动使用这个Gemini实例。 这简化了配置,但也需要注意避免全局状态的意外修改。- LlamaIndex同样支持流式响应,通过
stream_complete或stream_chat方法即可。
基础接入对比小结 : 在简单的模型调用层面,两者都非常直观,几乎不分伯仲。LangChain的接口更接近“标准”的聊天LLM抽象,而LlamaIndex通过 Settings 全局配置的方式,在与自身索引系统集成时显得更便捷。真正的分水岭,在我们开始构建实际应用时才会显现。
4. 进阶应用对比:构建一个文档问答系统
让我们用一个更实际的场景来对比:构建一个基于本地PDF文档的问答系统。这是RAG的经典用例。
4.1 使用LangChain构建RAG流水线
在LangChain中,你需要像组装管道一样,明确地定义每一个步骤。
from langchain_google_genai import ChatGoogleGenerativeAI, GoogleGenerativeAIEmbeddings
from langchain_community.document_loaders import PyPDFLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_chroma import Chroma
from langchain.chains import RetrievalQA
from langchain.prompts import PromptTemplate
# 1. 加载文档
loader = PyPDFLoader("./your_document.pdf")
documents = loader.load()
# 2. 分割文本
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000, # 每个块的大小
chunk_overlap=200 # 块之间的重叠,避免上下文断裂
)
texts = text_splitter.split_documents(documents)
# 3. 创建向量存储(使用Gemini的嵌入模型)
embeddings = GoogleGenerativeAIEmbeddings(model="models/embedding-001")
vectorstore = Chroma.from_documents(
documents=texts,
embedding=embeddings,
persist_directory="./chroma_db" # 可选:持久化存储
)
# 4. 创建检索器
retriever = vectorstore.as_retriever(
search_type="similarity", # 相似度搜索
search_kwargs={"k": 4} # 返回最相关的4个块
)
# 5. 定义自定义提示模板
prompt_template = """请根据以下上下文信息回答问题。如果你不知道答案,就说你不知道,不要编造答案。
上下文:
{context}
问题:{question}
请给出详细、准确的回答:"""
PROMPT = PromptTemplate(
template=prompt_template, input_variables=["context", "question"]
)
# 6. 创建LLM实例
llm = ChatGoogleGenerativeAI(model="gemini-1.5-pro-latest", temperature=0.1)
# 7. 组装成检索问答链
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff", # 最简单的方式:将所有检索到的上下文塞进提示词
retriever=retriever,
chain_type_kwargs={"prompt": PROMPT}, # 使用自定义提示
return_source_documents=True # 返回源文档,便于调试
)
# 8. 进行查询
query = "文档中提到的核心挑战是什么?"
result = qa_chain.invoke({"query": query})
print(f"答案:{result['result']}")
print(f"\n来源文档(前2个):")
for i, doc in enumerate(result['source_documents'][:2]):
print(f"[{i+1}] {doc.page_content[:200]}...")
LangChain RAG实操心得 :
- 显式控制 :你能清晰地看到并控制流水线的每一个环节:加载器、分割器、嵌入模型、向量库、检索器、提示模板、LLM。这带来了极大的灵活性和调试便利。例如,你可以轻松地将文本分割器从
RecursiveCharacterTextSplitter换成TokenTextSplitter,或者把向量数据库从Chroma换成Pinecone。 - 链的多样性 :
chain_type参数支持“stuff”、“map_reduce”、“refine”、“map_rerank”等多种模式,用于处理长上下文。“stuff”最简单,但如果检索到的文档总长度超过模型上下文窗口,就需要用“map_reduce”等更复杂的方法。 你需要根据文档长度和精度要求主动选择,这是LangChain灵活性的体现,也意味着更多的决策点。 - 调试友好 :通过
return_source_documents=True,你可以直接看到模型做出回答所依据的原文片段,这对于评估RAG效果、调整检索参数(如chunk_size,k值)至关重要。
4.2 使用LlamaIndex构建RAG流水线
在LlamaIndex中,同样的功能,代码会更加简洁和声明式。
from llama_index.llms.google import Gemini
from llama_index.embeddings.google import GoogleTextEmbedding
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader, Settings
from llama_index.core.node_parser import SentenceSplitter
# 1. 配置全局LLM和Embedding模型
Settings.llm = Gemini(model="models/gemini-1.5-pro-latest", temperature=0.1)
Settings.embed_model = GoogleTextEmbedding(model="models/embedding-001")
# 配置文本分割器
Settings.text_splitter = SentenceSplitter(chunk_size=1000, chunk_overlap=200)
# 2. 加载数据并创建索引(一步到位)
documents = SimpleDirectoryReader(input_dir="./data").load_data() # 假设PDF在./data目录下
index = VectorStoreIndex.from_documents(
documents,
show_progress=True # 显示创建进度
)
# 可选:持久化索引
index.storage_context.persist(persist_dir="./llama_index_storage")
# 3. 创建查询引擎
query_engine = index.as_query_engine(
similarity_top_k=4, # 检索前4个相似节点
response_mode="compact" # 响应模式,类似于“stuff”
)
# 4. 进行查询
query = "文档中提到的核心挑战是什么?"
response = query_engine.query(query)
print(f"答案:{response.response}")
# 5. 获取并查看来源(节点)
print(f"\n来源节点(前2个):")
for i, node in enumerate(response.source_nodes[:2]):
print(f"[{i+1}] {node.text[:200]}...")
LlamaIndex RAG实操心得 :
- 开箱即用与高抽象 :最直观的感受是代码量少。
VectorStoreIndex.from_documents()这一行代码,在背后帮你完成了文档加载(根据文件类型自动选择)、文本分割、嵌入生成、向量索引构建等一系列操作。你通过Settings进行全局配置,框架负责协调。 - 内置的智能处理 :LlamaIndex的
Node概念比LangChain的Document更丰富。一个Document可以被解析成多个Node,并且节点之间可以保留层次关系(父节点、子节点)。这对于处理结构复杂的文档(如带有标题、章节的论文)非常有利,其内置的检索器可以利用这种结构进行更精准的检索。 - 响应模式(response_mode) :
“compact”模式对应LangChain的“stuff”。LlamaIndex还提供了“refine”,“tree_summarize”等高级模式。 关键在于,这些模式在LlamaIndex中通常是作为查询引擎的一个参数,而不是在创建链时就需要决定的“类型”,感觉上更贴近“查询时优化”。 - 存储上下文(StorageContext) :LlamaIndex对索引的持久化和加载有更原生的支持,
storage_context.persist()和load_index_from_storage()用起来非常顺手。
4.3 深度对比与选型建议
通过上面的例子,我们可以总结出更细致的对比:
| 特性维度 | LangChain | LlamaIndex | 选型建议 |
|---|---|---|---|
| 设计理念 | 通用工作流编排框架 | 数据连接与RAG专家框架 | LangChain适合构建复杂、多步骤、涉及外部工具或自定义逻辑的AI应用(如智能体)。LlamaIndex适合以数据检索为核心、追求快速搭建和高质量检索效果的RAG应用。 |
| 学习曲线 | 较陡峭,概念多,需理解底层组件 | 在RAG领域相对平缓,API更高层 | 如果你是RAG新手,想快速看到一个可用的文档问答Demo,LlamaIndex更容易上手。如果你想深入理解RAG的每一个环节,或有非标准需求,LangChain更合适。 |
| 代码控制粒度 | 细粒度 。你可以替换、定制流水线中的任意一个组件。 | 粗粒度 。框架封装了“最佳实践”管道,定制需要通过覆盖组件或使用底层API。 | 需要极致优化或特殊处理流程时选LangChain;认可框架默认设计且想提升开发速度时选LlamaIndex。 |
| 生态系统 | 极其庞大 。集成无数工具、数据库、内存方案等,社区活跃。 | 聚焦而深入 。在数据连接器、索引结构、检索策略上非常专业。 | 项目需要集成大量外部工具(如Slack、GitHub、各种API)时,LangChain的生态是巨大优势。项目数据源复杂(Notion、Confluence、数据库)或需要高级检索时,LlamaIndex可能更专业。 |
| 调试与透明度 | 高。可以轻松插入回调、检查中间步骤结果。 | 中。高级抽象有时会隐藏细节,但通过 response.source_nodes 等仍可调试。 |
当应用出现问题时,LangChain的细粒度流水线让你能更快定位是加载、分割、检索还是生成环节出了问题。 |
我的个人经验 :
- 原型验证阶段 :我倾向于使用 LlamaIndex 。它能让我在几十分钟内,就把一堆杂乱的技术文档、会议纪要丢进去,做出一个能回答基本问题的聊天机器人,快速验证想法和数据的可行性。
- 生产系统构建阶段 :随着需求复杂化(比如需要对话历史、需要根据答案触发特定工具、需要复杂的后处理逻辑),我会转向 LangChain 。它的模块化设计让我能像搭积木一样,逐步构建起一个健壮、可维护的系统架构。例如,我可以很方便地加入
ConversationBufferMemory来让机器人拥有记忆,或者用AgentExecutor来让它学会使用计算器、搜索网络。 - 混合使用 :这完全可行!你可以用LlamaIndex来构建高效、专业的 数据索引和检索层 ,然后将检索到的结果节点,作为输入传递给一个由LangChain构建的、功能更复杂的 处理与生成链或智能体 。这结合了两者的优势。
5. 高级特性与避坑指南
在实际使用中,还有一些高级功能和常见“坑点”值得分享。
5.1 流式输出与异步支持
两者都对流式输出和异步操作有良好支持。
LangChain流式输出 :
llm = ChatGoogleGenerativeAI(model="gemini-1.5-pro-latest", streaming=True)
chain = prompt | llm # 使用LCEL语法创建链
for chunk in chain.stream({"question": "讲一个故事"}):
print(chunk.content, end="", flush=True)
LlamaIndex流式输出 :
query_engine = index.as_query_engine(streaming=True)
streaming_response = query_engine.query("讲一个故事")
streaming_response.print_response_stream()
异步调用 对于构建高并发API服务至关重要。两者都支持 ainvoke , astream , acomplete , achat 等异步方法。在FastAPI或Django异步视图中,使用异步接口可以显著提升吞吐量。
5.2 提示工程与系统消息
如何给Gemini设定角色和指令?两者处理方式不同。
-
在LangChain中 ,你可以使用
SystemMessage,但务必记得设置convert_system_message_to_human=True,或者更推荐地,使用HumanMessagePromptTemplate来模拟系统指令。from langchain_core.prompts import ChatPromptTemplate from langchain_core.messages import SystemMessage # 方式1:使用SystemMessage(需配合convert_system_message_to_human) prompt = ChatPromptTemplate.from_messages([ SystemMessage(content="你是一个专业的科技文章翻译助手。"), ("human", "请将以下英文翻译成中文:{text}") ]) # 方式2:更稳妥的方式,将系统指令放在第一条人类消息中 prompt = ChatPromptTemplate.from_messages([ ("human", "你是一个专业的科技文章翻译助手。请将以下英文翻译成中文:{text}") ]) -
在LlamaIndex中 ,你可以在创建查询引擎时,通过
text_qa_template或refine_template来注入系统指令,这些模板是PromptTemplate对象。from llama_index.core import PromptTemplate qa_template = PromptTemplate( """你是一位资深法律顾问,请根据以下法律条文上下文,用严谨的专业语言回答问题。 上下文: {context_str} 问题:{query_str} 答案:""" ) query_engine = index.as_query_engine(text_qa_template=qa_template)
5.3 常见问题与排查
-
错误:API密钥未设置或无效
- 现象 :
google.api_core.exceptions.PermissionDenied: 403 ... API key not valid... - 解决 :确保
GOOGLE_API_KEY环境变量已正确设置且未过期。在代码开头用print(os.getenv('GOOGLE_API_KEY'))检查。
- 现象 :
-
错误:模型名称错误或区域限制
- 现象 :
google.api_core.exceptions.InvalidArgument: 400 ... Model ‘gemini-pro’ is not supported... - 解决 :使用正确的模型名称。对于Gemini 1.5 Pro,LangChain用
“gemini-1.5-pro-latest”,LlamaIndex用“models/gemini-1.5-pro-latest”。同时确认该模型在你的区域可用。
- 现象 :
-
问题:响应速度慢
- 排查 :首先确认是否是网络问题。其次,对于文本任务,可以尝试切换到
“gemini-1.5-flash-latest”模型,它在保持较好能力的同时速度更快、成本更低。检查你的提示词是否过于冗长,或检索返回的上下文(k值)是否过大。
- 排查 :首先确认是否是网络问题。其次,对于文本任务,可以尝试切换到
-
问题:RAG答案质量不高,胡编乱造
- 排查 :这是RAG系统的核心挑战。首先, 检查你的检索质量 。打印出
source_documents或source_nodes,看返回的文本片段是否真的与问题相关。如果不相关,需要调整:- 文本分割 :
chunk_size可能不合适。太小会丢失上下文,太大会引入噪声。尝试500-1500之间的值。 - 检索策略 :尝试不同的
search_type(如“mmr”最大边际相关性来兼顾相关性和多样性)或调整similarity_top_k。 - 嵌入模型 :确保使用了合适的嵌入模型。对于中文,
text-embedding-004或专门的多语言模型可能比默认的更好。
- 文本分割 :
- 提示词优化 :在提示词中明确指令“严格根据上下文回答,不知道就说不知道”。
- 排查 :这是RAG系统的核心挑战。首先, 检查你的检索质量 。打印出
-
LangChain特定:系统指令不生效
- 解决 :确保在初始化
ChatGoogleGenerativeAI时设置了convert_system_message_to_human=True。或者,避免使用SystemMessage,直接将指令放在第一条人类消息中。
- 解决 :确保在初始化
经过这几个项目的实战,我个人最大的体会是: 没有绝对的“更好”,只有“更合适” 。LangChain和LlamaIndex都是极其优秀的框架,它们代表了构建LLM应用的两种不同但互补的范式。我的建议是,不妨两个都上手试一试,用同一个简单的项目(比如给你自己的简历PDF做个问答机器人)分别实现一遍。这个过程本身,就能让你深刻理解它们各自的抽象层次和设计美学,从而为你未来更复杂的项目做出最明智的技术选型。
更多推荐

所有评论(0)