[langgraph] Build Agent
一天可以快速过一遍的课程:LangGraph大模型全套教程,从入门到企业级实战
Since: 3 langgraph2 使用LangGraph构建Agent智能体 上
好的 AI Agent 的标准:"仅通过简单沟通,即可把一个复杂的任务交给对方"。
"Through simple communication, you can delegate a complex task to it."
1. 构建智能体
构建时考虑到了 tools。
from langgraph.prebuilt import create_react_agent agent = create_react_agent( model=llm, tools=[], prompt="you are a helpful assistant", )
打印的两种方式:
agent.invoke( { "messages": [ { "role": "user", "content": "你是谁?能帮我解决什么问题?", } ] } )
########################################################### for chunk in agent.stream( { "messages": [ { "role": "user", "content": "你是谁?能帮我解决什么问题?", } ] }, # stream_mode="messages" ): print(chunk) print("\n")
2. 工具调用
LangGraph 里工具调用至少有两种风格。
a) 这个是 ReAct agent 的风格。
LLM 决定调用什么工具ToolNode 负责执行工具
适用于:
- 开放式问答
- 让 Agent 自己判断是否需要工具
- 工具很多
- 任务路径不固定
核心代码:
from typing import Annotated, TypedDict from langchain_core.messages import AnyMessage from langchain_core.tools import tool from langgraph.graph import StateGraph, START, END from langgraph.graph.message import add_messages from langgraph.prebuilt import ToolNode, tools_condition # State 只负责保存消息历史 class State(TypedDict): messages: Annotated[list[AnyMessage], add_messages] # 工具 1:查日期 @tool def get_current_date() -> str: """获取今天日期。""" return "2026-05-17" # 工具 2:做乘法 @tool def multiply(a: int, b: int) -> int: """计算两个整数的乘法。""" return a * b tools = [get_current_date, multiply]
------------------------------------------------------ # 关键 1:把工具告诉 LLM # LLM 现在知道自己有两个工具可以选 llm_with_tools = llm.bind_tools(tools)
builder = StateGraph(State)
# 关键 2:LLM 节点 # 它只负责判断: # - 要不要调用工具 # - 调用哪个工具 # - 参数是什么 def agent_node(state: State): ai_msg = llm_with_tools.invoke(state["messages"]) return {"messages": [ai_msg]} builder.add_node("agent", agent_node) # 关键 3:ToolNode # 它只负责执行 LLM 生成的 tool_calls tool_node = ToolNode(tools) builder.add_node("tools", tool_node)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++
builder.add_edge(START, "agent") # 如果 agent 生成了 tool_calls,就去 tools # 如果没有 tool_calls,就结束 builder.add_conditional_edges("agent", tools_condition) # 工具执行完后,回到 agent # 因为 LLM 还要读取工具结果,再生成最终回答 builder.add_edge("tools", "agent")
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
graph = builder.compile()
#
# 一个更为高级的 add_conditional_edges 的例子~
#
builder.add_node("agent", agent_node) builder.add_node("tools", tool_node) builder.add_node("summary", summary_node) builder.add_edge(START, "agent") builder.add_conditional_edges( "agent", tools_condition, { "tools": "tools", END: "summary", }, ) builder.add_edge("tools", "agent") builder.add_edge("summary", END)
解释:
agent 执行完后: 如果 tools_condition 返回 "tools" → 去 tools 节点 如果 tools_condition 返回 END → 不是真的结束,而是转去 summary 节点
agent ├── 有 tool_calls → tools → agent └── 没有 tool_calls → summary → END
若自定义一个 tools_condition,例如 route_after_agent。
def route_after_agent(state): last_msg = state["messages"][-1] if getattr(last_msg, "tool_calls", None): return "tools" return "summary"
就会 适配下面的 写法。
builder.add_conditional_edges( "agent", route_after_agent, { "tools": "tools", "summary": "summary", }, )
最后补充一个典型的node的实现方式:
def summary_node(state): response = llm.invoke( [ { "role": "system", "content": "请把前面的对话和工具结果总结成一句清晰的中文答案。", }, *state["messages"], ] ) return { "messages": [response], "final_summary": response.content, }
b) 另一种则是:Workflow-driven tool calling。
- Graph 里的某个 node 固定调用某个工具
- LLM 不决定工具调用
from typing import TypedDict from langgraph.graph import StateGraph, START, END class State(TypedDict): city: str weather: str final_answer: str # 普通工具函数:查询天气 # 注意:这个工具不是给 LLM 自动选择的 # 它是我们在 weather_node 里手动调用的 def get_weather(city: str) -> str: if city == "Sydney": return "sunny" return "rainy" # Node 1:程序员决定,这一步就是查天气 def weather_node(state: State): weather = get_weather(state["city"]) return { "weather": weather } # Router:根据 state 决定下一步走哪条分支 def route_by_weather(state: State): if state["weather"] == "sunny": return "sunny" return "not_sunny" # 晴天分支 def outdoor_node(state: State): return { "final_answer": "今天是晴天,适合出门散步。" } # 非晴天分支 def indoor_node(state: State): return { "final_answer": "今天不是晴天,建议安排室内活动。" } builder = StateGraph(State) builder.add_node("weather", weather_node) builder.add_node("outdoor", outdoor_node) builder.add_node("indoor", indoor_node) builder.add_edge(START, "weather") builder.add_conditional_edges( "weather", route_by_weather, { "sunny": "outdoor", "not_sunny": "indoor", }, ) builder.add_edge("outdoor", END) builder.add_edge("indoor", END) graph = builder.compile()
现在我们设计一个 "综合性" 的例子:

main.py
from langchain_core.messages import HumanMessage from langchain_community.chat_models import ChatTongyi from langgraph.types import Command from config.load_key import load_key from graph_builder import SmartPlanGraphBuilder from services import PlanSaver from tools import get_weather, multiply llm = ChatTongyi( model="qwen-plus", api_key=load_key("BAILIAN_API_KEY"), ) tools = [ get_weather, # ---- ----> multiply, # ---- ----> ] graph = SmartPlanGraphBuilder( # ----> llm=llm, tools=tools, plan_saver=PlanSaver(), ).build() config = { "configurable": { "thread_id": "smart-plan-demo-001" } } first_result = graph.invoke( { "messages": [ HumanMessage( content=( "我在 Sydney。" "如果天气好,帮我安排一个户外计划;" "另外帮我算一下 23 乘以 17。" "最后生成计划草稿。" ) ) ], "draft_plan": None, "approved": None, "final_answer": None, }, config=config, ) if "__interrupt__" in first_result: print(first_result["__interrupt__"]) user_input = input("Approve saving this plan? y/n: ").strip().lower() approved = user_input in {"y", "yes"} final_result = graph.invoke( Command( resume={ "approved": approved } ), config=config, ) else: final_result = first_result print(final_result["messages"][-1].content)
# tools.py
from langchain_core.tools import tool @tool def get_weather(city: str) -> str: """ 查询某个城市今天的天气。 Args: city: 城市名称,例如 Sydney。 """ if city.lower() == "sydney": return "sunny" return "rainy" @tool def multiply(a: int, b: int) -> int: """ 计算两个整数的乘法。 Args: a: 第一个整数。 b: 第二个整数。 """ return a * b
graph_builder.py
先理解下面这段:
# state.py
from typing import Annotated, Optional, TypedDict from langchain_core.messages import AnyMessage from langgraph.graph.message import add_messages class PlanState(TypedDict): """ 整个 graph 共享的状态。 messages: 对话历史。ToolNode 和 LLM 都依赖它。 draft_plan: LLM 最后生成的计划草稿。 approved: 人工是否批准保存。 final_answer: 最终返回给用户的答案。 """
# 哪些数据需要被多个 node 共享、读取、更新,就放进PlanState。 messages: Annotated[list[AnyMessage], add_messages] draft_plan: Optional[str] approved: Optional[bool] final_answer: Optional[str]
也就是,加装好了弹药 ~
builder = StateGraph(PlanState)
进入最重要的 “ 构图”过程~
# graph_builder.py
from langgraph.checkpoint.memory import InMemorySaver from langgraph.graph import END, START, StateGraph from langgraph.prebuilt import ToolNode from nodes import ( ApprovalNode, PlanningAgentNode, PrepareReviewNode, RejectNode, SaveNode, ) from routers import route_after_agent, route_after_approval from state import PlanState class SmartPlanGraphBuilder: """ 负责构建 SmartPlan Agent 的 graph。 这个类的职责很单一: 把 nodes、tools、routers 组装成一个 LangGraph workflow。 """ def __init__(self, llm, tools, plan_saver): self.llm = llm self.tools = tools self.plan_saver = plan_saver def build(self):
# 我要开始搭建一个 LangGraph 流程图,而这个流程图里所有节点共享的数据结构,叫PlanState。 builder = StateGraph(PlanState) # ----------------------------- # 1. 注册节点 # ----------------------------- builder.add_node( "agent", PlanningAgentNode( llm=self.llm, tools=self.tools, ), ) # ToolNode 是模式 A 的核心: # 它负责执行 LLM 产生的 tool_calls。 builder.add_node( "tools", ToolNode(self.tools), ) builder.add_node( "prepare_review", PrepareReviewNode(), ) builder.add_node( "approval", ApprovalNode(), ) builder.add_node( "save", SaveNode(self.plan_saver), # ---- ----> ) builder.add_node( "reject", RejectNode(), ) # ----------------------------- # 2. 注册边 # ----------------------------- builder.add_edge(START, "agent") # agent 后面有两个可能: # 1. LLM 想调用工具 → tools # 2. LLM 已经完成草稿 → prepare_review builder.add_conditional_edges( "agent", route_after_agent, # ---- ----> { "tools": "tools", "prepare_review": "prepare_review", }, ) # 工具执行完后,必须回到 agent。 # 因为 LLM 需要读取 ToolMessage,然后继续生成答案。 builder.add_edge("tools", "agent") builder.add_edge("prepare_review", "approval") # approval 后面有两个可能: # 1. 人工批准 → save # 2. 人工拒绝 → reject builder.add_conditional_edges( "approval", route_after_approval, # ---- ----> { "save": "save", "reject": "reject", }, ) builder.add_edge("save", END) builder.add_edge("reject", END) # demo 用内存 checkpointer。 # 生产环境应该换成 PostgresSaver 等持久化 checkpointer。 checkpointer = InMemorySaver() return builder.compile(checkpointer=checkpointer)
# services.py
from pathlib import Path class PlanSaver: """ 负责保存计划。 注意: 这个类不是给 LLM 自动调用的 tool。 它只会在人工批准之后,由 save_node 调用。 """ def __init__(self, output_path: str = "saved_plan.txt"): self.output_path = Path(output_path) def save(self, plan: str) -> str: self.output_path.write_text(plan, encoding="utf-8") return str(self.output_path.resolve())
# routers.py
from langgraph.graph import END from state import PlanState def route_after_agent(state: PlanState) -> str: """ agent_node 执行完以后,决定下一步。 如果 LLM 生成了 tool_calls: 说明它想调用工具。 走 tools 节点。 如果没有 tool_calls: 说明 LLM 已经生成了计划草稿。 走 prepare_review 节点。 """ last_message = state["messages"][-1] if getattr(last_message, "tool_calls", None): return "tools" return "prepare_review" def route_after_approval(state: PlanState) -> str: """ approval_node 执行完以后,决定下一步。 如果 approved=True: 去 save_node。 如果 approved=False: 去 reject_node。 """ if state.get("approved"): return "save" return "reject"
节点定义与实现:nodes.py
from langchain_core.messages import AIMessage from langgraph.types import interrupt from state import PlanState class PlanningAgentNode: """ LLM 节点。 职责: 1. 读取 messages。 2. 让 LLM 判断是否需要调用工具。 3. 如果需要工具,LLM 会生成 tool_calls。 4. 如果不需要工具,LLM 会生成最终计划草稿。 注意: 这个 node 不执行工具。 工具由 ToolNode 执行。 """ def __init__(self, llm, tools): self.llm_with_tools = llm.bind_tools(tools) def __call__(self, state: PlanState) -> dict: system_message = { "role": "system", "content": ( "你是一个计划助手。\n" "你可以使用工具查询天气,也可以使用工具做数学计算。\n" "如果用户要求安排计划,你应该先调用需要的工具。\n" "拿到工具结果后,生成一个清晰的中文计划草稿。\n" "不要自己保存计划,保存动作需要人工审批。" ), } response = self.llm_with_tools.invoke( [system_message, *state["messages"]] ) return { "messages": [response] } class PrepareReviewNode: """ 审批准备节点。 职责: 1. 读取 LLM 最后一条回答。 2. 把它保存成 draft_plan。 3. 后面 approval_node 会让人类审批这个 draft_plan。 """ def __call__(self, state: PlanState) -> dict: last_message = state["messages"][-1] return { "draft_plan": last_message.content } class ApprovalNode: """ 人工审批节点。 职责: 1. 暂停 graph。 2. 把计划草稿展示给人类。 3. 等人类批准或拒绝。 """ def __call__(self, state: PlanState) -> dict: human_decision = interrupt( { "question": "是否批准保存下面这个计划?", "draft_plan": state["draft_plan"], } ) if isinstance(human_decision, dict): approved = bool(human_decision.get("approved", False)) else: approved = bool(human_decision) return { "approved": approved } class SaveNode: """ 保存节点。 职责: 只有人工批准后,才真正保存计划。 这是模式 B: 程序员明确决定什么时候执行保存动作。 """ def __init__(self, plan_saver): self.plan_saver = plan_saver def __call__(self, state: PlanState) -> dict: saved_path = self.plan_saver.save(state["draft_plan"]) answer = f"计划已保存:{saved_path}" return { "final_answer": answer, "messages": [AIMessage(content=answer)], } class RejectNode: """ 拒绝节点。 职责: 如果人工没有批准,就不保存。 """ def __call__(self, state: PlanState) -> dict: answer = "你没有批准保存,所以计划没有被保存。" return { "final_answer": answer, "messages": [AIMessage(content=answer)], }
LangChain 重要版本时间线
| 版本 | PyPI 首个对应版本日期 | 官方公告/说明日期 | 重点意义 |
|---|---|---|---|
| 早期 0.0.x | 2022–2023 期间持续演进 | — | LangChain 早期快速迭代阶段,版本号长期停留在 0.0.x。 |
langchain 0.1.0 |
2024-01-06 | 2024-01-08 | 第一个 stable release;官方称其为首个稳定版本,强调 backward compatibility、LCEL、streaming 等。 |
langchain 0.2.0 |
2024-05-17 | 2024-05-20 | 重要架构调整:langchain 与 langchain-community 解耦,提升稳定性和安全性。 |
langchain 0.3.0 |
2024-09-13 | 2024-09-16/17 | Pydantic 2 支持、工具定义与 chat model utilities 改进;这个时期官方也强调 LangGraph 已成为推荐 agent 构建方式。 |
langchain 1.0.0 |
2025-10-17 | 2025-10-20/22 | 第一个 major stable release;create_agent 成为新的高层 agent 入口,底层构建在 LangGraph runtime 上。 |
langchain 1.1.0 |
2025-11-24 | 2025-12-02 | 增强 agent 开发能力:model profiles、summarization middleware、structured output、retry middleware、moderation middleware 等。 |
langchain 1.2.0 |
2025-12-15 | — | v1 系列后续稳定演进版本。PyPI release history 显示 1.2.0 于 2025-12-15 发布。 |
langchain 1.3 |
— | 2026-05-12 | 支持 stream_events / astream_events 的 version="v3"。 |
LangGraph 重要版本时间线
| 版本 | PyPI 首个对应版本日期 | 官方公告/说明日期 | 重点意义 |
|---|---|---|---|
| LangGraph 初次发布 | — | 2024-01-08 | 随 LangChain v0.1.0 公告一起宣布:LangGraph 作为“用 graph 创建 language agents”的新库出现。 |
langgraph v0.1 |
PyPI 0.1.1:2024-06-22 |
2024-06-27 | 官方发布 LangGraph v0.1 与 LangGraph Cloud,强调可靠运行 agent、状态化 workflow、可扩展部署。 |
langgraph 0.2.0 |
2024-08-07 | 2024-08-07 | 加强 customization,引入新的 checkpointer libraries;围绕 session memory、error recovery、human-in-the-loop 等能力。 |
langgraph 0.3.0 |
2025-02-26,但该 PyPI 版本被 yanked | 2025-02-27 | v0.3 的主题是 prebuilt agents;create_react_agent 等高层抽象拆到 langgraph-prebuilt,并引入 Supervisor、Swarm 等预构建 agent 生态。注意 PyPI 上 0.3.0 被 yanked,原因是缺少 langgraph-prebuilt 依赖。 |
langgraph 1.0.0 |
2025-10-17 | 2025-10-22 | 第一个 major stable release;官方称其为 durable agent framework 的首个稳定大版本,重点是生产级 durable execution、stateful agent runtime。 |
langgraph v1.1 |
— | 2026-03-10 | 引入 opt-in 的 type-safe streaming / type-safe invoke v2,以及 interrupt/subgraph time travel 修复等。 |
langgraph v1.2 |
2026-05-12 | 2026-05-12 | 增加更细粒度 node execution 控制:timeouts、error recovery、graceful shutdown;新增 DeltaChannel 和 v3 streaming API。 |

浙公网安备 33010602011771号