Skip to content

LangGraph核心概念详解

概述

LangGraph是LangChain团队开发的基于图的Agent工作流框架,用于构建复杂的、有状态的LLM应用。本章将深入介绍LangGraph的核心概念,包括图结构、状态管理、节点和边等。

核心概念

1. 图结构(Graph)

LangGraph基于有向图结构:

  • 节点(Nodes):执行具体操作的单元
  • 边(Edges):连接节点的转移关系
  • 条件边(Conditional Edges):根据条件选择路径
  • 状态(State):在节点间传递的数据

2. 状态管理(State Management)

LangGraph的状态管理特点:

  • 全局状态:所有节点共享的状态对象
  • 状态更新:节点可以读取和更新状态
  • 状态持久化:支持状态持久化和恢复
  • 状态验证:支持状态模式验证

3. 工作流模式

LangGraph支持多种工作流模式:

  • 顺序执行:节点按顺序执行
  • 条件分支:根据条件选择执行路径
  • 并行执行:多个节点并行执行
  • 循环执行:重复执行直到满足条件

4. 人机交互

LangGraph支持人机交互模式:

  • 人工审批:在关键节点等待人工审批
  • 人工输入:在需要时获取人工输入
  • 中断恢复:支持中断和恢复执行
  • 检查点:支持执行检查点

技术原理

1. 图执行引擎

LangGraph的图执行引擎:

  • 拓扑排序:确定节点执行顺序
  • 状态传递:在节点间传递状态
  • 条件评估:评估条件边的条件
  • 循环检测:检测和处理循环

2. 状态管理机制

状态管理的核心机制:

  • 状态定义:使用TypedDict定义状态结构
  • 状态更新:节点返回状态更新
  • 状态合并:合并多个节点的状态更新
  • 状态验证:验证状态的有效性

3. 检查点机制

检查点机制的核心:

  • 检查点创建:在关键节点创建检查点
  • 检查点恢复:从检查点恢复执行
  • 检查点存储:持久化存储检查点
  • 检查点查询:查询检查点信息

核心API

1. 状态图定义

python
# 导入LangGraph的核心组件
# langgraph.graph.StateGraph:状态图,LangGraph的核心类
# langgraph.graph.END:结束节点,表示图执行结束
from langgraph.graph import StateGraph, END

# 导入Python类型提示模块
# typing.TypedDict:定义字典的类型结构
# typing.Annotated:添加类型注解
from typing import TypedDict, Annotated

# 定义状态类型
# TypedDict:定义字典的键和值的类型
# 这里定义了状态对象必须包含的字段
class State(TypedDict):
    """
    状态定义
    
    属性:
        messages (list): 消息列表,存储对话历史
        current_step (str): 当前执行步骤
        results (dict): 结果字典,存储执行结果
    """
    messages: list  # 消息列表
    current_step: str  # 当前步骤
    results: dict  # 结果字典

# 创建状态图
# StateGraph(State):创建状态图实例
# 参数:状态类型定义
# 返回值:StateGraph实例
graph = StateGraph(State)

2. 节点定义

python
# 导入LangChain的OpenAI模型
from langchain_openai import ChatOpenAI

# 导入LangChain的消息类型
# HumanMessage:用户消息
# AIMessage:AI助手消息
from langchain_core.messages import HumanMessage

# 定义节点函数
# 节点是LangGraph中执行具体操作的单元
# 每个节点函数接收当前状态,返回更新后的状态

def process_input(state: State) -> State:
    """
    处理输入节点
    
    参数:
        state (State): 当前状态对象
    
    返回值:
        State: 更新后的状态对象
    
    功能:处理用户输入,准备后续处理
    """
    # 从状态中获取消息列表
    messages = state["messages"]
    
    # 处理逻辑(这里可以添加具体的处理代码)
    # ...
    
    # 返回更新后的状态
    # 返回的字典会与现有状态合并
    return {"messages": messages, "current_step": "processed"}

def generate_response(state: State) -> State:
    """
    生成响应节点
    
    参数:
        state (State): 当前状态对象
    
    返回值:
        State: 更新后的状态对象
    
    功能:调用LLM生成响应
    """
    # 创建OpenAI模型实例
    llm = ChatOpenAI(model="gpt-4o-mini")
    
    # 调用模型生成响应
    # invoke()方法:发送请求并获取响应
    response = llm.invoke(state["messages"])
    
    # 返回更新后的状态
    # 将响应添加到消息列表
    return {"messages": [response], "current_step": "completed"}

# 添加节点到图
# add_node()方法:向图中添加节点
# 参数:
#   name:节点名称(字符串)
#   action:节点函数(可调用对象)
graph.add_node("process_input", process_input)
graph.add_node("generate_response", generate_response)

3. 边定义

python
# 添加普通边
# add_edge()方法:添加普通边(无条件转移)
# 参数:
#   start:起始节点名称
#   end:结束节点名称
# 这里表示从process_input节点转移到generate_response节点
graph.add_edge("process_input", "generate_response")

# 添加条件边
# 条件边根据条件函数的返回值选择不同的路径

def should_continue(state: State) -> str:
    """
    判断是否继续执行
    
    参数:
        state (State): 当前状态对象
    
    返回值:
        str: 决定下一步的字符串
            "continue":继续执行
            "end":结束执行
    """
    # 根据当前步骤决定下一步
    if state["current_step"] == "completed":
        return "end"  # 已完成,结束执行
    return "continue"  # 未完成,继续执行

# add_conditional_edges()方法:添加条件边
# 参数:
#   start:起始节点名称
#   condition:条件函数
#   path_map:路径映射字典
#       键:条件函数的返回值
#       值:目标节点名称
graph.add_conditional_edges(
    "generate_response",  # 起始节点
    should_continue,  # 条件函数
    {
        "continue": "process_input",  # 继续执行,回到process_input
        "end": END  # 结束执行,END是LangGraph的特殊标记
    }
)

4. 图编译和执行

python
# 设置入口点
# set_entry_point()方法:设置图的入口节点
# 入口点是图执行的起始节点
graph.set_entry_point("process_input")

# 编译图
# compile()方法:编译图,使其可执行
# 返回值:编译后的图应用
app = graph.compile()

# 执行图
# invoke()方法:执行图
# 参数:初始状态字典
# 返回值:最终状态
initial_state = {
    "messages": [HumanMessage(content="你好!")],  # 初始消息
    "current_step": "start",  # 初始步骤
    "results": {}  # 初始结果
}
result = app.invoke(initial_state)

5. 流式执行

python
# 流式执行
# stream()方法:流式执行图
# 与invoke()不同,stream()会逐步返回每个节点的执行结果
# 适用于需要实时查看执行过程的场景
for event in app.stream(initial_state):
    # event是一个字典,包含当前节点的执行结果
    # 键是节点名称,值是该节点的输出状态
    print(event)

实践指南

1. 环境准备

bash
# 安装LangGraph
pip install langgraph langchain-openai

# 设置环境变量
export OPENAI_API_KEY="your-openai-key"

2. 基础示例

python
# 导入必要的组件
from langgraph.graph import StateGraph, END  # 状态图和结束标记
from langchain_openai import ChatOpenAI  # OpenAI模型
from langchain_core.messages import HumanMessage, AIMessage  # 消息类型
from typing import TypedDict, Annotated  # 类型提示

# 定义Agent状态
class AgentState(TypedDict):
    """
    Agent状态定义
    
    属性:
        messages (list): 消息列表
        next_step (str): 下一步执行的节点
    """
    messages: list  # 消息列表
    next_step: str  # 下一步

# 定义聊天节点
def chat_node(state: AgentState) -> AgentState:
    """
    聊天节点
    
    参数:
        state (AgentState): 当前状态
    
    返回值:
        AgentState: 更新后的状态
    """
    # 创建OpenAI模型
    llm = ChatOpenAI(model="gpt-4o-mini")
    
    # 调用模型生成响应
    response = llm.invoke(state["messages"])
    
    # 返回更新后的状态
    # 将AI响应添加到消息列表
    return {
        "messages": state["messages"] + [response],  # 追加响应
        "next_step": "end"  # 设置下一步为结束
    }

# 创建图
graph = StateGraph(AgentState)

# 添加节点
graph.add_node("chat", chat_node)

# 设置边
graph.set_entry_point("chat")  # 设置入口点
graph.add_edge("chat", END)  # chat节点直接结束

# 编译和执行
app = graph.compile()

# 执行图
result = app.invoke({
    "messages": [HumanMessage(content="你好!")],  # 初始消息
    "next_step": "start"  # 初始步骤
})

# 获取最后一条消息(AI的响应)
print(result["messages"][-1].content)

3. 条件路由示例

python
# 导入必要的组件
from langgraph.graph import StateGraph, END
from typing import TypedDict, Literal

# 定义路由器状态
class RouterState(TypedDict):
    """
    路由器状态定义
    
    属性:
        input (str): 用户输入
        route (str): 路由决定(weather/news/general)
        output (str): 输出结果
    """
    input: str  # 用户输入
    route: str  # 路由决定
    output: str  # 输出结果

# 定义分类节点
def classify_input(state: RouterState) -> RouterState:
    """
    分类输入节点
    
    参数:
        state (RouterState): 当前状态
    
    返回值:
        RouterState: 更新后的状态
    
    功能:根据用户输入内容决定路由
    """
    input_text = state["input"].lower()  # 转换为小写
    
    # 根据关键词决定路由
    if "天气" in input_text:
        route = "weather"  # 天气查询
    elif "新闻" in input_text:
        route = "news"  # 新闻查询
    else:
        route = "general"  # 一般查询
    
    return {"input": state["input"], "route": route, "output": ""}

# 定义处理节点
def handle_weather(state: RouterState) -> RouterState:
    """处理天气查询"""
    return {"input": state["input"], "route": state["route"], "output": "天气查询结果"}

def handle_news(state: RouterState) -> RouterState:
    """处理新闻查询"""
    return {"input": state["input"], "route": state["route"], "output": "新闻查询结果"}

def handle_general(state: RouterState) -> RouterState:
    """处理一般查询"""
    return {"input": state["input"], "route": state["route"], "output": "一般查询结果"}

# 创建图
graph = StateGraph(RouterState)

# 添加节点
graph.add_node("classify", classify_input)  # 分类节点
graph.add_node("weather", handle_weather)  # 天气处理节点
graph.add_node("news", handle_news)  # 新闻处理节点
graph.add_node("general", handle_general)  # 一般处理节点

# 设置条件边
def route_function(state: RouterState) -> str:
    """
    路由函数
    
    参数:
        state (RouterState): 当前状态
    
    返回值:
        str: 路由决定
    """
    return state["route"]

# 添加条件边
# add_conditional_edges():根据条件选择路径
graph.add_conditional_edges(
    "classify",  # 起始节点
    route_function,  # 路由函数
    {
        "weather": "weather",  # 天气路由
        "news": "news",  # 新闻路由
        "general": "general"  # 一般路由
    }
)

# 设置出口
graph.add_edge("weather", END)  # 天气处理后结束
graph.add_edge("news", END)  # 新闻处理后结束
graph.add_edge("general", END)  # 一般处理后结束

# 编译和执行
graph.set_entry_point("classify")  # 设置入口点
app = graph.compile()

# 执行图
result = app.invoke({"input": "北京天气怎么样?", "route": "", "output": ""})
print(result["output"])

4. 人机交互示例

python
# 导入必要的组件
from langgraph.graph import StateGraph, END
from langgraph.checkpoint.memory import MemorySaver  # 内存检查点
from typing import TypedDict

# 定义人机交互状态
class HumanLoopState(TypedDict):
    """
    人机交互状态定义
    
    属性:
        messages (list): 消息列表
        human_input (str): 人工输入
        approved (bool): 是否已批准
    """
    messages: list  # 消息列表
    human_input: str  # 人工输入
    approved: bool  # 是否已批准

# 定义节点
def process_node(state: HumanLoopState) -> HumanLoopState:
    """
    处理节点
    
    参数:
        state (HumanLoopState): 当前状态
    
    返回值:
        HumanLoopState: 更新后的状态
    """
    return {
        "messages": state["messages"],
        "human_input": "",
        "approved": False
    }

def human_approval_node(state: HumanLoopState) -> HumanLoopState:
    """
    人工审批节点
    
    参数:
        state (HumanLoopState): 当前状态
    
    返回值:
        HumanLoopState: 更新后的状态
    
    功能:等待人工审批
    """
    # 这里可以集成人工审批界面
    # 例如:Web界面、命令行输入等
    print("需要人工审批")
    return {
        "messages": state["messages"],
        "human_input": "approved",  # 模拟人工输入
        "approved": True
    }

def final_node(state: HumanLoopState) -> HumanLoopState:
    """
    最终节点
    
    参数:
        state (HumanLoopState): 当前状态
    
    返回值:
        HumanLoopState: 更新后的状态
    """
    return {
        "messages": state["messages"],
        "human_input": state["human_input"],
        "approved": True
    }

# 创建图
graph = StateGraph(HumanLoopState)

# 添加节点
graph.add_node("process", process_node)
graph.add_node("human_approval", human_approval_node)
graph.add_node("final", final_node)

# 设置边
graph.set_entry_point("process")  # 入口点
graph.add_edge("process", "human_approval")  # 处理后进入人工审批
graph.add_edge("human_approval", "final")  # 审批后进入最终节点
graph.add_edge("final", END)  # 最终节点后结束

# 使用检查点
# MemorySaver():内存检查点,用于保存和恢复状态
checkpointer = MemorySaver()

# 编译图,启用检查点和中断
# checkpointer:检查点实例
# interrupt_before:在指定节点前中断执行
app = graph.compile(checkpointer=checkpointer, interrupt_before=["human_approval"])

# 执行配置
# config:配置字典,包含thread_id用于标识执行线程
config = {"configurable": {"thread_id": "1"}}

# 执行图
result = app.invoke({
    "messages": [],
    "human_input": "",
    "approved": False
}, config)

最佳实践

1. 图设计原则

  • 模块化设计:每个节点只做一件事
  • 清晰的状态定义:明确定义状态结构
  • 合理的错误处理:添加错误处理节点
  • 可测试性:设计可测试的节点

2. 性能优化

  • 并行执行:独立节点并行执行
  • 状态优化:减少状态传递的数据量
  • 缓存机制:缓存中间结果
  • 异步执行:使用异步节点提升性能

3. 调试技巧

  • 可视化图:使用可视化工具查看图结构
  • 日志记录:记录每个节点的执行日志
  • 断点调试:在关键节点设置断点
  • 状态检查:检查每个节点的状态变化

常见问题

1. 状态管理问题

  • 状态不一致:确保所有节点正确更新状态
  • 状态丢失:使用检查点持久化状态
  • 状态验证:添加状态验证逻辑

2. 执行流程问题

  • 死循环:设置最大执行次数
  • 条件判断错误:仔细检查条件函数
  • 节点执行失败:添加错误处理和重试机制

3. 性能问题

  • 执行慢:优化节点函数,减少不必要的计算
  • 内存占用高:优化状态结构,减少数据传递
  • 并发限制:使用异步执行提升并发能力

下一步学习