1. 为什么需要 LangGraph?(解决了什么痛点)
传统的 LangChain 链(基于 LCEL)本质上是一个有向无环图(DAG - Directed Acyclic Graph)。数据从输入端流向输出端,中间经过一系列处理节点,但它不能回头,无法形成循环。
这对于简单的问答、摘要、格式转换等任务来说足够了。但对于更复杂的智能体(Agent)应用,我们通常需要:
- 循环(Cycles): 智能体需要反复地“思考 -> 行动 -> 观察结果 -> 再思考…”,直到任务完成。这是一个典型的循环过程。例如,ReAct 框架就是这种模式。
- 分支(Branching): 根据上一步的结果,智能体需要决定下一步该做什么。例如,“如果模型认为需要调用工具,就去执行工具节点;否则,就直接生成最终答案。”
- 状态管理(State Management): 在整个任务执行过程中,需要有一个地方来记录和更新信息,比如已经收集到的数据、历史对话、任务的当前进展等。这个“记忆”对智能体的决策至关重要。
- 可控性与可观测性: 对于复杂的流程,开发者需要清晰地知道每一步发生了什么,为什么会从 A 节点走到 B 节点,而不是 C 节点。
LCEL 在处理这些需求时会变得非常笨拙和复杂,而 LangGraph 正是为了优雅地解决这些问题而生的。
2. LangGraph 的核心思想与组件
LangGraph 的核心思想非常直观:将应用流程建模为一个“状态图”。
想象一下,你有一个中央白板(State),上面记录了任务的所有信息。然后有几个专家(Nodes),每个专家负责一项特定工作(如调用 LLM、执行工具、格式化输出)。你还需要一个调度员(Edges),他会根据白板上的当前信息,决定接下来该请哪位专家工作。
这个过程不断重复,直到任务完成。
下面是 LangGraph 的核心组件:
a. State (状态)
这是 LangGraph 的灵魂。它是一个贯穿整个图运行过程的中心数据结构。通常定义为一个 Python 的 TypedDict
或 Pydantic
模型。
- 作用: 充当所有节点共享的“内存”或“白板”。
- 特点:
- 每个节点都可以读取完整的当前状态。
- 每个节点执行完毕后,返回的是对状态的更新(可以是新增、修改或删除字段),而不是一个全新的状态。LangGraph 会自动将这个更新合并回主状态中。
from typing import TypedDict, List
class AgentState(TypedDict):
question: str # 初始问题
messages: List[str] # 对话历史或思考过程
tool_output: str # 工具调用的结果
final_answer: str # 最终答案
b. Nodes (节点)
节点是图中的“执行单元”,代表一个具体的计算步骤。
- 作用: 执行一项任务,比如调用 LLM、运行一段代码、调用一个 API(工具)等。
- 形式: 可以是一个普通的 Python 函数,也可以是一个 LCEL Runnable 对象。
- 输入/输出: 每个节点的输入都是当前的
State
对象,输出是一个字典,表示对State
的更新。
def call_llm(state: AgentState):
# 读取状态
question = state['question']
messages = state['messages']
# ... 调用 LLM ...
response = llm.invoke(messages)
# 返回对状态的更新
return {"messages": messages + [response]}
c. Edges (边)
边负责连接节点,定义了图的“流程控制逻辑”。
- 作用: 决定在当前节点执行完毕后,接下来应该去往哪个节点。
- 分类:
- 普通边 (Normal Edges): 无条件的连接。例如,执行完
A
节点后,总是去执行B
节点。 - 条件边 (Conditional Edges): 这是 LangGraph 强大的关键。它通过一个专门的函数来检查当前
State
,然后根据其中的信息动态地决定下一个节点的走向。这实现了分支逻辑。
- 普通边 (Normal Edges): 无条件的连接。例如,执行完
# 条件边函数
def should_continue(state: AgentState):
# 检查 LLM 的最新回复是否包含工具调用指令
last_message = state['messages'][-1]
if "tool_call" in last_message.content:
return "call_tool_node" # 返回下一个节点的名称
else:
return "generate_final_answer_node"
d. Graph (图)
这是将所有节点和边组合在一起的容器。
- 使用
StateGraph
类来构建图。 - 你需要:
- 用
State
模型初始化StateGraph
。 - 使用
add_node()
添加所有节点。 - 使用
set_entry_point()
指定从哪个节点开始。 - 使用
add_edge()
添加普通边。 - 使用
add_conditional_edges()
添加条件边。 - 最后调用
compile()
方法,将整个图编译成一个可执行的Runnable
对象。
- 用
3. 一个简单的工作流程示例
假设我们要构建一个简单的 ReAct 智能体,它的逻辑是:
- 接收问题。
- 调用 LLM 进行思考,判断是直接回答还是需要使用搜索工具。
- 如果需要搜索,则调用搜索工具。
- 将搜索结果喂给 LLM,让它基于新信息再次思考。
- 如果不需要搜索或搜索已完成,则生成最终答案。
用 LangGraph 实现这个流程:
-
定义 State:
class ReActState(TypedDict): question: str thought: str tool_name: str tool_input: str observation: str answer: str
-
定义 Nodes:
think_node
: 调用 LLM,生成思考过程(thought
)和下一步行动(调用工具或回答)。tool_node
: 根据tool_name
和tool_input
执行工具,并将结果存入observation
。answer_node
: 调用 LLM,根据已有信息生成最终答案answer
。
-
定义 Edges:
- 从
think_node
出发,有一个条件边decide_next_step
:- 如果
thought
中包含调用工具的指令,则路由到tool_node
。 - 否则,路由到
answer_node
。
- 如果
- 从
tool_node
出发,有一个普通边,总是返回到think_node
,形成循环,让 LLM 可以基于工具结果进行下一步思考。 answer_node
是一个终点,可以连接到一个特殊的END
节点。
- 从
-
构建并编译 Graph:
from langgraph.graph import StateGraph, END workflow = StateGraph(ReActState) workflow.add_node("thinker", think_node) workflow.add_node("tool", tool_node) workflow.add_node("answerer", answer_node) workflow.set_entry_point("thinker") workflow.add_conditional_edges( "thinker", decide_next_step, # 条件函数 { "call_tool": "tool", "give_answer": "answerer" } ) workflow.add_edge("tool", "thinker") # 形成循环 workflow.add_edge("answerer", END) # 结束流程 # 编译成可运行对象 app = workflow.compile() # 运行 result = app.invoke({"question": "LangGraph 是什么?"})
4. LangGraph 的主要优势总结
- 强大的控制流: 通过条件边轻松实现循环和分支,这是构建复杂智能体的基础。
- 显式的状态管理:
State
对象让多步任务中的信息传递和“记忆”变得清晰可控。 - 高度模块化和可维护性: 每个节点都是一个独立的单元,易于开发、测试和复用。整个逻辑由图的结构定义,一目了然。
- 极佳的可观测性与调试性: 由于流程被显式地定义为图,配合 LangSmith 等工具,你可以清晰地看到每一步的状态变化和节点跳转路径,极大地简化了调试过程。
- 为多智能体而生: 非常适合构建“主管-专家”模式的多智能体系统。一个主管(Supervisor)节点可以根据任务状态,将工作分派给不同的专家(Specialist)节点。
结论
LangGraph 不是要替代 LangChain LCEL,而是对它的补充和升华。
- 用 LCEL:当你需要构建一个线性的、一步到位的、无状态的数据处理管道时。
- 用 LangGraph:当你需要构建一个需要思考、决策、循环、与外部世界交互的智能体时,或者需要协调多个智能体共同完成一个复杂任务时。
它为构建真正健壮、可控、可调试的 AI 应用和智能体系统提供了一个坚实而优雅的框架。
Last updated on