← 返回上一页

第10章: LangGraph 工作流编排与智能体

从线性链路到复杂工作流,构建企业级 AI 应用架构

📖 10.1 什么是 LangGraph?

一句话理解

LangGraph 是 LangChain 官方推出的「有状态、可循环」的工作流编排框架。它把 AI 应用建模为一张「图」(Graph),图上的节点是处理步骤,边是流转规则,状态在节点之间自动传递。

它解决什么问题?

虽然 LangChain 的 LCEL 支持分支逻辑(RunnableBranch)和条件路由,但在处理复杂的有状态工作流时仍有局限:

LangGraph 正是为解决这些问题而生,它提供了统一的图结构编排、自动状态管理、内置循环/重试、人工介入点等企业级特性。

🔧 LangChain LCEL

输入 → 节点A → RunnableBranch
              ├→ 分支B → 输出
              └→ 分支C → 输出

✅ 支持分支和条件路由
⚠️ 复杂循环和状态管理需手动编排

✅ LangGraph(图结构)

         ┌→ 节点B → 节点D ─┐
输入 → 节点A ─┤                  ├→ 输出
         └→ 节点C ←─────┘(循环)

✅ 内置循环、重试、并行、人工介入
✅ 自动状态管理、可视化调试

安装

text
# 安装 LangGraph(它会自动安装 langchain-core 依赖)
pip install langgraph

# 如果使用通义千问模型
pip install dashscope langchain-community

核心类一览

整个 LangGraph 只需要掌握以下几个核心类,就能构建大部分工作流:

类 / 函数 作用 来源
StateGraph 创建一张带状态的图,是一切的起点 langgraph.graph
END 特殊常量,表示图的终点 langgraph.graph
START 特殊常量,表示图的起点(新版) langgraph.graph
MemorySaver 内存级别的状态持久化(检查点) langgraph.checkpoint.memory
Annotated 配合 reducer 定义状态字段的合并策略 typing
create_react_agent 快速创建一个 ReAct 模式的智能体(⚠️ 最新版本) langgraph.prebuilt

✅ Agent 创建最新用法(LangGraph V1.0+)

✅ 最新推荐(V1.0+):from langchain.agents import create_agent

⚠️ 即将废弃:from langgraph.prebuilt import create_react_agent(V1.0 已移至 langchain.agents)
已废弃:from langchain.agents import create_react_agent
已废弃:from langchain_classic.agents import create_react_agent

💡 完整可执行示例(LangGraph V1.0+ 最新 API):
python
"""
LangGraph V1.0+ Agent 创建完整示例
使用 create_agent 构建智能体
"""
import os
from langchain.agents import create_agent
from langgraph.checkpoint.memory import MemorySaver
from langchain_core.tools import tool
from langchain_community.chat_models import ChatTongyi
from langchain_core.messages import SystemMessage

# ============ 1. 定义工具 ============
@tool
def search_database(query: str) -> str:
    """在数据库中搜索信息"""
    # 模拟数据库搜索
    db = {
        "LangChain": "LangChain 是一个用于构建 LLM 应用的框架",
        "LangGraph": "LangGraph 是 LangChain 的工作流编排框架"
    }
    return db.get(query, f"未找到关于 {query} 的信息")

@tool
def calculate(expression: str) -> str:
    """计算数学表达式"""
    try:
        result = eval(expression)
        return f"计算结果: {result}"
    except Exception as e:
        return f"计算错误: {str(e)}"

@tool
def get_user_info(user_id: str) -> str:
    """获取用户信息"""
    users = {
        "001": "张三,VIP用户,余额: 1000元",
        "002": "李四,普通用户,余额: 500元"
    }
    return users.get(user_id, "用户不存在")

# ============ 2. 创建模型 ============
api_key = os.getenv("DASHSCOPE_API_KEY", "your-api-key")
model = ChatTongyi(
    model_name="qwen-turbo",
    dashscope_api_key=api_key,
    temperature=0
)

# ============ 3. 创建 Agent(展示新特性)============
# ✨ 新特性: checkpointer - 自动保存会话状态
memory = MemorySaver()

# ✨ 创建 Agent(V1.0+ 最简化 API)
# 注意:create_agent 当前版本只支持 model、tools、checkpointer 三个核心参数
agent = create_agent(
    model=model,                      # 模型
    tools=[search_database, calculate, get_user_info],  # 工具列表
    checkpointer=memory,              # ✨ 检查点(会话持久化)
)

# 💡 如果需要自定义系统提示,可以在调用时通过 messages 参数传入
# 例如:
# from langchain_core.messages import SystemMessage
# result = agent.invoke({
#     "messages": [
#         SystemMessage(content="你是一个专业的AI助手"),
#         ("user", "你的问题")
#     ]
# }, config=config)

# ============ 4. 使用 Agent(展示新优势)============
if __name__ == "__main__":
    # ✨ 优势1: 多轮对话 + 会话持久化
    config = {"configurable": {"thread_id": "conversation-001"}}
    
    print("=== 第1轮对话 ===")
    result1 = agent.invoke(
        {"messages": [("user", "搜索 LangGraph 的信息")]},
        config=config
    )
    print(result1["messages"][-1].content)
    
    print("\n=== 第2轮对话(记住上下文)===")
    result2 = agent.invoke(
        {"messages": [("user", "帮我计算 123 * 456")]},
        config=config  # 同一个 thread_id,自动记住历史
    )
    print(result2["messages"][-1].content)
    
    print("\n=== 第3轮对话(多步骤推理)===")
    result3 = agent.invoke(
        {"messages": [("user", "查询用户001的信息,然后计算他的余额打8折是多少")]},
        config=config
    )
    print(result3["messages"][-1].content)
    
    # ✨ 优势2: 流式输出(实时观察推理过程)
    print("\n=== 流式输出(观察推理过程)===")
    for chunk in agent.stream(
        {"messages": [("user", "搜索 LangChain,然后告诉我它是什么")]},
        config={"configurable": {"thread_id": "conversation-002"}}
    ):
        if "agent" in chunk:
            print(chunk["agent"]["messages"][-1].content)
        if "tools" in chunk:
            print(f"🔧 工具调用结果: {chunk['tools']['messages'][-1].content}")

print("""
🎯 create_agent (V1.0+) 的核心优势:

1️⃣ API 极简化
   - 旧版: create_react_agent(llm, tools, prompt) + AgentExecutor
   - V1.0+: create_agent(model, tools, checkpointer) 直接返回可执行 graph

2️⃣ 内置会话持久化
   - checkpointer: 自动保存会话历史,支持多轮对话
   - thread_id: 通过配置隔离不同会话

3️⃣ 原生支持流式输出
   - stream() 方法实时观察 Agent 的推理过程
   - 可以看到每一步的工具调用和思考

4️⃣ 更好的可观测性
   - 返回的是 LangGraph,可以可视化工作流
   - 自动记录每个节点的状态变化

5️⃣ 无需 AgentExecutor
   - 旧版需要: executor = AgentExecutor(agent=agent, tools=tools)
   - V1.0+: 直接 agent.invoke() 或 agent.stream()

6️⃣ 灵活的系统提示
   - 通过 messages 参数在调用时动态传入 SystemMessage
   - 每次调用可以使用不同的系统提示
""")
🆚 版本演进对比
❌ 旧版 (已废弃) ⚠️ 过渡版 (即将废弃) ✅ V1.0+ (最新)
langchain.agents
.create_react_agent
langgraph.prebuilt
.create_react_agent
langchain.agents
.create_agent
(llm, tools, prompt) (model, tools) (model, tools)
需要 AgentExecutor 直接返回 graph 直接返回 graph
手动管理状态 ❌ 不支持修改器 通过 messages 参数传入
无持久化 ✨ checkpointer ✨ checkpointer
流式需配置 ✨ 原生 stream() ✨ 原生 stream()
⚠️ 重要提示:
langgraph.prebuilt.create_react_agent 在 V1.0 中已移至 langchain.agents.create_agent
create_agent 当前只支持 3 个核心参数:modeltoolscheckpointer
• 系统提示词需通过 invoke()messages 参数传入,不支持 state_modifiermessages_modifier
• 建议立即迁移到 create_agent 以避免未来版本不兼容

🏗️ 10.2 核心三要素:State、Node、Edge

LangGraph 的所有工作流都由三个要素组成,理解它们就掌握了 LangGraph 的 80%。

┌──────────────────────────────────────────────────┐
│              LangGraph 工作流结构                  │
│                                                    │
│   State(状态)= 一个在所有节点间共享的字典        │
│                                                    │
│   ┌─────────┐    Edge    ┌─────────┐              │
│   │  Node A │ ─────────→ │  Node B │              │
│   │ (函数)  │            │ (函数)  │              │
│   └─────────┘            └────┬────┘              │
│                               │ 条件Edge           │
│                          ┌────┴────┐              │
│                          │  Node C │              │
│                          │ (函数)  │              │
│                          └─────────┘              │
│                                                    │
│   每个 Node 接收 State,返回要更新的字段            │
│   Edge 决定下一个执行哪个 Node                     │
└──────────────────────────────────────────────────┘

要素一:State(状态)—— 工作流的"共享内存"

💡 什么是 State?

State 是一个 TypedDict(类型化字典),它就像一个"共享内存",在整个工作流的所有节点之间传递和共享数据。

  • 📦 数据容器:存储工作流运行过程中的所有数据(用户输入、中间结果、最终输出等)
  • 🔄 自动传递:每个节点执行时都会收到最新的 State
  • ✏️ 部分更新:节点只需返回要修改的字段,LangGraph 自动合并
  • 🎯 类型安全:TypedDict 提供类型提示,IDE 可以自动补全

📝 State 的三种定义方式

python
from typing import TypedDict, Annotated, List
import operator

# ========== 方式1:最简单的 State(覆盖模式)==========
class SimpleState(TypedDict):
    """每个字段都是覆盖模式:新值直接替换旧值"""
    user_name: str      # 用户名
    age: int            # 年龄
    score: float        # 分数

# 示例:覆盖模式的行为
# 初始: state = {"user_name": "张三", "age": 25, "score": 80.0}
# 节点返回: {"age": 26}
# 结果: state = {"user_name": "张三", "age": 26, "score": 80.0}
#                                      ↑ 只有 age 被更新


# ========== 方式2:带 Reducer 的 State(累加模式)==========
class ChatState(TypedDict):
    """使用 Annotated 定义字段的合并策略"""
    # operator.add:新值追加到列表末尾(不覆盖)
    messages: Annotated[List[str], operator.add]
    
    # 无 Annotated:普通覆盖模式
    current_speaker: str
    turn_count: int

# 示例:Reducer 的行为
# 初始: state = {
#     "messages": ["用户: 你好"],
#     "current_speaker": "user",
#     "turn_count": 1
# }
# 节点返回: {
#     "messages": ["AI: 你好,我是助手"],
#     "current_speaker": "bot",
#     "turn_count": 2
# }
# 结果: state = {
#     "messages": ["用户: 你好", "AI: 你好,我是助手"],  # ← 追加(operator.add)
#     "current_speaker": "bot",                        # ← 覆盖
#     "turn_count": 2                                  # ← 覆盖
# }


# ========== 方式3:自定义 Reducer 函数 ==========
def merge_metadata(old: dict, new: dict) -> dict:
    """自定义合并逻辑:合并两个字典"""
    return {**old, **new}

class AdvancedState(TypedDict):
    messages: Annotated[List[str], operator.add]
    # 自定义 reducer:合并元数据字典
    metadata: Annotated[dict, merge_metadata]
    status: str

# 示例:自定义 Reducer 的行为
# 初始: state = {
#     "messages": ["消息1"],
#     "metadata": {"source": "web", "user_id": "001"},
#     "status": "processing"
# }
# 节点返回: {
#     "metadata": {"timestamp": "2024-03-21", "version": "1.0"}
# }
# 结果: state = {
#     "messages": ["消息1"],
#     "metadata": {"source": "web", "user_id": "001", "timestamp": "2024-03-21", "version": "1.0"},
#     "status": "processing"
# }
🤔 常见问题:为什么需要 Reducer?

问题:在对话系统中,如果不用 operator.add,会发生什么?

text
# ❌ 错误示例:不使用 Reducer
class BadState(TypedDict):
    messages: List[str]  # 没有 Annotated

# 节点1返回: {"messages": ["用户: 你好"]}
# 节点2返回: {"messages": ["AI: 你好"]}
# 结果: state = {"messages": ["AI: 你好"]}  # ❌ 用户消息丢失了!
text
# ✅ 正确示例:使用 Reducer
class GoodState(TypedDict):
    messages: Annotated[List[str], operator.add]

# 节点1返回: {"messages": ["用户: 你好"]}
# 节点2返回: {"messages": ["AI: 你好"]}
# 结果: state = {"messages": ["用户: 你好", "AI: 你好"]}  # ✅ 消息都保留了!

要素二:Node(节点)—— 工作流的"处理单元"

💡 什么是 Node?

Node(节点)就是一个普通的 Python 函数,它是工作流中的"处理单元",负责执行具体的业务逻辑。

  • 📥 输入:接收当前的 State(完整的状态字典)
  • ⚙️ 处理:执行业务逻辑(调用 LLM、查询数据库、调用工具等)
  • 📤 输出:返回一个字典,只包含要更新的字段(不需要返回完整 State)
  • 🔄 自动合并:LangGraph 自动将返回值合并到 State 中

📝 Node 的四种常见模式

python
# ========== 模式1:简单处理节点 ==========
def greet_node(state: ChatState) -> dict:
    """读取 state,返回要更新的字段"""
    user_name = state.get("current_speaker", "朋友")
    return {
        "messages": [f"你好 {user_name},欢迎使用 LangGraph!"],
        "turn_count": state.get("turn_count", 0) + 1
    }


# ========== 模式2:调用 LLM 的节点 ==========
def llm_node(state: ChatState) -> dict:
    """调用大语言模型处理用户消息"""
    from langchain_community.chat_models import ChatTongyi
    
    llm = ChatTongyi(model_name="qwen-turbo")
    user_message = state["messages"][-1]  # 获取最后一条消息
    
    # 调用 LLM
    response = llm.invoke(user_message)
    
    return {
        "messages": [f"AI: {response.content}"],
        "current_speaker": "bot"
    }


# ========== 模式3:条件判断节点 ==========
def check_sentiment_node(state: ChatState) -> dict:
    """分析用户情绪"""
    last_message = state["messages"][-1]
    
    # 简单的情绪判断逻辑
    if "谢谢" in last_message or "感谢" in last_message:
        sentiment = "positive"
    elif "问题" in last_message or "错误" in last_message:
        sentiment = "negative"
    else:
        sentiment = "neutral"
    
    return {
        "metadata": {"sentiment": sentiment},
        "messages": [f"[系统] 检测到情绪: {sentiment}"]
    }


# ========== 模式4:工具调用节点 ==========
def search_database_node(state: ChatState) -> dict:
    """查询数据库的节点"""
    query = state["messages"][-1]
    
    # 模拟数据库查询
    db_results = {
        "LangChain": "LangChain 是一个用于构建 LLM 应用的框架",
        "LangGraph": "LangGraph 是 LangChain 的工作流编排框架"
    }
    
    result = db_results.get(query, "未找到相关信息")
    
    return {
        "messages": [f"数据库查询结果: {result}"],
        "metadata": {"source": "database"}
    }


# ========== 关键点:节点的返回值 ==========
# ✅ 正确:只返回要更新的字段
def good_node(state: ChatState) -> dict:
    return {"messages": ["新消息"]}  # ✅ 只返回需要更新的字段

# ❌ 错误:返回完整的 state(不推荐,但也能工作)
def bad_node(state: ChatState) -> dict:
    state["messages"].append("新消息")  # ❌ 直接修改 state(不推荐)
    return state  # ❌ 返回完整 state(冗余)
⚡ 最佳实践:节点设计原则
  • 单一职责:每个节点只做一件事(如:只调用 LLM,或只查询数据库)
  • 部分更新:只返回需要修改的字段,不要返回完整 State
  • 不修改输入:不要直接修改 state 参数,而是返回新的字典
  • 类型提示:使用类型提示让代码更清晰(def node(state: MyState) -> dict
  • 错误处理:添加 try-except 处理可能的异常

要素三:Edge(边)—— 工作流的"路由规则"

💡 什么是 Edge?

Edge(边)定义了节点之间的连接关系,决定工作流的执行顺序。它就像"路由规则",告诉 LangGraph:"执行完节点 A 后,下一步该执行哪个节点?"

📝 Edge 的两种类型

1️⃣ 普通边(固定路由)

用法:add_edge("A", "B")
含义:A 执行完后,一定执行 B
场景:固定的线性流程

START → A → B → C → END
2️⃣ 条件边(动态路由)

用法:add_conditional_edges("A", 路由函数)
含义:A 执行完后,根据 State 动态决定下一个节点
场景:需要分支逻辑的流程

     ┌→ B (条件1)
A ──┼→ C (条件2)
     └→ D (其他)

📝 Edge 的实际例子

python
# ========== 示例1:普通边(固定流程)==========
from langgraph.graph import StateGraph, START, END

graph = StateGraph(ChatState)

# 添加节点
graph.add_node("greet", greet_node)
graph.add_node("process", process_node)
graph.add_node("farewell", farewell_node)

# 添加普通边:固定顺序执行
graph.add_edge(START, "greet")      # 开始 → greet
graph.add_edge("greet", "process")  # greet → process
graph.add_edge("process", "farewell")  # process → farewell
graph.add_edge("farewell", END)     # farewell → 结束

# 流程图:START → greet → process → farewell → END


# ========== 示例2:条件边(动态路由)==========
# 定义路由函数
def route_by_sentiment(state: ChatState) -> str:
    """根据情绪决定下一个节点"""
    sentiment = state.get("metadata", {}).get("sentiment", "neutral")
    
    if sentiment == "positive":
        return "thank_you_node"  # 积极情绪 → 感谢节点
    elif sentiment == "negative":
        return "help_node"       # 消极情绪 → 帮助节点
    else:
        return "continue_node"   # 中性情绪 → 继续对话节点

graph = StateGraph(ChatState)

# 添加节点
graph.add_node("check_sentiment", check_sentiment_node)
graph.add_node("thank_you_node", thank_you_node)
graph.add_node("help_node", help_node)
graph.add_node("continue_node", continue_node)

# 添加条件边
graph.add_edge(START, "check_sentiment")
graph.add_conditional_edges(
    "check_sentiment",           # 从哪个节点出发
    route_by_sentiment,          # 路由函数(返回下一个节点的名称)
    {
        "thank_you_node": "thank_you_node",  # 映射:函数返回值 → 实际节点名
        "help_node": "help_node",
        "continue_node": "continue_node"
    }
)

# 所有分支最终都到 END
graph.add_edge("thank_you_node", END)
graph.add_edge("help_node", END)
graph.add_edge("continue_node", END)

# 流程图:
#                  ┌→ thank_you_node → END (positive)
# START → check ──┼→ help_node → END (negative)
#                  └→ continue_node → END (neutral)


# ========== 示例3:复杂条件边(多条件判断)==========
def complex_router(state: ChatState) -> str:
    """复杂的路由逻辑"""
    last_message = state["messages"][-1]
    turn_count = state.get("turn_count", 0)
    
    # 多条件判断
    if turn_count > 10:
        return END  # 超过10轮对话,直接结束
    elif "再见" in last_message or "拜拜" in last_message:
        return "farewell_node"  # 用户说再见
    elif "帮助" in last_message or "?" in last_message:
        return "help_node"  # 用户需要帮助
    else:
        return "llm_node"  # 正常对话

graph.add_conditional_edges(
    "process",
    complex_router,
    {
        "farewell_node": "farewell_node",
        "help_node": "help_node",
        "llm_node": "llm_node",
        END: END  # 特殊:直接结束
    }
)
🎯 关键点:路由函数的返回值

路由函数必须返回节点名称(字符串)END

  • return "node_name" - 跳转到指定节点
  • return END - 直接结束工作流
  • return None - 会报错
  • ❌ 返回不存在的节点名 - 会报错

🚀 10.3 第一个 LangGraph 工作流(完整可运行)

我们用一个最简单的例子把三要素串起来:构建一个「问候→处理→告别」的工作流。

流程图

START → greet → process → farewell → END
python
from typing import TypedDict, Annotated, List
import operator
from langgraph.graph import StateGraph, START, END

# ===== 第1步:定义 State =====
class MyState(TypedDict):
    messages: Annotated[List[str], operator.add]  # 消息会累加
    user_name: str

# ===== 第2步:定义 Node(普通 Python 函数)=====
def greet(state: MyState):
    return {"messages": [f"👋 你好 {state['user_name']}!"]}

def process(state: MyState):
    return {"messages": ["⚙️ 正在处理你的请求..."]}

def farewell(state: MyState):
    return {"messages": ["👋 处理完成,再见!"]}

# ===== 第3步:构建图 =====
graph = StateGraph(MyState)

# 添加节点
graph.add_node("greet", greet)
graph.add_node("process", process)
graph.add_node("farewell", farewell)

# 添加边(定义执行顺序)
graph.add_edge(START, "greet")       # 起点 → greet
graph.add_edge("greet", "process")   # greet → process
graph.add_edge("process", "farewell")# process → farewell
graph.add_edge("farewell", END)      # farewell → 终点

# ===== 第4步:编译并运行 =====
app = graph.compile()

result = app.invoke({
    "messages": [],
    "user_name": "小明"
})

print("最终状态:")
for msg in result["messages"]:
    print(f"  {msg}")

# 输出:
#   👋 你好 小明!
#   ⚙️ 正在处理你的请求...
#   👋 处理完成,再见!

💡 关键理解

  • StateGraph(MyState) — 用 State 类型创建图
  • add_node("名字", 函数) — 注册节点
  • add_edge("A", "B") — A 完成后执行 B
  • graph.compile() — 编译成可执行的应用
  • app.invoke(初始状态) — 传入初始状态并运行

🔀 10.4 条件分支:让工作流会「思考」

真实业务中,流程不会只有一条路。LangGraph 通过 add_conditional_edges 实现动态路由。

📞 案例:智能客服路由

                    START
                      │
                      ▼
               ┌──────────────┐
               │  classify     │  ← 意图识别节点
               └──────┬───────┘
                      │
            ┌─────────┼─────────┐
            ▼         ▼         ▼
     ┌──────────┐ ┌────────┐ ┌────────┐
     │ handle   │ │ handle │ │ handle │
     │ _query   │ │ _compla│ │ _chat  │
     └────┬─────┘ └───┬────┘ └───┬────┘
          │           │          │
          └───────────┼──────────┘
                      ▼
                     END
python
from typing import TypedDict, Annotated, List, Literal
import operator
from langgraph.graph import StateGraph, START, END

# ===== State =====
class CustomerState(TypedDict):
    messages: Annotated[List[str], operator.add]
    user_input: str
    intent: str       # 识别出的意图

# ===== 节点 =====
def classify(state: CustomerState):
    """意图识别节点"""
    text = state["user_input"].lower()
    if "退款" in text or "投诉" in text:
        intent = "complaint"
    elif "查" in text or "订单" in text:
        intent = "query"
    else:
        intent = "chat"
    return {"intent": intent, "messages": [f"🔍 识别意图: {intent}"]}

def handle_query(state: CustomerState):
    return {"messages": ["📋 正在查询您的订单信息..."]}

def handle_complaint(state: CustomerState):
    return {"messages": ["🔧 已记录您的投诉,客服将尽快处理"]}

def handle_chat(state: CustomerState):
    return {"messages": ["💬 感谢您的咨询,还有什么可以帮您?"]}

# ===== 路由函数 =====
# 返回值必须是下一个节点的名字
def route_by_intent(state: CustomerState) -> Literal["handle_query", "handle_complaint", "handle_chat"]:
    return f"handle_{state['intent']}"

# ===== 构建图 =====
graph = StateGraph(CustomerState)

graph.add_node("classify", classify)
graph.add_node("handle_query", handle_query)
graph.add_node("handle_complaint", handle_complaint)
graph.add_node("handle_chat", handle_chat)

graph.add_edge(START, "classify")

# 关键:条件边 —— classify 之后根据 route_by_intent 的返回值决定走哪条路
graph.add_conditional_edges("classify", route_by_intent)

graph.add_edge("handle_query", END)
graph.add_edge("handle_complaint", END)
graph.add_edge("handle_chat", END)

app = graph.compile()

# ===== 测试 =====
for question in ["查一下我的订单", "我要投诉!", "你好呀"]:
    result = app.invoke({"messages": [], "user_input": question, "intent": ""})
    print(f"\n用户: {question}")
    for msg in result["messages"]:
        print(f"  {msg}")

🔑 add_conditional_edges 详解

参数 说明
第1个参数 源节点名称(从哪个节点出发)
第2个参数 路由函数(接收 state,返回目标节点名称)
第3个参数(可选) 映射字典 {"返回值": "节点名"},当返回值和节点名不一致时使用

🔄 10.5 循环:让工作流能「重试」和「迭代」

循环是 LangGraph 相比传统 Chain 最强大的特性之一。通过条件边指回之前的节点,就实现了循环。

📝 案例:AI 写作 + 质量校验(不合格就重写)

START → generate → check_quality ─┬─ 合格 → END
                                  │
                                  └─ 不合格 → generate(循环重试)
python
from typing import TypedDict, Annotated, List
import operator
from langgraph.graph import StateGraph, START, END

class WriterState(TypedDict):
    messages: Annotated[List[str], operator.add]
    draft: str
    attempt: int       # 第几次尝试
    is_approved: bool  # 是否通过审核

def generate(state: WriterState):
    """生成内容(模拟)"""
    attempt = state["attempt"] + 1
    # 模拟:第3次才写出合格内容
    if attempt >= 3:
        draft = "这是一篇高质量的文章,包含详细数据和分析。共500字。"
    else:
        draft = f"这是第{attempt}次草稿,内容较短。"
    return {
        "draft": draft,
        "attempt": attempt,
        "messages": [f"✍️ 第{attempt}次生成: {draft[:30]}..."]
    }

def check_quality(state: WriterState):
    """质量检查"""
    # 这里是“示例用”的质量规则:要能稳定判定第3次之后的草稿为合格。
    # 真实项目中你会把它替换成:更严格的规则(字数/结构)或 LLM-as-a-judge。
    draft = state["draft"]
    is_good = (
        ("高质量" in draft)  # mock 输出的显式标记
        or ("共500字" in draft)  # mock 输出的显式标记
        or (len(draft) >= 20 and "数据" in draft)  # 兜底:有关键要素且长度足够
    )
    return {
        "is_approved": is_good,
        "messages": [f"{'✅ 质量合格!' if is_good else '❌ 质量不达标,需要重写'}"]
    }

def should_continue(state: WriterState):
    """路由:合格就结束,不合格就继续生成"""
    if state["is_approved"]:
        return END
    if state["attempt"] >= 5:  # 安全阀:最多5次
        return END
    return "generate"

# 构建图
graph = StateGraph(WriterState)
graph.add_node("generate", generate)
graph.add_node("check_quality", check_quality)

graph.add_edge(START, "generate")
graph.add_edge("generate", "check_quality")
# 关键:条件边指回 generate,形成循环
graph.add_conditional_edges("check_quality", should_continue)

app = graph.compile()

result = app.invoke({
    "messages": [], "draft": "", "attempt": 0, "is_approved": False
})

print("执行过程:")
for msg in result["messages"]:
    print(f"  {msg}")
print(f"\n最终草稿: {result['draft']}")
print(f"总共尝试: {result['attempt']}次")

# 输出:
#   ✍️ 第1次生成: 这是第1次草稿,内容较短。...
#   ❌ 质量不达标,需要重写
#   ✍️ 第2次生成: 这是第2次草稿,内容较短。...
#   ❌ 质量不达标,需要重写
#   ✍️ 第3次生成: 这是一篇高质量的文章,包含详细数据和分析...
#   ✅ 质量合格!

📡 10.6 流式执行与状态观察

LangGraph 支持流式输出,让你实时看到每个节点的执行情况。这对调试和用户体验非常重要。

python
from typing import TypedDict, Annotated, List
import operator
import asyncio
from langgraph.graph import StateGraph, START, END

class DemoState(TypedDict):
    messages: Annotated[List[str], operator.add]
    step: int

def node_a(state: DemoState):
    step = state["step"] + 1
    return {
        "step": step,
        "messages": [f"🧩 A 执行完成(step={step})"]
    }

def node_b(state: DemoState):
    step = state["step"] + 1
    return {
        "step": step,
        "messages": [f"🧩 B 执行完成(step={step})"]
    }

# 构建最小可运行 graph
graph = StateGraph(DemoState)
graph.add_node("A", node_a)
graph.add_node("B", node_b)
graph.add_edge(START, "A")
graph.add_edge("A", "B")
graph.add_edge("B", END)
app = graph.compile()

initial_state: DemoState = {"messages": [], "step": 0}

# ===== invoke vs stream =====

# 方式1: invoke — 一次性返回最终结果
final_state = app.invoke(initial_state)
print("invoke 最终状态:", final_state)

# 方式2: stream — 逐节点返回中间状态(增量更新)
print("\nstream 逐节点事件:")
for event in app.stream(initial_state):
    for node_name, updates in event.items():
        print(f"  📍 节点 [{node_name}] 完成 -> updates={updates}")

# 方式3: stream + stream_mode="values" — 返回每一步的完整状态
print("\nstream(values) 完整状态快照:")
for snapshot in app.stream(initial_state, stream_mode="values"):
    print(f"  snapshot={snapshot}")

# 方式4: astream — 异步流式(用于 Web 服务)
async def run_async_stream():
    print("\nastream 异步逐节点事件:")
    async for event in app.astream(initial_state):
        for node_name, updates in event.items():
            print(f"  📍 节点 [{node_name}] 完成 -> updates={updates}")

asyncio.run(run_async_stream())

📊 invoke vs stream 对比

方法 返回 适用场景
invoke() 最终完整状态 简单调用,只关心结果
stream() 每个节点的增量更新 调试、进度展示
astream() 异步流式 Web API、高并发

📋 app 方法详解表(编译后的工作流应用)

当调用 app = graph.compile() 后,得到的 app 对象具有以下方法:

方法 参数说明 返回值 适用场景
invoke(input, config=None, **kwargs) input: 初始状态字典
config: 配置字典,包含 thread_id 等
示例: {"messages": ["你好"]}
最终完整状态字典(所有节点执行完毕后的 State) 同步执行,一次性获取最终结果
stream(input, config=None, stream_mode="updates", **kwargs) input: 初始状态
stream_mode: 流式模式
- "updates"(默认): 返回增量更新
- "values": 返回完整状态快照
- "debug": 返回调试信息
生成器,每次迭代返回一个节点执行结果
{"节点名": {"字段": "更新值"}}
实时观察每个节点的执行,适合调试和展示进度
astream(input, config=None, **kwargs) input: 初始状态
config: 异步配置
与 stream() 参数相同
异步生成器 (AsyncGenerator)
需要使用 async for 遍历
Web 服务、高并发场景、异步处理
get_state(config) config: 必须包含 thread_id
示例:
{"configurable": {"thread_id": "xxx"}}
StateSnapshot 对象,包含:
- values: 当前状态值
- next: 下一个要执行的节点
- config: 配置信息
查看指定会话的当前状态,用于状态监控
get_state_history(config, filter=None) config: 包含 thread_id 的配置
filter: 可选过滤器
用于筛选特定类型的状态变更
列表,包含所有历史状态快照
每个快照记录了该时刻的完整状态
查看执行历史、审计日志、时间旅行调试
update_state(config, values, as_node=None, **kwargs) config: 包含 thread_id 的配置
values: 要更新的状态字段字典
as_node: 可选,指定以哪个节点身份写入
示例:
update_state(config, {"approved": True})
更新后的 StateSnapshot
状态被修改并可继续执行
人工审批时修改状态、从指定点重新执行

💡 使用示例详解

1️⃣ invoke() - 最简单的一次性执行
python
# 定义初始状态
initial_state = {"messages": [], "step": 0}

# 执行工作流,等待所有节点完成
final_state = app.invoke(initial_state)

# 获取最终结果
print(f"最终消息: {final_state['messages']}")
print(f"最终步骤: {final_state['step']}")
2️⃣ stream() - 实时观察执行过程
python
# 方式A: stream_mode="updates" (默认) - 返回增量更新
for event in app.stream(initial_state):
    for node_name, updates in event.items():
        print(f"📍 节点 [{node_name}] 执行完成")
        print(f"   更新内容: {updates}")

# 方式B: stream_mode="values" - 返回完整状态
for snapshot in app.stream(initial_state, stream_mode="values"):
    print(f"当前完整状态: {snapshot}")
    print(f"消息数量: {len(snapshot.get('messages', []))}")
3️⃣ get_state() - 查看当前状态快照
python
# 配置必须包含 thread_id
config = {"configurable": {"thread_id": "conversation-001"}}

# 获取当前状态快照
snapshot = app.get_state(config)

# 查看状态详情
print(f"当前状态值: {snapshot.values}")
print(f"下一个执行节点: {snapshot.next}")
print(f"配置信息: {snapshot.config}")
4️⃣ update_state() - 人工审批场景
python
# 配置 thread_id
config = {"configurable": {"thread_id": "approval-001"}}

# 第1步: 提交申请,工作流会在审批节点前暂停
result = app.invoke(initial_state, config=config)
print(f"工作流暂停,当前状态: {result['status']}")

# 第2步: 人工审批 - 修改 approved 状态
approval_update = {"approved": True, "approver": "经理张三"}
app.update_state(config, approval_update)
print("✅ 已批准,更新状态")

# 第3步: 恢复执行(传入 None 表示从 checkpoint 继续)
final_result = app.invoke(None, config=config)
print(f"审批完成: {final_result}")

💾 10.7 Checkpointer:持久化与人机协作

LangGraph 内置了检查点机制(Checkpointer),可以随时保存和恢复工作流状态。这是实现「人工审批」「断点续传」「跨会话恢复」的核心基础。

🎯 Checkpointer 核心能力

  • 状态持久化:自动保存每个节点执行后的完整状态,支持随时回溯
  • 断点续传:工作流可以在任意节点暂停,稍后从暂停点继续执行
  • 人工介入:通过 interrupt_before/after 实现 Human-in-the-Loop
  • 多会话管理:通过 thread_id 区分不同用户/会话的状态
  • 时间旅行:可以查看历史快照,甚至从历史状态重新执行

📦 支持的存储方案

LangGraph 提供了多种 Checkpointer 实现,适用于不同的场景和需求:

存储方案 适用场景 持久化 性能 依赖
MemorySaver 开发测试、Demo 演示 ❌ 内存(重启丢失) ⚡ 极快
SqliteSaver 单机应用、小型项目 ✅ 文件持久化 🔸 快 aiosqlite
PostgresSaver 生产环境、多用户系统 ✅ 数据库持久化 🔸 中 psycopg, asyncpg
MongoDBSaver 大规模分布式系统 ✅ NoSQL 持久化 🔸 中 pymongo
RedisSaver 高并发、短期缓存 🔸 可配置持久化 ⚡ 极快 redis

💡 选择建议

  • 开发阶段:使用 MemorySaver,无需配置,快速验证逻辑
  • 单机部署:使用 SqliteSaver,轻量级且持久化
  • 生产环境:使用 PostgresSaver,支持事务和复杂查询
  • 高并发场景:使用 RedisSaver,配合 PostgreSQL 做持久化备份
  • 大规模系统:使用 MongoDBSaver,支持水平扩展

MemorySaver:内存检查点(开发首选)

最简单的 Checkpointer 实现,将状态保存在内存中。适合开发测试,但服务重启后数据会丢失。

python
from typing import TypedDict, Annotated, List
import operator
from langgraph.graph import StateGraph, START, END
from langgraph.checkpoint.memory import MemorySaver

class ChatState(TypedDict):
    messages: Annotated[List[str], operator.add]
    user_name: str

def chat_node(state: ChatState):
    """简单的对话节点"""
    user_msg = state["messages"][-1] if state["messages"] else ""
    user_name = state.get("user_name", "用户")
    reply = f"你好 {user_name}!你说:'{user_msg}'"
    return {"messages": [reply]}

# 构建图
graph = StateGraph(ChatState)
graph.add_node("chat", chat_node)
graph.add_edge(START, "chat")
graph.add_edge("chat", END)

# 创建检查点存储并编译
memory = MemorySaver()
app = graph.compile(checkpointer=memory)

# 配置:thread_id 用于区分不同会话
config = {"configurable": {"thread_id": "conversation_001"}}

# 第一次运行
result1 = app.invoke(
    {"messages": ["你好"], "user_name": "小明"},
    config=config
)
print("第1次对话:", result1["messages"])

# 第二次运行(同一个 thread_id,会自动恢复上次的状态)
result2 = app.invoke(
    {"messages": ["继续之前的对话"]},
    config=config
)
print("第2次对话:", result2["messages"])
# result2 的 messages 会包含之前所有的消息!

Human-in-the-Loop(人工介入)

通过 interrupt_beforeinterrupt_after 参数,可以让工作流在指定节点暂停,等待人工确认后继续。

🔄 人工审批流程

START → 生成方案 → ⏸️ 暂停(等待人工审批)→ 执行方案 → END
                          ↑
                    人工查看并确认后
                    调用 app.invoke(None, config)
                    恢复执行
python
from typing import TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.checkpoint.memory import MemorySaver

class ApprovalState(TypedDict):
    plan: str
    approved: bool
    result: str

def generate_plan(state: ApprovalState):
    """生成执行方案"""
    plan = "计划:购买服务器,预算 5000 元"
    return {"plan": plan, "approved": False}

def execute_plan(state: ApprovalState):
    """执行方案(需要审批)"""
    if state["approved"]:
        result = f"已执行:{state['plan']}"
    else:
        result = "方案未通过审批,暂不执行"
    return {"result": result}

# 构建图
graph = StateGraph(ApprovalState)
graph.add_node("generate", generate_plan)
graph.add_node("execute", execute_plan)
graph.add_edge(START, "generate")
graph.add_edge("generate", "execute")
graph.add_edge("execute", END)

# 编译时设置检查点和中断点
memory = MemorySaver()
app = graph.compile(
    checkpointer=memory,
    interrupt_before=["execute"]  # 在 execute 节点前暂停
)

config = {"configurable": {"thread_id": "approval_001"}}
initial_state = {"plan": "", "approved": False, "result": ""}

# 第一次运行:会在 execute 节点前暂停
result = app.invoke(initial_state, config=config)
print("工作流已暂停,等待人工审批...")
print(f"当前状态: {result}")

# 模拟人工审批:修改 approved 状态
print("\n人工审批中...")
approval_input = {"approved": True}  # 人工批准

# 恢复执行(传入新的状态更新)
final = app.invoke(approval_input, config=config)
print(f"审批通过,继续执行: {final}")

SqliteSaver:文件持久化(生产推荐)

将状态保存到 SQLite 数据库文件,支持持久化存储,适合单机部署和小型项目。

python
from langgraph.checkpoint.sqlite import SqliteSaver
from langgraph.graph import StateGraph, START, END
from typing import TypedDict

class WorkflowState(TypedDict):
    step: int
    data: str

def process_node(state: WorkflowState):
    return {"step": state["step"] + 1, "data": f"处理步骤 {state['step']}"}

# 构建图
graph = StateGraph(WorkflowState)
graph.add_node("process", process_node)
graph.add_edge(START, "process")
graph.add_edge("process", END)

# 使用 SQLite 持久化(数据保存到文件)
with SqliteSaver.from_conn_string("checkpoints.db") as checkpointer:
    app = graph.compile(checkpointer=checkpointer)
    
    config = {"configurable": {"thread_id": "workflow_001"}}
    
    # 第一次运行
    result = app.invoke({"step": 0, "data": ""}, config=config)
    print(f"第1次运行: {result}")
    
    # 服务重启后,仍然可以恢复状态
    result2 = app.invoke({"step": 0, "data": ""}, config=config)
    print(f"恢复后运行: {result2}")  # step 会从上次的值继续

PostgresSaver:企业级持久化

使用 PostgreSQL 数据库存储状态,支持事务、并发控制和复杂查询,适合生产环境。

python
from langgraph.checkpoint.postgres import PostgresSaver
from psycopg import Connection

# 连接 PostgreSQL
connection_string = "postgresql://user:password@localhost:5432/langgraph_db"

with Connection.connect(connection_string) as conn:
    # 创建检查点表(首次使用需要)
    PostgresSaver.create_tables(conn)
    
    # 使用 PostgreSQL 持久化
    checkpointer = PostgresSaver(conn)
    app = graph.compile(checkpointer=checkpointer)
    
    config = {"configurable": {"thread_id": "prod_workflow_001"}}
    result = app.invoke({"step": 0, "data": ""}, config=config)

🔍 Checkpointer 高级功能

  • 查看历史快照:app.get_state_history(config) 获取所有历史状态
  • 从历史恢复:app.update_state(config, values, as_node="node_name") 修改状态并重新执行
  • 多线程隔离:不同 thread_id 的状态完全独立,互不干扰
  • 状态快照:app.get_state(config) 获取当前状态和元数据
  • 时间旅行:可以回到任意历史节点,从该点重新执行工作流

⚠️ 注意事项

  • thread_id 必须唯一:每个用户/会话使用不同的 thread_id,避免状态混乱
  • 状态序列化:State 中的数据必须可序列化(JSON 兼容),避免使用复杂对象
  • 数据库连接:生产环境使用连接池,避免频繁创建连接
  • 清理策略:定期清理过期的 checkpoint,避免数据库膨胀
  • 并发控制:同一 thread_id 的并发请求可能导致状态冲突,需要加锁

🤖 10.8 与 LLM 结合:构建 AI Agent

LangGraph 的真正威力在于与大语言模型结合。下面我们演示如何手动构建一个能调用工具的智能体。

手动构建 Agent 图

python
from typing import TypedDict, Annotated, List
import operator
from langgraph.graph import StateGraph, START, END
from langchain_community.chat_models import ChatTongyi
from langchain_core.messages import HumanMessage, AIMessage, BaseMessage

class AgentState(TypedDict):
    messages: Annotated[List[BaseMessage], operator.add]

# 初始化 LLM(通义千问)
llm = ChatTongyi(model="qwen-plus", temperature=0)

def call_model(state: AgentState):
    """调用 LLM"""
    response = llm.invoke(state["messages"])
    return {"messages": [response]}

def should_continue(state: AgentState):
    """判断是否需要继续(是否有工具调用)"""
    last_message = state["messages"][-1]
    if hasattr(last_message, "tool_calls") and last_message.tool_calls:
        return "tools"  # 有工具调用,去执行工具
    return END          # 没有工具调用,结束

# 构建 Agent 图
graph = StateGraph(AgentState)
graph.add_node("agent", call_model)
# graph.add_node("tools", tool_executor)  # 工具执行节点

graph.add_edge(START, "agent")
graph.add_conditional_edges("agent", should_continue)
# graph.add_edge("tools", "agent")  # 工具执行完回到 agent

app = graph.compile()

# ===== 运行示例 =====
# 简单对话(无工具调用)
result = app.invoke({
    "messages": [HumanMessage(content="你好,请介绍一下自己")]
})

print("Agent 回复:")
for msg in result["messages"]:
    print(f"- {type(msg).__name__}: {msg.content}")

# 输出示例:
# Agent 回复:
# - HumanMessage: 你好,请介绍一下自己
# - AIMessage: 我是通义千问大模型,可以回答问题、分析文本、协助创作等。

🔄 ReAct Agent 的内部循环

START → agent(LLM) ──→ 需要调用工具?──→ YES → tools → agent(LLM)
                       │                                  ↑
                       │                          (循环直到不需要工具)
                       └─ NO → END

🚀 10.9 项目实战:智能订单处理系统

综合运用以上所有知识,构建一个完整的订单处理工作流。

📦 订单处理流程图

START
  │
  ▼
┌──────────────┐
│  validate    │ ← 验证订单(金额、库存)
└──────┬───────┘
       │
  ┌────┴────┐
  ▼         ▼
有效      无效 → END(返回错误)
  │
  ▼
┌──────────────┐
│  payment     │ ← 处理支付
└──────┬───────┘
       │
  ┌────┴────┐
  ▼         ▼
成功      失败 → retry_payment(最多3次)
  │                    │
  │                    ▼
  │              ┌──────────┐
  │              │ payment  │(循环)
  │              └──────────┘
  ▼
┌──────────────┐
│  ship        │ ← 安排发货
└──────┬───────┘
       │
       ▼
┌──────────────┐
│  notify      │ ← 通知客户
└──────┬───────┘
       │
       ▼
      END
python
"""
智能订单处理系统 - 基于 LangGraph StateGraph
展示:条件分支 + 循环重试 + 流式输出
"""
from typing import TypedDict, Annotated, List, Optional
import operator
import random
from langgraph.graph import StateGraph, START, END

# ========== 1. 定义状态 ==========
class OrderState(TypedDict):
    messages: Annotated[List[str], operator.add]  # 处理日志
    order_id: str
    amount: float
    status: str              # pending/validated/paid/shipped/failed
    payment_attempts: int    # 支付尝试次数
    tracking_number: Optional[str]

# ========== 2. 定义节点 ==========
def validate(state: OrderState):
    """验证订单"""
    order_id = state["order_id"]
    amount = state["amount"]

    if amount <= 0:
        return {
            "status": "failed",
            "messages": [f"❌ 订单 {order_id} 验证失败:金额无效 (¥{amount})"]
        }
    if amount > 100000:
        return {
            "status": "failed",
            "messages": [f"❌ 订单 {order_id} 验证失败:超出限额 (¥{amount})"]
        }
    return {
        "status": "validated",
        "messages": [f"✅ 订单 {order_id} 验证通过,金额 ¥{amount}"]
    }

def payment(state: OrderState):
    """处理支付(模拟:有概率失败)"""
    attempts = state["payment_attempts"] + 1
    # 模拟支付:前2次50%概率失败,第3次一定成功
    success = attempts >= 3 or random.random() > 0.5

    if success:
        return {
            "status": "paid",
            "payment_attempts": attempts,
            "messages": [f"💳 第{attempts}次支付成功!"]
        }
    return {
        "payment_attempts": attempts,
        "messages": [f"⚠️ 第{attempts}次支付失败,准备重试..."]
    }

def ship(state: OrderState):
    """安排发货"""
    tracking = f"SF{random.randint(100000, 999999)}"
    return {
        "status": "shipped",
        "tracking_number": tracking,
        "messages": [f"📦 已发货,运单号: {tracking}"]
    }

def notify(state: OrderState):
    """通知客户"""
    return {
        "messages": [f"📧 已通知客户,订单 {state['order_id']} 处理完成"]
    }

# ========== 3. 定义路由 ==========
def after_validate(state: OrderState):
    """验证后的路由"""
    if state["status"] == "failed":
        return END
    return "payment"

def after_payment(state: OrderState):
    """支付后的路由"""
    if state["status"] == "paid":
        return "ship"
    if state["payment_attempts"] >= 3:
        return END  # 超过最大重试次数
    return "payment"  # 重试(循环)

# ========== 4. 构建图 ==========
graph = StateGraph(OrderState)

graph.add_node("validate", validate)
graph.add_node("payment", payment)
graph.add_node("ship", ship)
graph.add_node("notify", notify)

graph.add_edge(START, "validate")
graph.add_conditional_edges("validate", after_validate)
graph.add_conditional_edges("payment", after_payment)
graph.add_edge("ship", "notify")
graph.add_edge("notify", END)

app = graph.compile()

# ========== 5. 运行(流式输出)==========
print("=" * 50)
print("🚀 订单处理系统启动")
print("=" * 50)

initial = {
    "messages": [],
    "order_id": "ORD-2024-001",
    "amount": 5999.0,
    "status": "pending",
    "payment_attempts": 0,
    "tracking_number": None
}

# 使用 stream 观察每个节点的执行
for event in app.stream(initial):
    for node_name, updates in event.items():
        print(f"\n📍 节点 [{node_name}]:")
        if "messages" in updates:
            for msg in updates["messages"]:
                print(f"   {msg}")
        if "status" in updates:
            print(f"   状态: {updates['status']}")

print("\n" + "=" * 50)
print("✅ 订单处理完成")

📚 10.10 LangGraph API 速查表

🏗️ StateGraph 构建方法详解

方法 参数说明 功能说明
add_node(name, fn) name: 节点名称
fn: 接收state返回字典的函数
添加处理节点,是工作流的基本单元
add_edge(a, b) a: 起始节点或START
b: 目标节点或END
添加普通边,固定流转关系
add_conditional_edges(source, router_fn) source: 起始节点
router_fn: 返回目标节点名的函数
添加条件边,实现分支和循环逻辑
set_entry_point(node) node: 入口节点名称 设置图入口(替代add_edge(START, node))
set_finish_point(node) node: 结束节点名称 设置图结束点(替代add_edge(node, END))
compile(checkpointer, interrupt_before, debug) checkpointer: 持久化存储
interrupt_before: 暂停节点列表
debug: 调试模式
编译图为可执行应用,返回app对象

⚡ 编译后 App 方法(CompiledGraph)

调用 app = graph.compile() 后,使用以下方法执行工作流:

方法 参数 功能
invoke(input, config) input: 初始状态
config: 含thread_id的配置
同步执行,返回最终状态
stream(input, config, stream_mode) stream_mode: updates/values/debug 流式执行,逐节点返回更新
get_state(config) config: 含thread_id的配置 获取当前状态快照
update_state(config, values, as_node) values: 要更新的状态字段
as_node: 以哪个节点身份写入
人工修改状态,用于审批场景
get_state_history(config) config: 含thread_id的配置 获取历史状态列表(时间旅行)

💡 完整示例:从构建到执行

python
# ========== 步骤1: 定义状态 ==========
from typing import TypedDict, Annotated, List
import operator

class ChatState(TypedDict):
    messages: Annotated[List[str], operator.add]  # 累积消息
    user_name: str

# ========== 步骤2: 定义节点函数 ==========
def chat_node(state: ChatState):
    """聊天节点:处理消息"""
    return {"messages": [f"回复: {state['messages'][-1]}"]}

def check_node(state: ChatState):
    """检查节点:判断继续或结束"""
    return {"continue": len(state["messages"]) < 4}

# ========== 步骤3: 定义路由 ==========
def router(state: ChatState):
    if state.get("continue"):
        return "chat"  # 继续循环
    return END        # 结束

# ========== 步骤4: 构建图 ==========
from langgraph.graph import StateGraph, START, END

graph = StateGraph(ChatState)
graph.add_node("chat", chat_node)
graph.add_node("check", check_node)

graph.add_edge(START, "chat")
graph.add_edge("chat", "check")
graph.add_conditional_edges("check", router)

# ========== 步骤5: 编译 ==========
app = graph.compile()

# ========== 步骤6: 执行 ==========
result = app.invoke({
    "messages": ["你好"],
    "user_name": "小明"
})
print(result["messages"])

State 定义模式

写法 合并行为
field: str 直接覆盖(后者覆盖前者)
field: Annotated[List, operator.add] 列表追加(累积式)
field: Annotated[int, lambda a,b: a+b] 自定义 reducer(如计数器累加)

🎯 本章总结

知识点回顾

推荐学习路径

🔗 下一步学习

← 上一章 下一章 → 返回上一页