Part 2 · 核心能力
04 图的三要素:节点、边、状态
04.1 节点就是一个普通函数
2025 年我带新人,最爱用这句话开场:"节点没那么神,它就是一个函数。"
def my_node(state: AgentState):
last = state["messages"][-1].content
return {"messages": [f"你说了:{last}"]}
节点返回什么,就更新状态的哪部分。返回 {"messages": [...]} 就往白板贴新消息。
04.2 边决定"下一步去哪"
普通边是无条件走:
g.add_edge("agent", "tools") # 永远从 agent 走到 tools
条件边看情况走,背后是个路由函数:
def route(state):
if state["messages"][-1].tool_calls:
return "tools" # 要走这条路
return END # 否则结束
g.add_conditional_edges("agent", route, {"tools": "tools", END: END})
重点看:
route返回的字符串。 它必须和字典的 key 对上。对不上,图编译直接报错。
04.3 状态:图的"共享白板"
最省事的状态是 MessagesState:
from langgraph.graph import MessagesState
g = StateGraph(MessagesState)
MessagesState 内部就一个 messages 字段,自带 add_messages 归约。90% 的场景用它足够。
要加自定义字段也简单:
from langgraph.graph import MessagesState
from typing import Annotated
import operator
class MyState(MessagesState):
# 在消息之外,再加一个累计分数
score: Annotated[int, operator.add] = 0
04.4 三种"图"的对比
| 维度 | 手写函数 | LangChain 链 | LangGraph 图 |
|---|---|---|---|
| 循环 | 自己写 while | 不支持 | 原生支持 |
| 暂停恢复 | 极难 | 不支持 | 原生支持 |
| 可视化 | 无 | 弱 | LangGraph Studio |
| 标叔的结论 | 玩具级 | 线性任务 | 真实 Agent |
核心建议:状态要"可序列化" State 里的东西最终要存进数据库。别塞不能 JSON 化的对象,否则中断恢复会炸。
▸ 向前桥接 节点会了。下一章讲 Agent 怎么"自己挑工具"。