LangGraph 从零到一:用图状态机构建你的第一个 AI Agent

5 阅读6分钟

LangGraph 是 LangChain 团队推出的有状态、多 Actor 应用框架,专为构建循环、有记忆的 LLM Agent 而设计。本文带你从核心概念出发,手把手搭建一个会思考、能工具调用的 AI 助手。


目录

  1. 为什么需要 LangGraph?
  2. 核心概念:图、节点、边、状态
  3. 环境安装与准备
  4. Hello World:最简单的 Graph
  5. 状态管理:让 Agent 有记忆
  6. 条件路由:让 Agent 做决策
  7. 实战:带工具调用的 ReAct Agent
  8. 人工介入(Human-in-the-loop)
  9. 总结与进阶方向

1. 为什么需要 LangGraph?

普通的 LangChain 链是单向的——输入进去,输出出来,一气呵成。但真实世界的 AI Agent 需要:

能力说明
🔄 循环执行Agent 需要反复调用工具,直到任务完成,而不是一次性输出
🧠 持久状态跨多步骤甚至跨多轮对话记住上下文信息
🔀 条件分支根据 LLM 输出动态决定下一步走哪条路径
👁️ 人工介入在关键节点暂停,让人类确认或修改,再继续执行

LangGraph 用 有向图(Directed Graph) 的思路解决了这些问题。每个节点是一个处理步骤,边决定控制流,整个图维护一份共享状态,支持循环,天然适合 Agent 场景。

能力LangChain ChainLangGraph
循环 / 重试✗ 不支持✓ 原生支持
条件分支有限支持✓ 完整路由系统
持久化状态✓ Checkpoint 机制
人工介入✓ interrupt_before/after
流式输出部分✓ 原生 streaming

2. 核心概念:图、节点、边、状态

LangGraph 的世界由四个要素构成,理解它们是一切的基础:

  • Graph(图) :整个 Agent 的控制流骨架,由节点和边组成,类似状态机。
  • Node(节点) :一个普通 Python 函数,接收 State 返回 State 的更新,是实际的计算单元。
  • Edge(边) :连接节点的路径。普通边固定跳转,条件边根据状态动态选择下一节点。
  • State(状态) :贯穿整个图执行的共享数据结构,每个节点读取并更新它的一部分。
START → [节点 A] → (条件路由) → [节点 B: 工具调用] → END
                               ↘ [节点 C: 生成回复] → END

        ↑__________ 共享 State 贯穿整个执行过程 __________↑

3. 环境安装与准备

ℹ️ 需要 Python 3.9+,建议使用虚拟环境(venv 或 conda)隔离依赖。

# 安装核心依赖
pip install langgraph langchain-openai langchain-core

# 可选:可视化依赖
pip install grandalf

配置 API Key:

import os
os.environ["OPENAI_API_KEY"] = "sk-..."

# 国内用户可以配置代理或使用兼容模型
# 例如 DeepSeek、Qwen 等 OpenAI 兼容接口均可

4. Hello World:最简单的 Graph

from langgraph.graph import StateGraph, START, END
from typing import TypedDict

# 1️⃣  定义状态结构
class MyState(TypedDict):
    message: str
    result: str

# 2️⃣  定义节点函数(输入 State,返回更新字典)
def greet(state: MyState) -> dict:
    name = state["message"]
    return {"result": f"你好,{name}!欢迎使用 LangGraph 🎉"}

# 3️⃣  构建图
builder = StateGraph(MyState)

builder.add_node("greet", greet)          # 注册节点
builder.add_edge(START, "greet")          # 起始边
builder.add_edge("greet", END)            # 终止边

# 4️⃣  编译图(生成可执行 Runnable)
graph = builder.compile()

# 5️⃣  运行!
result = graph.invoke({"message": "世界"})
print(result)

输出:

{'message': '世界', 'result': '你好,世界!欢迎使用 LangGraph 🎉'}

💡 关键点:节点函数只返回需要更新的字段,LangGraph 会自动合并到现有 State 中,不会覆盖未修改的字段。


5. 状态管理:让 Agent 有记忆

真实 Agent 需要维护对话历史。LangGraph 通过 Annotated + operator.add 实现消息的追加语义:

from typing import Annotated
import operator
from langchain_core.messages import BaseMessage, HumanMessage, AIMessage

class ChatState(TypedDict):
    # Annotated + operator.add 表示"列表追加"而非"覆盖"
    messages: Annotated[list[BaseMessage], operator.add]

def chat_node(state: ChatState) -> dict:
    from langchain_openai import ChatOpenAI
    llm = ChatOpenAI(model="gpt-4o-mini")
    response = llm.invoke(state["messages"])
    return {"messages": [response]}   # 返回列表,自动追加

builder = StateGraph(ChatState)
builder.add_node("chat", chat_node)
builder.add_edge(START, "chat")
builder.add_edge("chat", END)
graph = builder.compile()

# 多轮对话
state = {"messages": [HumanMessage("你好!")]}
state = graph.invoke(state)

state["messages"].append(HumanMessage("刚才我说了什么?"))
state = graph.invoke(state)   # 携带完整历史重新调用

⚠️ 注意 reducer 语义operator.add 对列表执行追加操作。如果你的节点返回 {"messages": [new_msg]},新消息会被追加到历史中,而不是替换整个列表——这是 LangGraph 状态更新的核心机制。


6. 条件路由:让 Agent 做决策

条件边是 LangGraph 最强大的功能之一,它让图可以根据当前状态动态选择执行路径:

# 路由函数:返回下一个节点的名称
def should_use_tool(state: ChatState) -> str:
    last_msg = state["messages"][-1]
    # 如果 LLM 请求调用工具,跳到 tools 节点
    if hasattr(last_msg, "tool_calls") and last_msg.tool_calls:
        return "tools"
    # 否则直接结束
    return END

builder.add_conditional_edges(
    "agent",          # 源节点
    should_use_tool,  # 路由函数
    {                 # 路由映射(可选,增加可读性)
        "tools": "tools",
        END: END
    }
)

执行流程:

START → [agent: 调用LLM] → 路由判断
                               ├─ 有工具调用 → [tools: 执行工具] → 循环回 agent
                               └─ 无工具调用 → END

7. 实战:带工具调用的 ReAct Agent

ReAct(Reason + Act)是目前最流行的 Agent 模式。下面完整实现一个会调用工具的 Agent:

Step 1:定义工具

from langchain_core.tools import tool
from langchain_openai import ChatOpenAI

@tool
def search_web(query: str) -> str:
    """在网络上搜索最新信息"""
    # 实际使用 Tavily / SerpAPI 等搜索服务
    return f"搜索结果:关于 '{query}' 的最新资讯..."

@tool
def calculator(expression: str) -> str:
    """计算数学表达式"""
    try:
        return str(eval(expression))
    except:
        return "计算错误"

tools = [search_web, calculator]
llm = ChatOpenAI(model="gpt-4o-mini").bind_tools(tools)

Step 2:定义 Agent 节点和 Tool 节点

from langgraph.prebuilt import ToolNode

def agent_node(state: ChatState) -> dict:
    """LLM 思考并决定是否调用工具"""
    response = llm.invoke(state["messages"])
    return {"messages": [response]}

# ToolNode 会自动执行所有工具调用并返回 ToolMessage
tool_node = ToolNode(tools)

Step 3:组装图并运行

from langgraph.graph import StateGraph, START, END

def should_continue(state: ChatState) -> str:
    last = state["messages"][-1]
    if hasattr(last, "tool_calls") and last.tool_calls:
        return "tools"
    return END

builder = StateGraph(ChatState)
builder.add_node("agent", agent_node)
builder.add_node("tools", tool_node)

builder.add_edge(START, "agent")
builder.add_conditional_edges("agent", should_continue)
builder.add_edge("tools", "agent")  # 工具结果返回 agent 继续思考

react_agent = builder.compile()

# 运行 Agent
result = react_agent.invoke({
    "messages": [HumanMessage("今天 1234 * 5678 等于多少?")]
})

for msg in result["messages"]:
    print(f"[{type(msg).__name__}]", msg.content[:100])

运行轨迹:

[HumanMessage]  今天 1234 * 5678 等于多少?
[AIMessage]     (调用工具: calculator)
[ToolMessage]   7006652
[AIMessage]     1234 × 5678 = 7,006,652 🎯

8. 人工介入(Human-in-the-loop)

生产级 Agent 通常需要在执行敏感操作前让人类确认。LangGraph 通过 Checkpointer + interrupt 实现:

from langgraph.checkpoint.memory import MemorySaver

# 1️⃣  添加 checkpointer(内存版,生产用 PostgresSaver / RedisSaver)
memory = MemorySaver()
agent_with_hitl = builder.compile(
    checkpointer=memory,
    interrupt_before=["tools"]   # 在执行工具前暂停!
)

config = {"configurable": {"thread_id": "user-42"}}

# 2️⃣  第一次 invoke:执行到 tools 节点前暂停
snapshot = agent_with_hitl.invoke(
    {"messages": [HumanMessage("搜索最新 AI 新闻")]},
    config
)

# 此时 Agent 已暂停,等待人类确认
print("Agent 准备调用工具,请确认...")
print(snapshot["messages"][-1].tool_calls)

# 3️⃣  人类审核后,传入 None 继续执行
final = agent_with_hitl.invoke(None, config)   # 从断点恢复
print(final["messages"][-1].content)

💡 Checkpoint 原理:每次执行前后,LangGraph 都会将完整 State 序列化保存到 Checkpointer 中。thread_id 是会话标识符,允许同一个 Agent 服务多个独立用户,实现真正的多会话持久化。


9. 总结与进阶方向

本文从零开始走了一遍 LangGraph 的核心路径:

已掌握:

  • ✅ StateGraph 基本用法
  • ✅ 节点 / 边定义
  • ✅ 条件路由
  • ✅ 多轮对话状态管理
  • ✅ 工具调用(ReAct 模式)
  • ✅ 人工介入(Human-in-the-loop)

进阶方向:

  • 🚀 多 Agent 协作(Supervisor 模式)
  • 🚀 子图(Subgraph)拆分复杂逻辑
  • 🚀 流式输出(stream / astream)
  • 🚀 持久化 Checkpointer(PostgreSQL / Redis)
  • 🚀 LangGraph Platform 生产部署

LangGraph 的设计哲学是把复杂性还给开发者:它不替你决定 Agent 应该怎么跑,而是给你一套清晰的原语来精确控制每一个执行步骤。这正是它在生产级 Agent 中越来越受欢迎的原因。

📚 推荐资源