从原理→工具集成→从零实现 ReAct Agent,
完整讲清楚 LangGraph 工具调用,
并对比为什么它比 LangChain Agent 更灵活、更好定制。
一、LangGraph 工具调用集成:核心原理
1.1 为什么 LangGraph 更适合做 Agent / 工具调用
LangChain Agent(旧):
- 黑盒化:
initialize_agent把循环、状态、路由全封装了 - 流程固定:思考→工具→观察→思考,很难插自定义节点(校验、重试、分支)
- 状态弱:靠消息链传递,无法自由加字段、做分支判断
LangGraph(新范式):
- 白盒化:节点、边、状态全由你定义,每一步可控
- 原生循环:支持任意有环图(ReAct、反思、多轮检索都很自然)
- 强状态 :全局
State,可随意加字段(loop_count、tool_results、reflection) - 条件边 :灵活
if/else,想走哪条分支就走哪条
一句话:
LangChain Agent = 预制好的房子;LangGraph = 你自己砌墙、布线、设计格局
1.2 LangGraph 工具调用的核心组件
- State(状态):全局共享,存消息、工具结果、计数器等
- Tool(工具) :LangChain 标准工具(
@tool装饰器),可无缝复用 - Node(节点) :
llm_node:LLM 思考并决定是否调用工具tool_node:执行工具调用
- Edge / Conditional Edge :
- 线性边:
llm → tool - 条件边:
tool → llm(循环)或llm → end(结束)
- 线性边:
1.3 ReAct 标准流程(LangGraph 原生支持)
用户问题 → LLM 思考 → 是否调用工具?
├─ 否 → 输出答案(结束)
└─ 是 → 执行工具 → 拿到结果 → 回到 LLM 再思考(循环)
二、从零实现:LangGraph ReAct Agent(可直接运行)
2.1 安装依赖
pip install -U langgraph langchain-openai python-dotenv
2.2 完整代码(含工具、状态、节点、条件边)
import os
from typing import TypedDict, Literal, Sequence, Annotated
from dotenv import load_dotenv
from langchain_core.messages import BaseMessage, HumanMessage
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI
from langgraph.constants import START, END
from langgraph.graph import StateGraph, add_messages
from langgraph.prebuilt import ToolNode
# 加载环境
load_dotenv()
# 定义工具 可任意扩展
@tool
def get_weather(city: str) -> str:
"""获取指定城市的天气"""
if city == "北京":
return f"北京:晴,25℃,微风"
elif city == "上海":
return f"上海:多云,28℃,东南风"
else:
return f"{city}:未知(仅支持北京/上海)"
@tool
def calculator(a: float, b: float, op: str) -> float :
"""计算器: 支持 + - * /"""
if op == "+":
return a + b
elif op == "-":
return a - b
elif op == "*":
return a * b
elif op == "/":
return a / b if b != 0 else "除数不能等于0"
else:
return "不支持的运算符"
tools = [get_weather, calculator]
# tools_zhihu = [convert_to_openai_tool(t) for t in tools]
# 定义 State (强状态 可自由扩展)
class ReActState(TypedDict):
# messages: Sequence[BaseMessage] # 消息历史
messages: Annotated[Sequence[BaseMessage], add_messages] # ✅ 明确指定追加模式
loop_count: int # 循环次数(防死循环)
# 初始化 LLM 绑定工具
llm = ChatOpenAI(
api_key=os.getenv("ZHIPU_API_KEY"),
base_url=os.getenv("ZHIPU_BASE_URL"),
model=os.getenv("LLM_MODEL"),
temperature=0
).bind_tools(tools)
# 关键: 绑定工具 让LLM能生成tool_call
# ✅ 智谱唯一能识别的工具绑定方式
# llm_with_tools = llm.bind(tools=tools)
# 定义节点
# 节点1: LLM思考节点
def llm_think(state: ReActState) -> ReActState:
print(f"\n-- 第 {state['loop_count']} 轮:LLM 思考--")
message = state["messages"]
response = llm.invoke(message)
# 返回新的state:追加AI 消息, 循环次数 + 1
return {
"messages": [response], # # 只返回新消息,LangGraph 自动追加
"loop_count": state["loop_count"] + 1
}
# 节点2:工具执行节点 LangGraph 内置 ToolNode 直接用
tool_executor = ToolNode(tools)
# 定义条件路由 核心 决定继续循环还是结束
def should_continue(state: ReActState) -> Literal["continue", "end"]:
last_msg = state["messages"][-1]
# 终止条件
# 1. LLM没有生成tool_call(直接回答)
# 2. 循环次数 >= 5 防死循环
if not last_msg.tool_calls or state["loop_count"] >= 5:
print("→ 结束:无需工具 或 达到最大轮次")
return "end"
else:
print("→ 继续:需要调佣工具")
return "continue"
# 构建图 ReAct 循环
builder = StateGraph(ReActState)
# 添加节点
builder.add_node("llm_think", llm_think)
builder.add_node("tool_executor", tool_executor)
# 边:开始 -> LLM 思考
builder.add_edge(START, "llm_think")
# 边:LLM 思考 -> 条件判断(继续/结束)
builder.add_conditional_edges(
"llm_think",
should_continue,
{
"continue": "tool_executor", # 继续:去执行工具
"end":END # 结束: 直接输出答案
}
)
# 边: 工具执行 -> 回到 LLM 思考 循环核心
builder.add_edge("tool_executor", "llm_think")
# 编译图
react_agent = builder.compile()
# 运行测试
if __name__ == "__main__":
print("===== LangGraph React Agent 启动 =====")
# 初始状态:用户问题 + 循环计数 = 0
initial_state = {
"messages": [HumanMessage(content = "北京今天天气? 上海比北京高3度,上海多少度?")],
"loop_count": 0
}
# 调用Agent
result = react_agent.invoke(initial_state)
# 输出最终答案
print("\n ===== 最终答案 =====")
print(result["messages"][-1].content)
三、运行结果解析(清晰看到循环)
===== LangGraph React Agent 启动 =====
-- 第 0 轮:LLM 思考--
→ 继续:需要调佣工具
-- 第 1 轮:LLM 思考--
→ 继续:需要调佣工具
-- 第 2 轮:LLM 思考--
→ 结束:无需工具 或 达到最大轮次
===== 最终答案 =====
根据查询结果:
**北京今天天气**:晴,25℃
**上海温度**:28℃(比北京高3度)
进程已结束,退出代码为 0
流程:
- 第 0 轮:LLM 决定调用
get_weather(北京) - 执行工具 → 拿到北京天气
- 第 1 轮:LLM 决定调用
calculator(25,3,+) - 执行工具 → 拿到上海温度
- 第 2 轮:LLM 直接回答,无工具调用 → 结束
四、LangGraph vs LangChain Agent:关键差异(为什么更灵活)
4.1 定制节点:想插就插
LangChain Agent:
- 无法在 "思考→工具" 中间插节点(如 "工具调用前校验参数")
LangGraph:
- 随便加节点:
llm → 校验参数 → 工具执行 → 结果格式化 → llm - 示例:加一个
validate_tool_call节点,检查参数是否合法
4.2 自由状态:想加字段就加
LangChain Agent:
- 状态固定为消息链,想加
reflection、confidence很难
LangGraph:
class ReActState(TypedDict):
messages: Sequence[BaseMessage]
loop_count: int
reflection: str # 可加反思结果
confidence: float # 可加置信度
tool_results: dict # 可加工具结果缓存
4.3 复杂分支:轻松实现
LangGraph 可做:
- 多工具并行调用
- 工具失败重试(最多 3 次)
- 不同工具结果走不同分支
- 人工介入审批(human-in-the-loop)
五、扩展:给 ReAct 加 "反思"(更高级)
只需 3 步:
- State 加
reflection字段 - 加
reflect_node:LLM 检查工具结果是否足够 - 条件边:反思不合格 → 重新生成工具调用;合格 → 结束
六、langchain 和 langgraph 模块相关版本
langchain 0.3.29
langchain-community 0.3.31
langchain-core 0.3.85
langchain-openai 0.3.35
langchain-text-splitters 0.3.11
langgraph 0.2.56
langgraph-checkpoint 2.1.2
langgraph-sdk 0.1.74
代码关于兼容智普AI做了优化