文章目录
本文内容主要转载自:
一、LangGraph入门
LangGraph 是基于 LangChain 构建的多智能体协作框架,专为构建复杂、动态的智能体工作流而设计。它采用图结构(Graph)建模任务流程,将多个智能体(Agent)作为节点,通过有向边定义交互逻辑,支持循环、分支、异步等控制模式。
(1)主要功能
- 循环和分支: 在应用程序中实现循环和条件语句。
- 持久性: 在图中的每个步骤之后自动保存状态,支持暂停和恢复执行,适用于错误恢复、“人机交互”工作流、时间旅行等场景。
- 人机交互: 中断执行以批准或编辑代理计划的下一个动作。
- 流支持: 在每个节点产生输出时流式传输输出(包括令牌流式传输)。
- 与 LangChain 集成: LangGraph 与 LangChain 和 LangSmith 无缝集成(但不需要它们)。
(2)核心特性
- 可视化编排,允许开发者通过API或UI拖拽编排智能体协作链路;
- 状态管理,内置持久化上下文存储,支持智能体间的增量式信息传递;
- 弹性扩展,可集成LLM、工具、人类反馈等异构节点。
(3)应用场景
典型应用场景包括自动化决策系统、多角色模拟仿真、复杂任务分解执行等,显著提升智能体系统的可解释性和任务处理上限。
1.1 Chain:线性执行的顺序链
LangChain的核心优势在于其能够轻松构建自定义链,这些链通常是线性的,类似于有向无环图(DAG),每个步骤都严格按顺序执行,每个任务只有一个输出和一个后续任务,形成一个没有循环的线性流程。
在遇到复杂任务时,比如第一次搜索没有找到想要的内容,我们可能需要进行第二次、第三次搜索,甚至可能需要调用网络搜索来完成。在这种情况下,顺序执行的任务(DAG)显然无法满足需求。
请求方和搜索方之间需要经历多次来回沟通,请求方可能会要求搜索方根据反馈调整搜索策略,这种多次的循环沟通才能逐步逼近最终答案。
1.2 LangChain代理:循环图,但是无法控制
这种情况下,我们需要的不再是DAG,而是一个循环图,它能够描述多个参与者之间的多轮对话和互动,以确认最终的答案。这种循环图能够处理更模糊、更复杂的用例,因为它允许系统根据反馈进行调整和迭代。那么,在循环图的运行模式就是智能代理,也就是AI Agent。

1.3 LangGraph,可以对智能代理进行更多的控制
在实际应用过程中,我们发现需要对智能代理进行更多的控制。例如,我们可能希望智能代理始终首先调用特定工具,或者我们可能希望对工具的调用方式有更多的控制,甚至可能希望根据智能代理的状态使用不同的提示。为了解决这些问题,LangGraph提出了 “状态机” 的概念。通过状态机为图创建对应的状态机,这种方法可以更好地控制智能代理的行动流程,使其更加灵活和有效地处理复杂任务。
补充:LangChain代理和LangGraph对比

二、LangGraph概念
2.1 StateGraph(状态图)
StateGraph是一个类,它负责表示整个图的结构;需要通过传入一个状态定义来初始化这个类(图状态),这个图状态代表了一个中心状态对象,它会在执行过程中不断更新。这个图状态对象由图中的节点更新,节点会以键值对的形式,返回对状态属性的操作。
状态对象的属性可以通过两种方式更新,在定义图状态的时候,需要指定属性的更新方式:(可以有多个属性,每个属性需要单独指定一种更新方式)
- 覆盖更新:如果一个属性需要被新的值替换,我们可以让节点返回这个新值进行替换。
- 增量更新:如果一个属性是一个动作列表(或类似的操作),我们可以在原有的列表上添加新的动作。
下面以旅行为例,具体步骤如下:
- 输入目的地(Edges里面的起始边)
- 任务规划,包括预定航班、预订酒店(Nodes里面的各个节点)
- 任务规划之间是有关联关系的,比如航班夜晚,我们就不要酒店了,航班改中间位置就要修改酒店地址,之间有因果关系(Edges里面的边,分为普通边和条件边,普通边就是任务有序执行,条件边就是可能根据情况不一样)
- StateGraph类就是这样的旅行计划,而节点就像是规划旅行的不同步骤,比如确定目的地、预定航班和预定酒店。每个步骤都会更新你的旅行计划,可能是完全替换旧的计划,也可能是添加新的信息到现有的计划中
2.2 Nodes(节点)
创建了StateGraph之后,需要向其中添加节点;每个节点都代表一个任务,它们执行的结果会影响StateGraph的状态。这些节点通过边相互连接,形成了一个有向无环图(DAG),确保了任务的正确执行顺序。
在 LangGraph 中,节点通常是 Python 函数(同步或 async),其中第一个位置参数是状态。(可选地),第二个位置参数是"配置",包含可选的可配置参数(例如 thread_id)。
类似于 NetworkX,您可以使用 add_node 方法将这些节点添加到图形中。
在上面旅行计划的例子中:
- Nodes(节点)就好像旅行计划中需要完成的任务,例如:预定航班、预订酒店。
- Nodes(节点)接受旅行计划(图状态对象)作为输入,并输出一个更新后的任务状态,例如:完成酒店的预订
2.2.1 START节点
START 节点是一个特殊节点,它代表将用户输入发送到图形的节点。引用此节点的主要目的是确定哪些节点应该首先被调用。
from langgraph.graph import START
graph.add_edge(START, "my_node")
graph.add_edge("my_node", "other_node")
注意:使用
add_edge函数添加 START节点,即 入口节点。
2.2.2 END节点
END 节点是一个特殊节点,它代表一个终端节点。当您想要指定哪些边在完成操作后没有动作时,会引用此节点。
from langgraph.graph import END
graph.add_edge("other_node", END)
2.3 Edges(边)
Edges(边)是连接Nodes(节点)并定义StateGraph(状态图)中节点执行顺序的关键部分。边主要分为以下几种:
- 普通边(Normal Edges):这些边表示一个节点总是要在另一个节点之后被调用。在旅行计划中,普通边就像是确定了任务执行的顺序。例如,在找到合适的航班之后,你可能会决定预订酒店。这个顺序确保了任务的有序执行。
- 条件边(Conditional Edges):使用函数(通常由LLM提供)来确定首先调用哪个节点。在旅行计划中,条件边就像是根据你的喜好或者天气情况来决定你的下一步行动。比如,如果你发现没有合适的航班,你可能会选择推迟预订酒店,而去查找火车车票。条件边提供了灵活性,使得系统可以根据不同的情况来调整执行的顺序。
- 起始边(Starting Edge):作为图的开始。比如在旅行计划中,起始边就是确定你的目的地。一旦目的地被确定,你的旅行计划就可以开始执行了。
边定义了节点之间的依赖关系和执行顺序。起始边确定了图的开始,普通边确保了任务的正确执行顺序,而条件边则根据特定的条件来决定下一步的操作。
2.3.1 普通边
如果您总是想从节点 A 到节点 B,您可以直接使用 add_edge 方法。
graph.add_edge("node_a", "node_b")
2.3.2 条件边
如果您想选择性地路由到一个或多个边(或选择性地终止),您可以使用 add_conditional_edges 方法。此方法接受节点的名称和一个"路由函数",该函数将在该节点执行后被调用。
graph.add_conditional_edges("node_a", routing_function)
类似于节点,routing_function 接受图形的当前 state 并返回一个值。

在上图中,routing_function 作为条件边的判定条件,如果返回True,则执行node_b,如果返回False,则执行node_c)
默认情况下,返回值 routing_function 用作要将状态发送到下一个节点的节点名称(或节点列表)。所有这些节点将在下一个超级步骤中进行运行。
您可以选择提供一个字典,该字典将 routing_function 的输出映射到下一个节点的名称。
graph.add_conditional_edges("node_a", routing_function, {True: "node_b", False: "node_c"})
2.3.2 入口点
入口点是图形启动时运行的第一个节点。您可以从虚拟的START节点使用add_edge方法到要执行的第一个节点,以指定进入图形的位置。
from langgraph.graph import START
graph.add_edge(START, "my_node")
2.3.3 条件入口点
条件入口点允许您根据自定义逻辑从不同的节点开始。您可以从虚拟的START节点使用add_conditional_edges来实现这一点。
from langgraph.graph import START
graph.add_conditional_edges(START, routing_function)
您可以选择提供一个字典,该字典将 routing_function 的输出映射到下一个节点的名称。
graph.add_conditional_edges(START, routing_my,(True: "my_node", False: "other_node"))
2.4 checkpoint(检查点)
LangGraph 具有一个内置的持久化层,通过 checkpoint(检查点)实现。当您将检查点与图形一起使用时,您可以与该图形的状态进行交互。当您将检查点与图形一起使用时,您可以与图形的状态进行交互并管理它。检查点在每个超级步骤中保存图形状态的检查点,从而实现一些强大的功能。
-
首先,检查点通过允许人类检查、中断和批准步骤来促进人机交互工作流工作流。检查点对于这些工作流是必需的,因为人类必须能够在任何时候查看图形的状态,并且图形必须能够在人类对状态进行任何更新后恢复执行。
-
其次,它允许在交互之间进行"记忆"。您可以使用检查点创建线程并在图形执行后保存线程的状态。在重复的人类交互(例如对话)的情况下,任何后续消息都可以发送到该检查点,该检查点将保留对其以前消息的记忆。
许多 AI 应用程序需要内存来跨多个交互共享上下文。在 LangGraph 中,通过 checkpoint(检查点)为任何 StateGraph 提供内存。
在创建任何 LangGraph 工作流时,您可以通过以下方式设置它们以持久保存其状态:
- 一个 检查点,例如 AsyncSqliteSaver
- 在编译图时调用 compile(checkpointer=my_checkpointer)。
二、LangGraph使用案例
2.1 使用步骤
-
初始化模型和工具(Model and Tool)
- 使用 ChatOpenAI 作为 LLM,并通过 .bind_tools() 方法将 LangChain 工具转换为 OpenAI 工具调用格式。
- 定义要使用的工具(例如搜索工具),并支持自定义工具。
-
用状态初始化图(StateGraph)
- 通过传递状态模式(如 MessagesState)初始化图(
StateGraph)。 - MessagesState 是预构建的状态模式,包含 LangChain Message 对象列表,并定义了节点更新合并逻辑。
- 通过传递状态模式(如 MessagesState)初始化图(
-
定义图节点(Node)
agent节点:决定是否采取行动。tools节点:若代理决定行动,则执行该行动。
节点的主要作用是调用函数,执行一个具体的功能。
- 定义入口点和图边
- 首先,我们需要设置图执行的入口点——agent 节点。
- 然后,我们定义一个普通边和一个条件边。条件边意味着目的地取决于图状态(MessageState)的内容。在本例中,目的地在代理(LLM)决定之前是未知的。
-
条件边:调用代理后,我们根据条件选择执行
a. 如果代理说要采取行动,则运行工具
b. 如果代理没有要求运行工具,则完成(回复用户)。 -
普通边:调用工具后,图应该始终返回到代理以决定下一步操作。
-
下面我们来看一个简单的状态图:
- 节点:start, agent, tools, end
- 虚线:条件边
- 实线:普通边
- 编译图
- 当我们编译图时,会将其转换为
LangChain Runnable,这使得可以使用.invoke()、.stream()和.batch()方法来调用输入。 - 我们还可以选择传递检查点对象,以在图运行之间持久化状态,并支持内存、"人机交互"工作流、时间旅行等功能。在本例中,我们使用MemorySaver——一个简单的内存中检查点。
- 执行图
- LangGraph 将输入消息添加到内部状态,然后将状态传递给入口点节点 “agent”。
- “agent” 节点执行,调用聊天模型。
- 聊天模型返回 AIMessage。LangGraph 将其添加到状态中。
- 图循环以下步骤,直到 AIMessage 上不再有 tool_calls:
- 如果 AIMessage 具有 tool_calls,则 “tools” 节点执行。
- “agent” 节点再次执行并返回 AIMessage。
- 执行进度到特殊的 END 值,并输出最终状态。因此,我们得到所有聊天消息的列表作为输出。
2.2 代码实例
- 实例一:状态图stateGraph和节点node的使用(不含图状态和注解)
实现功能:随着图的运行,x的值每次加1,y的值每次减1,z的值为10不变。
from langgraph.graph import START, StateGraph
#1.不涉及model和tool
#2.不涉及图状态
#3.定义图节点,定义节点需要设置一个节点的action(函数)
def my_node(state):
return {"x": state["x"] + 1, "y": state["y"]-1, "z":10}
#4.不需要条件边
#5.定义工作流状态图
builder = StateGraph(dict) #注意:没有传入图状态,dict默认全部input的字段都作为图状态属性传入,比如后面的x,y都是StateGraph的state
builder.add_node("my_node", my_node) #添加节点
builder.add_edge(START, "my_node") #添加起始边
#6.编译
graph = builder.compile()
step1 = graph.invoke({"x": 1, "y":2})
print(step1)
输出如下:
{'x': 2, 'y': 1, 'z': 10}
在上面的代码中,我们基于
state实现状态的传递。通过debug,可以看到 state 是一个包含 key 和 value 的字典,state = {'x': 1, 'y': 2},通过该字典实现参数的传递。
如果我们尝试更新节点的action,会发现每一步的图状态更新和节点的返回结果相关:
def my_node(state):
return {"x": state["x"] + 1}
- 实例二:状态图stateGraph和节点node的使用(包含图状态和注解)
from langchain_core.runnables import RunnableConfig
from typing_extensions import Annotated, TypedDict
from langgraph.graph import StateGraph
#1.不涉及model和tool
#2.定义图状态
def reducer(a: list, b: list | None) -> list: #进行追加
if b is not None:
return a + b
return a
class State(TypedDict): #这里是图中状态的定义,也是标识运行时的input中哪些字段可以透传给状态图
#https://blog.youkuaiyun.com/randy521520/article/details/133826255
x: Annotated[list, reducer] #Annotated: 用于添加类型注解的装饰器。可以在类型提示中添加额外的元数据信息;这里表示更新list数据的时候,通过reducer方法进行更新(第一个参数时原数据list,第二个参数是要更新的数据int)
class ConfigSchema(TypedDict): #这里是标识运行时的RunnableConfig中哪些字段可以透传给状态图
r: float
#3.定义图节点,定义节点需要设置一个节点的action(函数)
def node_runner(state: State, config: RunnableConfig) -> dict: #传入图状态,可以进行修改;还可以传入config,config在调用时传递,同时还需要在创建状态图时再声明哪些变量可以传递到图中
r = config["configurable"].get("r", 1.0) #从config中获取配置字段值
print(state["x"])
x = state["x"][-1] #获取最新的状态
next_value = x * r * (1 - x) #随便的操作
return {"x": [next_value],"y":10} #注意:返回的值,会通过注解里面的reducer进行更新(作为第二个参数),所以类型要一致
#4.不需要条件边
#5.定义工作流状态图,没有边
graph = StateGraph(State, config_schema=ConfigSchema)
graph.add_node("A", node_runner) #添加节点,包括节点的action
graph.set_entry_point("A") #设置进入节点
graph.set_finish_point("A") #设置结束节点
#6.编译
compiled = graph.compile()
step1 = compiled.invoke({"x": [0.5,1],"y":0.3}, {"configurable": {"r": 3.0}})
print(step1)
输出如下:

可以看到,我们显式定义了图状态里面的属性后,节点返回的数据在更新图状态时只会去获取图状态定义中的字段进行更新
- 实例三:包含了model和tool的使用
这里用到了一个新的模型anthropic.claude-3-5-sonnet,Claude 3 是一系列最先进的人工智能(AI)模型,可让您根据自己的需求,从智能、速度和成本方面考虑选择最适合的组合。并且该模型能够可靠地从图像等非结构化数据中提取信息。
https://docs.anthropic.com/zh-CN/docs/intro-to-claude
from typing import Literal
from langchain_core.messages import ToolMessage
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
from langgraph.checkpoint import MemorySaver
from langgraph.graph import END, StateGraph, MessagesState
from langgraph.prebuilt import ToolNode
from langchain.prompts import MessagesPlaceholder,ChatPromptTemplate
#1.初始化model和tools,可以通过agent关联models和tools
@tool
def search(query: str):
"""Call to surf the web."""
# This is a placeholder, but don't tell the LLM that...
if "sf" in query.lower() or "san francisco" in query.lower():
return ["It's 60 degrees and foggy."]
return ["It's 90 degrees and sunny."]
tools = [search]
prompt = ChatPromptTemplate.from_messages(
[
("system", "You are a helpful assistant"),
MessagesPlaceholder("chat_history", optional=True),
("human", "{messages}"),
MessagesPlaceholder("agent_scratchpad", optional=True),
]
)
#model在这里用于选取tools
model = prompt | ChatOpenAI(model="gpt-3.5-turbo", temperature=0).bind_tools(tools)
#2.定义图状态,这里使用的MessagesState,属性如下:
# class MessagesState(TypedDict):
# messages: Annotated[list[AnyMessage], add_messages]
# 3.定义图节点
# tool_node = ToolNode(tools) 这也是一种方法,定义tool和model的运行action
tools_by_name = {tool.name: tool for tool in tools}
def tool_node(state: dict):
result = []
for tool_call in state["messages"][-1].tool_calls:
tool = tools_by_name[tool_call["name"]]
observation = tool.invoke(tool_call["args"])
result.append(ToolMessage(content=observation, tool_call_id=tool_call["id"]))
return {"messages": result}
def call_model(state: MessagesState):
response = model.invoke(state)
return {"messages": [response]} #add_messages兼容list和非list,都会转成list
# 4.定义边的逻辑判断(条件边),判断是否继续
def should_continue(state: MessagesState) -> Literal["tools", "__end__"]: #Literal用于限制返回的值的可选值
messages = state['messages']
last_message = messages[-1]
if last_message.tool_calls: #判断models是否返回tools调用,有则告诉调用tools节点,否则结束
return "tools"
return END
# 5.定义工作流状态图
# 5.a.通过图状态初始化工作流
workflow = StateGraph(MessagesState)
# 5.b.添加节点、边
workflow.add_node("agent", call_model)
workflow.add_node("tools", tool_node)
workflow.set_entry_point("agent")
workflow.add_conditional_edges(
"agent",
should_continue, #判断下一个调用的节点
)
workflow.add_edge("tools", 'agent')
# 5.c.编译工作流成一个runnable,通过invoke调用
app = workflow.compile()
# 6.执行状态图
final_state = app.invoke(
{"messages": "what is the weather in sf"},
config={"configurable": {"thread_id": 42}}
)
print(final_state["messages"][-1].content)
输出如下:

参考资料
- B站视频教程 | 2025最新版LangGraph实战教程,以图的方式构建Agent智能体!
- LangChain补充五:Agent之LangGraph的使用
- LangGraph 入门+复习:官方术语表
- Agent:通过AGI之路:Agent + LangChain 等各种框架系列文章

4338

被折叠的 条评论
为什么被折叠?



