第 2 篇:框架选型——LangChain、LangGraph、CrewAI

系列记录:《从零搭建企业级 LLM 应用》,这是第 2 篇
上一篇:项目起点——《从零搭建企业级 LLM 应用》
下一篇:Dify——低代码开发平台,快速搭建一个智能体


主流 Agent 框架

选择框架时,不看框架有什么功能,先想清楚我需要什么。

仔细想了一下,我的路由需求其实很简单:

  1. 用户问一个问题
  2. 判断用户意图
  3. 发给对应的 Agent 去执行
  4. Agent 执行的时候,可能需要调好几次工具
  5. Agent 返回结果,原样展示
用户提问 → [判断用户query的意图] → 发给对应 Agent → Agent 自己决定怎么执行 → 返回结果

两个关键要求:

  • 意图判别决定路由分发
  • Agent 要自主——Agent 拿到任务后,自己决定调什么工具、调几次,不要我写死流程

先试了最简单的方案:关键词判断

一开始想的是,自己写个 if-else 不就行了:

def route(user_input):
    if "统计" in user_input or "排名" in user_input:
        return agent_1
    elif "编写" in user_input:
        return agent_2
    else:
        return agent_3

实际一跑就发现问题。用户说"帮我分析一下这个数据",没有"统计"两个字,直接误判。说"看看最近的情况",更无从判断了。

关键词匹配做路由,覆盖面和鲁棒性都不够。


差点选了 CrewAI

CrewAI——给每个 Agent 设定角色、目标、背景故事,让它们像团队一样协作。

1. 我真的需要 Agent 之间"协商"吗?

不需要。我的场景是:一次只有一个 Agent 干活,不需要它们互相讨论,只需要给不同的业务场景分发不同的 Agent 去处理

CrewAI 的优势是"多 Agent 协作",但我的场景根本没有协作需求。用它的代价(调度黑盒、调试困难)就不值得了。

2. 出问题了我能定位吗?

CrewAI 的调度过程比较黑盒——Agent 之间的协商逻辑我不完全可控。如果路由错了,很难知道是在哪个环节、什么原因导致的。

最后放弃 CrewAI 的核心原因是:我需要的是一个听话的路由器,不是一个会自己开会的团队。


LangChain 和 LangGraph 的区别

很多人以为 LangChain 和 LangGraph 是二选一的关系,我理解实际上它们解决的是不同层面的问题。

我项目里的第一个 import 就是 LangChain:

from langchain_openai import ChatOpenAI           # LLM 抽象层
from langchain_core.tools import tool              # 工具装饰器
from langchain_core.prompts import ChatPromptTemplate  # Prompt 模板
from langchain_core.messages import HumanMessage, AIMessage  # 消息类型

① 模型抽象层,换模型不用改业务代码

# 用 Qwen
llm = ChatOpenAI(model="qwen-plus", base_url="...", api_key="...")

# 哪天想换 Ollama 本地模型,只改 base_url 和 model
# llm = ChatOpenAI(model="qwen3:35b", base_url="...")

@tool 装饰器,定义工具只要写个函数

@tool
def search_kb(query: str) -> str:
    """检索知识库"""
    ...

ChatPromptTemplate,结构化写 prompt

QUERY_REWRITE_PROMPT = ChatPromptTemplate.from_messages([
    ("system", "你是检索专家。把用户问题改写为更适合语义搜索的形式..."),
    ("human", "原始问题:{question}"),
])
chain = QUERY_REWRITE_PROMPT | llm
result = chain.invoke({"question": user_input})

ChatPromptTemplate 的结构化 prompt 比手动拼接字符串清晰很多,后续加变量、调整 system prompt 都很方便。

HumanMessage / AIMessage,统一的消息类型

在流式处理中,我需要判断每条消息是工具调用还是最终回答,靠的就是 isinstance(msg, AIMessage)


但 LangChain 解决不了"流程怎么串"

LangChain 帮你调模型、定义工具、写 prompt,但它不管"先干啥、后干啥"。

举个例子:Data Agent 需要"先列文件 → 再检查结构 → 最后执行代码"。用纯 LangChain 的话,我得自己写 if-else 控制这串流程,多 Agent 之间的路由更得手写。

这就是 LangGraph 补上的那块拼图——它在 LangChain 之上,管编排。

LangChain 做的事 LangGraph 做的事
作用 调 Qwen、定义 tool、写 prompt、处理消息 创建 ReAct Agent、Supervisor 路由、对话记忆
缺少 流程怎么串、多个 Agent 怎么协作 怎么调 LLM、怎么定义工具
缺点 我得手动拼接 API 请求、手写 JSON schema 我得手写状态机、手写 Agent 循环

一句话:LangChain 帮我"造零件",LangGraph 帮我"画施工图"。两个一起用。


最后选了 LangGraph 的 Supervisor 模式

LangGraph 的 create_supervisor 模式恰好就是我想要的:一个用 LLM 做智能判断的路由器 + 多个独立执行的 Sub-Agent。

核心思路很简单——三个 Sub-Agent 各自封装自己的工具和逻辑,一个 Supervisor 通过 prompt 规则判断意图并转发。框架只需要几行代码就能搭起来:

# 创建子 Agent
agent_1  = create_agent_1(llm)
agent_2 = create_agent_2(llm)
agent_3  = create_agent_3(llm)

# 创建 Supervisor:指定子 Agent + 路由规则 + 对话记忆
router = create_supervisor(
    agents=[agent_1, agent_2, agent_3],
    model=llm,
    prompt="你是任务路由器。判断问题类型,转发给对应 Agent…",
    checkpointer=MemorySaver(),
).compile()

Supervisor 的 prompt 是这套架构的关键配置,但设计原则就一句话:明确告诉它哪些问题交给谁,以及禁止它自己做任何事。


执行一次请求,底层走了什么

以"上个月迟到最多的 3 个人是谁?"为例:

用户发问
  ↓
Supervisor 判断:"这是数据分析" → 转发给 data_agent
  ↓
data_agent 思考:"我需要知道有哪些文件"
  → 自己调用 list_files(),拿到考勤表
  ↓
data_agent 思考:"上个月是 1 月,需要检查文件结构"
  → 自己调用 inspect_file("考勤_2026-01.xlsx")
  ↓
data_agent 思考:"我知道列名了,生成统计代码"
  → 自己调用 execute_data_query("迟到次数排序 TOP3")
  ↓
拿到结果 → 生成自然语言回答 → 经由 Supervisor 返回用户

用户发起的一次请求调用了什么Agent,Agent 调了几次工具、每次的输入输出是什么,全部可以在 LangSmith 里看到。这个可观测性对调试太重要了,Agent 不按预期走的时候,能一眼看到是哪步出了问题。


多做了一件事:直调模式

Supervisor 自动路由适合"用户自由提问"的场景。但如果用户在首页明确点击了"数据统计"按钮,再让 Supervisor 判断一次不仅多余,还可能误判。

所以我额外实现了一个直调入口——当用户意图已经明确时,直接调用对应的 Agent,跳过路由环节。

两种模式各有用处:

  • 自由提问 → Supervisor 自动判断
  • 明确选了模式 → 直调,跳过路由

这个设计不是一开始就想到的。是做首页 UI 的时候才意识到:既然用户已经明确选择了模式,就没必要再分发一次路由了


踩坑记录

坑 1:一开始 stream 和 invoke 跑了两次

最早的代码先 stream() 收集日志,再 invoke() 拿最终答案——结果所有 LLM 调用和工具执行都重复了,每次对话慢一倍。后来改成单次 stream() 同时收集日志和提取答案,响应时间直接砍半。


一些感受

LangGraph 有一个特点我很喜欢——它不强加任何概念。你想让它怎么流转,你就怎么定义。Router 只路由、Agent 只执行、工具只做一件事。每一层的职责都很清楚。


下一篇记录:Dify ——低代码开发平台,快速搭建一个智能体

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐