从链到图:LangGraph 入门基础全解析

本教程基于 LangGraph 开发实战教程结合官方文档与实践经验,带你从零搭建 LangGraph 的核心认知体系。

为什么要学 LangGraph?

1.1 从 LangChain 到 LangGraph:一次思维的跃迁

在接触 LangGraph 之前,大多数开发者已经熟悉了 LangChain。LangChain 通过 LCEL(LangChain Expression Language)提供了一种极其简洁的方式来串联 LLM 调用:

python 复制代码
# LangChain 的经典写法:线性流水线
chain = prompt_template | model | output_parser
result = chain.invoke({"topic": "人工智能"})

这种写法优雅、直观,但它隐含了一个前提:流程是固定的、单向的、无环的。数据从起点流向终点,不会回头,也不会在中途分叉后再汇合。

然而,现实世界的 AI 应用远比这复杂:

  • 一个智能客服可能需要先判断用户意图,然后根据意图走不同的流程分支;
  • 一个代码生成 Agent 可能需要"写代码 → 跑测试 → 发现错误 → 修改代码 → 再跑测试"这样的循环;
  • 一个金融审批系统可能需要在关键节点暂停,等待人工审核员的确认。

这些场景需要的不是一个线性管道 ,而是一个有状态、可循环、可中断的执行引擎------这正是 LangGraph 诞生的原因。

一句话总结 :LangChain 让你快速跑起来 ,LangGraph 让你跑得复杂、跑得可控、跑得持久。两者不是替代关系,而是互补关系------你用 LangChain 的组件(模型、工具、检索器)作为积木,用 LangGraph 作为把这些积木粘合在一起的"蓝图与施工队"。

1.3 什么时候该用 LangGraph?

如果你遇到了以下任何一个问题,就该考虑从 LangChain 升级到 LangGraph:

  1. 你的 Agent 需要"反思":写完代码后自己检查,发现 bug 后重新修改。
  2. 你的流程需要"分叉":根据用户的输入,走完全不同的处理路径。
  3. 你的任务需要"暂停等人":在某个节点停下来,等领导审批通过后再继续。
  4. 你的系统需要"断点续跑":服务器重启后,Agent 能从上次中断的地方继续,而不是从头再来。
  5. 你有多个 Agent 需要协作:一个负责规划,一个负责执行,一个负责质检。

第二部分:LangGraph 三大核心要素

LangGraph 的所有能力都建立在三个核心概念之上:State(状态)Node(节点)Edge(边)。理解这三者,你就掌握了 LangGraph 的 80%。

2.1 State(状态):图流转的"公共黑板"

2.1.1 什么是 State?

State 是 LangGraph 的灵魂。它是一份全局共享的数据结构 ,图中的每一个节点都可以读取它、修改它。你可以把 State 想象成一块挂在墙上的公共黑板

  • 所有人(节点)都能看到黑板上写了什么;
  • 每个人都可以在上面添加新内容;
  • 后一个人能看到前一个人写下的所有内容。

2.1.2 如何定义 State?

在 LangGraph 中,State 通常用 Python 的 TypedDict 来定义:

python 复制代码
from typing import TypedDict, List, Annotated
from langgraph.graph.message import add_messages
import operator

# 定义一个 State
class AgentState(TypedDict):
    # 消息列表:使用 add_messages Reducer,实现追加而非覆盖
    messages: Annotated[List, add_messages]
    
    # 步骤计数器:使用 operator.add,每次累加
    step: Annotated[int, operator.add]
    
    # 用户意图:普通字段,会被直接覆盖
    intent: str
    
    # 是否已完成:布尔标志
    is_complete: bool

Annotated是什么? Annotated是 Python 标准库 typing模块里的一个工具,它的作用是给类型提示附加额外的元信息。 对于 Python 本身来说,Annotated几乎不做任何事情------它只是给类型标注"贴了个标签"。但 LangGraph 会读取这个标签,从中提取出第二个参数(add_messages),把它当作这个字段的更新策略

python 复制代码
from typing import Annotated

# 语法:Annotated[实际类型, 额外信息1, 额外信息2, ...]
x: Annotated[int, "这是一个年龄字段"]

2.1.3 Reducer(归约器)

Reducer(归约器)是一个函数,它定义了"当多个节点都想修改同一个 State 字段时,如何把这些修改合并成一个最终结果"。

或者说:Reducer 决定了 State 中某个字段的更新策略------是覆盖?是追加?是累加?还是其他自定义规则?

这是 LangGraph 新手最容易踩坑的地方。默认情况下,节点返回的状态会覆盖旧状态 。但在很多场景中,我们需要的是追加累加,而不是覆盖。

比如对话消息列表:如果每个节点都返回 {"messages": [new_msg]},默认行为会让旧消息消失。这时候就需要 Reducer 来救命。

python 复制代码
# 不使用 Reducer:覆盖行为
state["messages"] = ["你好"]  # 第一次设置
state["messages"] = ["世界"]  # 第二次设置 → 覆盖了"你好"

# 使用 add_messages Reducer:追加行为
state["messages"] = ["你好"]  # 第一次设置
state["messages"] = ["世界"]  # 第二次设置 → 结果是 ["你好", "世界"]

LangGraph 内置了几种常用的 Reducer:

  • add_messages:消息列表追加(最常用)
  • operator.add:数值累加或列表拼接
  • operator.set:集合合并

你也可以自定义 Reducer 函数,实现任何你想要的合并逻辑。

2.2 Node(节点):图里的"执行工人"

2.2.1 什么是 Node?

Node 就是图中的一个个执行单元 。每个节点都是一个普通的 Python 函数,它接收当前的 State,执行一些业务逻辑,然后返回一个状态更新字典

python 复制代码
# 一个最简单的节点函数
def my_node(state: AgentState) -> dict:
    """
    参数 state:当前的全局状态(只读+可写)
    返回值 dict:要更新的状态字段
    """
    # 读取当前状态
    current_step = state.get("step", 0)
    last_message = state["messages"][-1] if state["messages"] else ""
    
    # 执行业务逻辑(比如调用 LLM)
    # response = llm.invoke(last_message)
    
    # 返回要更新的状态
    return {
        "messages": ["这是节点处理后的回复"],
        "step": 1,  # 由于 Reducer 是 operator.add,实际效果是 step += 1
    }

2.2.2 节点的设计原则

  1. 无副作用:理想的节点应该只通过返回值修改 State,不要直接操作文件、数据库或全局变量。这样 LangGraph 才能正确地进行状态回溯和断点恢复。
  2. 单一职责:一个节点只做一件事。比如"调用 LLM"是一个节点,"格式化输出"是另一个节点。
  3. 可观测:节点的输入(State)和输出(State 更新)都应该清晰可辨。

2.2.3 将节点添加到图中

python 复制代码
from langgraph.graph import StateGraph

# 创建图
workflow = StateGraph(AgentState)

# 添加节点
workflow.add_node("llm_call", my_node)        # 节点名称 + 节点函数
workflow.add_node("tool_execute", tool_node)   # 可以有多个节点
workflow.add_node("format_output", format_node)

2.3 Edge(边):控制流的"神经脉络"

如果说 Node 是图中的"名词",那么 Edge 就是图中的"动词"------它决定了节点之间的连接关系和执行顺序

2.3.1 普通边(Normal Edge)

普通边定义的是确定性的先后顺序:A 执行完之后,一定执行 B。

python 复制代码
# 从 START 到 NodeA(入口)
workflow.add_edge(START, "llm_call")

# 从 NodeA 到 NodeB(顺序执行)
workflow.add_edge("llm_call", "tool_execute")

# 从 NodeB 到 END(出口)
workflow.add_edge("tool_execute", END)

这段代码的含义是:START → llm_call → tool_execute → END,一个标准的线性流程。

2.3.2 条件边(Conditional Edge)

条件边是 LangGraph 真正的杀手锏。它允许你根据当前 State 的内容,动态决定下一步去哪一个节点

python 复制代码
# 定义路由函数:根据状态决定下一步
def router_function(state: AgentState) -> str:
    """返回值是目标节点的名称"""
    last_message = state["messages"][-1].content if state["messages"] else ""
    
    # 判断逻辑
    if "需要查资料" in last_message:
        return "search_tool"       # 去搜索节点
    elif "需要计算" in last_message:
        return "calculator_tool"   # 去计算节点
    elif "已完成" in last_message:
        return END                 # 结束
    else:
        return "llm_call"          # 继续对话(循环回 LLM 节点)

# 添加条件边
workflow.add_conditional_edges(
    "llm_call",                    # 源节点
    router_function,               # 路由函数
    {                              # 路径映射(可选,用于校验)
        "search_tool": "search_tool",
        "calculator_tool": "calculator_tool",
        END: END,
        "llm_call": "llm_call",
    }
)

条件边的威力 :它让图具备了动态决策能力。同样的输入,可能走完全不同的路径;同一个节点,可以被反复访问(形成循环)。

2.3.3 START 与 END

LangGraph 预定义了两个特殊节点:

  • START:图的入口点。所有从外部 invoke() 传入的数据,都会首先到达 START 节点。
  • END:图的终止点。当执行流到达 END 时,图执行完毕,返回最终 State。

注意:你必须至少有一条路径从 START 出发,也必须至少有一条路径到达 END,否则编译会报错。

下面展示了从零构建一张可运行图的完整流程。

python 复制代码
from typing import TypedDict, List, Annotated
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, AIMessage
import operator

# ===== Step 1: 定义 State =====
class AgentState(TypedDict):
    messages: Annotated[List, add_messages]  # 消息历史(追加)
    reflection_count: Annotated[int, operator.add]  # 反思次数(累加)
    max_reflections: int  # 最大反思次数(覆盖)

# ===== Step 2: 定义 Node 函数 =====
llm = ChatOpenAI(model="gpt-4o-mini")

def call_model(state: AgentState) -> dict:
    """调用 LLM 生成回复"""
    response = llm.invoke(state["messages"])
    return {"messages": [response]}

def should_continue(state: AgentState) -> str:
    """条件路由:判断是否需要继续反思"""
    # 如果反思次数已达上限,结束
    if state["reflection_count"] >= state.get("max_reflections", 3):
        return END
    # 如果最后一条消息来自 AI,检查是否需要反思
    last_message = state["messages"][-1]
    if isinstance(last_message, AIMessage):
        # 假设如果 AI 回复中包含"让我再想想",则需要反思
        if "让我再想想" in last_message.content:
            return "reflect"
    return END

def reflect_node(state: AgentState) -> dict:
    """反思节点:对之前的回答进行自我审查"""
    # 构造反思提示
    reflection_prompt = f"""
    请对你之前的回答进行反思,找出可能的不足或遗漏。
    如果需要改进,请给出更好的版本。
    原始回答:{state['messages'][-1].content}
    """
    response = llm.invoke([HumanMessage(content=reflection_prompt)])
    return {"messages": [response], "reflection_count": 1}

# ===== Step 3: 构建图 =====
workflow = StateGraph(AgentState)

# 添加节点
workflow.add_node("call_model", call_model)
workflow.add_node("reflect", reflect_node)

# 添加边
workflow.add_edge(START, "call_model")           # 入口 → 调用模型
workflow.add_conditional_edges(                   # 模型调用后,根据条件路由
    "call_model",
    should_continue,
    {"reflect": "reflect", END: END}
)
workflow.add_edge("reflect", "call_model")        # 反思后回到模型调用(形成循环)

# ===== Step 4: 编译图 =====
app = workflow.compile()

# ===== Step 5: 运行图 =====
initial_state = {
    "messages": [HumanMessage(content="帮我写一首关于秋天的诗,要有意境")],
    "reflection_count": 0,
    "max_reflections": 2,
}

result = app.invoke(initial_state)
print(result["messages"][-1].content)
相关推荐
新知图书1 小时前
11.3 详细实现与核心配置(作业批改智能体开发)
人工智能·agent·ai agent·智能体·扣子
皮皮林5511 小时前
开源实力派,给本地 AI 装上深度调研能力,一张 3090 跑到 95.7 分!
后端
小林ixn2 小时前
从 ??= 到 onKeyDown:一个 React 组件的“自我修养”
前端·javascript·react.js
努力的小雨2 小时前
把 Windows 桌面图标做成一个会自己运转的小世界
后端
REDcker2 小时前
显示分辨率标准对照详解
前端·网络·分辨率·显示·屏幕
深度研习笔记2 小时前
OpenCV工业视觉实战11|多线程解耦+视频流稳流+推理加速,彻底解决卡顿阻塞,实现毫秒级工业实时检测
人工智能·opencv·计算机视觉
delishcomcn2 小时前
AI视觉识别+分切算法:电化铝缺陷检测与裁切一体化解锁
人工智能·算法
এ慕ོ冬℘゜2 小时前
前端实战:jQuery 多条件联合搜索(标题模糊查询 + 日历时间段筛选)
前端·javascript·jquery
触底反弹3 小时前
深入理解大模型采样:Temperature、Top-K、Top-P 的原理与实战
人工智能·算法·面试