# 实践案例01:用LangGraph构建空间决策工作流 ## 目标 通过本实践,你将: 1. 理解状态驱动的Agent设计 2. 实现一个简单的空间决策工作流 3. 添加Human-in-the-Loop审查点 4. 处理工作流中的错误和重试 --- ## 背景知识 ### 什么是LangGraph LangGraph是构建**有状态**的多Agent应用的框架: ``` 核心概念: 1. State(状态): 在节点间传递的数据 2. Node(节点): 处理状态的函数 3. Edge(边): 节点之间的连接 4. Graph(图): 节点和边组成的完整工作流 ``` ### 为什么用LangGraph - **状态管理**: 自动管理工作流状态 - **可视化**: 可以绘制和查看工作流图 - **持久化**: 支持中断和恢复 - **条件路由**: 基于状态动态选择路径 --- ## 实践步骤 ### 步骤1:安装依赖 ```bash pip install langgraph langchain-core langchain-anthropic ``` ### 步骤2:定义状态 ```python 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:定义节点 ```python 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:定义路由 ```python def should_review(state: EcologicalAnalysisState) -> str: """决定是否需要审查""" sources = state["intermediate_results"].get("sources", []) if len(sources) > 2: # 源地数量多时需要审查 return "review" return "continue" ``` ### 步骤5:构建图 ```python 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:运行工作流 ```python 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. 添加错误处理 ```python 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. 添加检查点(持久化) ```python 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. 可视化工作流 ```python from IPython.display import Image, display # 生成图 app = build_workflow() display(Image(app.get_graph().draw_mermaid_png())) ``` --- ## 完整代码示例 ```python """ 完整的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(空间智能)**