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实操心得

  1. 显式控制 :你能清晰地看到并控制流水线的每一个环节:加载器、分割器、嵌入模型、向量库、检索器、提示模板、LLM。这带来了极大的灵活性和调试便利。例如,你可以轻松地将文本分割器从 RecursiveCharacterTextSplitter 换成 TokenTextSplitter ,或者把向量数据库从Chroma换成Pinecone。
  2. 链的多样性 chain_type 参数支持 “stuff” “map_reduce” “refine” “map_rerank” 等多种模式,用于处理长上下文。 “stuff” 最简单,但如果检索到的文档总长度超过模型上下文窗口,就需要用 “map_reduce” 等更复杂的方法。 你需要根据文档长度和精度要求主动选择,这是LangChain灵活性的体现,也意味着更多的决策点。
  3. 调试友好 :通过 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实操心得

  1. 开箱即用与高抽象 :最直观的感受是代码量少。 VectorStoreIndex.from_documents() 这一行代码,在背后帮你完成了文档加载(根据文件类型自动选择)、文本分割、嵌入生成、向量索引构建等一系列操作。你通过 Settings 进行全局配置,框架负责协调。
  2. 内置的智能处理 :LlamaIndex的 Node 概念比LangChain的 Document 更丰富。一个 Document 可以被解析成多个 Node ,并且节点之间可以保留层次关系(父节点、子节点)。这对于处理结构复杂的文档(如带有标题、章节的论文)非常有利,其内置的检索器可以利用这种结构进行更精准的检索。
  3. 响应模式(response_mode) “compact” 模式对应LangChain的 “stuff” 。LlamaIndex还提供了 “refine” , “tree_summarize” 等高级模式。 关键在于,这些模式在LlamaIndex中通常是作为查询引擎的一个参数,而不是在创建链时就需要决定的“类型”,感觉上更贴近“查询时优化”。
  4. 存储上下文(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 常见问题与排查

  1. 错误:API密钥未设置或无效

    • 现象 google.api_core.exceptions.PermissionDenied: 403 ... API key not valid...
    • 解决 :确保 GOOGLE_API_KEY 环境变量已正确设置且未过期。在代码开头用 print(os.getenv('GOOGLE_API_KEY')) 检查。
  2. 错误:模型名称错误或区域限制

    • 现象 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” 。同时确认该模型在你的区域可用。
  3. 问题:响应速度慢

    • 排查 :首先确认是否是网络问题。其次,对于文本任务,可以尝试切换到 “gemini-1.5-flash-latest” 模型,它在保持较好能力的同时速度更快、成本更低。检查你的提示词是否过于冗长,或检索返回的上下文( k 值)是否过大。
  4. 问题:RAG答案质量不高,胡编乱造

    • 排查 :这是RAG系统的核心挑战。首先, 检查你的检索质量 。打印出 source_documents source_nodes ,看返回的文本片段是否真的与问题相关。如果不相关,需要调整:
      • 文本分割 chunk_size 可能不合适。太小会丢失上下文,太大会引入噪声。尝试500-1500之间的值。
      • 检索策略 :尝试不同的 search_type (如 “mmr” 最大边际相关性来兼顾相关性和多样性)或调整 similarity_top_k
      • 嵌入模型 :确保使用了合适的嵌入模型。对于中文, text-embedding-004 或专门的多语言模型可能比默认的更好。
    • 提示词优化 :在提示词中明确指令“严格根据上下文回答,不知道就说不知道”。
  5. LangChain特定:系统指令不生效

    • 解决 :确保在初始化 ChatGoogleGenerativeAI 时设置了 convert_system_message_to_human=True 。或者,避免使用 SystemMessage ,直接将指令放在第一条人类消息中。

经过这几个项目的实战,我个人最大的体会是: 没有绝对的“更好”,只有“更合适” 。LangChain和LlamaIndex都是极其优秀的框架,它们代表了构建LLM应用的两种不同但互补的范式。我的建议是,不妨两个都上手试一试,用同一个简单的项目(比如给你自己的简历PDF做个问答机器人)分别实现一遍。这个过程本身,就能让你深刻理解它们各自的抽象层次和设计美学,从而为你未来更复杂的项目做出最明智的技术选型。

更多推荐