LangGraph 构建 AI 智能体的两种方式:手动搭建 vs 一行代码
在学习 LangGraph 多智能体系统时(项目来源:Multi-Agent-AI-System),遇到两种构建 ReAct Agent 的方式。一种是从零开始手动搭建图结构,另一种是用预构建函数一行搞定。
背景:什么是 ReAct Agent?
ReAct = Reasoning + Acting(推理 + 行动)。
一个 ReAct Agent 的工作循环很简单:
用户提问 → LLM 思考 → 决定调用工具 → 执行工具 → 拿到结果 → 继续思考 → ... → 给出最终回答
这个"思考-行动-观察"的循环会一直重复,直到 LLM 认为已经有足够信息来回答用户。
方式一:手动搭建(从零构建图结构)
这是项目中 音乐目录子智能体(music_catalog_subagent) 的构建方式。你需要自己定义图的每一个组成部分。
第一步:定义 State(状态)
State 是在图中流动的数据结构,相当于智能体的"工作记忆":
from typing_extensions import TypedDict
from typing import Annotated
from langgraph.graph.message import AnyMessage, add_messages
from langgraph.managed.is_last_step import RemainingSteps
class State(TypedDict):
customer_id: str # 客户 ID
messages: Annotated[list[AnyMessage], add_messages] # 对话历史(自动追加)
loaded_memory: str # 从长期记忆加载的用户偏好
remaining_steps: RemainingSteps # 防止无限循环的计数器
第二步:定义 Tools(工具)
工具是 LLM 可以调用的外部函数。这里定义了 4 个查询音乐数据库的工具:
from langchain_core.tools import tool
@tool
def get_albums_by_artist(artist: str):
"""根据艺术家名字查找专辑"""
return db.run(f"""
SELECT Album.Title, Artist.Name
FROM Album
JOIN Artist ON Album.ArtistId = Artist.ArtistId
WHERE Artist.Name LIKE '%{artist}%';
""", include_columns=True)
@tool
def get_tracks_by_artist(artist: str):
"""根据艺术家名字查找曲目"""
return db.run(f"""
SELECT Track.Name as SongName, Artist.Name as ArtistName
FROM Album
LEFT JOIN Artist ON Album.ArtistId = Artist.ArtistId
LEFT JOIN Track ON Track.AlbumId = Album.AlbumId
WHERE Artist.Name LIKE '%{artist}%';
""", include_columns=True)
@tool
def get_songs_by_genre(genre: str):
"""根据流派查找歌曲"""
# ... 省略具体实现
@tool
def check_for_songs(song_title):
"""检查某首歌是否存在"""
# ... 省略具体实现
# 把工具绑定到 LLM
music_tools = [get_albums_by_artist, get_tracks_by_artist, get_songs_by_genre, check_for_songs]
llm_with_music_tools = llm.bind_tools(music_tools)
第三步:定义 Nodes(节点)
节点是图中的处理单元。ReAct Agent 需要两个节点:
from langgraph.prebuilt import ToolNode
from langchain_core.messages import SystemMessage
# 节点 1:工具执行节点(自动处理工具调用)
music_tool_node = ToolNode(music_tools)
# 节点 2:LLM 推理节点(决定下一步做什么)
def music_assistant(state: State, config):
memory = state.get("loaded_memory", "None")
system_prompt = f"你是音乐目录助手...用户偏好:{memory}"
response = llm_with_music_tools.invoke(
[SystemMessage(system_prompt)] + state["messages"]
)
return {"messages": [response]}
第四步:定义 Edges(边)
边决定了节点之间的执行流向。关键是条件边——根据 LLM 的输出决定下一步:
def should_continue(state: State, config):
last_message = state["messages"][-1]
if not last_message.tool_calls:
return "end" # 没有工具调用 → 结束
else:
return "continue" # 有工具调用 → 继续执行工具
第五步:组装并编译
把所有部件拼在一起:
from langgraph.graph import StateGraph, START, END
# 创建图
music_workflow = StateGraph(State)
# 添加节点
music_workflow.add_node("music_assistant", music_assistant)
music_workflow.add_node("music_tool_node", music_tool_node)
# 添加边
music_workflow.add_edge(START, "music_assistant") # 入口 → LLM
music_workflow.add_conditional_edges( # LLM → 工具 or 结束
"music_assistant",
should_continue,
{"continue": "music_tool_node", "end": END}
)
music_workflow.add_edge("music_tool_node", "music_assistant") # 工具 → 回到 LLM
# 编译
music_catalog_subagent = music_workflow.compile(
name="music_catalog_subagent",
checkpointer=checkpointer,
store=in_memory_store
)
最终得到的图结构:
┌─────────────────┐
│ START │
└────────┬────────┘
▼
┌─────────────────┐
│ music_assistant │◄──────────┐
└────────┬────────┘ │
│ │
┌────┴────┐ │
│ 有工具 │无工具调用 │
│ 调用? │───────► END │
└────┬────┘ │
▼ │
┌─────────────────┐ │
│ music_tool_node │───────────┘
└─────────────────┘
方式二:预构建函数(一行代码)
这是项目中 发票信息子智能体(invoice_information_subagent) 的构建方式。
只需要准备工具和提示词
@tool
def get_invoices_by_customer_sorted_by_date(customer_id: str):
"""按日期排序获取客户的所有发票"""
return db.run(f"SELECT * FROM Invoice WHERE CustomerId = {customer_id} ORDER BY InvoiceDate DESC;")
@tool
def get_invoices_sorted_by_unit_price(customer_id: str):
"""按单价排序获取客户的发票明细"""
# ... 省略
@tool
def get_employee_by_invoice_and_customer(invoice_id: str, customer_id: str):
"""获取发票关联的客服员工信息"""
# ... 省略
invoice_tools = [
get_invoices_by_customer_sorted_by_date,
get_invoices_sorted_by_unit_price,
get_employee_by_invoice_and_customer
]
invoice_subagent_prompt = "你是发票信息助手,负责处理客户的账单查询..."
一行代码创建 Agent
from langgraph.prebuilt import create_react_agent
invoice_information_subagent = create_react_agent(
model=llm, # LLM 模型
tools=invoice_tools, # 工具列表
name="invoice_subagent", # 名称
prompt=invoice_subagent_prompt, # 系统提示词
state_schema=State, # 状态定义
checkpointer=checkpointer, # 短期记忆
store=in_memory_store # 长期记忆
)
就这样,完成了。create_react_agent 内部自动帮你做了方式一中的所有步骤:定义节点、添加条件边、编译图。
对比总结
| 维度 | 手动搭建 | create_react_agent |
|---|---|---|
| 代码量 | ~40 行(不含工具定义) | 1 行调用 |
| 灵活性 | 完全自定义,可以加任意节点和边 | 标准 ReAct 循环,定制空间有限 |
| 学习价值 | 深入理解 LangGraph 底层原理 | 快速上手,适合生产使用 |
| 适用场景 | 需要非标准流程(如多分支、自定义路由) | 标准的"思考-行动-观察"循环 |
| 最终效果 | 完全相同 | 完全相同 |
什么时候用哪种?
用手动搭建,当你需要:
- 在 ReAct 循环中插入额外步骤(如审核节点、日志节点)
- 自定义路由逻辑(不只是"有工具调用就执行")
- 多个 LLM 节点协作
- 学习和理解 LangGraph 的工作原理
用 create_react_agent,当你需要:
- 快速创建一个标准的 ReAct Agent
- 不需要自定义图结构
- 作为多智能体系统中的子智能体(像本项目中的 invoice 子智能体)
补充:LangChain v1 的 create_agent
LangChain v1 引入了更高层的 create_agent 函数,它在 create_react_agent 基础上增加了 Middleware(中间件) 系统:
from langchain.agents import create_agent
from langchain.agents.middleware import PIIMiddleware, SummarizationMiddleware
agent = create_agent(
model="claude-sonnet-4-6",
tools=[search_web, send_email],
middleware=[
PIIMiddleware("email", strategy="redact"), # 自动脱敏
SummarizationMiddleware(trigger={"tokens": 500}), # 对话过长自动摘要
]
)
三者的关系是:
手动搭建 StateGraph(最底层,最灵活)
↑ 封装
create_react_agent(标准 ReAct 循环)
↑ 封装
create_agent(加入中间件系统,LangChain v1 推荐)
层级越高越简洁,层级越低越灵活。根据你的需求选择合适的抽象层级即可。

LangGraph 构建 AI 智能体的两种方式
浙公网安备 33010602011771号