LangGraph 中 State、Node、Edge 是怎么协作的?

文章目录

前言:为什么我会学这个问题

上篇我们区分了 Function Call 和 Agent 工具调用,但那只是"调用方式"的区别,没解决更根本的问题------当一个复杂任务需要多步骤时,Agent 内部是怎么组织这些步骤的?

我第一次看 LangGraph 代码时,看到 StateNodeEdge 三个东西混在一起,完全不知道它们各自负责什么。这篇就用大白话把它们的协作关系讲清楚。

前文关联:本文接续《Function Call 和 Agent 工具调用到底有什么区别?》,在上文理解 Agent 工具调用的基础上,深入探讨 Agent 内部的流程编排机制。

一、问题背景

假设你要实现一个客服 Agent,它的工作流程是:

  1. 接收用户问题
  2. 判断是技术问题还是售后问题
  3. 技术问题 → 查文档;售后问题 → 查订单
  4. 根据查询结果组织回复

这个流程里,有分支、有顺序、有数据传递。怎么用代码组织这种流程?LangGraph 提供了 State、Node、Edge 三个概念来描述这种流程。

二、核心概念

State 是什么?

State = 流转的数据。就像工厂流水线上的产品,每个工位加工后产品状态会变化,最后变成成品。

在 LangGraph 里,State 是一个 dict,包含了在整个流程中传递的所有数据:

python 复制代码
from typing import TypedDict

class CustomerServiceState(TypedDict):
    """客服 Agent 的状态"""
    user_question: str          # 用户问题
    question_type: str          # 问题类型:tech / aftersales / unknown
    retrieved_info: str         # 查询到的信息
    response: str               # 最终回复
    step_count: int            # 走了几步(用于调试)

Node 是什么?

Node = 处理步骤。每个 Node 接收当前 State,做一些处理,然后返回新的 State。

就像工厂里的工位:输入一个半成品,输出一个加工后的半成品。

python 复制代码
def classify_question(state: CustomerServiceState) -> CustomerServiceState:
    """Node 1: 判断问题类型"""
    question = state["user_question"]
    
    # 简单规则判断(实际项目用 LLM)
    if "怎么" in question or "如何" in question:
        qtype = "tech"
    elif "订单" in question or "快递" in question:
        qtype = "aftersales"
    else:
        qtype = "unknown"
    
    return {"question_type": qtype, "step_count": state["step_count"] + 1}

Edge 是什么?

Edge = 流转规则。决定了下一步应该走哪个 Node。

就像工厂里的传送带和分流器:不是简单地从 A 到 B,而是根据产品状态决定去 A1 还是 A2。

python 复制代码
from langgraph.graph import StateGraph

# 构建图
graph = StateGraph(CustomerServiceState)

# 添加 Node(工位)
graph.add_node("classify", classify_question)
graph.add_node("query_docs", query_technical_docs)
graph.add_node("query_orders", query_order_status)
graph.add_node("generate_response", generate_response)

# 添加 Edge(流转规则)
graph.add_edge("classify", "query_docs", condition=lambda s: s["question_type"] == "tech")
graph.add_edge("classify", "query_orders", condition=lambda s: s["question_type"] == "aftersales")
graph.add_edge("classify", "generate_response", condition=lambda s: s["question_type"] == "unknown")

三、协作流程图

关键点:State 在整个流程中流转,每个 Node 读写 State,Edge 根据 State 决定下一步走哪里。

四、错误理解 / 常见误区

误区1:State 是全局变量

错。State 不是共享的全局变量,而是消息传递的载体。每个 Node 接收上一个 Node 返回的 State,不是直接读写共享内存。

误区2:Node 执行顺序就是代码添加顺序

错。Node 的执行顺序完全由 Edge 决定,跟代码里 add_node 的顺序无关。你完全可以先加 Node F 再加 Node A,但让 Edge 指向 A 先执行。

误区3:Edge 只能是简单的 if-else

不完全对。Edge 的 condition 可以是任意函数,只要返回目标 Node 名称就行。可以是复杂的 LLM 决策,可以是规则引擎,可以是任何逻辑。

误区4:State 必须包含所有字段

错。State 可以包含可选字段,Node 可以只读写需要的字段,其他字段保持不变。

五、代码示例

完整可运行的客服 Agent 示例

python 复制代码
from typing import TypedDict
from langgraph.graph import StateGraph

# ========== 1. 定义 State ==========
class CustomerServiceState(TypedDict):
    """客服 Agent 状态"""
    user_question: str
    question_type: str  # tech / aftersales / unknown
    retrieved_info: str
    response: str
    step_count: int

# ========== 2. 定义 Node ==========

def classify_question(state: CustomerServiceState) -> CustomerServiceState:
    """Node 1: 判断问题类型"""
    question = state["user_question"]
    
    if any(k in question for k in ["怎么", "如何", "设置", "配置"]):
        qtype = "tech"
    elif any(k in question for k in ["订单", "快递", "发货"]):
        qtype = "aftersales"
    else:
        qtype = "unknown"
    
    print(f"  [classify] 问题: {question} -> 类型: {qtype}")
    return {"question_type": qtype, "step_count": state["step_count"] + 1}

def query_technical_docs(state: CustomerServiceState) -> CustomerServiceState:
    """Node 2: 查技术文档"""
    print(f"  [query_docs] 正在查询技术文档...")
    docs = "技术文档:根据您的问题,我们推荐您查看 XXX 配置文档。"
    return {"retrieved_info": docs, "step_count": state["step_count"] + 1}

def query_order_status(state: CustomerServiceState) -> CustomerServiceState:
    """Node 3: 查订单状态"""
    print(f"  [query_orders] 正在查询订单...")
    order_info = "订单状态:您的订单已于昨天发货,预计明天送达。"
    return {"retrieved_info": order_info, "step_count": state["step_count"] + 1}

def generate_response(state: CustomerServiceState) -> CustomerServiceState:
    """Node 4: 组织回复"""
    info = state.get("retrieved_info", "")
    if info:
        response = f"您好!根据您的问题:{info}"
    else:
        response = "您好!您的问题已收到,我会尽力帮助您。"
    
    print(f"  [generate] 最终回复: {response}")
    return {"response": response, "step_count": state["step_count"] + 1}

# ========== 3. 构建 Graph ==========
graph = StateGraph(CustomerServiceState)

# 添加 Node
graph.add_node("classify", classify_question)
graph.add_node("query_docs", query_technical_docs)
graph.add_node("query_orders", query_order_status)
graph.add_node("generate_response", generate_response)

# 设置起点和终点
graph.set_entry_point("classify")
graph.set_finish_point("generate_response")

# 添加条件边(根据 question_type 决定下一步)
graph.add_conditional_edges(
    "classify",
    lambda s: s["question_type"],
    {
        "tech": "query_docs",
        "aftersales": "query_orders",
        "unknown": "generate_response"
    }
)

# query_docs 和 query_orders 之后都去 generate_response
graph.add_edge("query_docs", "generate_response")
graph.add_edge("query_orders", "generate_response")

# 编译
app = graph.compile()

# ========== 4. 运行 ==========
if __name__ == "__main__":
    print("=== 测试技术问题 ===")
    result = app.invoke({
        "user_question": "怎么配置 API 密钥?",
        "question_type": "",
        "retrieved_info": "",
        "response": "",
        "step_count": 0
    })
    print(f"最终状态: question_type={result['question_type']}, response={result['response']}")
    print()
    
    print("=== 测试售后问题 ===")
    result = app.invoke({
        "user_question": "我的订单什么时候发货?",
        "question_type": "",
        "retrieved_info": "",
        "response": "",
        "step_count": 0
    })
    print(f"最终状态: question_type={result['question_type']}, response={result['response']}")

六、运行结果 / 流程图

运行结果:

复制代码
=== 测试技术问题 ===
  [classify] 问题: 怎么配置 API 密钥? -> 类型: tech
  [query_docs] 正在查询技术文档...
  [generate] 最终回复: 您好!根据您的问题:技术文档:根据您的问题,我们推荐您查看 XXX 配置文档。

最终状态: question_type=tech, response=您好!根据您的问题:技术文档:根据您的问题,我们推荐您查看 XXX 配置文档。

=== 测试售后问题 ===
  [classify] 问题: 我的订单什么时候发货? -> 类型: aftersales
  [query_orders] 正在查询订单...
  [generate] 最终回复: 您好!根据您的问题:订单状态:您的订单已于昨天发货,预计明天送达。

最终状态: question_type=aftersales, response=您好!根据您的问题:订单状态:您的订单已于昨天发货,预计明天送达。

七、项目中怎么用

实际场景

  • 客服机器人:根据问题类型走不同流程
  • 数据分析 Agent:清洗 → 分析 → 可视化
  • 代码审查 Agent:接收代码 → 审查 → 输出报告

最佳实践

  1. State 要精简:只传递必要数据,避免过大的 State
  2. Node 要原子化:每个 Node 只做一件事,便于调试和复用
  3. Edge 逻辑要清晰:condition 函数最好单独定义,不要写太复杂的 lambda
  4. 添加 step_count:方便追踪流程走了多少步,也用于防止死循环

系列关联:本文讲了 LangGraph 的核心组件。下篇《Agent 为什么需要 Checkpoint?》会讲到,当 Agent 执行到一半被打断(断电、超时),没有 Checkpoint 的话 State 就丢了,流程得从头来。

八、总结 + 下篇预告

核心要点

  • State = 流程中传递的数据,像流水线上的产品
  • Node = 处理步骤,接收 State、返回新的 State
  • Edge = 流转规则,根据当前 State 决定下一步走哪个 Node
  • 三者配合:Node 处理数据,Edge 控制流向,State 承载数据

下篇预告:下一篇我们将探讨《Agent 为什么需要 Checkpoint?》,理解当 Agent 执行中断时,Checkpoint 如何帮助恢复状态,避免从头开始。

延伸阅读

相关推荐
MATLAB代码顾问2 小时前
AI Agent智能体开发实战:LangChain自动化工作流
人工智能·langchain·自动化
AI科技星2 小时前
全域数学三元公理体系下π的射影几何本源阐释 - 基于兵棋推演框架改造:常温超导仿真验证【乖乖数学】
人工智能·线性代数·机器学习·量子计算·agi
艺杯羹2 小时前
全网首发!Claude Code 国内用法保姆级教程:API配置+VS Code插件,15分钟轻松上手
vscode·ai·ai编程·模型·claude code
Hooray2 小时前
用时7天,花费30元,我vibe coding这个网站
前端·agent·ai编程
Arhero2 小时前
Semantic Chunk 为什么需要 Embedding API
动态规划·embedding·rag·文本切分·语义分块
Java后端的Ai之路2 小时前
TRAE AI编程工具终端乱码解决方案
ai编程·trae
鱼跃厂长3 小时前
这份skill,能将你的简历提升到字节的水平!
c++·ai·ai编程
好运的阿财3 小时前
OpenClaw工具拆解之host_workspace_write+host_workspace_edit
前端·javascript·人工智能·机器学习·ai编程·openclaw·openclaw工具
庞轩px4 小时前
Prompt Engineering——从随意提问到工程化调用
prompt·engineering·角色设定·输出格式约束·prompt模版·复杂prompt