在上一篇文章《寻找 Agent 时代的 Kubernetes》中,我们探讨了单纯的线性“Chain”架构为什么无法满足复杂的 Agent 业务场景,并得出了一个结论:我们需要基于“图”与“状态机”的架构来重塑 Agent 的运行编排。
今天,我们将聚焦于目前在这条道路上走得最远、社区生态最繁荣的框架之一 —— LangGraph。这篇文章不仅会带你理解它的核心理念,还会附带一个可以直接跑起来的极简 Demo 代码,让你在脑海里建立起它运行时的“画面感”。
为什么我们需要“图 (Graph)”?
传统的 LLM 开发逻辑(例如早期的 LangChain)是一条单向的流水线:Input -> Prompt -> LLM -> Output。
但在真实的 Agent 场景中,模型经常需要:
- 调用工具(如果失败了还要捕获异常重试)。
- 多步规划(调用工具 A 获取数据后,再决定是否调用工具 B)。
- 自我反思(评估自己生成的答案,不满意则重写)。
这些动作本质上是一个带有循环(Loop)和条件分支(If-Else)的有向图结构。LangGraph 的核心使命,就是为你提供一套原生的 API,用来定义这个图中的节点、边和状态流转。
LangGraph 的三大核心积木
在使用 LangGraph 之前,你需要彻底理解它的三个核心概念。可以把它想象成一个接力赛跑:
-
State(状态黑板): 你可以把 State 想象成全图共享的一块“黑板”(通常是一个 Python 的
TypedDict或Pydantic模型)。每当执行到一个节点时,节点都会从黑板上读取当前的数据,执行完毕后,再把新的数据写回黑板。 -
Nodes(执行节点): 节点就是具体的执行逻辑。在一个最经典的 Agent 图中,通常只有两个核心节点:
- Agent Node:负责调用大模型,根据黑板上的信息决定下一步是回复用户,还是输出函数调用(Function Calling)的指令。
- Tool Node:负责真正执行具体的外部工具(比如查询天气、执行代码),并把执行结果写回黑板。
-
Edges(边)与 Conditional Edges(条件边): 边决定了控制流。
- 普通边是固定的路由:比如
Tool Node执行完后,必须无脑回到Agent Node,让模型看看工具的返回结果。 - 条件边是灵魂所在:比如
Agent Node执行完后,到底是结束运行(走往__END__节点),还是去执行工具(走往Tool Node)?这就需要通过代码逻辑动态判断。
- 普通边是固定的路由:比如
极简实战 Demo:带反思的循环 Agent
百闻不如一见,让我们来看一段极其简化的 Python 代码,它构建了一个最经典的 Agent -> Tools -> Agent 循环状态机。
from typing import TypedDict, Annotated
from langgraph.graph import StateGraph, END
from langchain_core.messages import AnyMessage, HumanMessage, ToolMessage
# 1. 定义 State 黑板(保存消息历史)
class AgentState(TypedDict):
messages: list[AnyMessage]
# 2. 定义执行节点 Nodes
def agent_node(state: AgentState):
"""大模型节点:读取消息历史,生成回复或工具调用指令"""
print("🧠 模型正在思考...")
# 假设这里调用了 llm.invoke(state['messages'])
# 我们模拟模型决定调用 "get_weather" 工具
mock_llm_response = {"type": "tool_call", "tool": "get_weather", "args": {"city": "Beijing"}}
return {"messages": [mock_llm_response]}
def tool_node(state: AgentState):
"""工具节点:执行具体的函数"""
print("🛠️ 正在执行工具...")
last_message = state['messages'][-1]
# 模拟执行查询并返回结果
mock_tool_result = f"Weather in {last_message['args']['city']} is Sunny!"
return {"messages": [ToolMessage(content=mock_tool_result)]}
# 3. 定义条件路由
def should_continue(state: AgentState):
"""条件边判断:模型是想调用工具,还是想结束对话?"""
last_message = state['messages'][-1]
if last_message.get("type") == "tool_call":
return "continue" # 路由到 tool_node
return "end" # 路由到 END
# 4. 组装图 Graph
workflow = StateGraph(AgentState)
# 添加节点
workflow.add_node("agent", agent_node)
workflow.add_node("tools", tool_node)
# 设置入口点
workflow.set_entry_point("agent")
# 添加条件边:agent 节点之后,由 should_continue 决定去哪
workflow.add_conditional_edges(
"agent",
should_continue,
{
"continue": "tools",
"end": END
}
)
# 添加普通边:tools 节点执行完后,必须回到 agent 重新思考
workflow.add_edge("tools", "agent")
# 编译并运行
app = workflow.compile()
print("🎉 图已就绪!")
仔细观察这幅“图”的流转逻辑:
入口是 agent,它思考完后,触发 should_continue 检查。如果是调用工具,就会进入 tools 节点;tools 节点执行完毕后,强制(通过 add_edge)回到 agent 节点。agent 此时看到了工具的返回结果,它会再次思考,这一次它得出了最终答案,should_continue 检查返回 "end",图流转到内置的 __END__ 节点,执行结束!
这种架构彻底解决了模型“一次性输出”的局限,让模型在一个安全的沙盒轨道内,能够进行多轮的反思和迭代。
LangGraph 的杀手锏功能
除了基础的循环,LangGraph 还提供了极其强大的工程特性:
- 中断与人机协同 (Human-in-the-loop):在企业级应用中,我们不敢让 Agent 直接执行“删除数据库”的操作。LangGraph 允许你在特定的图节点设置断点,Agent 运行到这里会暂停,等待人类审批通过后才继续执行。
- 时间旅行与长程记忆:通过引入
Checkpointer,LangGraph 可以把每一次节点流转的状态都持久化到数据库中。你可以随时“时光倒流”,让 Agent 恢复到之前的某个状态重新执行。
总结
引入状态机(State Machine)的概念,是 Agent 工程化发展的一大步。LangGraph 提供了一套相对底层的 API,虽然起步时的概念学习曲线比简单的 Prompt 调用要陡峭一些,但它赋予了开发者真正掌控“智能体命脉”的能力。
赶快在你的本地跑起上面的 Demo 吧!让你的大语言模型在可控的轨道上狂奔!