Skip to content

AI Agent 工具链:LangChain / LlamaIndex / CrewAI 的架构设计,编排 vs 灵活性的平衡

为什么框架选型是个真问题?

2024-2025 年,AI Agent 框架像雨后春笋一样冒出来。LangChain、LlamaIndex、CrewAI、AutoGen、Semantic Kernel、Dify……每个都在说自己的框架最好用。但团队真正开始做项目时,面临的问题不是「哪个框架最火」,而是「选了之后换不掉了怎么办」。

AI 框架的锁定期比传统后端框架更致命:你的 Agent 逻辑、Tool 注册、Memory 管理、Callback 链全都绑在框架 API 上。框架一个 breaking change,整个项目重构。更糟的是,框架抽象层太厚,会掩盖你对核心逻辑的理解——出了问题都不知道是模型的问题还是框架的问题。

一个真实案例:某团队 2024 年用 LangChain 0.1 做了客服 Agent,生产环境跑了 3 个月。2025 年 LangChain 0.3 发布后想升级获取新功能,结果 LLMChain 被移除、AgentExecutor 的 API 签名叫变、Callback 接口改了——3 个人修了 2 周,期间还因为回调没正确迁移丢了一批日志,线上查问题全靠猜。

本文从三个主流框架的架构设计出发,分析它们的定位、优势和局限,最后给出一个务实的选型策略。

LangChain:最通用的 LLM 应用框架

核心抽象

LangChain 的架构围绕几个核心抽象层展开:

  • ChatModel:模型封装层,统一接口调用不同 LLM
  • PromptTemplate:Prompt 模板管理,支持变量注入和部分格式化
  • Chain:将多个 LLM 调用和工具调用串联成流水线
  • Agent:在 Chain 基础上实现 ReAct 循环——思考、行动、观察、再思考
  • Tool:外部工具的统一抽象,定义了输入输出 Schema
  • Memory:对话历史管理,支持多种存储方式
  • Callback:事件回调系统,用于日志、追踪、监控

执行时序图(文字描述)

用户请求 → AgentExecutor.run()
  → Agent.plan() 调用 LLM 生成下一步动作
    → 如果返回 Action(工具调用)
      → Tool.run() 执行具体工具
      → 结果回填到 Agent 的 scratchpad
      → 回到 Agent.plan() 继续循环
    → 如果返回 Final Answer
      → 返回给用户
  → Memory 自动追加本轮对话
python
from langchain_openai import ChatOpenAI
from langchain.agents import create_react_agent, AgentExecutor
from langchain.tools import tool
from langchain.prompts import PromptTemplate
from langchain.memory import ConversationBufferMemory

# 定义一个工具
@tool
def search_weather(city: str) -> str:
    """查询指定城市的天气。"""
    # 实际调用天气 API
    return f"{city} 今天气温 25°C,多云"

# 定义 Prompt
prompt = PromptTemplate.from_template(
    "你是一个天气助手。\n"
    "工具列表:{tools}\n"
    "工具名称:{tool_names}\n"
    "对话历史:{chat_history}\n"
    "用户输入:{input}\n"
    "思考过程:{agent_scratchpad}"
)

# 创建 Agent
llm = ChatOpenAI(model="gpt-4o", temperature=0)
agent = create_react_agent(llm, [search_weather], prompt)

# 带记忆的 Agent Executor
memory = ConversationBufferMemory(memory_key="chat_history")
agent_executor = AgentExecutor(
    agent=agent,
    tools=[search_weather],
    memory=memory,
    verbose=True,
    max_iterations=5,  # 防止无限循环
    handle_parsing_errors=True
)

# 执行
result = agent_executor.invoke({"input": "北京今天天气怎么样?"})
print(result["output"])

好的方面

LangChain 的生态在 2025 年仍然是最大的。集成模型超过 500 种,Tool 集成超过 200 个,社区问答和教程数量碾压其他框架。如果你需要快速连接各种模型和工具,LangChain 是第一选择。

问题出在哪

抽象层太多。 LangChain 有 5 层以上的抽象(BaseModel → Chain → Agent → Executor → Runnable),每层新增一个概念。调试时的栈堆叠几十层,出错信息看不懂。很多开发者最后放弃了框架内的 Agent 实现,直接用 Runnable 手写逻辑。

版本兼容性差。 从 0.1 到 0.3 做了两次大重构,API 不兼容。LLMChain 从 0.2 开始 deprecated,到 0.3 完全移除。社区里「做项目时版本锁死」是共识。

性能开销。 每个 Chain 调用都经过多次序列化/反序列化、Schema 校验、Callback 分发,简单调用层叠加 50-100ms 延迟。假设你的 Agent 在 5 步循环内每步调 2 次模型,框架层就吃掉 500-1000ms 的纯 overhead——这还不算模型推理时间。

开源 vs 收费的割裂。 2025 年 LangChain 推出了 LangSmith 收费平台,部分高级功能(如 LangGraph 的持久化状态管理)需要付费才能使用。开源版本和 SaaS 版本之间的功能差距在拉大,这对自建部署的团队不友好。

LlamaIndex:RAG 场景的深度优化者

核心抽象

LlamaIndex 的架构核心是围绕检索构建的:

  • Document / Node:数据文档和切分后的节点
  • Index:索引构建器,支持 10+ 种索引类型
  • Retriever:检索器,从索引中检索相关节点
  • Response Synthesizer:基于检索结果生成回答
  • Query Engine:统一查询接口,封装了检索 + 生成

RAG 查询流程时序

用户查询 → QueryEngine.query()
  → Retriever 从 Index 检索 Top-K 相关 Node
    → 向量检索(Embedding 相似度)
    → 可选:关键词检索(BM25)+ 混合融合
  → NodePostProcessor 过滤低分/去重
  → ResponseSynthesizer 将检索结果 + 查询拼成 Prompt
  → LLM 生成最终回答
  → 返回给用户
python
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
from llama_index.core.retrievers import VectorIndexRetriever
from llama_index.core.query_engine import RetrieverQueryEngine
from llama_index.core.postprocessor import SimilarityPostprocessor

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

# 2. 构建索引
index = VectorStoreIndex.from_documents(documents)

# 3. 配置检索器
retriever = VectorIndexRetriever(
    index=index,
    similarity_top_k=5,  # 检索 Top-5
)

# 4. 配置后处理器(过滤低分结果)
postprocessor = SimilarityPostprocessor(similarity_cutoff=0.7)

# 5. 构建查询引擎
query_engine = RetrieverQueryEngine(
    retriever=retriever,
    node_postprocessors=[postprocessor],
)

# 6. 查询
response = query_engine.query("什么是向量索引?")
print(response)

LlamaIndex 的独特优势

数据连接器生态。 LlamaIndex 的 Reader 层支持 100+ 数据源:PDF、Notion、数据库、Google Drive、Slack、GitHub……自动解析和索引构建,这不是 LangChain 的 DocumentLoader 能比的。

一个真实场景:某金融客户要做 2000 份 PDF 研报的智能问答,每份 PDF 100-300 页,格式各异(扫描件、表格、图表混排)。用 LlamaIndex 的 PDFReader + SimpleDirectoryReader,一条命令加载所有文件,自动解析文本和元数据。如果用 LangChain 的 PyPDFLoader,需要自己写 PDF 解析逻辑、分页处理、元数据提取——至少多花 3 天。

索引类型丰富。 不只是向量索引,还支持 Summary Index(对整个文档做摘要)、Tree Index(构建树形结构做分层检索)、Keyword Table Index(关键词映射表)、Knowledge Graph Index(知识图谱索引)。

索引类型对比表

索引类型适用场景检索精度构建成本内存占用
VectorStoreIndex语义相似度搜索中等(取决于 Embedding 质量)高(需 Embedding 模型)高(向量存储在内存)
SummaryIndex文档级摘要问答低(粗粒度)低(不需 Embedding)
TreeIndex长文档分层检索高(逐层缩小范围)中(需递归构建)
KeywordTableIndex精确关键词匹配高(精确匹配时)低(只需分词)
KnowledgeGraphIndex实体关系查询高(关系推理)高(需 NER + 关系抽取)

关键点:如果你的文档是结构化的(如 API 文档、产品手册),混合使用 Vector + Keyword 索引的效果比单用向量索引好 15-30%(基于公开基准测试,如 BEIR 数据集)。LlamaIndex 的 HybridRetriever 原生支持这种融合。

局限性

非 RAG 场景支持弱。 LlamaIndex 的 Agent 实现是后来加的,不如 LangChain 成熟。如果你需要做多步工具调用、Agent 协作、代码生成等场景,LlamaIndex 不是最佳选择。

索引构建的灵活性不足。 框架对索引构建流程做了很多假设(比如文档需要先切分成固定大小的 Node),如果你有自定义的切分策略或索引逻辑,需要绕开框架的默认行为,反而比直接用 Embedding API 更麻烦。

一个踩坑案例:某团队要索引代码仓库(.py 文件),每个文件按函数粒度切分,函数之间保留调用关系。LlamaIndex 默认的 SentenceSplitter 按字符数切分,完全打乱了函数边界。他们不得不自己实现一个 CodeAwareNodeParser,绕过了框架的 NodeParser 抽象——最终代码量比直接调 OpenAI Embedding API 还多 200 行。对于这种场景,LlamaIndex 的「帮你做太多」反而成了负担。

CrewAI:多 Agent 协作编排

核心抽象

CrewAI 的架构围绕团队协作展开:

  • Agent:角色定义,包含 role、goal、backstory、tools
  • Task:任务描述,包含 description、expected_output、agent(负责执行的 Agent)
  • Crew:团队编排,包含 agents、tasks、process(执行流程)
  • Process:执行流程,支持 sequential(顺序)、hierarchical(层级)、consensual(协商)

多 Agent 协作流程(sequential 模式):

Crew.kickoff()
  → Task 1(Researcher)
    → Agent 调用 search_tool → 收集信息
    → 输出研究报告
  → Task 2(Writer)
    → 接收 Task 1 的输出作为上下文
    → Agent 调用 LLM 撰写文章
    → 输出最终文章
  → 返回最终结果

hierarchical 模式多了一步 Manager Agent 的中间调度:

Crew.kickoff()
  → Manager Agent 拿到所有 Task 列表
    → 分配给 Researcher Agent:执行研究任务
    → 检查 Researcher 输出质量
      → 不达标 → 打回重做
    → 分配给 Writer Agent:撰写任务
    → 检查 Writer 输出质量
    → 汇总最终输出
python
from crewai import Agent, Task, Crew, Process

# 定义研究员 Agent
researcher = Agent(
    role="资深研究员",
    goal="从互联网上找到最准确、最新的信息",
    backstory="你是一个有 10 年经验的行业研究员,擅长从多源信息中提取核心观点。",
    tools=[search_tool, scrape_tool],
    verbose=True,
    allow_delegation=False
)

# 定义写手 Agent
writer = Agent(
    role="技术写手",
    goal="将研究结果写成清晰易懂的技术文章",
    backstory="你是一个技术博客写手,擅长将复杂技术概念用简单语言表达。",
    tools=[],  # 写手不需要外部工具
    verbose=True,
    allow_delegation=False
)

# 定义任务
research_task = Task(
    description="研究 2025 年 AI Agent 框架的最新发展趋势,"
                "重点关注 LangChain、LlamaIndex、CrewAI 的版本更新。",
    expected_output="一份包含关键发现和引用的研究报告",
    agent=researcher
)

write_task = Task(
    description="基于研究结果,写一篇 2000 字左右的技术博客",
    expected_output="一篇完整的 Markdown 格式技术文章",
    agent=writer
)

# 创建团队,顺序执行
crew = Crew(
    agents=[researcher, writer],
    tasks=[research_task, write_task],
    process=Process.sequential,
    verbose=True
)

# 执行
result = crew.kickoff()
print(result)

CrewAI 的优势

多 Agent 协作的 API 设计最优雅。 角色分工、任务分配、流程定义,三行代码就能搭一个多 Agent 流水线。Process.hierarchical 模式里,一个 Manager Agent 自动管理其他 Agent 的工作,不需要手动编排任务依赖。

可观测性内置。 每个 Agent 的决策过程、Tool 调用、输出结果都被记录,通过 Crewverbose=True 可以查看完整的执行轨迹。

问题

单 Agent 场景不如 LangChain 灵活。 CrewAI 为多 Agent 协作设计,单个 Agent 的能力(Tool 管理、Memory 系统)不如 LangChain 丰富。

复杂场景的编排能力有限。 如果任务需要动态分支(根据执行结果决定下一步做什么),CrewAI 的 sequential / hierarchical 模式不够灵活,需要自己实现条件判断。

Agent 间通信是隐式的。 前一个 Agent 的输出作为后一个 Agent 的输入,但中间没有格式校验和上下文管理,Agent 之间的信息传递容易丢失或变形。

一个真实踩坑:某团队用 CrewAI 做「研究报告自动生成」,流程是 Researcher → Analyst → Writer。Researcher 抓了 10 篇资料,输出 5000 字的原始摘要。Analyst 接收后,把 5000 字压缩成 500 字的分析要点——但压缩过程中丢失了关键数据引用。Writer 拿到 500 字后写出的文章「看着像模像样,但数据都是编的」。根因:CrewAI 的 Task 之间没有数据结构定义,全靠 Prompt 暗示「保留引用数据」,但 Prompt 约束力不够。解决方案:在 Task 之间定义一个 ResearchReport 数据结构(带 sourceskey_findingsraw_data 字段),让 Agent 的输出严格遵循 Schema。

选型决策:不是非黑即白

场景匹配的决策树

应用场景是什么?
├── RAG 系统(知识库问答、文档检索)
│   └── → LlamaIndex(数据连接器 + 索引类型丰富)
├── 多工具 Agent(工作流编排、自动化)
│   └── → LangChain(生态最广,工具集成最多)
├── 多 Agent 协作(研究报告生成、团队分工)
│   └── → CrewAI(API 最优雅,开箱即用)
└── 简单调用(单一模型、单一工具)
    └── → 直接写 Python(框架是过度设计)

三框架功能对比表

评估维度LangChainLlamaIndexCrewAI
模型集成数500+50+20+
工具集成数200+30+(RAG 相关)10+
RAG 能力中等(需额外配)强(原生场景)弱(无原生 RAG)
多 Agent 协作弱(需 LangGraph)强(核心场景)
学习曲线陡峭(5层抽象)中等(3层抽象)平缓(3个核心概念)
性能开销50-100ms/层30-50ms/层20-40ms/层
版本稳定性差(0.1→0.3 大重构)中(1.x 相对稳定)好(版本迭代克制)
社区活跃度
收费趋势部分功能需付费(LangSmith)开源为主开源为主

复杂度评估

不是所有场景都需要框架。如果你的 Agent 逻辑很简单——一个模型、一个工具、一次调用——直接用 Python 调用 OpenAI API 反而更简单:

python
import openai

def simple_agent(user_input: str):
    """一个简单的 ReAct Agent,不用框架。"""
    messages = [
        {"role": "system", "content": "你是一个助手,可以查询天气和计算器。"},
        {"role": "user", "content": user_input}
    ]

    # 你的工具列表
    tools = [
        {
            "type": "function",
            "function": {
                "name": "get_weather",
                "description": "查询天气",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "city": {"type": "string"}
                    },
                    "required": ["city"]
                }
            }
        }
    ]

    # 调用模型
    response = openai.chat.completions.create(
        model="gpt-4o",
        messages=messages,
        tools=tools,
        tool_choice="auto"
    )

    # 处理工具调用
    if response.choices[0].message.tool_calls:
        # 执行工具并返回结果
        ...

    return response.choices[0].message.content

没有框架锁风险,没有抽象层陷阱,每条代码都在你掌控之中。对于简单场景,这才是最优解。

实用策略:框架做壳,核心自己写

大厂落地的共识是:只把框架当工具用,核心逻辑不依赖框架。

python
# 只用框架的模型封装和 Tool 注册
from langchain_openai import ChatOpenAI
from langchain.tools import tool

# 但 Agent 循环自己写
class MyAgent:
    def __init__(self, llm, tools):
        self.llm = llm
        self.tools = {t.name: t for t in tools}
        self.memory = []

    def run(self, user_input: str) -> str:
        self.memory.append({"role": "user", "content": user_input})

        for _ in range(5):  # 最多 5 步
            response = self.llm.invoke(self.memory, tools=self.tools)
            if response.tool_calls:
                # 自己处理工具调用
                for tc in response.tool_calls:
                    result = self.execute_tool(tc)
                    self.memory.append({"role": "tool", "content": result})
            else:
                self.memory.append({"role": "assistant", "content": response.content})
                return response.content

        return "超出最大迭代次数"

这种做法的好处:框架的模型封装和 Tool 抽象帮你省了重复代码,但 Agent 循环、Memory 管理、错误处理都掌握在自己手里。框架升级?不影响核心逻辑。

总结

AI Agent 框架的选型不是技术问题,是工程管理问题。三个框架各有定位:

  • LangChain:生态最广,适合快速原型和工具集成密集的场景,但抽象层太厚,需要谨慎使用。版本锁定是生产环境第一原则。
  • LlamaIndex:RAG 场景的最优解,数据连接器生态无可替代,但非 RAG 场景不要硬用。如果文档格式统一,它的索引构建是加速器;如果格式各异,自己写解析可能更可控。
  • CrewAI:多 Agent 协作最优雅的开箱方案,但复杂场景的编排能力有限,Agent 间通信需要显式数据结构定义。

核心原则:不要被框架绑架。 框架只做基础搭建——模型封装、Tool 注册、数据连接——Agent 的核心逻辑、状态管理、错误处理自己实现。这样既利用了框架的生态优势,又保持了代码的可控性。

最后,如果真的拿不准选哪个,先写一个纯 Python 的原型。跑通流程后,再判断哪个框架能帮你省最多代码。框架是来帮你省时间的,不是来给你加时间债的。

手撕 → 框架 → 生产化,一步步把 AI Agent 工程化搞透。