Files
2026_DesignAI/officefile/md/supplements/01-foundations/practice/langgraph-workflow
pengxiao a90f7adfa1 refactor(officefile): 按 md/latex/word 三层结构重组文档目录
将 Markdown 源文件移入 md/,LaTeX 工作目录保留在 latex/,
Word 导出移入 word/;删除临时脚本、调试截图和空 stub。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 14:25:21 +08:00
..

实践案例01:用LangGraph构建空间决策工作流

目标

通过本实践,你将:

  1. 理解状态驱动的Agent设计
  2. 实现一个简单的空间决策工作流
  3. 添加Human-in-the-Loop审查点
  4. 处理工作流中的错误和重试

背景知识

什么是LangGraph

LangGraph是构建有状态的多Agent应用的框架:

核心概念:

1. State(状态): 在节点间传递的数据
2. Node(节点): 处理状态的函数
3. Edge(边): 节点之间的连接
4. Graph(图): 节点和边组成的完整工作流

为什么用LangGraph

  • 状态管理: 自动管理工作流状态
  • 可视化: 可以绘制和查看工作流图
  • 持久化: 支持中断和恢复
  • 条件路由: 基于状态动态选择路径

实践步骤

步骤1:安装依赖

pip install langgraph langchain-core langchain-anthropic

步骤2:定义状态

from typing import TypedDict, Annotated, List, Optional
from operator import add
from typing_extensions import TypedDict

class EcologicalAnalysisState(TypedDict):
    """生态网络分析状态"""

    # 输入
    input_path: str
    parameters: dict

    # 处理过程
    current_step: str
    intermediate_results: dict

    # 人机交互
    review_requested: bool
    human_feedback: Optional[str]

    # 输出
    final_result: Optional[dict]
    errors: Annotated[List[str], add]

步骤3:定义节点

def load_data_node(state: EcologicalAnalysisState) -> EcologicalAnalysisState:
    """加载数据节点"""
    print("执行: load_data")
    # 实际实现中读取文件
    return {
        **state,
        "current_step": "data_loaded",
        "intermediate_results": {"data": "loaded"}
    }

def identify_sources_node(state: EcologicalAnalysisState) -> EcologicalAnalysisState:
    """识别源地节点"""
    print("执行: identify_sources")
    # 实际实现中运行源地识别算法
    sources = [{"id": 1, "area": 1000}, {"id": 2, "area": 800}]
    return {
        **state,
        "current_step": "sources_identified",
        "intermediate_results": {**state["intermediate_results"], "sources": sources}
    }

def human_review_node(state: EcologicalAnalysisState) -> EcologicalAnalysisState:
    """人类审查节点"""
    print("执行: human_review")
    print(f"待审查: {state['intermediate_results']}")
    # 实际实现中等待人类输入
    return {
        **state,
        "review_requested": False,
        "human_feedback": "approved"
    }

def build_resistance_node(state: EcologicalAnalysisState) -> EcologicalAnalysisState:
    """构建阻力面节点"""
    print("执行: build_resistance")
    return {
        **state,
        "current_step": "resistance_built"
    }

步骤4:定义路由

def should_review(state: EcologicalAnalysisState) -> str:
    """决定是否需要审查"""
    sources = state["intermediate_results"].get("sources", [])
    if len(sources) > 2:  # 源地数量多时需要审查
        return "review"
    return "continue"

步骤5:构建图

from langgraph.graph import StateGraph, END

def build_workflow():
    """构建工作流图"""

    # 创建图
    workflow = StateGraph(EcologicalAnalysisState)

    # 添加节点
    workflow.add_node("load_data", load_data_node)
    workflow.add_node("identify_sources", identify_sources_node)
    workflow.add_node("human_review", human_review_node)
    workflow.add_node("build_resistance", build_resistance_node)

    # 设置入口
    workflow.set_entry_point("load_data")

    # 添加边
    workflow.add_edge("load_data", "identify_sources")

    # 添加条件边
    workflow.add_conditional_edges(
        "identify_sources",
        should_review,
        {
            "review": "human_review",
            "continue": "build_resistance"
        }
    )

    workflow.add_edge("human_review", "build_resistance")
    workflow.add_edge("build_resistance", END)

    # 编译
    return workflow.compile()

步骤6:运行工作流

def run_workflow():
    """运行工作流"""

    # 初始状态
    initial_state = {
        "input_path": "data.geojson",
        "parameters": {},
        "current_step": "start",
        "intermediate_results": {},
        "review_requested": False,
        "human_feedback": None,
        "final_result": None,
        "errors": []
    }

    # 构建并运行
    app = build_workflow()
    result = app.invoke(initial_state)

    print("\n=== 最终结果 ===")
    print(result)

扩展练习

1. 添加错误处理

def with_error_handling(node_func):
    """装饰器:添加错误处理"""
    def wrapper(state):
        try:
            return node_func(state)
        except Exception as e:
            return {
                **state,
                "errors": [str(e)]
            }
    return wrapper

# 使用
@with_error_handling
def risky_node(state):
    # 可能出错的节点
    ...

2. 添加检查点(持久化)

from langgraph.checkpoint.memory import MemorySaver

# 创建检查点保存器
memory = MemorySaver()

# 编译时添加检查点
app = workflow.compile(checkpointer=memory, interrupt_before=["human_review"])

# 运行时可以指定thread_id
config = {"configurable": {"thread_id": "conversation-1"}}
result = app.invoke(initial_state, config=config)

3. 可视化工作流

from IPython.display import Image, display

# 生成图
app = build_workflow()
display(Image(app.get_graph().draw_mermaid_png()))

完整代码示例

"""
完整的LangGraph空间决策工作流示例
"""
from typing import TypedDict, Annotated, List, Optional, Literal
from operator import add
from langgraph.graph import StateGraph, END

class State(TypedDict):
    """工作流状态"""
    step: int
    data: Optional[dict]
    sources: Optional[list]
    reviewed: bool
    result: Optional[str]
    errors: Annotated[List[str], add]

# 节点函数
def load_node(state: State) -> State:
    """加载数据"""
    print(f"[节点: load] 步骤 {state['step']}")
    return {**state, "step": state["step"] + 1, "data": {"loaded": True}}

def analyze_node(state: State) -> State:
    """分析数据"""
    print(f"[节点: analyze] 步骤 {state['step']}")
    return {
        **state,
        "step": state["step"] + 1,
        "sources": [{"id": 1, "value": 100}]
    }

def review_node(state: State) -> State:
    """人类审查"""
    print(f"[节点: review] 步骤 {state['step']}")
    print("等待人类审查...")
    # 实际实现中等待输入
    return {**state, "step": state["step"] + 1, "reviewed": True}

def finalize_node(state: State) -> State:
    """完成"""
    print(f"[节点: finalize] 步骤 {state['step']}")
    return {**state, "result": "completed"}

# 路由函数
def route_after_analyze(state: State) -> Literal["review", "finalize"]:
    """分析后的路由"""
    if state.get("sources") and len(state["sources"]) > 0:
        return "review"
    return "finalize"

# 构建图
def build_graph():
    """构建工作流图"""
    graph = StateGraph(State)

    # 添加节点
    graph.add_node("load", load_node)
    graph.add_node("analyze", analyze_node)
    graph.add_node("review", review_node)
    graph.add_node("finalize", finalize_node)

    # 添加边
    graph.set_entry_point("load")
    graph.add_edge("load", "analyze")

    # 条件边
    graph.add_conditional_edges(
        "analyze",
        route_after_analyze,
        {"review": "review", "finalize": "finalize"}
    )

    graph.add_edge("review", "finalize")
    graph.add_edge("finalize", END)

    return graph.compile()

# 运行
if __name__ == "__main__":
    print("=== LangGraph 空间决策工作流 ===\n")

    app = build_graph()

    initial_state: State = {
        "step": 1,
        "data": None,
        "sources": None,
        "reviewed": False,
        "result": None,
        "errors": []
    }

    result = app.invoke(initial_state)

    print(f"\n最终状态: {result['step']}")
    print(f"结果: {result['result']}")

反思问题

  1. 状态设计:你的状态中哪些信息是必需的?哪些可以省略?

  2. 节点粒度:节点应该多大?如何平衡?

  3. 错误处理:当节点失败时,工作流应该如何处理?

  4. 审查点:你的工作流中哪些地方需要人类介入?


下一步

完成这个实践后,你已经:

  • 理解了状态驱动的Agent设计
  • 实现了一个简单的LangGraph工作流
  • 掌握了条件路由的基本方法
  • 了解了如何添加HITL审查点

准备好进入下一章:02-spatial-intelligence(空间智能)